Wi-Fi setup portal
No passwords in the sketch: the board opens its own setup network with a page to pick your Wi-Fi, saves it, and joins it from then on.
Wiring
Nothing to wire. This one runs on the bare board.
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32 Dev Module
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>
const int BOOT_BUTTON = 0; // the board's BOOT button
const char* AP_PASS = "bench1234"; // password for the setup network, at least 8 characters
Preferences prefs;
WebServer server(80);
DNSServer dns;
bool portal = false;
String options; // network names found when the portal started
unsigned long lastBeat = 0;
String escapeHtml(const String& s) {
String out;
for (char c : s) {
if (c == '<') out += "<";
else if (c == '&') out += "&";
else if (c == '"') out += """;
else out += c;
}
return out;
}
void handleRoot() {
String page = "<!doctype html><html><head><meta name=viewport content='width=device-width,initial-scale=1'>";
page += "<title>Wi-Fi setup</title></head><body style='font-family:sans-serif;margin:16px'><h1>Wi-Fi setup</h1>";
page += "<form method=post action=/save><p><label>Network<br><input name=ssid list=nets required></label></p>";
page += "<datalist id=nets>";
page += options;
page += "</datalist><p><label>Password<br><input name=pass type=password></label></p>";
page += "<p><button>Save and restart</button></p></form></body></html>";
server.send(200, "text/html", page);
}
void handleSave() {
prefs.putString("ssid", server.arg("ssid"));
prefs.putString("pass", server.arg("pass"));
server.send(200, "text/html", "<p style='font-family:sans-serif'>Saved. The board restarts and joins the network.</p>");
Serial.printf("saved network %s, restarting\n", server.arg("ssid").c_str());
delay(1000);
ESP.restart();
}
void startPortal() {
portal = true;
WiFi.mode(WIFI_AP_STA);
int n = WiFi.scanNetworks();
for (int i = 0; i < n; i++) {
options += "<option value=\"";
options += escapeHtml(WiFi.SSID(i));
options += "\">";
}
char name[24];
snprintf(name, sizeof name, "ESP32-Setup-%04x", (unsigned)(ESP.getEfuseMac() >> 32) & 0xffff);
WiFi.softAP(name, AP_PASS);
delay(100);
dns.start(53, "*", WiFi.softAPIP()); // answer every name with the board, so phones open the page
server.on("/", handleRoot);
server.on("/save", HTTP_POST, handleSave);
server.onNotFound([]() {
server.sendHeader("Location", "http://192.168.4.1/", true);
server.send(302, "text/plain", "");
});
server.begin();
Serial.printf("portal up: join %s (password %s), then open http://192.168.4.1\n", name, AP_PASS);
}
void setup() {
Serial.begin(115200);
pinMode(BOOT_BUTTON, INPUT_PULLUP);
prefs.begin("wifi", false);
// Holding BOOT while the board resets enters download mode, so the button is
// only checked in the first two seconds after start-up.
Serial.println("hold BOOT now to forget the saved network");
unsigned long start = millis();
while (millis() - start < 2000) {
if (digitalRead(BOOT_BUTTON) == LOW) {
prefs.clear();
Serial.println("BOOT held: saved network cleared");
break;
}
delay(20);
}
String ssid = prefs.getString("ssid", "");
if (ssid.length() == 0) {
Serial.println("no saved network");
startPortal();
return;
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), prefs.getString("pass", "").c_str());
Serial.printf("wifi joining %s\n", ssid.c_str());
if (WiFi.waitForConnectResult(15000) == WL_CONNECTED) {
Serial.printf("wifi ok %s\n", WiFi.localIP().toString().c_str());
} else {
Serial.println("wifi failed");
startPortal();
}
}
void loop() {
if (portal) {
dns.processNextRequest();
server.handleClient();
return;
}
// Your own code goes here. The board reconnects by itself if the network drops.
if (millis() - lastBeat > 10000) {
lastBeat = millis();
Serial.printf("online, rssi %d dBm\n", WiFi.RSSI());
}
}Wiring
Nothing to wire. This one runs on the bare board.
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S3 Dev Module
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>
const int BOOT_BUTTON = 0; // the board's BOOT button
const char* AP_PASS = "bench1234"; // password for the setup network, at least 8 characters
Preferences prefs;
WebServer server(80);
DNSServer dns;
bool portal = false;
String options; // network names found when the portal started
unsigned long lastBeat = 0;
String escapeHtml(const String& s) {
String out;
for (char c : s) {
if (c == '<') out += "<";
else if (c == '&') out += "&";
else if (c == '"') out += """;
else out += c;
}
return out;
}
void handleRoot() {
String page = "<!doctype html><html><head><meta name=viewport content='width=device-width,initial-scale=1'>";
page += "<title>Wi-Fi setup</title></head><body style='font-family:sans-serif;margin:16px'><h1>Wi-Fi setup</h1>";
page += "<form method=post action=/save><p><label>Network<br><input name=ssid list=nets required></label></p>";
page += "<datalist id=nets>";
page += options;
page += "</datalist><p><label>Password<br><input name=pass type=password></label></p>";
page += "<p><button>Save and restart</button></p></form></body></html>";
server.send(200, "text/html", page);
}
void handleSave() {
prefs.putString("ssid", server.arg("ssid"));
prefs.putString("pass", server.arg("pass"));
server.send(200, "text/html", "<p style='font-family:sans-serif'>Saved. The board restarts and joins the network.</p>");
Serial.printf("saved network %s, restarting\n", server.arg("ssid").c_str());
delay(1000);
ESP.restart();
}
void startPortal() {
portal = true;
WiFi.mode(WIFI_AP_STA);
int n = WiFi.scanNetworks();
for (int i = 0; i < n; i++) {
options += "<option value=\"";
options += escapeHtml(WiFi.SSID(i));
options += "\">";
}
char name[24];
snprintf(name, sizeof name, "ESP32-Setup-%04x", (unsigned)(ESP.getEfuseMac() >> 32) & 0xffff);
WiFi.softAP(name, AP_PASS);
delay(100);
dns.start(53, "*", WiFi.softAPIP()); // answer every name with the board, so phones open the page
server.on("/", handleRoot);
server.on("/save", HTTP_POST, handleSave);
server.onNotFound([]() {
server.sendHeader("Location", "http://192.168.4.1/", true);
server.send(302, "text/plain", "");
});
server.begin();
Serial.printf("portal up: join %s (password %s), then open http://192.168.4.1\n", name, AP_PASS);
}
void setup() {
Serial.begin(115200);
pinMode(BOOT_BUTTON, INPUT_PULLUP);
prefs.begin("wifi", false);
// Holding BOOT while the board resets enters download mode, so the button is
// only checked in the first two seconds after start-up.
Serial.println("hold BOOT now to forget the saved network");
unsigned long start = millis();
while (millis() - start < 2000) {
if (digitalRead(BOOT_BUTTON) == LOW) {
prefs.clear();
Serial.println("BOOT held: saved network cleared");
break;
}
delay(20);
}
String ssid = prefs.getString("ssid", "");
if (ssid.length() == 0) {
Serial.println("no saved network");
startPortal();
return;
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), prefs.getString("pass", "").c_str());
Serial.printf("wifi joining %s\n", ssid.c_str());
if (WiFi.waitForConnectResult(15000) == WL_CONNECTED) {
Serial.printf("wifi ok %s\n", WiFi.localIP().toString().c_str());
} else {
Serial.println("wifi failed");
startPortal();
}
}
void loop() {
if (portal) {
dns.processNextRequest();
server.handleClient();
return;
}
// Your own code goes here. The board reconnects by itself if the network drops.
if (millis() - lastBeat > 10000) {
lastBeat = millis();
Serial.printf("online, rssi %d dBm\n", WiFi.RSSI());
}
}Wiring
Nothing to wire. This one runs on the bare board.
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32C3 Dev Module
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <Preferences.h>
const int BOOT_BUTTON = 9; // the board's BOOT button
const char* AP_PASS = "bench1234"; // password for the setup network, at least 8 characters
Preferences prefs;
WebServer server(80);
DNSServer dns;
bool portal = false;
String options; // network names found when the portal started
unsigned long lastBeat = 0;
String escapeHtml(const String& s) {
String out;
for (char c : s) {
if (c == '<') out += "<";
else if (c == '&') out += "&";
else if (c == '"') out += """;
else out += c;
}
return out;
}
void handleRoot() {
String page = "<!doctype html><html><head><meta name=viewport content='width=device-width,initial-scale=1'>";
page += "<title>Wi-Fi setup</title></head><body style='font-family:sans-serif;margin:16px'><h1>Wi-Fi setup</h1>";
page += "<form method=post action=/save><p><label>Network<br><input name=ssid list=nets required></label></p>";
page += "<datalist id=nets>";
page += options;
page += "</datalist><p><label>Password<br><input name=pass type=password></label></p>";
page += "<p><button>Save and restart</button></p></form></body></html>";
server.send(200, "text/html", page);
}
void handleSave() {
prefs.putString("ssid", server.arg("ssid"));
prefs.putString("pass", server.arg("pass"));
server.send(200, "text/html", "<p style='font-family:sans-serif'>Saved. The board restarts and joins the network.</p>");
Serial.printf("saved network %s, restarting\n", server.arg("ssid").c_str());
delay(1000);
ESP.restart();
}
void startPortal() {
portal = true;
WiFi.mode(WIFI_AP_STA);
int n = WiFi.scanNetworks();
for (int i = 0; i < n; i++) {
options += "<option value=\"";
options += escapeHtml(WiFi.SSID(i));
options += "\">";
}
char name[24];
snprintf(name, sizeof name, "ESP32-Setup-%04x", (unsigned)(ESP.getEfuseMac() >> 32) & 0xffff);
WiFi.softAP(name, AP_PASS);
delay(100);
dns.start(53, "*", WiFi.softAPIP()); // answer every name with the board, so phones open the page
server.on("/", handleRoot);
server.on("/save", HTTP_POST, handleSave);
server.onNotFound([]() {
server.sendHeader("Location", "http://192.168.4.1/", true);
server.send(302, "text/plain", "");
});
server.begin();
Serial.printf("portal up: join %s (password %s), then open http://192.168.4.1\n", name, AP_PASS);
}
void setup() {
Serial.begin(115200);
pinMode(BOOT_BUTTON, INPUT_PULLUP);
prefs.begin("wifi", false);
// Holding BOOT while the board resets enters download mode, so the button is
// only checked in the first two seconds after start-up.
Serial.println("hold BOOT now to forget the saved network");
unsigned long start = millis();
while (millis() - start < 2000) {
if (digitalRead(BOOT_BUTTON) == LOW) {
prefs.clear();
Serial.println("BOOT held: saved network cleared");
break;
}
delay(20);
}
String ssid = prefs.getString("ssid", "");
if (ssid.length() == 0) {
Serial.println("no saved network");
startPortal();
return;
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), prefs.getString("pass", "").c_str());
Serial.printf("wifi joining %s\n", ssid.c_str());
if (WiFi.waitForConnectResult(15000) == WL_CONNECTED) {
Serial.printf("wifi ok %s\n", WiFi.localIP().toString().c_str());
} else {
Serial.println("wifi failed");
startPortal();
}
}
void loop() {
if (portal) {
dns.processNextRequest();
server.handleClient();
return;
}
// Your own code goes here. The board reconnects by itself if the network drops.
if (millis() - lastBeat > 10000) {
lastBeat = millis();
Serial.printf("online, rssi %d dBm\n", WiFi.RSSI());
}
}Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 dev board | Buy at Amazon |
Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.
Libraries
WiFi (built in), WebServer (built in), DNSServer (built in), Preferences (built in)
Load it
- In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
- Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
- Close the IDE's serial monitor, then open the bench here to watch the output.
Field notes
- First start: join the ESP32-Setup network from a phone with the password bench1234. Most phones open the setup page by themselves; if not, browse to http://192.168.4.1.
- The network list comes from a scan when the portal starts. Type the name instead if yours is hidden.
- The password is stored in the chip's flash without encryption. Anyone with the board and a USB cable can read it back, so do not ship boards with a network you care about.
- To change networks, reset the board and hold BOOT within two seconds. Holding it while pressing reset puts the board in download mode instead.
- If the saved network is out of reach at start-up, the portal opens again, so a board that moves house can be set up without a cable.
- The WiFiManager library does the same with more polish. This sketch shows how it works in about a hundred lines with only built-in libraries.
Not tested yet
- Not tested on hardware yet. Compiles for every board listed with Arduino-ESP32 core 3.3.12.
Built it? Send a build report with your board and what the serial port printed. Working reports become the "Tested on this board" entries above.