ESP8266 Wifi Configuration Part 2
In Part 1 of the Wifi configuration tutorial we learned how to set up the device to read/write from non volatile areas of memory so that we can recall a new wifi ssid and password between reboots. In Part 2 we're going to provide an interface that allows you to set the new values from a web page. This tutorial is a combination of all the previous parts so I'll include links to the relevant parts rather than run through it all again.
The one part we missed from the last tutorial was actually creating a wifi network. We need to include the ESP8266Wifi library and open up a new access point. Do this after loading the new wifi values to ensure the network has the correct name.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
WiFi.mode(WIFI_AP); | |
WiFi.softAP(config.ssid, config.password); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
webServer.on("/", [](){ | |
webServer.send(200, "text/plain", "visit /admin for wifi configuration"); | |
}); | |
webServer.serveStatic("/admin", SPIFFS, "/admin.html"); | |
webServer.on("/admin", handleAdmin); | |
webServer.begin(); |
With those parts implemented we're now ready to actually create the user interface. We're going to take a departure from the Arduino IDE at this point, all of the interface can be written in html using javascript to perform some basic data validation before it gets sent to the ESP device. There are lots of online editors to aid development of html pages but I found JSFiddle to be very useful. (although you'll have to manually switch to the results tab on the following script to see the output)
The basic input form is written in HTML, it's simply 4 input text boxes with prompts as to what the values should be. The old password box is provided to check if the user has the right password and therefore permissions to change the values. The new SSID box is just a new text value and there are 2 password input boxes just to confirm that the new password has been typed correctly (given that the data is blanked). HTML already deals with most of the validation required, you can specify how long or short text needs to be (in this case more than seven characters and less than sixteen) and it deals with the password inputs. The only small piece of JavaScript we have to add is to validate that password1 contains the same value as password2. This gets checked when the HTML form is submitted, again html deals with creating the string of arguments to post to the ESP8266.
Once the form is successfully filled out it will POST a series of arguments to the ESP device. The form only needs to contain new ssid or new password information, and it is only input data with a name attribute that gets sent so we should be seeing upto 3 arguments, 'oldpassword', 'newssid' and 'newpassword'. Our handleAdmin() function will be sent the incoming data and it's ready for us to process, lets take a look at that function.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void handleAdmin() { | |
// Create a string containing all the arguments, send them out to the serial port | |
Serial.printf("handleAdmin\n"); | |
// Construct a list of all the arguments for debug | |
String message = "#Args:" + String(webServer.args()) + "\n"; | |
for (int i = 0; i < webServer.args(); i++) | |
message.concat(webServer.argName(i) + ": "+ webServer.arg(i) + "\n"); | |
Serial.print(message); | |
// Check to see that the submitted password is the same as the current password | |
if(webServer.hasArg("oldpassword")) { | |
// Check to see that the submitted password is the same as the current password | |
if(strcmp(webServer.arg("oldpassword").c_str(), config.password) == 0) { | |
} | |
} | |
} |
The first thing we do is look at all the arguments and pass them out to the serial port, a handy way to make sure that we're sending the values we think we're sending. The web server library has more specific functions that allow us to check and see if a specific arguments exists before we try to process the data. We can pass the argument name to the web server using the 'hasArgs()' function and check to see if the old password has been supplied. The first test is to make sure that the password matches the one we're currently using, if these don't match we should ignore all the rest of the data.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// If the passwords match we can update the config with the new settings | |
// Check to see if there is a new value (also doubles to check the length of the new value is long enough) | |
if(webServer.arg("newpassword").length() > 7) | |
webServer.arg("newpassword").toCharArray(config.password, sizeof(config.password)); | |
if(webServer.arg("newssid").length() > 7) | |
webServer.arg("newssid").toCharArray(config.ssid, sizeof(config.ssid)); | |
// Store the new settings to EEPROM | |
EEPROM_writeAnything(0, config); | |
EEPROM.commit(); | |
// Construct a message to tell the user that the change worked | |
message = "New settings will take effect after restart"; | |
Serial.println(message); | |
// Reply with a web page to indicate success or failure | |
message = "<html><head><meta http-equiv='refresh' content='5;url=/' /></head><body><h1>" + message; | |
message += "<br/>Redirecting in 5 seconds...</h1></body></html>"; | |
webServer.send(200, "text/html", message); |
If the password matches we then need to copy the values from the newssid and newpassword arguments into our own variable. We can check at this point that the arguments are also the right length (and if they're not greater than zero they don't exist at all). We copy the new values over to our config variables and store them to EEPROM, it's as simple as that. It's polite to let the user know if there was success or failure at this point so we construct a short html response string with the pass or fail result, being html we can also add in a browser redirect back to the main page. I have looked at performing an automatic reboot at this point to start using the new values but the Wemos D1 doesn't appear to restart correctly, this is probably something to do with the USB serial device and the arduino programming.
Here is the whole program, the next step would be to roll all of this access point configuration into a single library that could be easily imported into any project.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
#include <ESP8266WebServer.h> | |
#include <FS.h> | |
#include "EEPROMAnything.h" | |
#define RESETPIN D1 | |
ESP8266WebServer webServer(80); | |
struct config_t{ | |
char ssid[17] = "ESP8266 Test"; | |
char password[17] = "password"; | |
} config; | |
void setup() { | |
Serial.begin(115200); | |
pinMode(RESETPIN, INPUT_PULLUP); | |
SPIFFS.begin(); | |
//Start the EEPROM library with enough memory to hold all the config data | |
EEPROM.begin(sizeof(config)); | |
//Check the reset pin and overwrite the eeprom with the default values if required | |
if(!digitalRead(RESETPIN)) { | |
Serial.printf("Firmware Reset Detected\n"); | |
EEPROM_writeAnything(0, config); | |
EEPROM.commit(); | |
} | |
//Retreive the network values from the EEPROM and output them to serial as a confirmation test. | |
EEPROM_readAnything(0, config); | |
Serial.printf("SSID: '%s'\n", config.ssid); | |
Serial.printf("Password: '%s'\n", config.password); | |
WiFi.mode(WIFI_AP); | |
WiFi.softAP(config.ssid, config.password); | |
webServer.on("/", [](){ | |
webServer.send(200, "text/plain", "visit /admin for wifi configuration"); | |
}); | |
webServer.serveStatic("/admin", SPIFFS, "/admin.html"); | |
webServer.on("/admin", handleAdmin); | |
webServer.begin(); | |
} | |
void loop() { | |
// put your main code here, to run repeatedly: | |
webServer.handleClient(); | |
} | |
void handleAdmin() { | |
// Create a string containing all the arguments, send them out to the serial port | |
Serial.printf("handleAdmin\n"); | |
// Construct a list of all the arguments for debug | |
String message = "#Args:" + String(webServer.args()) + "\n"; | |
for (int i = 0; i < webServer.args(); i++) | |
message.concat(webServer.argName(i) + ": "+ webServer.arg(i) + "\n"); | |
Serial.print(message); | |
// Check to see that the submitted password is the same as the current password | |
if(webServer.hasArg("oldpassword")) { | |
// Check to see that the submitted password is the same as the current password | |
if(strcmp(webServer.arg("oldpassword").c_str(), config.password) == 0) { | |
// If the passwords match we can update the config with the new settings | |
// Check to see if there is a new value (also doubles to check the length of the new value is long enough) | |
if(webServer.arg("newpassword").length() > 7) | |
webServer.arg("newpassword").toCharArray(config.password, sizeof(config.password)); | |
if(webServer.arg("newssid").length() > 7) | |
webServer.arg("newssid").toCharArray(config.ssid, sizeof(config.ssid)); | |
// Store the new settings to EEPROM | |
EEPROM_writeAnything(0, config); | |
EEPROM.commit(); | |
// Construct a message to tell the user that the change worked | |
message = "New settings will take effect after restart"; | |
Serial.println(message); | |
} | |
else { | |
// Construct a message to tell the user that the change failed | |
message = "Incorrect password please try again"; | |
} | |
// Reply with a web page to indicate success or failure | |
message = "<html><head><meta http-equiv='refresh' content='5;url=/' /></head><body><h1>" + message; | |
message += "<br/>Redirecting in 5 seconds...</h1></body></html>"; | |
webServer.send(200, "text/html", message); | |
} | |
} |