ESP32 Part 2 - Wireless

E12. Credentials, Reconnect and SoftAP Provisioning

Try to join Wi-Fi and fall back to the ESP32's own network if it fails, then collect a network name and password from a phone.

Estimated time 3-4 hours

Learning outcomes

  • Explain why real products do not have the Wi-Fi password typed into the code
  • Give up joining after a set number of tries and start an access point instead
  • Serve a form and read what the user typed into it

Parts and preparation

ESP32 DevKit, USB, phone or PC that can join the ESP32's network.

Before power: inspect wiring, confirm supply voltage and ensure all connected circuits share GND.

Libraries for this lesson

In Arduino IDE 2 open Tools → Manage Libraries…. Search by the Library Manager name and install the package by the exact author below. Similar names from other authors can use different APIs and will break the example.

IncludeLibrary Manager nameAuthorInstall note
WiFi.hWiFiEspressifBuilt into the esp32 Arduino core.
WebServer.hWebServerEspressifBuilt into the esp32 Arduino core.

What you will do

A smart plug you buy cannot have your Wi-Fi password in its code. Instead, the first time it starts it makes its own network, you join it with your phone, and you type your password into a page. This is called provisioning.

Sketch 1 tries your Wi-Fi for 10 seconds and makes its own network if that fails. Sketch 2 shows a form where you type a network name and password.

STA fail then SoftAP setup with phone
Try to join; if that fails, make a setup network.

Give up after a while

The Wi-Fi sketches so far wait for ever. Sketch 1 counts its tries and stops after 20 (20 x half a second = 10 seconds). The while loop keeps going only while both things are true: not connected AND fewer than 20 tries. && means AND.

What happensWhat you see
TryingDots on Serial
JoinedThe IP address
Gave upESP32-Setup appears in your phone's Wi-Fi list

Forms

An HTML form has boxes to type in and a button. Each box has a name, such as name='ssid'. When you press the button, the browser sends what you typed to /save, and server.arg("ssid") reads it.

Sketch 2 only prints what you typed. A real product would save it with the Preferences library so it is kept after power-off, then restart and join the network. The optional extension at the end of this lesson does exactly that.

How to test

Leave YOUR_WIFI_SSID in sketch 1 to see it give up and make ESP32-Setup. Type your real network to see it join. The setup network's address is usually 192.168.4.1.

Wiring and safe build sequence

  1. USB only
  2. Phone joins ESP32-Setup for the tests
Power rule: switch off before moving wires. Arduino I/O pins are control signals; high-current loads require a driver and suitable external supply.

Worked sketch 1: Join, or make a setup network

Download .ino

What this sketch is for: Upload it with the placeholders left in. After 10 seconds, ESP32-Setup appears on your phone.

#include <WiFi.h>                         // the Wi-Fi commands

String ssid = "YOUR_WIFI_SSID";           // your Wi-Fi network name
String password = "YOUR_WIFI_PASSWORD";   // your Wi-Fi password

void setup() {                            // runs once
  Serial.begin(115200);                   // start Serial at the ESP32 speed

  WiFi.begin(ssid, password);             // start joining the network
  Serial.print("Trying Wi-Fi");           // tell the user we are trying
  int tries = 0;                          // how many times we have checked
  while (WiFi.status() != WL_CONNECTED && tries < 20) { // not joined AND under 20 tries
    delay(500);                           // wait half a second
    Serial.print(".");                    // print a dot each time
    tries = tries + 1;                    // count this try
  }
  Serial.println();                       // end the line of dots

  if (WiFi.status() == WL_CONNECTED) {    // did we join?
    Serial.print("Joined. IP address: "); // yes - say so
    Serial.println(WiFi.localIP());       // and show the address
  } else {                                // no - we gave up
    WiFi.softAP("ESP32-Setup");           // make our own network (no password)
    Serial.print("Could not join. Connect to ESP32-Setup, address: "); // tell the user
    Serial.println(WiFi.softAPIP());      // usually 192.168.4.1
  }
}

void loop() {                             // nothing to repeat in this sketch
}

How the code works

  1. && means AND: the loop stops as soon as either test is false.
  2. WiFi.softAP("ESP32-Setup") with no password makes an open network, which is fine for a short setup step.

Worked sketch 2: A setup form

Download .ino

What this sketch is for: Join ESP32-Setup on your phone, open http://192.168.4.1, fill in the form and press Send. Serial shows what you typed. Use a made-up password for testing.

#include <WiFi.h>                         // the Wi-Fi commands
#include <WebServer.h>                    // the web server commands

WebServer server(80);                     // a web server on port 80

void sendForm() {                         // runs when a browser asks for /
  String page = "<h1>Wi-Fi setup</h1>";   // a heading
  page += "<form method='POST' action='/save'>"; // a form that sends to /save
  page += "<p>Network name <input name='ssid'></p>"; // a box called ssid
  page += "<p>Password <input name='password'></p>"; // a box called password
  page += "<button>Send</button>";        // the send button
  page += "</form>";                      // end of the form
  server.send(200, "text/html", page);    // send the page to the browser
}

void handleSave() {                       // runs when the form is sent to /save
  Serial.print("Network name: ");         // a label
  Serial.println(server.arg("ssid"));     // what was typed in the ssid box
  Serial.print("Password: ");             // a label
  Serial.println(server.arg("password")); // what was typed in the password box
  server.send(200, "text/html", "<h1>Received - thank you</h1>"); // reply to the phone
}

void setup() {                            // runs once
  Serial.begin(115200);                   // start Serial at the ESP32 speed
  WiFi.softAP("ESP32-Setup");             // make our own network
  Serial.print("Join ESP32-Setup and open http://"); // tell the user what to do
  Serial.println(WiFi.softAPIP());        // usually 192.168.4.1

  server.on("/", sendForm);               // path / shows the form
  server.on("/save", handleSave);         // path /save reads the form
  server.begin();                         // start the web server
}

void loop() {                             // runs over and over
  server.handleClient();                  // answer any browser that is asking
}

How the code works

  1. method='POST' sends the boxes hidden inside the request, not in the address bar.
  2. The names in name='ssid' and server.arg("ssid") must match exactly.
  3. Printing a password is only for learning - a real product saves it and never prints it.

Test and record evidence

Expected result: Sketch 1 with the placeholders: dots for 10 seconds, then Could not join, and ESP32-Setup appears on your phone. With your real network: Joined and the IP address. Sketch 2: the form appears at 192.168.4.1, and after Send, Serial shows the name and password you typed.

Practical evidence checklist

Common faults and checks
  • ESP32-Setup missing: wait the full 10 seconds and watch Serial.
  • Phone switches back to home Wi-Fi: it may dislike networks without internet - tell it to stay connected.
  • Form page does not open: type http://192.168.4.1 exactly, while joined to ESP32-Setup.
Challenge (extension - optional): Change sketch 1 so it tries for 20 seconds instead of 10. Which number do you change, and to what?

Check your understanding

Q1. Why do real products not have the Wi-Fi password in their code?

Show answer

Every home has a different network, so the user types it in at setup.

Q2. What is provisioning?

Show answer

Giving a new device its Wi-Fi details, usually from a phone.

Q3. What does && mean?

Show answer

AND - both tests must be true.

Q4. How long does sketch 1 try before giving up?

Show answer

10 seconds: 20 tries of half a second.

Q5. What is the usual address of the ESP32's own network?

Show answer

192.168.4.1.

Q6. How does handleSave read the network name?

Show answer

server.arg("ssid") - the name of the form box.

Q7. What would a real product do instead of printing the password?

Show answer

Save it with the Preferences library and use it to join the network.

Optional extension - skip on your first pass through this lesson.

Save the Wi-Fi details with Preferences

This sketch joins sketch 1 and sketch 2 together and adds saving. When it starts, it reads the saved network name and password and tries to join. If nothing is saved yet, or joining fails, it makes ESP32-Setup and shows the form. When you press Send, it saves what you typed and restarts. This time it joins your network.

Preferences is a small storage area in the ESP32's flash memory. It keeps values after power-off, like a notebook. Each value has a name: saved.putString("ssid", ...) writes the name, and saved.getString("ssid", "") reads it back, giving "" (nothing) if nothing was saved yet.

To test: upload, join ESP32-Setup on your phone, open http://192.168.4.1 and type your real network name and password. The ESP32 restarts and Serial shows Joined. Unplug it and plug it back in: it joins again without the form. There is no password in the code at all.

#include <WiFi.h>                         // the Wi-Fi commands
#include <WebServer.h>                    // the web server commands
#include <Preferences.h>                  // saves small values that survive power-off

WebServer server(80);                     // a web server on port 80
Preferences saved;                        // our notebook of saved values

void sendForm() {                         // runs when a browser asks for /
  String page = "<h1>Wi-Fi setup</h1>";   // a heading
  page += "<form method='POST' action='/save'>"; // a form that sends to /save
  page += "<p>Network name <input name='ssid'></p>"; // a box called ssid
  page += "<p>Password <input name='password'></p>"; // a box called password
  page += "<button>Send</button>";        // the send button
  page += "</form>";                      // end of the form
  server.send(200, "text/html", page);    // send the page to the browser
}

void handleSave() {                       // runs when the form is sent to /save
  saved.putString("ssid", server.arg("ssid"));         // save the network name
  saved.putString("password", server.arg("password")); // save the password
  server.send(200, "text/html", "<h1>Saved - restarting</h1>"); // reply to the phone
  delay(1000);                            // give the reply time to reach the phone
  ESP.restart();                          // start again, this time with the saved details
}

void setup() {                            // runs once
  Serial.begin(115200);                   // start Serial at the ESP32 speed
  saved.begin("wifi", false);             // open the notebook called wifi (false = we may write)
  String ssid = saved.getString("ssid", "");         // the saved name, or "" if none yet
  String password = saved.getString("password", ""); // the saved password, or "" if none yet

  WiFi.begin(ssid, password);             // start joining the saved network
  Serial.print("Trying Wi-Fi");           // tell the user we are trying
  int tries = 0;                          // how many times we have checked
  while (WiFi.status() != WL_CONNECTED && tries < 20) { // not joined AND under 20 tries
    delay(500);                           // wait half a second
    Serial.print(".");                    // print a dot each time
    tries = tries + 1;                    // count this try
  }
  Serial.println();                       // end the line of dots

  if (WiFi.status() == WL_CONNECTED) {    // did we join?
    Serial.print("Joined. IP address: "); // yes - say so
    Serial.println(WiFi.localIP());       // and show the address
  } else {                                // no - nothing saved, or the details were wrong
    WiFi.softAP("ESP32-Setup");           // make our own network
    Serial.print("Join ESP32-Setup and open http://"); // tell the user what to do
    Serial.println(WiFi.softAPIP());      // usually 192.168.4.1
    server.on("/", sendForm);             // path / shows the form
    server.on("/save", handleSave);       // path /save saves the form
    server.begin();                       // start the web server
  }
}

void loop() {                             // runs over and over
  server.handleClient();                  // answer any browser (only in setup mode)
}

How the saving sketch works

The very first time, nothing is saved, so it tries an empty network name for 10 seconds and then makes ESP32-Setup. That is expected.

"wifi" in saved.begin("wifi", false) is the notebook's name. Other sketches can use their own notebook without mixing values.

If you type the password wrong, it will fail to join after the restart and show the form again, so you can fix it.

Uploading a new sketch does not wipe Preferences. The saved details stay until you save new ones.