Wi-Fi station mode
Joining a network you do not own, and doing it in a way that survives a router being slow, a password being wrong, and the network disappearing halfway through.
What it does
Station mode means the board joins a network somebody else runs, the same way a laptop does. It asks the router for an address, the router hands one out, and from then on the board can reach anything on that network and anything on the internet beyond it.
The part people underestimate is that joining takes time — usually two to six seconds, occasionally much longer, and sometimes never. Code that assumes the connection is up by the next line will work on your desk and fail in someone's house.
Treat "connected" as something that becomes true later
Start the connection in setup(), then check the state once per pass through
loop(). Everything that needs the network — a web server, an MQTT client, an
NTP sync — waits for that check rather than assuming.
The alternative is a device that does nothing at all while the router is slow: no display, no button, no readings, and a watchdog reset if it goes on long enough.
Two facts that catch everybody
- There is no 5 GHz radio on any ESP32. A network that works on your phone and not on the board is usually two bands sharing one name.
- The reason code is worth printing. Reason 15 is a bad password, 201 is a network that was never found, and those two send you in completely different directions.
The code
Connect once in setup(), then check the state instead of assuming it. The loop below never blocks for longer than a second.
#include <WiFi.h>
const char* SSID = "your-network";
const char* PASS = "your-password";
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false); // otherwise replies can take seconds
WiFi.begin(SSID, PASS);
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < 15000) {
delay(250);
Serial.print(".");
}
if (WiFi.status() != WL_CONNECTED) {
Serial.printf("\nfailed, reason %d\n", WiFi.status());
return; // carry on offline rather than hanging
}
Serial.println(WiFi.localIP());
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect(); // cheap, and it does nothing if already up
delay(1000);
}
}WiFi.setSleep(false) is the line most people are missing when a board answers slowly. It costs about 20 mA and removes multi-second response times.
Same idea, and you can paste it straight into the REPL to watch it happen. Credentials live in a file on the board, so changing the password does not mean reflashing.
import network, time
SSID = "your-network"
PASS = "your-password"
def connect(timeout=15):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if wlan.isconnected():
return wlan
wlan.connect(SSID, PASS)
start = time.ticks_ms()
while not wlan.isconnected():
if time.ticks_diff(time.ticks_ms(), start) > timeout * 1000:
print("failed, status", wlan.status())
return None # carry on offline rather than hanging
time.sleep_ms(250)
print(wlan.ifconfig()[0])
return wlan
wlan = connect()wlan.status() returns a number here too. 1010 is connected, 201 means the network was not found, 202 is a wrong password.
When it does not work
Almost always a 5 GHz network. No ESP32 has a 5 GHz radio. The phone that set up the router may be hiding that there are two bands with the same name.
Check power before code. Transmit bursts pull over 300 mA and a thin USB cable browns the board out. The reboot looks exactly like a crash.
On the original ESP32 the second ADC block shares hardware with the radio. Move the sensor to an ADC1 pin. This does not affect the newer chips.
Print the disconnect reason code instead of guessing. Reason 15 is a bad password, reason 201 means the network was never found, and those two send you in completely different directions.
Joining somebody else's network is half of it. Running your own is what a board does when there is no network to join.
Access point mode →Edit this page — content/esp32/wifi-station-mode.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.