├── .gitignore ├── LICENSE ├── README.md ├── examples ├── AutoConnect │ └── AutoConnect.ino ├── AutoConnectWithFSParameters │ └── AutoConnectWithFSParameters.ino ├── AutoConnectWithFSParametersAndCustomIP │ └── AutoConnectWithFSParametersAndCustomIP.ino ├── AutoConnectWithFeedback │ └── AutoConnectWithFeedback.ino ├── AutoConnectWithFeedbackLED │ └── AutoConnectWithFeedbackLED.ino ├── AutoConnectWithReset │ └── AutoConnectWithReset.ino ├── AutoConnectWithStaticIP │ └── AutoConnectWithStaticIP.ino ├── AutoConnectWithTimeout │ └── AutoConnectWithTimeout.ino ├── ModelessConnect │ └── ModelessConnect.ino ├── ModelessWithInterrupts │ └── ModelessWithInterrupts.ino └── OnDemandConfigPortal │ └── OnDemandConfigPortal.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── ESPAsyncWiFiManager.cpp └── ESPAsyncWiFiManager.h /.gitignore: -------------------------------------------------------------------------------- 1 | include 2 | .pio 3 | .vscode 4 | platformio.ini 5 | *.code-workspace 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 tzapu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AsyncWiFiManager 2 | ESP8266 Async WiFi Connection manager with fallback web configuration portal 3 | 4 | [![Build Status](https://travis-ci.org/tzapu/WiFiManager.svg?branch=master)](https://travis-ci.org/tzapu/WiFiManager) 5 | 6 | The configuration portal is of the captive variety, so on various devices it will present the configuration dialogue as soon as you connect to the created access point. 7 | 8 | First attempt at a library. Lots more changes and fixes to do. Contributions are welcome. 9 | 10 | #### This works with the ESP8266 Arduino platform with a recent stable release(2.0.0 or newer) https://github.com/esp8266/Arduino 11 | 12 | ## Contents 13 | - [How it works](#how-it-works) 14 | - [Wishlist](#wishlist) 15 | - [Quick start](#quick-start) 16 | - Installing 17 | - [Through Library Manager](#install-through-library-manager) 18 | - [From Github](#checkout-from-github) 19 | - [Using](#using) 20 | - [Documentation](#documentation) 21 | - [Access Point Password](#password-protect-the-configuration-access-point) 22 | - [Callbacks](#callbacks) 23 | - [Configuration Portal Timeout](#configuration-portal-timeout) 24 | - [On Demand Configuration](#on-demand-configuration-portal) 25 | - [Custom Parameters](#custom-parameters) 26 | - [Custom IP Configuration](#custom-ip-configuration) 27 | - [Filter Low Quality Networks](#filter-networks) 28 | - [Debug Output](#debug) 29 | - [Troubleshooting](#troubleshooting) 30 | - [Releases](#releases) 31 | - [Contributors](#contributions-and-thanks) 32 | 33 | 34 | ## How It Works 35 | - when your ESP starts up, it sets it up in Station mode and tries to connect to a previously saved Access Point 36 | - if this is unsuccessful (or no previous network saved) it moves the ESP into Access Point mode and spins up a DNS and WebServer (default ip 192.168.4.1) 37 | - using any wifi enabled device with a browser (computer, phone, tablet) connect to the newly created Access Point 38 | - because of the Captive Portal and the DNS server you will either get a 'Join to network' type of popup or get any domain you try to access redirected to the configuration portal 39 | - choose one of the access points scanned, enter password, click save 40 | - ESP will try to connect. If successful, it relinquishes control back to your app. If not, reconnect to AP and reconfigure. 41 | 42 | ## How It Looks 43 | ![ESP8266 WiFi Captive Portal Homepage](http://i.imgur.com/YPvW9eql.png) ![ESP8266 WiFi Captive Portal Configuration](http://i.imgur.com/oicWJ4gl.png) 44 | 45 | ## Wishlist 46 | - ~~remove dependency on EEPROM library~~ 47 | - ~~move HTML Strings to PROGMEM~~ 48 | - ~~cleanup and streamline code~~ (although this is ongoing) 49 | - if timeout is set, extend it when a page is fetched in AP mode 50 | - ~~add ability to configure more parameters than ssid/password~~ 51 | - ~~maybe allow setting ip of ESP after reboot~~ 52 | - ~~add to Arduino Library Manager~~ 53 | - ~~add to PlatformIO~~ 54 | - add multiple sets of network credentials 55 | - ~~allow users to customize CSS~~ 56 | 57 | ## Quick Start 58 | 59 | ### Installing 60 | You can either install through the Arduino Library Manager or checkout the latest changes or a release from github 61 | 62 | #### Install through Library Manager 63 | __Currently version 0.8+ works with release 2.0.0 or newer of the [ESP8266 core for Arduino](https://github.com/esp8266/Arduino)__ 64 | - in Arduino IDE got to Sketch/Include Library/Manage Libraries 65 | ![Manage Libraries](http://i.imgur.com/9BkEBkR.png) 66 | 67 | - search for WiFiManager 68 | ![WiFiManager package](http://i.imgur.com/18yIai8.png) 69 | 70 | - click Install and start [using it](#using) 71 | 72 | #### Checkout from github 73 | __Github version works with release 2.0.0 or newer of the [ESP8266 core for Arduino](https://github.com/esp8266/Arduino)__ 74 | - Checkout library to your Arduino libraries folder 75 | 76 | ### Using 77 | - Include in your sketch 78 | ```cpp 79 | #if defined(ESP8266) 80 | #include //https://github.com/esp8266/Arduino 81 | #else 82 | #include 83 | #endif 84 | 85 | #include //Local WebServer used to serve the configuration portal 86 | #include //https://github.com/tzapu/WiFiManager WiFi Configuration Magic 87 | ``` 88 | 89 | - Initialize library, in your setup function add 90 | ```cpp 91 | AsyncWebServer server(80); 92 | DNSServer dns; 93 | ``` 94 | 95 | - Also in the setup function add 96 | ```cpp 97 | //first parameter is name of access point, second is the password 98 | AsyncWiFiManager wifiManager(&server,&dns); 99 | 100 | wifiManager.autoConnect("AP-NAME", "AP-PASSWORD"); 101 | ``` 102 | if you just want an unsecured access point 103 | ```cpp 104 | wifiManager.autoConnect("AP-NAME"); 105 | ``` 106 | or if you want to use and auto generated name from 'ESP' and the esp's Chip ID use 107 | ```cpp 108 | wifiManager.autoConnect(); 109 | ``` 110 | 111 | After you write your sketch and start the ESP, it will try to connect to WiFi. If it fails it starts in Access Point mode. 112 | While in AP mode, connect to it then open a browser to the gateway IP, default 192.168.4.1, configure wifi, save and it should reboot and connect. 113 | 114 | Also see [examples](https://github.com/alanswx/ESPAsyncWiFiManager/tree/master/examples). 115 | 116 | ## Documentation 117 | 118 | #### Password protect the configuration Access Point 119 | You can and should password protect the configuration access point. Simply add the password as a second parameter to `autoConnect`. 120 | A short password seems to have unpredictable results so use one that's around 8 characters or more in length. 121 | The guidelines are that a wifi password must consist of 8 to 63 ASCII-encoded characters in the range of 32 to 126 (decimal) 122 | ```cpp 123 | wifiManager.autoConnect("AutoConnectAP", "password") 124 | ``` 125 | 126 | #### Callbacks 127 | ##### Enter Config mode 128 | Use this if you need to do something when your device enters configuration mode on failed WiFi connection attempt. 129 | Before `autoConnect()` 130 | ```cpp 131 | wifiManager.setAPCallback(configModeCallback); 132 | ``` 133 | `configModeCallback` declaration and example 134 | ```cpp 135 | void configModeCallback (WiFiManager *myWiFiManager) { 136 | Serial.println("Entered config mode"); 137 | Serial.println(WiFi.softAPIP()); 138 | 139 | Serial.println(myWiFiManager->getConfigPortalSSID()); 140 | } 141 | ``` 142 | 143 | ##### Save settings 144 | This gets called when custom parameters have been set **AND** a connection has been established. Use it to set a flag, so when all the configuration finishes, you can save the extra parameters somewhere. 145 | 146 | See [AutoConnectWithFSParameters Example](https://github.com/alanswx/ESPAsyncWiFiManager/tree/master/examples/AutoConnectWithFSParameters). 147 | ```cpp 148 | wifiManager.setSaveConfigCallback(saveConfigCallback); 149 | ``` 150 | `saveConfigCallback` declaration and example 151 | ```cpp 152 | //flag for saving data 153 | bool shouldSaveConfig = false; 154 | 155 | //callback notifying us of the need to save config 156 | void saveConfigCallback () { 157 | Serial.println("Should save config"); 158 | shouldSaveConfig = true; 159 | } 160 | ``` 161 | 162 | #### Configuration Portal Timeout 163 | If you need to set a timeout so the ESP doesn't hang waiting to be configured, for instance after a power failure, you can add 164 | ```cpp 165 | wifiManager.setConfigPortalTimeout(180); 166 | ``` 167 | which will wait 3 minutes (180 seconds). When the time passes, the autoConnect function will return, no matter the outcome. 168 | Check for connection and if it's still not established do whatever is needed (on some modules I restart them to retry, on others I enter deep sleep) 169 | 170 | #### On Demand Configuration Portal 171 | If you would rather start the configuration portal on demand rather than automatically on a failed connection attempt, then this is for you. 172 | 173 | Instead of calling `autoConnect()` which does all the connecting and failover configuration portal setup for you, you need to use `startConfigPortal()`. __Do not use BOTH.__ 174 | 175 | Example usage 176 | ```cpp 177 | void loop() { 178 | // is configuration portal requested? 179 | if ( digitalRead(TRIGGER_PIN) == LOW ) { 180 | WiFiManager wifiManager; 181 | wifiManager.startConfigPortal("OnDemandAP"); 182 | Serial.println("connected...yeey :)"); 183 | } 184 | } 185 | ``` 186 | See example for a more complex version. [OnDemandConfigPortal](https://github.com/alanswx/ESPAsyncWiFiManager/tree/master/examples/OnDemandConfigPortal) 187 | 188 | #### Custom Parameters 189 | You can use WiFiManager to collect more parameters than just SSID and password. 190 | This could be helpful for configuring stuff like MQTT host and port, [blynk](http://www.blynk.cc) or [emoncms](http://emoncms.org) tokens, just to name a few. 191 | **You are responsible for saving and loading these custom values.** The library just collects and displays the data for you as a convenience. 192 | Usage scenario would be: 193 | - load values from somewhere (EEPROM/FS) or generate some defaults 194 | - add the custom parameters to WiFiManager using 195 | ```cpp 196 | // id/name, placeholder/prompt, default, length 197 | AsyncWiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40); 198 | wifiManager.addParameter(&custom_mqtt_server); 199 | 200 | ``` 201 | - if connection to AP fails, configuration portal starts and you can set /change the values (or use on demand configuration portal) 202 | - once configuration is done and connection is established [save config callback]() is called 203 | - once WiFiManager returns control to your application, read and save the new values using the `AsyncWiFiManagerParameter` object. 204 | ```cpp 205 | mqtt_server = custom_mqtt_server.getValue(); 206 | ``` 207 | This feature is a lot more involved than all the others, so here are some examples to fully show how it is done. 208 | You should also take a look at adding custom HTML to your form. 209 | 210 | - Save and load custom parameters to file system in json form [AutoConnectWithFSParameters](https://github.com/alanswx/ESPAsyncWiFiManager/tree/master/examples/AutoConnectWithFSParameters) 211 | - *Save and load custom parameters to EEPROM* (not done yet) 212 | 213 | #### Custom IP Configuration 214 | You can set a custom IP for both AP (access point, config mode) and STA (station mode, client mode, normal project state) 215 | 216 | ##### Custom Access Point IP Configuration 217 | This will set your captive portal to a specific IP should you need/want such a feature. Add the following snippet before `autoConnect()` 218 | ```cpp 219 | //set custom ip for portal 220 | wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); 221 | ``` 222 | 223 | ##### Custom Station (client) Static IP Configuration 224 | This will make use the specified IP configuration instead of using DHCP in station mode. 225 | ```cpp 226 | wifiManager.setSTAStaticIPConfig(IPAddress(192,168,0,99), IPAddress(192,168,0,1), IPAddress(255,255,255,0)); 227 | ``` 228 | There are a couple of examples in the examples folder that show you how to set a static IP and even how to configure it through the web configuration portal. 229 | 230 | #### Custom HTML, CSS, Javascript 231 | There are various ways in which you can inject custom HTML, CSS or Javascript into the configuration portal. 232 | The options are: 233 | - inject custom head element 234 | You can use this to any html bit to the head of the configuration portal. If you add a `"); 237 | ``` 238 | - inject a custom bit of html in the configuration form 239 | ```cpp 240 | WiFiManagerParameter custom_text("

This is just a text paragraph

"); 241 | wifiManager.addParameter(&custom_text); 242 | ``` 243 | - inject a custom bit of html in a configuration form element 244 | Just add the bit you want added as the last parameter to the custom parameter constructor. 245 | ```cpp 246 | WiFiManagerParameter custom_mqtt_server("server", "mqtt server", "iot.eclipse", 40, " readonly"); 247 | ``` 248 | 249 | #### Filter Networks 250 | You can filter networks based on signal quality and show/hide duplicate networks. 251 | 252 | - If you would like to filter low signal quality networks you can tell WiFiManager to not show networks below an arbitrary quality %; 253 | ```cpp 254 | wifiManager.setMinimumSignalQuality(10); 255 | ``` 256 | will not show networks under 10% signal quality. If you omit the parameter it defaults to 8%; 257 | 258 | - You can also remove or show duplicate networks (default is remove). 259 | Use this function to show (or hide) all networks. 260 | ```cpp 261 | wifiManager.setRemoveDuplicateAPs(false); 262 | ``` 263 | 264 | #### Debug 265 | Debug is enabled by default on Serial. To disable add before autoConnect 266 | ```cpp 267 | wifiManager.setDebugOutput(false); 268 | ``` 269 | 270 | 271 | -------------------------------------------------------------------------------- /examples/AutoConnect/AutoConnect.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | AsyncWebServer server(80); 12 | DNSServer dns; 13 | 14 | void setup() { 15 | // put your setup code here, to run once: 16 | Serial.begin(115200); 17 | 18 | //WiFiManager 19 | //Local intialization. Once its business is done, there is no need to keep it around 20 | AsyncWiFiManager wifiManager(&server,&dns); 21 | //reset saved settings 22 | //wifiManager.resetSettings(); 23 | //set custom ip for portal 24 | //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); 25 | //fetches ssid and pass from eeprom and tries to connect 26 | //if it does not connect it starts an access point with the specified name 27 | //here "AutoConnectAP" 28 | //and goes into a blocking loop awaiting configuration 29 | wifiManager.autoConnect("AutoConnectAP"); 30 | //or use this for auto generated name ESP + ChipID 31 | //wifiManager.autoConnect(); 32 | //if you get here you have connected to the WiFi 33 | Serial.println("connected...yeey :)"); 34 | } 35 | 36 | void loop() { 37 | // put your main code here, to run repeatedly: 38 | } 39 | -------------------------------------------------------------------------------- /examples/AutoConnectWithFSParameters/AutoConnectWithFSParameters.ino: -------------------------------------------------------------------------------- 1 | #include //this needs to be first, or it all crashes and burns... 2 | 3 | #if defined(ESP8266) 4 | #include //https://github.com/esp8266/Arduino 5 | #else 6 | #include 7 | #endif 8 | 9 | //needed for library 10 | #include 11 | #include //https://github.com/tzapu/WiFiManager 12 | 13 | #include //https://github.com/bblanchon/ArduinoJson 14 | 15 | //define your default values here, if there are different values in config.json, they are overwritten. 16 | char mqtt_server[40]; 17 | char mqtt_port[6] = "8080"; 18 | char blynk_token[34] = "YOUR_BLYNK_TOKEN"; 19 | 20 | //flag for saving data 21 | bool shouldSaveConfig = false; 22 | 23 | //callback notifying us of the need to save config 24 | void saveConfigCallback () { 25 | Serial.println("Should save config"); 26 | shouldSaveConfig = true; 27 | } 28 | 29 | AsyncWebServer server(80); 30 | DNSServer dns; 31 | 32 | 33 | void setup() { 34 | // put your setup code here, to run once: 35 | Serial.begin(115200); 36 | Serial.println(); 37 | 38 | //clean FS, for testing 39 | //SPIFFS.format(); 40 | 41 | //read configuration from FS json 42 | Serial.println("mounting FS..."); 43 | 44 | if (SPIFFS.begin()) { 45 | Serial.println("mounted file system"); 46 | if (SPIFFS.exists("/config.json")) { 47 | //file exists, reading and loading 48 | Serial.println("reading config file"); 49 | File configFile = SPIFFS.open("/config.json", "r"); 50 | if (configFile) { 51 | Serial.println("opened config file"); 52 | size_t size = configFile.size(); 53 | // Allocate a buffer to store contents of the file. 54 | std::unique_ptr buf(new char[size]); 55 | 56 | configFile.readBytes(buf.get(), size); 57 | DynamicJsonBuffer jsonBuffer; 58 | JsonObject& json = jsonBuffer.parseObject(buf.get()); 59 | json.printTo(Serial); 60 | if (json.success()) { 61 | Serial.println("\nparsed json"); 62 | 63 | strcpy(mqtt_server, json["mqtt_server"]); 64 | strcpy(mqtt_port, json["mqtt_port"]); 65 | strcpy(blynk_token, json["blynk_token"]); 66 | 67 | } else { 68 | Serial.println("failed to load json config"); 69 | } 70 | } 71 | } 72 | } else { 73 | Serial.println("failed to mount FS"); 74 | } 75 | //end read 76 | 77 | 78 | 79 | // The extra parameters to be configured (can be either global or just in the setup) 80 | // After connecting, parameter.getValue() will get you the configured value 81 | // id/name placeholder/prompt default length 82 | AsyncWiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40); 83 | AsyncWiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5); 84 | AsyncWiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 32); 85 | 86 | //WiFiManager 87 | //Local intialization. Once its business is done, there is no need to keep it around 88 | AsyncWiFiManager wifiManager(&server,&dns); 89 | 90 | //set config save notify callback 91 | wifiManager.setSaveConfigCallback(saveConfigCallback); 92 | 93 | //set static ip 94 | wifiManager.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); 95 | 96 | //add all your parameters here 97 | wifiManager.addParameter(&custom_mqtt_server); 98 | wifiManager.addParameter(&custom_mqtt_port); 99 | wifiManager.addParameter(&custom_blynk_token); 100 | 101 | //reset settings - for testing 102 | //wifiManager.resetSettings(); 103 | 104 | //set minimu quality of signal so it ignores AP's under that quality 105 | //defaults to 8% 106 | //wifiManager.setMinimumSignalQuality(); 107 | 108 | //sets timeout until configuration portal gets turned off 109 | //useful to make it all retry or go to sleep 110 | //in seconds 111 | //wifiManager.setTimeout(120); 112 | 113 | //fetches ssid and pass and tries to connect 114 | //if it does not connect it starts an access point with the specified name 115 | //here "AutoConnectAP" 116 | //and goes into a blocking loop awaiting configuration 117 | if (!wifiManager.autoConnect("AutoConnectAP", "password")) { 118 | Serial.println("failed to connect and hit timeout"); 119 | delay(3000); 120 | //reset and try again, or maybe put it to deep sleep 121 | ESP.reset(); 122 | delay(5000); 123 | } 124 | 125 | //if you get here you have connected to the WiFi 126 | Serial.println("connected...yeey :)"); 127 | 128 | //read updated parameters 129 | strcpy(mqtt_server, custom_mqtt_server.getValue()); 130 | strcpy(mqtt_port, custom_mqtt_port.getValue()); 131 | strcpy(blynk_token, custom_blynk_token.getValue()); 132 | 133 | //save the custom parameters to FS 134 | if (shouldSaveConfig) { 135 | Serial.println("saving config"); 136 | DynamicJsonBuffer jsonBuffer; 137 | JsonObject& json = jsonBuffer.createObject(); 138 | json["mqtt_server"] = mqtt_server; 139 | json["mqtt_port"] = mqtt_port; 140 | json["blynk_token"] = blynk_token; 141 | 142 | File configFile = SPIFFS.open("/config.json", "w"); 143 | if (!configFile) { 144 | Serial.println("failed to open config file for writing"); 145 | } 146 | 147 | json.printTo(Serial); 148 | json.printTo(configFile); 149 | configFile.close(); 150 | //end save 151 | } 152 | 153 | Serial.println("local ip"); 154 | Serial.println(WiFi.localIP()); 155 | 156 | } 157 | 158 | void loop() { 159 | // put your main code here, to run repeatedly: 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /examples/AutoConnectWithFSParametersAndCustomIP/AutoConnectWithFSParametersAndCustomIP.ino: -------------------------------------------------------------------------------- 1 | #include //this needs to be first, or it all crashes and burns... 2 | 3 | #if defined(ESP8266) 4 | #include //https://github.com/esp8266/Arduino 5 | #else 6 | #include 7 | #endif 8 | 9 | //needed for library 10 | #include 11 | #include //https://github.com/tzapu/WiFiManager 12 | 13 | #include //https://github.com/bblanchon/ArduinoJson 14 | 15 | //define your default values here, if there are different values in config.json, they are overwritten. 16 | //length should be max size + 1 17 | char mqtt_server[40]; 18 | char mqtt_port[6] = "8080"; 19 | char blynk_token[33] = "YOUR_BLYNK_TOKEN"; 20 | //default custom static IP 21 | char static_ip[16] = "10.0.1.56"; 22 | char static_gw[16] = "10.0.1.1"; 23 | char static_sn[16] = "255.255.255.0"; 24 | 25 | //flag for saving data 26 | bool shouldSaveConfig = false; 27 | 28 | //callback notifying us of the need to save config 29 | void saveConfigCallback () { 30 | Serial.println("Should save config"); 31 | shouldSaveConfig = true; 32 | } 33 | 34 | AsyncWebServer server(80); 35 | DNSServer dns; 36 | 37 | void setup() { 38 | // put your setup code here, to run once: 39 | Serial.begin(115200); 40 | Serial.println(); 41 | 42 | //clean FS, for testing 43 | //SPIFFS.format(); 44 | 45 | //read configuration from FS json 46 | Serial.println("mounting FS..."); 47 | 48 | if (SPIFFS.begin()) { 49 | Serial.println("mounted file system"); 50 | if (SPIFFS.exists("/config.json")) { 51 | //file exists, reading and loading 52 | Serial.println("reading config file"); 53 | File configFile = SPIFFS.open("/config.json", "r"); 54 | if (configFile) { 55 | Serial.println("opened config file"); 56 | size_t size = configFile.size(); 57 | // Allocate a buffer to store contents of the file. 58 | std::unique_ptr buf(new char[size]); 59 | 60 | configFile.readBytes(buf.get(), size); 61 | DynamicJsonBuffer jsonBuffer; 62 | JsonObject& json = jsonBuffer.parseObject(buf.get()); 63 | json.printTo(Serial); 64 | if (json.success()) { 65 | Serial.println("\nparsed json"); 66 | 67 | strcpy(mqtt_server, json["mqtt_server"]); 68 | strcpy(mqtt_port, json["mqtt_port"]); 69 | strcpy(blynk_token, json["blynk_token"]); 70 | 71 | if(json["ip"]) { 72 | Serial.println("setting custom ip from config"); 73 | //static_ip = json["ip"]; 74 | strcpy(static_ip, json["ip"]); 75 | strcpy(static_gw, json["gateway"]); 76 | strcpy(static_sn, json["subnet"]); 77 | //strcat(static_ip, json["ip"]); 78 | //static_gw = json["gateway"]; 79 | //static_sn = json["subnet"]; 80 | Serial.println(static_ip); 81 | /* Serial.println("converting ip"); 82 | IPAddress ip = ipFromCharArray(static_ip); 83 | Serial.println(ip);*/ 84 | } else { 85 | Serial.println("no custom ip in config"); 86 | } 87 | } else { 88 | Serial.println("failed to load json config"); 89 | } 90 | } 91 | } 92 | } else { 93 | Serial.println("failed to mount FS"); 94 | } 95 | //end read 96 | Serial.println(static_ip); 97 | Serial.println(blynk_token); 98 | Serial.println(mqtt_server); 99 | 100 | 101 | // The extra parameters to be configured (can be either global or just in the setup) 102 | // After connecting, parameter.getValue() will get you the configured value 103 | // id/name placeholder/prompt default length 104 | AsyncWiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40); 105 | AsyncWiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5); 106 | AsyncWiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34); 107 | 108 | //WiFiManager 109 | //Local intialization. Once its business is done, there is no need to keep it around 110 | AsyncWiFiManager wifiManager(&server,&dns); 111 | 112 | //set config save notify callback 113 | wifiManager.setSaveConfigCallback(saveConfigCallback); 114 | 115 | //set static ip 116 | IPAddress _ip,_gw,_sn; 117 | _ip.fromString(static_ip); 118 | _gw.fromString(static_gw); 119 | _sn.fromString(static_sn); 120 | 121 | wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn); 122 | 123 | //add all your parameters here 124 | wifiManager.addParameter(&custom_mqtt_server); 125 | wifiManager.addParameter(&custom_mqtt_port); 126 | wifiManager.addParameter(&custom_blynk_token); 127 | 128 | //reset settings - for testing 129 | //wifiManager.resetSettings(); 130 | 131 | //set minimu quality of signal so it ignores AP's under that quality 132 | //defaults to 8% 133 | wifiManager.setMinimumSignalQuality(); 134 | 135 | //sets timeout until configuration portal gets turned off 136 | //useful to make it all retry or go to sleep 137 | //in seconds 138 | //wifiManager.setTimeout(120); 139 | 140 | //fetches ssid and pass and tries to connect 141 | //if it does not connect it starts an access point with the specified name 142 | //here "AutoConnectAP" 143 | //and goes into a blocking loop awaiting configuration 144 | if (!wifiManager.autoConnect("AutoConnectAP", "password")) { 145 | Serial.println("failed to connect and hit timeout"); 146 | delay(3000); 147 | //reset and try again, or maybe put it to deep sleep 148 | ESP.reset(); 149 | delay(5000); 150 | } 151 | 152 | //if you get here you have connected to the WiFi 153 | Serial.println("connected...yeey :)"); 154 | 155 | //read updated parameters 156 | strcpy(mqtt_server, custom_mqtt_server.getValue()); 157 | strcpy(mqtt_port, custom_mqtt_port.getValue()); 158 | strcpy(blynk_token, custom_blynk_token.getValue()); 159 | 160 | //save the custom parameters to FS 161 | if (shouldSaveConfig) { 162 | Serial.println("saving config"); 163 | DynamicJsonBuffer jsonBuffer; 164 | JsonObject& json = jsonBuffer.createObject(); 165 | json["mqtt_server"] = mqtt_server; 166 | json["mqtt_port"] = mqtt_port; 167 | json["blynk_token"] = blynk_token; 168 | 169 | json["ip"] = WiFi.localIP().toString(); 170 | json["gateway"] = WiFi.gatewayIP().toString(); 171 | json["subnet"] = WiFi.subnetMask().toString(); 172 | 173 | File configFile = SPIFFS.open("/config.json", "w"); 174 | if (!configFile) { 175 | Serial.println("failed to open config file for writing"); 176 | } 177 | 178 | json.prettyPrintTo(Serial); 179 | json.printTo(configFile); 180 | configFile.close(); 181 | //end save 182 | } 183 | 184 | Serial.println("local ip"); 185 | Serial.println(WiFi.localIP()); 186 | Serial.println(WiFi.gatewayIP()); 187 | Serial.println(WiFi.subnetMask()); 188 | } 189 | 190 | void loop() { 191 | // put your main code here, to run repeatedly: 192 | 193 | 194 | } 195 | -------------------------------------------------------------------------------- /examples/AutoConnectWithFeedback/AutoConnectWithFeedback.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | void configModeCallback (AsyncWiFiManager *myWiFiManager) { 12 | Serial.println("Entered config mode"); 13 | Serial.println(WiFi.softAPIP()); 14 | //if you used auto generated SSID, print it 15 | Serial.println(myWiFiManager->getConfigPortalSSID()); 16 | } 17 | 18 | AsyncWebServer server(80); 19 | DNSServer dns; 20 | 21 | void setup() { 22 | // put your setup code here, to run once: 23 | Serial.begin(115200); 24 | 25 | //WiFiManager 26 | //Local intialization. Once its business is done, there is no need to keep it around 27 | AsyncWiFiManager wifiManager(&server,&dns); 28 | //reset settings - for testing 29 | //wifiManager.resetSettings(); 30 | 31 | //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode 32 | wifiManager.setAPCallback(configModeCallback); 33 | 34 | //fetches ssid and pass and tries to connect 35 | //if it does not connect it starts an access point with the specified name 36 | //here "AutoConnectAP" 37 | //and goes into a blocking loop awaiting configuration 38 | if(!wifiManager.autoConnect()) { 39 | Serial.println("failed to connect and hit timeout"); 40 | //reset and try again, or maybe put it to deep sleep 41 | ESP.reset(); 42 | delay(1000); 43 | } 44 | 45 | //if you get here you have connected to the WiFi 46 | Serial.println("connected...yeey :)"); 47 | 48 | } 49 | 50 | void loop() { 51 | // put your main code here, to run repeatedly: 52 | 53 | } 54 | -------------------------------------------------------------------------------- /examples/AutoConnectWithFeedbackLED/AutoConnectWithFeedbackLED.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | //for LED status 12 | #include 13 | Ticker ticker; 14 | 15 | void tick() 16 | { 17 | //toggle state 18 | int state = digitalRead(BUILTIN_LED); // get the current state of GPIO1 pin 19 | digitalWrite(BUILTIN_LED, !state); // set pin to the opposite state 20 | } 21 | 22 | //gets called when WiFiManager enters configuration mode 23 | void configModeCallback (AsyncWiFiManager *myWiFiManager) { 24 | Serial.println("Entered config mode"); 25 | Serial.println(WiFi.softAPIP()); 26 | //if you used auto generated SSID, print it 27 | Serial.println(myWiFiManager->getConfigPortalSSID()); 28 | //entered config mode, make led toggle faster 29 | ticker.attach(0.2, tick); 30 | } 31 | 32 | AsyncWebServer server(80); 33 | DNSServer dns; 34 | 35 | void setup() { 36 | // put your setup code here, to run once: 37 | Serial.begin(115200); 38 | 39 | //set led pin as output 40 | pinMode(BUILTIN_LED, OUTPUT); 41 | // start ticker with 0.5 because we start in AP mode and try to connect 42 | ticker.attach(0.6, tick); 43 | 44 | //WiFiManager 45 | //Local intialization. Once its business is done, there is no need to keep it around 46 | AsyncWiFiManager wifiManager(&server,&dns); 47 | //reset settings - for testing 48 | //wifiManager.resetSettings(); 49 | 50 | //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode 51 | wifiManager.setAPCallback(configModeCallback); 52 | 53 | //fetches ssid and pass and tries to connect 54 | //if it does not connect it starts an access point with the specified name 55 | //here "AutoConnectAP" 56 | //and goes into a blocking loop awaiting configuration 57 | if (!wifiManager.autoConnect()) { 58 | Serial.println("failed to connect and hit timeout"); 59 | //reset and try again, or maybe put it to deep sleep 60 | ESP.reset(); 61 | delay(1000); 62 | } 63 | 64 | //if you get here you have connected to the WiFi 65 | Serial.println("connected...yeey :)"); 66 | ticker.detach(); 67 | //keep LED on 68 | digitalWrite(BUILTIN_LED, LOW); 69 | } 70 | 71 | void loop() { 72 | // put your main code here, to run repeatedly: 73 | 74 | } 75 | -------------------------------------------------------------------------------- /examples/AutoConnectWithReset/AutoConnectWithReset.ino: -------------------------------------------------------------------------------- 1 | #include //this needs to be first, or it all crashes and burns... 2 | 3 | #if defined(ESP8266) 4 | #include //https://github.com/esp8266/Arduino 5 | #else 6 | #include 7 | #endif 8 | 9 | //needed for library 10 | #include 11 | #include //https://github.com/tzapu/WiFiManager 12 | 13 | AsyncWebServer server(80); 14 | DNSServer dns; 15 | 16 | void setup() { 17 | // put your setup code here, to run once: 18 | Serial.begin(115200); 19 | Serial.println(); 20 | 21 | //WiFiManager 22 | //Local intialization. Once its business is done, there is no need to keep it around 23 | AsyncWiFiManager wifiManager(&server,&dns); 24 | 25 | //exit after config instead of connecting 26 | wifiManager.setBreakAfterConfig(true); 27 | 28 | //reset settings - for testing 29 | //wifiManager.resetSettings(); 30 | 31 | 32 | //tries to connect to last known settings 33 | //if it does not connect it starts an access point with the specified name 34 | //here "AutoConnectAP" with password "password" 35 | //and goes into a blocking loop awaiting configuration 36 | if (!wifiManager.autoConnect("AutoConnectAP", "password")) { 37 | Serial.println("failed to connect, we should reset as see if it connects"); 38 | delay(3000); 39 | ESP.reset(); 40 | delay(5000); 41 | } 42 | 43 | //if you get here you have connected to the WiFi 44 | Serial.println("connected...yeey :)"); 45 | 46 | 47 | Serial.println("local ip"); 48 | Serial.println(WiFi.localIP()); 49 | } 50 | 51 | void loop() { 52 | // put your main code here, to run repeatedly: 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /examples/AutoConnectWithStaticIP/AutoConnectWithStaticIP.ino: -------------------------------------------------------------------------------- 1 | #include //this needs to be first, or it all crashes and burns... 2 | 3 | #if defined(ESP8266) 4 | #include //https://github.com/esp8266/Arduino 5 | #else 6 | #include 7 | #endif 8 | 9 | //needed for library 10 | #include 11 | #include //https://github.com/tzapu/WiFiManager 12 | 13 | AsyncWebServer server(80); 14 | DNSServer dns; 15 | 16 | 17 | /************************************************************************************** 18 | * this example shows how to set a static IP configuration for the ESP 19 | * although the IP shows in the config portal, the changes will revert 20 | * to the IP set in the source file. 21 | * if you want the ability to configure and persist the new IP configuration 22 | * look at the FS examples, which save the config to file 23 | *************************************************************************************/ 24 | 25 | 26 | 27 | //default custom static IP 28 | //char static_ip[16] = "10.0.1.59"; 29 | //char static_gw[16] = "10.0.1.1"; 30 | //char static_sn[16] = "255.255.255.0"; 31 | 32 | void setup() { 33 | // put your setup code here, to run once: 34 | Serial.begin(115200); 35 | Serial.println(); 36 | 37 | //WiFiManager 38 | //Local intialization. Once its business is done, there is no need to keep it around 39 | AsyncWiFiManager wifiManager(&server,&dns); 40 | 41 | //reset settings - for testing 42 | //wifiManager.resetSettings(); 43 | 44 | //set static ip 45 | //the commented bit only works for ESP8266 core 2.1.0 or newer 46 | /*IPAddress _ip,_gw,_sn; 47 | _ip.fromString(static_ip); 48 | _gw.fromString(static_gw); 49 | _sn.fromString(static_sn); 50 | */ 51 | IPAddress _ip = IPAddress(10, 0, 1, 78); 52 | IPAddress _gw = IPAddress(10, 0, 1, 1); 53 | IPAddress _sn = IPAddress(255, 255, 255, 0); 54 | 55 | wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn); 56 | 57 | 58 | //tries to connect to last known settings 59 | //if it does not connect it starts an access point with the specified name 60 | //here "AutoConnectAP" with password "password" 61 | //and goes into a blocking loop awaiting configuration 62 | if (!wifiManager.autoConnect("AutoConnectAP", "password")) { 63 | Serial.println("failed to connect, we should reset as see if it connects"); 64 | delay(3000); 65 | ESP.reset(); 66 | delay(5000); 67 | } 68 | 69 | //if you get here you have connected to the WiFi 70 | Serial.println("connected...yeey :)"); 71 | 72 | 73 | Serial.println("local ip"); 74 | Serial.println(WiFi.localIP()); 75 | } 76 | 77 | void loop() { 78 | // put your main code here, to run repeatedly: 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /examples/AutoConnectWithTimeout/AutoConnectWithTimeout.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | AsyncWebServer server(80); 12 | DNSServer dns; 13 | 14 | 15 | 16 | void setup() { 17 | // put your setup code here, to run once: 18 | Serial.begin(115200); 19 | 20 | //WiFiManager 21 | //Local intialization. Once its business is done, there is no need to keep it around 22 | AsyncWiFiManager wifiManager(&server,&dns); 23 | //reset settings - for testing 24 | //wifiManager.resetSettings(); 25 | 26 | //sets timeout until configuration portal gets turned off 27 | //useful to make it all retry or go to sleep 28 | //in seconds 29 | wifiManager.setTimeout(180); 30 | 31 | //fetches ssid and pass and tries to connect 32 | //if it does not connect it starts an access point with the specified name 33 | //here "AutoConnectAP" 34 | //and goes into a blocking loop awaiting configuration 35 | if(!wifiManager.autoConnect("AutoConnectAP")) { 36 | Serial.println("failed to connect and hit timeout"); 37 | delay(3000); 38 | //reset and try again, or maybe put it to deep sleep 39 | ESP.reset(); 40 | delay(5000); 41 | } 42 | 43 | //if you get here you have connected to the WiFi 44 | Serial.println("connected...yeey :)"); 45 | 46 | } 47 | 48 | void loop() { 49 | // put your main code here, to run repeatedly: 50 | 51 | } 52 | -------------------------------------------------------------------------------- /examples/ModelessConnect/ModelessConnect.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | AsyncWebServer server(80); 12 | DNSServer dns; 13 | AsyncWiFiManager wifiManager(&server,&dns); 14 | 15 | void setup() { 16 | // put your setup code here, to run once: 17 | Serial.begin(115200); 18 | 19 | //WiFiManager 20 | //Local intialization. Once its business is done, there is no need to keep it around 21 | //reset saved settings 22 | //wifiManager.resetSettings(); 23 | 24 | //set custom ip for portal 25 | //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); 26 | 27 | //fetches ssid and pass from eeprom and tries to connect 28 | //if it does not connect it starts an access point with the specified name 29 | //here "AutoConnectAP" 30 | //and goes into a blocking loop awaiting configuration 31 | //wifiManager.autoConnect("AutoConnectAP"); 32 | //or use this for auto generated name ESP + ChipID 33 | //wifiManager.autoConnect(); 34 | wifiManager.startConfigPortalModeless("ModelessAP", "Password"); 35 | 36 | 37 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ 38 | request->send(200, "text/plain", "Hello World"); 39 | }); 40 | 41 | } 42 | 43 | void loop() { 44 | // put your main code here, to run repeatedly: 45 | wifiManager.loop(); 46 | } 47 | -------------------------------------------------------------------------------- /examples/ModelessWithInterrupts/ModelessWithInterrupts.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | AsyncWebServer server(80); 12 | DNSServer dns; 13 | AsyncWiFiManager wifiManager(&server,&dns); 14 | 15 | volatile boolean guard = true; 16 | const uint32_t callCycleCount = ESP.getCpuFreqMHz() * 1024 / 8; 17 | 18 | /* 19 | * If some other code was in the process of calling a method in 20 | * WiFi, ESP or EEPROM, and we do too, there is a very good chance 21 | * a reset will happen. 22 | */ 23 | void ICACHE_RAM_ATTR interruptFunction() { 24 | /* 25 | * This is equivalent to: 26 | * timer0_write(ESP.getCycleCount() + ESP.getCpuFreqMHz() * 1024 / 8) 27 | * But that could clash with the main thread code. 28 | */ 29 | uint32_t ccount; 30 | __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount)); 31 | 32 | timer0_write(ccount + callCycleCount); 33 | 34 | if (!guard) { 35 | // An example call that would cause a reset if it happens 36 | // to be called while AsyncWiFiManager is also calling 37 | // methods that modify flash. 38 | ESP.getChipId(); 39 | } 40 | } 41 | 42 | void setup() { 43 | // put your setup code here, to run once: 44 | Serial.begin(115200); 45 | 46 | //WiFiManager 47 | //Local intialization. Once its business is done, there is no need to keep it around 48 | //reset saved settings 49 | //wifiManager.resetSettings(); 50 | 51 | //set custom ip for portal 52 | //wifiManager.setAPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); 53 | 54 | //fetches ssid and pass from eeprom and tries to connect 55 | //if it does not connect it starts an access point with the specified name 56 | //here "AutoConnectAP" 57 | //and goes into a blocking loop awaiting configuration 58 | //wifiManager.autoConnect("AutoConnectAP"); 59 | //or use this for auto generated name ESP + ChipID 60 | //wifiManager.autoConnect(); 61 | wifiManager.startConfigPortalModeless("ModelessAP", "Password"); 62 | 63 | 64 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ 65 | request->send(200, "text/plain", "Hello World"); 66 | }); 67 | 68 | // Set up some code that could cause a reset. 69 | guard = true; 70 | timer0_isr_init(); 71 | timer0_attachInterrupt(interruptFunction); 72 | timer0_write(ESP.getCycleCount() + ESP.getCpuFreqMHz() * 1024); // Wait 2 seconds before displaying 73 | } 74 | 75 | void loop() { 76 | // put your main code here, to run repeatedly: 77 | // This can be called with guarding interrupt routines - so 78 | // interrupt routines can interrupt it! 79 | wifiManager.safeLoop(); 80 | 81 | guard = true; 82 | // This call can not be interrupted without possibly causing a reset. 83 | wifiManager.criticalLoop(); 84 | guard = false; 85 | } 86 | -------------------------------------------------------------------------------- /examples/OnDemandConfigPortal/OnDemandConfigPortal.ino: -------------------------------------------------------------------------------- 1 | #if defined(ESP8266) 2 | #include //https://github.com/esp8266/Arduino 3 | #else 4 | #include 5 | #endif 6 | 7 | //needed for library 8 | #include 9 | #include //https://github.com/tzapu/WiFiManager 10 | 11 | 12 | AsyncWebServer server(80); 13 | DNSServer dns; 14 | 15 | // select wich pin will trigger the configuraton portal when set to LOW 16 | // ESP-01 users please note: the only pins available (0 and 2), are shared 17 | // with the bootloader, so always set them HIGH at power-up 18 | #define TRIGGER_PIN 0 19 | 20 | 21 | void setup() { 22 | // put your setup code here, to run once: 23 | Serial.begin(115200); 24 | Serial.println("\n Starting"); 25 | 26 | pinMode(TRIGGER_PIN, INPUT); 27 | } 28 | 29 | 30 | void loop() { 31 | // is configuration portal requested? 32 | if ( digitalRead(TRIGGER_PIN) == LOW ) { 33 | //WiFiManager 34 | //Local intialization. Once its business is done, there is no need to keep it around 35 | AsyncWiFiManager wifiManager(&server,&dns); 36 | 37 | //reset settings - for testing 38 | //wifiManager.resetSettings(); 39 | 40 | //sets timeout until configuration portal gets turned off 41 | //useful to make it all retry or go to sleep 42 | //in seconds 43 | //wifiManager.setTimeout(120); 44 | 45 | //it starts an access point with the specified name 46 | //here "AutoConnectAP" 47 | //and goes into a blocking loop awaiting configuration 48 | 49 | //WITHOUT THIS THE AP DOES NOT SEEM TO WORK PROPERLY WITH SDK 1.5 , update to at least 1.5.1 50 | //WiFi.mode(WIFI_STA); 51 | 52 | if (!wifiManager.startConfigPortal("OnDemandAP")) { 53 | Serial.println("failed to connect and hit timeout"); 54 | delay(3000); 55 | //reset and try again, or maybe put it to deep sleep 56 | ESP.reset(); 57 | delay(5000); 58 | } 59 | 60 | //if you get here you have connected to the WiFi 61 | Serial.println("connected...yeey :)"); 62 | } 63 | 64 | 65 | // put your main code here, to run repeatedly: 66 | 67 | } 68 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For WifiManager 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | AsyncWiFiManager KEYWORD1 10 | AsyncWiFiManagerParameter KEYWORD1 11 | 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | autoConnect KEYWORD2 17 | getConfigPortalSSID KEYWORD2 18 | resetSettings KEYWORD2 19 | setConfigPortalTimeout KEYWORD2 20 | setConnectTimeout KEYWORD2 21 | setDebugOutput KEYWORD2 22 | setMinimumSignalQuality KEYWORD2 23 | setAPStaticIPConfig KEYWORD2 24 | setSTAStaticIPConfig KEYWORD2 25 | setAPCallback KEYWORD2 26 | setSaveConfigCallback KEYWORD2 27 | addParameter KEYWORD2 28 | getID KEYWORD2 29 | getValue KEYWORD2 30 | getPlaceholder KEYWORD2 31 | getValueLength KEYWORD2 32 | 33 | ####################################### 34 | # Constants (LITERAL1) 35 | ####################################### 36 | 37 | # LITERAL1 38 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESPAsyncWiFiManager", 3 | "keywords": "wifi, wi-fi", 4 | "description": "ESP8266 Async WiFi Connection manager with fallback web configuration portal", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/alanswx/ESPAsyncWiFiManager.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": ["espressif8266", "espressif32"], 12 | "version": "0.31" 13 | } 14 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP Async WiFi Manager 2 | version=0.31 3 | author=alanswx 4 | maintainer=alanswx 5 | sentence=ESP8266 and ESP32 Async WiFi Connection manager with fallback web configuration portal 6 | paragraph=Library for configuring ESP8266 and ESP32 modules WiFi credentials at runtime. 7 | category=Communication 8 | url=https://github.com/alanswx/ESPAsyncWiFiManager.git 9 | architectures=esp8266,esp32 10 | -------------------------------------------------------------------------------- /src/ESPAsyncWiFiManager.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************** 2 | AsyncWiFiManager is a library for the ESP8266/Arduino platform 3 | (https://github.com/esp8266/Arduino) to enable easy 4 | configuration and reconfiguration of WiFi credentials using a Captive Portal 5 | inspired by: 6 | http://www.esp8266.com/viewtopic.php?f=29&t=2520 7 | https://github.com/chriscook8/esp-arduino-apboot 8 | https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced 9 | Built by AlexT https://github.com/tzapu 10 | Ported to Async Web Server by https://github.com/alanswx 11 | Licensed under MIT license 12 | **************************************************************/ 13 | 14 | #include "ESPAsyncWiFiManager.h" 15 | 16 | AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *custom) 17 | { 18 | _id = NULL; 19 | _placeholder = NULL; 20 | _length = 0; 21 | _value = NULL; 22 | 23 | _customHTML = custom; 24 | } 25 | 26 | AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id, 27 | const char *placeholder, 28 | const char *defaultValue, 29 | unsigned int length) 30 | { 31 | init(id, placeholder, defaultValue, length, ""); 32 | } 33 | 34 | AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id, 35 | const char *placeholder, 36 | const char *defaultValue, 37 | unsigned int length, 38 | const char *custom) 39 | { 40 | init(id, placeholder, defaultValue, length, custom); 41 | } 42 | 43 | void AsyncWiFiManagerParameter::init(const char *id, 44 | const char *placeholder, 45 | const char *defaultValue, 46 | unsigned int length, 47 | const char *custom) 48 | { 49 | _id = id; 50 | _placeholder = placeholder; 51 | _length = length; 52 | _value = new char[length + 1]; 53 | 54 | for (unsigned int i = 0; i < length; i++) 55 | { 56 | _value[i] = 0; 57 | } 58 | if (defaultValue != NULL) 59 | { 60 | strncpy(_value, defaultValue, length); 61 | } 62 | 63 | _customHTML = custom; 64 | } 65 | 66 | const char *AsyncWiFiManagerParameter::getValue() 67 | { 68 | return _value; 69 | } 70 | const char *AsyncWiFiManagerParameter::getID() 71 | { 72 | return _id; 73 | } 74 | const char *AsyncWiFiManagerParameter::getPlaceholder() 75 | { 76 | return _placeholder; 77 | } 78 | unsigned int AsyncWiFiManagerParameter::getValueLength() 79 | { 80 | return _length; 81 | } 82 | const char *AsyncWiFiManagerParameter::getCustomHTML() 83 | { 84 | return _customHTML; 85 | } 86 | 87 | #ifdef USE_EADNS 88 | AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server, 89 | AsyncDNSServer *dns) : server(server), dnsServer(dns) 90 | { 91 | #else 92 | AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server, 93 | DNSServer *dns) : server(server), dnsServer(dns) 94 | { 95 | #endif 96 | wifiSSIDs = NULL; 97 | wifiSSIDscan = true; 98 | _modeless = false; 99 | shouldscan = true; 100 | } 101 | 102 | void AsyncWiFiManager::addParameter(AsyncWiFiManagerParameter *p) 103 | { 104 | _params[_paramsCount] = p; 105 | _paramsCount++; 106 | DEBUG_WM(F("Adding parameter")); 107 | DEBUG_WM(p->getID()); 108 | } 109 | 110 | void AsyncWiFiManager::setupConfigPortal() 111 | { 112 | // dnsServer.reset(new DNSServer()); 113 | // server.reset(new ESP8266WebServer(80)); 114 | server->reset(); 115 | 116 | DEBUG_WM(F("")); 117 | _configPortalStart = millis(); 118 | 119 | DEBUG_WM(F("Configuring access point... ")); 120 | DEBUG_WM(_apName); 121 | if (_apPassword != NULL) 122 | { 123 | if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) 124 | { 125 | // fail passphrase to short or long! 126 | DEBUG_WM(F("Invalid AccessPoint password. Ignoring")); 127 | _apPassword = NULL; 128 | } 129 | DEBUG_WM(_apPassword); 130 | } 131 | 132 | // optional soft ip config 133 | if (_ap_static_ip) 134 | { 135 | DEBUG_WM(F("Custom AP IP/GW/Subnet")); 136 | WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn); 137 | } 138 | 139 | if (_apPassword != NULL) 140 | { 141 | WiFi.softAP(_apName, _apPassword); // password option 142 | } 143 | else 144 | { 145 | WiFi.softAP(_apName); 146 | } 147 | 148 | delay(500); // without delay I've seen the IP address blank 149 | DEBUG_WM(F("AP IP address: ")); 150 | DEBUG_WM(WiFi.softAPIP()); 151 | 152 | // setup the DNS server redirecting all the domains to the apIP 153 | #ifdef USE_EADNS 154 | dnsServer->setErrorReplyCode(AsyncDNSReplyCode::NoError); 155 | #else 156 | dnsServer->setErrorReplyCode(DNSReplyCode::NoError); 157 | #endif 158 | if (!dnsServer->start(DNS_PORT, "*", WiFi.softAPIP())) 159 | { 160 | DEBUG_WM(F("Could not start Captive DNS Server!")); 161 | } 162 | 163 | setInfo(); 164 | 165 | // setup web pages: root, wifi config pages, SO captive portal detectors and not found 166 | server->on("/", 167 | std::bind(&AsyncWiFiManager::handleRoot, this, std::placeholders::_1)) 168 | .setFilter(ON_AP_FILTER); 169 | server->on("/wifi", 170 | std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, true)) 171 | .setFilter(ON_AP_FILTER); 172 | server->on("/0wifi", 173 | std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, false)) 174 | .setFilter(ON_AP_FILTER); 175 | server->on("/wifisave", 176 | std::bind(&AsyncWiFiManager::handleWifiSave, this, std::placeholders::_1)) 177 | .setFilter(ON_AP_FILTER); 178 | server->on("/i", 179 | std::bind(&AsyncWiFiManager::handleInfo, this, std::placeholders::_1)) 180 | .setFilter(ON_AP_FILTER); 181 | server->on("/r", 182 | std::bind(&AsyncWiFiManager::handleReset, this, std::placeholders::_1)) 183 | .setFilter(ON_AP_FILTER); 184 | server->on("/fwlink", 185 | std::bind(&AsyncWiFiManager::handleRoot, this, std::placeholders::_1)) 186 | .setFilter(ON_AP_FILTER); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. 187 | server->onNotFound(std::bind(&AsyncWiFiManager::handleNotFound, this, std::placeholders::_1)); 188 | server->begin(); // web server start 189 | DEBUG_WM(F("HTTP server started")); 190 | } 191 | 192 | static const char HEX_CHAR_ARRAY[17] = "0123456789ABCDEF"; 193 | 194 | #if !defined(ESP8266) 195 | /** 196 | * convert char array (hex values) to readable string by seperator 197 | * buf: buffer to convert 198 | * length: data length 199 | * strSeperator seperator between each hex value 200 | * return: formated value as String 201 | */ 202 | static String byteToHexString(uint8_t *buf, uint8_t length, String strSeperator = "-") 203 | { 204 | String dataString = ""; 205 | for (uint8_t i = 0; i < length; i++) 206 | { 207 | byte v = buf[i] / 16; 208 | byte w = buf[i] % 16; 209 | if (i > 0) 210 | { 211 | dataString += strSeperator; 212 | } 213 | dataString += String(HEX_CHAR_ARRAY[v]); 214 | dataString += String(HEX_CHAR_ARRAY[w]); 215 | } 216 | dataString.toUpperCase(); 217 | return dataString; 218 | } // byteToHexString 219 | 220 | String getESP32ChipID() 221 | { 222 | uint64_t chipid; 223 | chipid = ESP.getEfuseMac(); // the chip ID is essentially its MAC address (length: 6 bytes) 224 | uint8_t chipid_size = 6; 225 | uint8_t chipid_arr[chipid_size]; 226 | for (uint8_t i = 0; i < chipid_size; i++) 227 | { 228 | chipid_arr[i] = (chipid >> (8 * i)) & 0xff; 229 | } 230 | return byteToHexString(chipid_arr, chipid_size, ""); 231 | } 232 | #endif 233 | 234 | boolean AsyncWiFiManager::autoConnect(unsigned long maxConnectRetries, 235 | unsigned long retryDelayMs) 236 | { 237 | String ssid = "ESP"; 238 | #if defined(ESP8266) 239 | ssid += String(ESP.getChipId()); 240 | #else 241 | ssid += getESP32ChipID(); 242 | #endif 243 | return autoConnect(ssid.c_str(), NULL); 244 | } 245 | 246 | boolean AsyncWiFiManager::autoConnect(char const *apName, 247 | char const *apPassword, 248 | unsigned long maxConnectRetries, 249 | unsigned long retryDelayMs) 250 | { 251 | DEBUG_WM(F("")); 252 | 253 | // attempt to connect; should it fail, fall back to AP 254 | WiFi.mode(WIFI_STA); 255 | 256 | for (unsigned long tryNumber = 0; tryNumber < maxConnectRetries; tryNumber++) 257 | { 258 | DEBUG_WM(F("AutoConnect Try No.:")); 259 | DEBUG_WM(tryNumber); 260 | 261 | if (connectWifi("", "") == WL_CONNECTED) 262 | { 263 | DEBUG_WM(F("IP Address:")); 264 | DEBUG_WM(WiFi.localIP()); 265 | // connected 266 | return true; 267 | } 268 | 269 | if (tryNumber + 1 < maxConnectRetries) 270 | { 271 | // we might connect during the delay 272 | unsigned long restDelayMs = retryDelayMs; 273 | while (restDelayMs != 0) 274 | { 275 | if (WiFi.status() == WL_CONNECTED) 276 | { 277 | DEBUG_WM(F("IP Address (connected during delay):")); 278 | DEBUG_WM(WiFi.localIP()); 279 | return true; 280 | } 281 | unsigned long thisDelay = std::min(restDelayMs, 100ul); 282 | delay(thisDelay); 283 | restDelayMs -= thisDelay; 284 | } 285 | } 286 | } 287 | 288 | return startConfigPortal(apName, apPassword); 289 | } 290 | 291 | String AsyncWiFiManager::networkListAsString() 292 | { 293 | String pager; 294 | // display networks in page 295 | for (int i = 0; i < wifiSSIDCount; i++) 296 | { 297 | if (wifiSSIDs[i].duplicate == true) 298 | { 299 | continue; // skip dups 300 | } 301 | unsigned int quality = getRSSIasQuality(wifiSSIDs[i].RSSI); 302 | 303 | if (_minimumQuality == 0 || _minimumQuality < quality) 304 | { 305 | String item = FPSTR(HTTP_ITEM); 306 | String rssiQ; 307 | rssiQ += quality; 308 | item.replace("{v}", wifiSSIDs[i].SSID); 309 | item.replace("{r}", rssiQ); 310 | #if defined(ESP8266) 311 | if (wifiSSIDs[i].encryptionType != ENC_TYPE_NONE) 312 | { 313 | #else 314 | if (wifiSSIDs[i].encryptionType != WIFI_AUTH_OPEN) 315 | { 316 | #endif 317 | item.replace("{i}", "l"); 318 | } 319 | else 320 | { 321 | item.replace("{i}", ""); 322 | } 323 | pager += item; 324 | } 325 | else 326 | { 327 | DEBUG_WM(F("Skipping due to quality")); 328 | } 329 | } 330 | return pager; 331 | } 332 | 333 | String AsyncWiFiManager::scanModal() 334 | { 335 | shouldscan = true; 336 | scan(); 337 | String pager = networkListAsString(); 338 | return pager; 339 | } 340 | 341 | void AsyncWiFiManager::scan(boolean async) 342 | { 343 | if (!shouldscan) 344 | { 345 | return; 346 | } 347 | DEBUG_WM(F("About to scan()")); 348 | if (wifiSSIDscan) 349 | { 350 | wifi_ssid_count_t n = WiFi.scanNetworks(async); 351 | copySSIDInfo(n); 352 | } 353 | } 354 | 355 | void AsyncWiFiManager::copySSIDInfo(wifi_ssid_count_t n) 356 | { 357 | if (n == WIFI_SCAN_FAILED) 358 | { 359 | DEBUG_WM(F("scanNetworks returned: WIFI_SCAN_FAILED!")); 360 | } 361 | else if (n == WIFI_SCAN_RUNNING) 362 | { 363 | DEBUG_WM(F("scanNetworks returned: WIFI_SCAN_RUNNING!")); 364 | } 365 | else if (n < 0) 366 | { 367 | DEBUG_WM(F("scanNetworks failed with unknown error code!")); 368 | } 369 | else if (n == 0) 370 | { 371 | DEBUG_WM(F("No networks found")); 372 | // page += F("No networks found. Refresh to scan again."); 373 | } 374 | else 375 | { 376 | DEBUG_WM(F("Scan done")); 377 | } 378 | 379 | if (n > 0) 380 | { 381 | // WE SHOULD MOVE THIS IN PLACE ATOMICALLY 382 | if (wifiSSIDs) 383 | { 384 | delete[] wifiSSIDs; 385 | } 386 | wifiSSIDs = new WiFiResult[n]; 387 | wifiSSIDCount = n; 388 | 389 | if (n > 0) 390 | { 391 | shouldscan = false; 392 | } 393 | for (wifi_ssid_count_t i = 0; i < n; i++) 394 | { 395 | wifiSSIDs[i].duplicate = false; 396 | 397 | #if defined(ESP8266) 398 | WiFi.getNetworkInfo(i, 399 | wifiSSIDs[i].SSID, 400 | wifiSSIDs[i].encryptionType, 401 | wifiSSIDs[i].RSSI, 402 | wifiSSIDs[i].BSSID, 403 | wifiSSIDs[i].channel, 404 | wifiSSIDs[i].isHidden); 405 | #else 406 | WiFi.getNetworkInfo(i, 407 | wifiSSIDs[i].SSID, 408 | wifiSSIDs[i].encryptionType, 409 | wifiSSIDs[i].RSSI, 410 | wifiSSIDs[i].BSSID, 411 | wifiSSIDs[i].channel); 412 | #endif 413 | } 414 | 415 | // RSSI SORT 416 | 417 | // old sort 418 | for (int i = 0; i < n; i++) 419 | { 420 | for (int j = i + 1; j < n; j++) 421 | { 422 | if (wifiSSIDs[j].RSSI > wifiSSIDs[i].RSSI) 423 | { 424 | std::swap(wifiSSIDs[i], wifiSSIDs[j]); 425 | } 426 | } 427 | } 428 | 429 | // remove duplicates ( must be RSSI sorted ) 430 | if (_removeDuplicateAPs) 431 | { 432 | String cssid; 433 | for (int i = 0; i < n; i++) 434 | { 435 | if (wifiSSIDs[i].duplicate == true) 436 | { 437 | continue; 438 | } 439 | cssid = wifiSSIDs[i].SSID; 440 | for (int j = i + 1; j < n; j++) 441 | { 442 | if (cssid == wifiSSIDs[j].SSID) 443 | { 444 | DEBUG_WM("DUP AP: " + wifiSSIDs[j].SSID); 445 | wifiSSIDs[j].duplicate = true; // set dup aps to NULL 446 | } 447 | } 448 | } 449 | } 450 | } 451 | } 452 | 453 | void AsyncWiFiManager::startConfigPortalModeless(char const *apName, char const *apPassword) 454 | { 455 | _modeless = true; 456 | _apName = apName; 457 | _apPassword = apPassword; 458 | 459 | /* 460 | AJS - do we want this? 461 | */ 462 | 463 | // setup AP 464 | WiFi.mode(WIFI_AP_STA); 465 | DEBUG_WM(F("SET AP STA")); 466 | 467 | // try to connect 468 | if (connectWifi("", "") == WL_CONNECTED) 469 | { 470 | DEBUG_WM(F("IP Address:")); 471 | DEBUG_WM(WiFi.localIP()); 472 | // connected 473 | // call the callback! 474 | if (_savecallback != NULL) 475 | { 476 | // TODO: check if any custom parameters actually exist, and check if they really changed maybe 477 | _savecallback(); 478 | } 479 | } 480 | 481 | // notify we entered AP mode 482 | if (_apcallback != NULL) 483 | { 484 | _apcallback(this); 485 | } 486 | 487 | connect = false; 488 | setupConfigPortal(); 489 | scannow = 0; 490 | } 491 | 492 | void AsyncWiFiManager::loop() 493 | { 494 | safeLoop(); 495 | criticalLoop(); 496 | } 497 | 498 | void AsyncWiFiManager::setInfo() 499 | { 500 | if (needInfo) 501 | { 502 | pager = infoAsString(); 503 | wifiStatus = WiFi.status(); 504 | needInfo = false; 505 | } 506 | } 507 | 508 | // anything that accesses WiFi, ESP or EEPROM goes here 509 | void AsyncWiFiManager::criticalLoop() 510 | { 511 | if (_modeless) 512 | { 513 | if (scannow == 0 || millis() - scannow >= 60000) 514 | { 515 | scannow = millis(); 516 | scan(true); 517 | } 518 | 519 | wifi_ssid_count_t n = WiFi.scanComplete(); 520 | if (n >= 0) 521 | { 522 | copySSIDInfo(n); 523 | WiFi.scanDelete(); 524 | } 525 | 526 | if (connect) 527 | { 528 | connect = false; 529 | //delay(2000); 530 | DEBUG_WM(F("Connecting to new AP")); 531 | 532 | // using user-provided _ssid, _pass in place of system-stored ssid and pass 533 | if (connectWifi(_ssid, _pass) != WL_CONNECTED) 534 | { 535 | DEBUG_WM(F("Failed to connect")); 536 | } 537 | else 538 | { 539 | // connected 540 | // alanswx - should we have a config to decide if we should shut down AP? 541 | // WiFi.mode(WIFI_STA); 542 | // notify that configuration has changed and any optional parameters should be saved 543 | if (_savecallback != NULL) 544 | { 545 | // TODO: check if any custom parameters actually exist, and check if they really changed maybe 546 | _savecallback(); 547 | } 548 | 549 | return; 550 | } 551 | 552 | if (_shouldBreakAfterConfig) 553 | { 554 | // flag set to exit after config after trying to connect 555 | // notify that configuration has changed and any optional parameters should be saved 556 | if (_savecallback != NULL) 557 | { 558 | // TODO: check if any custom parameters actually exist, and check if they really changed maybe 559 | _savecallback(); 560 | } 561 | } 562 | } 563 | } 564 | } 565 | 566 | // anything that doesn't access WiFi, ESP or EEPROM can go here 567 | void AsyncWiFiManager::safeLoop() 568 | { 569 | #ifndef USE_EADNS 570 | dnsServer->processNextRequest(); 571 | #endif 572 | } 573 | 574 | boolean AsyncWiFiManager::startConfigPortal(char const *apName, char const *apPassword) 575 | { 576 | // setup AP 577 | WiFi.mode(WIFI_AP_STA); 578 | DEBUG_WM(F("SET AP STA")); 579 | 580 | _apName = apName; 581 | _apPassword = apPassword; 582 | bool connectedDuringConfigPortal = false; 583 | 584 | // notify we entered AP mode 585 | if (_apcallback != NULL) 586 | { 587 | _apcallback(this); 588 | } 589 | 590 | connect = false; 591 | setupConfigPortal(); 592 | scannow = 0; 593 | while (_configPortalTimeout == 0 || millis() - _configPortalStart < _configPortalTimeout) 594 | { 595 | // DNS 596 | #ifndef USE_EADNS 597 | dnsServer->processNextRequest(); 598 | #endif 599 | // 600 | // we should do a scan every so often here and 601 | // try to reconnect to AP while we are at it 602 | // 603 | if (scannow == 0 || millis() - scannow >= 10000) 604 | { 605 | DEBUG_WM(F("About to scan()")); 606 | shouldscan = true; // since we are modal, we can scan every time 607 | #if defined(ESP8266) 608 | // we might still be connecting, so that has to stop for scanning 609 | ETS_UART_INTR_DISABLE(); 610 | wifi_station_disconnect(); 611 | ETS_UART_INTR_ENABLE(); 612 | #else 613 | WiFi.disconnect(false); 614 | #endif 615 | scanModal(); 616 | if (_tryConnectDuringConfigPortal) 617 | { 618 | WiFi.begin(); // try to reconnect to AP 619 | connectedDuringConfigPortal = true; 620 | } 621 | scannow = millis(); 622 | } 623 | 624 | // attempts to reconnect were successful 625 | if (WiFi.status() == WL_CONNECTED) 626 | { 627 | // connected 628 | WiFi.mode(WIFI_STA); 629 | // notify that configuration has changed and any optional parameters should be saved 630 | // configuraton should not be saved when just connected using stored ssid and password during config portal 631 | if (!connectedDuringConfigPortal && _savecallback != NULL) 632 | { 633 | // TODO: check if any custom parameters actually exist, and check if they really changed maybe 634 | _savecallback(); 635 | } 636 | break; 637 | } 638 | 639 | if (connect) 640 | { 641 | connect = false; 642 | delay(2000); 643 | DEBUG_WM(F("Connecting to new AP")); 644 | 645 | // using user-provided _ssid, _pass in place of system-stored ssid and pass 646 | WiFi.persistent(true); 647 | if (_tryConnectDuringConfigPortal and connectWifi(_ssid, _pass) == WL_CONNECTED) 648 | { 649 | WiFi.persistent(false); 650 | // connected 651 | WiFi.mode(WIFI_STA); 652 | // notify that configuration has changed and any optional parameters should be saved 653 | if (_savecallback != NULL) 654 | { 655 | // TODO: check if any custom parameters actually exist, and check if they really changed maybe 656 | _savecallback(); 657 | } 658 | break; 659 | } 660 | else 661 | { 662 | if(_tryConnectDuringConfigPortal) 663 | DEBUG_WM(F("Failed to connect")); 664 | } 665 | 666 | if (_shouldBreakAfterConfig) 667 | { 668 | // flag set to exit after config after trying to connect 669 | // notify that configuration has changed and any optional parameters should be saved 670 | if (_savecallback != NULL) 671 | { 672 | // TODO: check if any custom parameters actually exist, and check if they really changed maybe 673 | _savecallback(); 674 | } 675 | break; 676 | } 677 | } 678 | yield(); 679 | } 680 | 681 | server->reset(); 682 | dnsServer->stop(); 683 | 684 | return WiFi.status() == WL_CONNECTED; 685 | } 686 | 687 | uint8_t AsyncWiFiManager::connectWifi(String ssid, String pass) 688 | { 689 | DEBUG_WM(F("Connecting as wifi client...")); 690 | 691 | // check if we've got static_ip settings, if we do, use those 692 | if (_sta_static_ip) 693 | { 694 | DEBUG_WM(F("Custom STA IP/GW/Subnet/DNS")); 695 | WiFi.config(_sta_static_ip, _sta_static_gw, _sta_static_sn, _sta_static_dns1, _sta_static_dns2); 696 | DEBUG_WM(WiFi.localIP()); 697 | } 698 | // fix for auto connect racing issue 699 | // if (WiFi.status() == WL_CONNECTED) { 700 | // DEBUG_WM("Already connected. Bailing out."); 701 | // return WL_CONNECTED; 702 | // } 703 | // check if we have ssid and pass and force those, if not, try with last saved values 704 | if (ssid != "") 705 | { 706 | #if defined(ESP8266) 707 | // trying to fix connection in progress hanging 708 | ETS_UART_INTR_DISABLE(); 709 | wifi_station_disconnect(); 710 | ETS_UART_INTR_ENABLE(); 711 | #else 712 | WiFi.disconnect(false); 713 | #endif 714 | WiFi.begin(ssid.c_str(), pass.c_str()); 715 | } 716 | else 717 | { 718 | if (WiFi.SSID().length() > 0) 719 | { 720 | DEBUG_WM(F("Using last saved values, should be faster")); 721 | #if defined(ESP8266) 722 | // trying to fix connection in progress hanging 723 | ETS_UART_INTR_DISABLE(); 724 | wifi_station_disconnect(); 725 | ETS_UART_INTR_ENABLE(); 726 | #else 727 | WiFi.disconnect(false); 728 | #endif 729 | WiFi.begin(); 730 | } 731 | else 732 | { 733 | DEBUG_WM(F("Try to connect with saved credentials")); 734 | WiFi.begin(); 735 | } 736 | } 737 | 738 | uint8_t connRes = waitForConnectResult(); 739 | DEBUG_WM(F("Connection result: ")); 740 | DEBUG_WM(connRes); 741 | // not connected, WPS enabled, no pass - first attempt 742 | #ifdef NO_EXTRA_4K_HEAP 743 | if (_tryWPS && connRes != WL_CONNECTED && pass == "") 744 | { 745 | startWPS(); 746 | // should be connected at the end of WPS 747 | connRes = waitForConnectResult(); 748 | } 749 | #endif 750 | needInfo = true; 751 | setInfo(); 752 | return connRes; 753 | } 754 | 755 | uint8_t AsyncWiFiManager::waitForConnectResult() 756 | { 757 | if (_connectTimeout == 0) 758 | { 759 | return WiFi.waitForConnectResult(); 760 | } 761 | else 762 | { 763 | DEBUG_WM(F("Waiting for connection result with time out")); 764 | unsigned long start = millis(); 765 | boolean keepConnecting = true; 766 | uint8_t status; 767 | while (keepConnecting) 768 | { 769 | status = WiFi.status(); 770 | if (millis() > start + _connectTimeout) 771 | { 772 | keepConnecting = false; 773 | DEBUG_WM(F("Connection timed out")); 774 | } 775 | if (status == WL_CONNECTED || status == WL_CONNECT_FAILED) 776 | { 777 | keepConnecting = false; 778 | } 779 | delay(100); 780 | } 781 | return status; 782 | } 783 | } 784 | #ifdef NO_EXTRA_4K_HEAP 785 | void AsyncWiFiManager::startWPS() 786 | { 787 | DEBUG_WM(F("START WPS")); 788 | #if defined(ESP8266) 789 | WiFi.beginWPSConfig(); 790 | #else 791 | //esp_wps_config_t config = WPS_CONFIG_INIT_DEFAULT(ESP_WPS_MODE); 792 | esp_wps_config_t config = {}; 793 | config.wps_type = ESP_WPS_MODE; 794 | config.crypto_funcs = &g_wifi_default_wps_crypto_funcs; 795 | strcpy(config.factory_info.manufacturer, "ESPRESSIF"); 796 | strcpy(config.factory_info.model_number, "ESP32"); 797 | strcpy(config.factory_info.model_name, "ESPRESSIF IOT"); 798 | strcpy(config.factory_info.device_name, "ESP STATION"); 799 | 800 | esp_wifi_wps_enable(&config); 801 | esp_wifi_wps_start(0); 802 | #endif 803 | DEBUG_WM(F("END WPS")); 804 | } 805 | #endif 806 | String AsyncWiFiManager::getConfigPortalSSID() 807 | { 808 | return _apName; 809 | } 810 | 811 | void AsyncWiFiManager::resetSettings() 812 | { 813 | DEBUG_WM(F("settings invalidated")); 814 | DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA.")); 815 | 816 | WiFi.mode(WIFI_AP_STA); // cannot erase if not in STA mode ! 817 | WiFi.persistent(true); 818 | #if defined(ESP8266) 819 | WiFi.disconnect(true); 820 | #else 821 | WiFi.disconnect(true, true); 822 | #endif 823 | WiFi.persistent(false); 824 | 825 | //delay(200); 826 | } 827 | void AsyncWiFiManager::setTimeout(unsigned long seconds) 828 | { 829 | setConfigPortalTimeout(seconds); 830 | } 831 | 832 | void AsyncWiFiManager::setConfigPortalTimeout(unsigned long seconds) 833 | { 834 | _configPortalTimeout = seconds * 1000; 835 | } 836 | 837 | void AsyncWiFiManager::setConnectTimeout(unsigned long seconds) 838 | { 839 | _connectTimeout = seconds * 1000; 840 | } 841 | 842 | void AsyncWiFiManager::setTryConnectDuringConfigPortal(boolean v) 843 | { 844 | _tryConnectDuringConfigPortal = v; 845 | } 846 | 847 | void AsyncWiFiManager::setDebugOutput(boolean debug) 848 | { 849 | _debug = debug; 850 | } 851 | 852 | void AsyncWiFiManager::setAPStaticIPConfig(IPAddress ip, 853 | IPAddress gw, 854 | IPAddress sn) 855 | { 856 | _ap_static_ip = ip; 857 | _ap_static_gw = gw; 858 | _ap_static_sn = sn; 859 | } 860 | 861 | void AsyncWiFiManager::setSTAStaticIPConfig(IPAddress ip, 862 | IPAddress gw, 863 | IPAddress sn, 864 | IPAddress dns1, 865 | IPAddress dns2) 866 | { 867 | _sta_static_ip = ip; 868 | _sta_static_gw = gw; 869 | _sta_static_sn = sn; 870 | _sta_static_dns1 = dns1; 871 | _sta_static_dns2 = dns2; 872 | } 873 | 874 | void AsyncWiFiManager::setMinimumSignalQuality(unsigned int quality) 875 | { 876 | _minimumQuality = quality; 877 | } 878 | 879 | void AsyncWiFiManager::setBreakAfterConfig(boolean shouldBreak) 880 | { 881 | _shouldBreakAfterConfig = shouldBreak; 882 | } 883 | 884 | // handle root or redirect to captive portal 885 | void AsyncWiFiManager::handleRoot(AsyncWebServerRequest *request) 886 | { 887 | // AJS - maybe we should set a scan when we get to the root??? 888 | // and only scan on demand? timer + on demand? plus a link to make it happen? 889 | 890 | shouldscan = true; 891 | scannow = 0; 892 | DEBUG_WM(F("Handle root")); 893 | 894 | if (captivePortal(request)) 895 | { 896 | // if captive portal redirect instead of displaying the page 897 | return; 898 | } 899 | 900 | DEBUG_WM(F("Sending Captive Portal")); 901 | 902 | String page = FPSTR(WFM_HTTP_HEAD); 903 | page.replace("{v}", "Options"); 904 | page += FPSTR(HTTP_SCRIPT); 905 | page += FPSTR(HTTP_STYLE); 906 | page += _customHeadElement; 907 | page += FPSTR(HTTP_HEAD_END); 908 | page += "

"; 909 | page += _apName; 910 | page += "

"; 911 | page += F("

AsyncWiFiManager

"); 912 | page += FPSTR(HTTP_PORTAL_OPTIONS); 913 | page += _customOptionsElement; 914 | page += FPSTR(HTTP_END); 915 | 916 | request->send(200, "text/html", page); 917 | DEBUG_WM(F("Sent...")); 918 | } 919 | 920 | // wifi config page handler 921 | void AsyncWiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan) 922 | { 923 | shouldscan = true; 924 | scannow = 0; 925 | 926 | DEBUG_WM(F("Handle wifi")); 927 | 928 | String page = FPSTR(WFM_HTTP_HEAD); 929 | page.replace("{v}", "Config ESP"); 930 | page += FPSTR(HTTP_SCRIPT); 931 | page += FPSTR(HTTP_STYLE); 932 | page += _customHeadElement; 933 | page += FPSTR(HTTP_HEAD_END); 934 | 935 | if (scan) 936 | { 937 | wifiSSIDscan = false; 938 | 939 | DEBUG_WM(F("Scan done")); 940 | if (wifiSSIDCount == 0) 941 | { 942 | DEBUG_WM(F("No networks found")); 943 | page += F("No networks found. Refresh to scan again"); 944 | } 945 | else 946 | { 947 | // display networks in page 948 | String pager = networkListAsString(); 949 | page += pager; 950 | page += "
"; 951 | } 952 | } 953 | wifiSSIDscan = true; 954 | 955 | page += FPSTR(HTTP_FORM_START); 956 | char parLength[2]; 957 | 958 | // add the extra parameters to the form 959 | for (unsigned int i = 0; i < _paramsCount; i++) 960 | { 961 | if (_params[i] == NULL) 962 | { 963 | break; 964 | } 965 | 966 | String pitem = FPSTR(HTTP_FORM_PARAM); 967 | if (_params[i]->getID() != NULL) 968 | { 969 | pitem.replace("{i}", _params[i]->getID()); 970 | pitem.replace("{n}", _params[i]->getID()); 971 | pitem.replace("{p}", _params[i]->getPlaceholder()); 972 | snprintf(parLength, 2, "%d", _params[i]->getValueLength()); 973 | pitem.replace("{l}", parLength); 974 | pitem.replace("{v}", _params[i]->getValue()); 975 | pitem.replace("{c}", _params[i]->getCustomHTML()); 976 | } 977 | else 978 | { 979 | pitem = _params[i]->getCustomHTML(); 980 | } 981 | 982 | page += pitem; 983 | } 984 | if (_params[0] != NULL) 985 | { 986 | page += "
"; 987 | } 988 | if (_sta_static_ip) 989 | { 990 | String item = FPSTR(HTTP_FORM_PARAM); 991 | item.replace("{i}", "ip"); 992 | item.replace("{n}", "ip"); 993 | item.replace("{p}", "Static IP"); 994 | item.replace("{l}", "15"); 995 | item.replace("{v}", _sta_static_ip.toString()); 996 | 997 | page += item; 998 | 999 | item = FPSTR(HTTP_FORM_PARAM); 1000 | item.replace("{i}", "gw"); 1001 | item.replace("{n}", "gw"); 1002 | item.replace("{p}", "Static Gateway"); 1003 | item.replace("{l}", "15"); 1004 | item.replace("{v}", _sta_static_gw.toString()); 1005 | 1006 | page += item; 1007 | 1008 | item = FPSTR(HTTP_FORM_PARAM); 1009 | item.replace("{i}", "sn"); 1010 | item.replace("{n}", "sn"); 1011 | item.replace("{p}", "Subnet"); 1012 | item.replace("{l}", "15"); 1013 | item.replace("{v}", _sta_static_sn.toString()); 1014 | 1015 | page += item; 1016 | 1017 | item = FPSTR(HTTP_FORM_PARAM); 1018 | item.replace("{i}", "dns1"); 1019 | item.replace("{n}", "dns1"); 1020 | item.replace("{p}", "DNS1"); 1021 | item.replace("{l}", "15"); 1022 | item.replace("{v}", _sta_static_dns1.toString()); 1023 | 1024 | page += item; 1025 | 1026 | item = FPSTR(HTTP_FORM_PARAM); 1027 | item.replace("{i}", "dns2"); 1028 | item.replace("{n}", "dns2"); 1029 | item.replace("{p}", "DNS2"); 1030 | item.replace("{l}", "15"); 1031 | item.replace("{v}", _sta_static_dns2.toString()); 1032 | 1033 | page += item; 1034 | page += "
"; 1035 | } 1036 | page += FPSTR(HTTP_FORM_END); 1037 | page += FPSTR(HTTP_SCAN_LINK); 1038 | page += FPSTR(HTTP_END); 1039 | 1040 | request->send(200, "text/html", page); 1041 | 1042 | DEBUG_WM(F("Sent config page")); 1043 | } 1044 | 1045 | // handle the WLAN save form and redirect to WLAN config page again 1046 | void AsyncWiFiManager::handleWifiSave(AsyncWebServerRequest *request) 1047 | { 1048 | DEBUG_WM(F("WiFi save")); 1049 | 1050 | // SAVE/connect here 1051 | needInfo = true; 1052 | _ssid = request->arg("s").c_str(); 1053 | _pass = request->arg("p").c_str(); 1054 | 1055 | // parameters 1056 | for (unsigned int i = 0; i < _paramsCount; i++) 1057 | { 1058 | if (_params[i] == NULL) 1059 | { 1060 | break; 1061 | } 1062 | // read parameter 1063 | String value = request->arg(_params[i]->getID()).c_str(); 1064 | // store it in array 1065 | value.toCharArray(_params[i]->_value, _params[i]->_length); 1066 | 1067 | DEBUG_WM(F("Parameter")); 1068 | DEBUG_WM(_params[i]->getID()); 1069 | DEBUG_WM(value); 1070 | } 1071 | 1072 | if (request->hasArg("ip")) 1073 | { 1074 | DEBUG_WM(F("static ip")); 1075 | DEBUG_WM(request->arg("ip")); 1076 | //_sta_static_ip.fromString(request->arg("ip")); 1077 | String ip = request->arg("ip"); 1078 | optionalIPFromString(&_sta_static_ip, ip.c_str()); 1079 | } 1080 | if (request->hasArg("gw")) 1081 | { 1082 | DEBUG_WM(F("static gateway")); 1083 | DEBUG_WM(request->arg("gw")); 1084 | String gw = request->arg("gw"); 1085 | optionalIPFromString(&_sta_static_gw, gw.c_str()); 1086 | } 1087 | if (request->hasArg("sn")) 1088 | { 1089 | DEBUG_WM(F("static netmask")); 1090 | DEBUG_WM(request->arg("sn")); 1091 | String sn = request->arg("sn"); 1092 | optionalIPFromString(&_sta_static_sn, sn.c_str()); 1093 | } 1094 | if (request->hasArg("dns1")) 1095 | { 1096 | DEBUG_WM(F("static DNS 1")); 1097 | DEBUG_WM(request->arg("dns1")); 1098 | String dns1 = request->arg("dns1"); 1099 | optionalIPFromString(&_sta_static_dns1, dns1.c_str()); 1100 | } 1101 | if (request->hasArg("dns2")) 1102 | { 1103 | DEBUG_WM(F("static DNS 2")); 1104 | DEBUG_WM(request->arg("dns2")); 1105 | String dns2 = request->arg("dns2"); 1106 | optionalIPFromString(&_sta_static_dns2, dns2.c_str()); 1107 | } 1108 | 1109 | String page = FPSTR(WFM_HTTP_HEAD); 1110 | page.replace("{v}", "Credentials Saved"); 1111 | page += FPSTR(HTTP_SCRIPT); 1112 | page += FPSTR(HTTP_STYLE); 1113 | page += _customHeadElement; 1114 | page += F(""); 1115 | page += FPSTR(HTTP_HEAD_END); 1116 | page += FPSTR(HTTP_SAVED); 1117 | page += FPSTR(HTTP_END); 1118 | 1119 | request->send(200, "text/html", page); 1120 | 1121 | DEBUG_WM(F("Sent wifi save page")); 1122 | 1123 | connect = true; // signal ready to connect/reset 1124 | } 1125 | 1126 | // handle the info page 1127 | String AsyncWiFiManager::infoAsString() 1128 | { 1129 | String page; 1130 | page += F("
Chip ID
"); 1131 | #if defined(ESP8266) 1132 | page += ESP.getChipId(); 1133 | #else 1134 | page += getESP32ChipID(); 1135 | #endif 1136 | page += F("
"); 1137 | page += F("
Flash Chip ID
"); 1138 | #if defined(ESP8266) 1139 | page += ESP.getFlashChipId(); 1140 | #else 1141 | page += F("N/A for ESP32"); 1142 | #endif 1143 | page += F("
"); 1144 | page += F("
IDE Flash Size
"); 1145 | page += ESP.getFlashChipSize(); 1146 | page += F(" bytes
"); 1147 | page += F("
Real Flash Size
"); 1148 | #if defined(ESP8266) 1149 | page += ESP.getFlashChipRealSize(); 1150 | #else 1151 | page += F("N/A for ESP32"); 1152 | #endif 1153 | page += F(" bytes
"); 1154 | page += F("
Soft AP IP
"); 1155 | page += WiFi.softAPIP().toString(); 1156 | page += F("
"); 1157 | page += F("
Soft AP MAC
"); 1158 | page += WiFi.softAPmacAddress(); 1159 | page += F("
"); 1160 | page += F("
Station SSID
"); 1161 | page += WiFi.SSID(); 1162 | page += F("
"); 1163 | page += F("
Station IP
"); 1164 | page += WiFi.localIP().toString(); 1165 | page += F("
"); 1166 | page += F("
Station MAC
"); 1167 | page += WiFi.macAddress(); 1168 | page += F("
"); 1169 | page += F(""); 1170 | return page; 1171 | } 1172 | 1173 | void AsyncWiFiManager::handleInfo(AsyncWebServerRequest *request) 1174 | { 1175 | DEBUG_WM(F("Info")); 1176 | 1177 | String page = FPSTR(WFM_HTTP_HEAD); 1178 | page.replace("{v}", "Info"); 1179 | page += FPSTR(HTTP_SCRIPT); 1180 | page += FPSTR(HTTP_STYLE); 1181 | page += _customHeadElement; 1182 | if (connect == true) 1183 | { 1184 | page += F(""); 1185 | } 1186 | page += FPSTR(HTTP_HEAD_END); 1187 | page += F("
"); 1188 | if (connect == true) 1189 | { 1190 | page += F("
Trying to connect
"); 1191 | page += wifiStatus; 1192 | page += F("
"); 1193 | } 1194 | page += pager; 1195 | page += FPSTR(HTTP_END); 1196 | 1197 | request->send(200, "text/html", page); 1198 | 1199 | DEBUG_WM(F("Sent info page")); 1200 | } 1201 | 1202 | // handle the reset page 1203 | void AsyncWiFiManager::handleReset(AsyncWebServerRequest *request) 1204 | { 1205 | DEBUG_WM(F("Reset")); 1206 | 1207 | String page = FPSTR(WFM_HTTP_HEAD); 1208 | page.replace("{v}", "Info"); 1209 | page += FPSTR(HTTP_SCRIPT); 1210 | page += FPSTR(HTTP_STYLE); 1211 | page += _customHeadElement; 1212 | page += FPSTR(HTTP_HEAD_END); 1213 | page += F("Module will reset in a few seconds"); 1214 | page += FPSTR(HTTP_END); 1215 | request->send(200, "text/html", page); 1216 | 1217 | DEBUG_WM(F("Sent reset page")); 1218 | delay(5000); 1219 | #if defined(ESP8266) 1220 | ESP.reset(); 1221 | #else 1222 | ESP.restart(); 1223 | #endif 1224 | delay(2000); 1225 | } 1226 | 1227 | void AsyncWiFiManager::handleNotFound(AsyncWebServerRequest *request) 1228 | { 1229 | DEBUG_WM(F("Handle not found")); 1230 | if (captivePortal(request)) 1231 | { 1232 | // if captive portal redirect instead of displaying the error page 1233 | return; 1234 | } 1235 | 1236 | String message = "File Not Found\n\n"; 1237 | message += "URI: "; 1238 | message += request->url(); 1239 | message += "\nMethod: "; 1240 | message += (request->method() == HTTP_GET) ? "GET" : "POST"; 1241 | message += "\nArguments: "; 1242 | message += request->args(); 1243 | message += "\n"; 1244 | 1245 | for (unsigned int i = 0; i < request->args(); i++) 1246 | { 1247 | message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; 1248 | } 1249 | AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", message); 1250 | response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); 1251 | response->addHeader("Pragma", "no-cache"); 1252 | response->addHeader("Expires", "-1"); 1253 | request->send(response); 1254 | } 1255 | 1256 | /** Redirect to captive portal if we got a request for another domain. 1257 | * Return true in that case so the page handler do not try to handle the request again. */ 1258 | boolean AsyncWiFiManager::captivePortal(AsyncWebServerRequest *request) 1259 | { 1260 | if (!isIp(request->host())) 1261 | { 1262 | DEBUG_WM(F("Request redirected to captive portal")); 1263 | AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", ""); 1264 | response->addHeader("Location", String("http://") + toStringIp(request->client()->localIP())); 1265 | request->send(response); 1266 | return true; 1267 | } 1268 | return false; 1269 | } 1270 | 1271 | // start up config portal callback 1272 | void AsyncWiFiManager::setAPCallback(std::function func) 1273 | { 1274 | _apcallback = func; 1275 | } 1276 | 1277 | // start up save config callback 1278 | void AsyncWiFiManager::setSaveConfigCallback(std::function func) 1279 | { 1280 | _savecallback = func; 1281 | } 1282 | 1283 | // sets a custom element to add to head, like a new style tag 1284 | void AsyncWiFiManager::setCustomHeadElement(const char *element) 1285 | { 1286 | _customHeadElement = element; 1287 | } 1288 | 1289 | // sets a custom element to add to options page 1290 | void AsyncWiFiManager::setCustomOptionsElement(const char *element) 1291 | { 1292 | _customOptionsElement = element; 1293 | } 1294 | 1295 | // if this is true, remove duplicated Access Points - defaut true 1296 | void AsyncWiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) 1297 | { 1298 | _removeDuplicateAPs = removeDuplicates; 1299 | } 1300 | 1301 | template 1302 | void AsyncWiFiManager::DEBUG_WM(Generic text) 1303 | { 1304 | if (_debug) 1305 | { 1306 | Serial.print(F("*WM: ")); 1307 | Serial.println(text); 1308 | } 1309 | } 1310 | 1311 | unsigned int AsyncWiFiManager::getRSSIasQuality(int RSSI) 1312 | { 1313 | unsigned int quality = 0; 1314 | 1315 | if (RSSI <= -100) 1316 | { 1317 | quality = 0; 1318 | } 1319 | else if (RSSI >= -50) 1320 | { 1321 | quality = 100; 1322 | } 1323 | else 1324 | { 1325 | quality = 2 * (RSSI + 100); 1326 | } 1327 | return quality; 1328 | } 1329 | 1330 | // is this an IP? 1331 | boolean AsyncWiFiManager::isIp(String str) 1332 | { 1333 | for (unsigned int i = 0; i < str.length(); i++) 1334 | { 1335 | int c = str.charAt(i); 1336 | if (c != '.' && (c < '0' || c > '9')) 1337 | { 1338 | return false; 1339 | } 1340 | } 1341 | return true; 1342 | } 1343 | 1344 | // IP to String? 1345 | String AsyncWiFiManager::toStringIp(IPAddress ip) 1346 | { 1347 | String res = ""; 1348 | for (int i = 0; i < 3; i++) 1349 | { 1350 | res += String((ip >> (8 * i)) & 0xFF) + "."; 1351 | } 1352 | res += String(((ip >> 8 * 3)) & 0xFF); 1353 | return res; 1354 | } 1355 | -------------------------------------------------------------------------------- /src/ESPAsyncWiFiManager.h: -------------------------------------------------------------------------------- 1 | /************************************************************** 2 | WiFiManager is a library for the ESP8266/Arduino platform 3 | (https://github.com/esp8266/Arduino) to enable easy 4 | configuration and reconfiguration of WiFi credentials using a Captive Portal 5 | inspired by: 6 | http://www.esp8266.com/viewtopic.php?f=29&t=2520 7 | https://github.com/chriscook8/esp-arduino-apboot 8 | https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced 9 | Built by AlexT https://github.com/tzapu 10 | Ported to Async Web Server by https://github.com/alanswx 11 | Licensed under MIT license 12 | **************************************************************/ 13 | 14 | #ifndef ESPAsyncWiFiManager_h 15 | #define ESPAsyncWiFiManager_h 16 | 17 | #if defined(ESP8266) 18 | #include // https://github.com/esp8266/Arduino 19 | #else 20 | #include 21 | #include "esp_wps.h" 22 | #define ESP_WPS_MODE WPS_TYPE_PBC 23 | #endif 24 | #include 25 | 26 | //#define USE_EADNS // uncomment to use ESPAsyncDNSServer 27 | #ifdef USE_EADNS 28 | #include // https://github.com/devyte/ESPAsyncDNSServer 29 | // https://github.com/me-no-dev/ESPAsyncUDP 30 | #else 31 | #include 32 | #endif 33 | #include 34 | 35 | // fix crash on ESP32 (see https://github.com/alanswx/ESPAsyncWiFiManager/issues/44) 36 | #if defined(ESP8266) 37 | typedef int wifi_ssid_count_t; 38 | #else 39 | typedef int16_t wifi_ssid_count_t; 40 | #endif 41 | 42 | #if defined(ESP8266) 43 | extern "C" 44 | { 45 | #include "user_interface.h" 46 | } 47 | #else 48 | #include 49 | #endif 50 | 51 | const char WFM_HTTP_HEAD[] PROGMEM = "{v}"; 52 | const char HTTP_STYLE[] PROGMEM = ""; 53 | const char HTTP_SCRIPT[] PROGMEM = ""; 54 | const char HTTP_HEAD_END[] PROGMEM = "
"; 55 | const char HTTP_PORTAL_OPTIONS[] PROGMEM = "



"; 56 | const char HTTP_ITEM[] PROGMEM = "
{v} {r}%
"; 57 | const char HTTP_FORM_START[] PROGMEM = "


"; 58 | const char HTTP_FORM_PARAM[] PROGMEM = "
"; 59 | const char HTTP_FORM_END[] PROGMEM = "
"; 60 | const char HTTP_SCAN_LINK[] PROGMEM = "
"; 61 | const char HTTP_SAVED[] PROGMEM = "
Credentials Saved
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; 62 | const char HTTP_END[] PROGMEM = "
"; 63 | 64 | #define WIFI_MANAGER_MAX_PARAMS 10 65 | 66 | class AsyncWiFiManagerParameter 67 | { 68 | public: 69 | AsyncWiFiManagerParameter(const char *custom); 70 | AsyncWiFiManagerParameter(const char *id, 71 | const char *placeholder, 72 | const char *defaultValue, 73 | unsigned int length); 74 | AsyncWiFiManagerParameter(const char *id, 75 | const char *placeholder, 76 | const char *defaultValue, 77 | unsigned int length, 78 | const char *custom); 79 | 80 | const char *getID(); 81 | const char *getValue(); 82 | const char *getPlaceholder(); 83 | unsigned int getValueLength(); 84 | const char *getCustomHTML(); 85 | 86 | private: 87 | const char *_id; 88 | const char *_placeholder; 89 | char *_value; 90 | unsigned int _length; 91 | const char *_customHTML; 92 | 93 | void init(const char *id, 94 | const char *placeholder, 95 | const char *defaultValue, 96 | unsigned int length, 97 | const char *custom); 98 | 99 | friend class AsyncWiFiManager; 100 | }; 101 | 102 | class WiFiResult 103 | { 104 | public: 105 | bool duplicate; 106 | String SSID; 107 | uint8_t encryptionType; 108 | int32_t RSSI; 109 | uint8_t *BSSID; 110 | int32_t channel; 111 | bool isHidden; 112 | 113 | WiFiResult() 114 | { 115 | } 116 | }; 117 | 118 | class AsyncWiFiManager 119 | { 120 | public: 121 | #ifdef USE_EADNS 122 | AsyncWiFiManager(AsyncWebServer *server, AsyncDNSServer *dns); 123 | #else 124 | AsyncWiFiManager(AsyncWebServer *server, DNSServer *dns); 125 | #endif 126 | 127 | void scan(boolean async = false); 128 | String scanModal(); 129 | void loop(); 130 | void safeLoop(); 131 | void criticalLoop(); 132 | String infoAsString(); 133 | 134 | boolean autoConnect(unsigned long maxConnectRetries = 1, 135 | unsigned long retryDelayMs = 1000); 136 | boolean autoConnect(char const *apName, 137 | char const *apPassword = NULL, 138 | unsigned long maxConnectRetries = 1, 139 | unsigned long retryDelayMs = 1000); 140 | 141 | // if you want to always start the config portal, without trying to connect first 142 | boolean startConfigPortal(char const *apName, char const *apPassword = NULL); 143 | void startConfigPortalModeless(char const *apName, char const *apPassword); 144 | 145 | // get the AP name of the config portal, so it can be used in the callback 146 | String getConfigPortalSSID(); 147 | 148 | void resetSettings(); 149 | 150 | // sets timeout before webserver loop ends and exits even if there has been no setup. 151 | // usefully for devices that failed to connect at some point and got stuck in a webserver loop. 152 | // in seconds, setConfigPortalTimeout is a new name for setTimeout 153 | void setConfigPortalTimeout(unsigned long seconds); 154 | void setTimeout(unsigned long seconds); 155 | 156 | // sets timeout for which to attempt connecting, usefull if you get a lot of failed connects 157 | void setConnectTimeout(unsigned long seconds); 158 | 159 | // wether or not the wifi manager tries to connect to configured access point even when 160 | // configuration portal (ESP as access point) is running [default true/on] 161 | void setTryConnectDuringConfigPortal(boolean v); 162 | 163 | void setDebugOutput(boolean debug); 164 | // defaults to not showing anything under 8% signal quality if called 165 | void setMinimumSignalQuality(unsigned int quality = 8); 166 | // sets a custom ip /gateway /subnet configuration 167 | void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn); 168 | // sets config for a static IP 169 | void setSTAStaticIPConfig(IPAddress ip, 170 | IPAddress gw, 171 | IPAddress sn, 172 | IPAddress dns1 = (uint32_t)0x00000000, 173 | IPAddress dns2 = (uint32_t)0x00000000); 174 | // called when AP mode and config portal is started 175 | void setAPCallback(std::function); 176 | // called when settings have been changed and connection was successful 177 | void setSaveConfigCallback(std::function func); 178 | //adds a custom parameter 179 | void addParameter(AsyncWiFiManagerParameter *p); 180 | // if this is set, it will exit after config, even if connection is unsucessful 181 | void setBreakAfterConfig(boolean shouldBreak); 182 | // if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins) 183 | // TODO 184 | // if this is set, customise style 185 | void setCustomHeadElement(const char *element); 186 | // if this is true, remove duplicated Access Points - defaut true 187 | void setRemoveDuplicateAPs(boolean removeDuplicates); 188 | // sets a custom element to add to options page 189 | void setCustomOptionsElement(const char *element); 190 | 191 | String getConfiguredSTASSID(){ 192 | return _ssid; 193 | } 194 | String getConfiguredSTAPassword(){ 195 | return _pass; 196 | } 197 | 198 | private: 199 | AsyncWebServer *server; 200 | #ifdef USE_EADNS 201 | AsyncDNSServer *dnsServer; 202 | #else 203 | DNSServer *dnsServer; 204 | #endif 205 | 206 | boolean _modeless; 207 | unsigned long scannow; 208 | boolean shouldscan = true; 209 | boolean needInfo = true; 210 | 211 | //const int WM_DONE = 0; 212 | //const int WM_WAIT = 10; 213 | //const String HTTP_HEAD = "{v}"; 214 | 215 | void setupConfigPortal(); 216 | #ifdef NO_EXTRA_4K_HEAP 217 | void startWPS(); 218 | #endif 219 | String pager; 220 | wl_status_t wifiStatus; 221 | const char *_apName = "no-net"; 222 | const char *_apPassword = NULL; 223 | String _ssid = ""; 224 | String _pass = ""; 225 | unsigned long _configPortalTimeout = 0; 226 | unsigned long _connectTimeout = 0; 227 | unsigned long _configPortalStart = 0; 228 | 229 | IPAddress _ap_static_ip; 230 | IPAddress _ap_static_gw; 231 | IPAddress _ap_static_sn; 232 | IPAddress _sta_static_ip; 233 | IPAddress _sta_static_gw; 234 | IPAddress _sta_static_sn; 235 | IPAddress _sta_static_dns1 = (uint32_t)0x00000000; 236 | IPAddress _sta_static_dns2 = (uint32_t)0x00000000; 237 | 238 | unsigned int _paramsCount = 0; 239 | unsigned int _minimumQuality = 0; 240 | boolean _removeDuplicateAPs = true; 241 | boolean _shouldBreakAfterConfig = false; 242 | #ifdef NO_EXTRA_4K_HEAP 243 | boolean _tryWPS = false; 244 | #endif 245 | const char *_customHeadElement = ""; 246 | const char *_customOptionsElement = ""; 247 | 248 | //String getEEPROMString(int start, int len); 249 | //void setEEPROMString(int start, int len, String string); 250 | 251 | uint8_t status = WL_IDLE_STATUS; 252 | uint8_t connectWifi(String ssid, String pass); 253 | uint8_t waitForConnectResult(); 254 | void setInfo(); 255 | void copySSIDInfo(wifi_ssid_count_t n); 256 | String networkListAsString(); 257 | 258 | void handleRoot(AsyncWebServerRequest *); 259 | void handleWifi(AsyncWebServerRequest *, boolean scan); 260 | void handleWifiSave(AsyncWebServerRequest *); 261 | void handleInfo(AsyncWebServerRequest *); 262 | void handleReset(AsyncWebServerRequest *); 263 | void handleNotFound(AsyncWebServerRequest *); 264 | boolean captivePortal(AsyncWebServerRequest *); 265 | 266 | // DNS server 267 | const byte DNS_PORT = 53; 268 | 269 | // helpers 270 | unsigned int getRSSIasQuality(int RSSI); 271 | boolean isIp(String str); 272 | String toStringIp(IPAddress ip); 273 | 274 | boolean connect; 275 | boolean _debug = true; 276 | 277 | WiFiResult *wifiSSIDs; 278 | wifi_ssid_count_t wifiSSIDCount; 279 | boolean wifiSSIDscan; 280 | 281 | boolean _tryConnectDuringConfigPortal = true; 282 | 283 | std::function _apcallback; 284 | std::function _savecallback; 285 | 286 | AsyncWiFiManagerParameter *_params[WIFI_MANAGER_MAX_PARAMS]; 287 | 288 | template 289 | void DEBUG_WM(Generic text); 290 | 291 | template 292 | auto optionalIPFromString(T *obj, const char *s) -> decltype(obj->fromString(s)) 293 | { 294 | return obj->fromString(s); 295 | } 296 | auto optionalIPFromString(...) -> bool 297 | { 298 | DEBUG_WM(F("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work.")); 299 | return false; 300 | } 301 | }; 302 | 303 | #endif 304 | --------------------------------------------------------------------------------