Few things are more frustrating than an ESP32 that prints dots forever, or one that worked all week and silently went offline overnight. The good news is that nearly every ESP32 WiFi failure falls into a short list of causes, and the chip will usually tell you which one if you ask it the right way.
This guide is a diagnosis tool, not a pile of random tips. First you will make the ESP32 report why it failed, using status codes and disconnect reason codes. Then you will work through the real causes in order of how often they happen: credentials and band, router settings, signal strength, power, DHCP and DNS, and sleep behavior. Finally you will add a reconnect strategy that recovers from outages without a manual reset. If you are new to the basics, start with the ESP32 WiFi tutorial.
Step 1: Make the ESP32 Tell You Why
Do not guess. Register an event handler that prints the disconnect reason code. This one change turns "it does not work" into a specific problem.
#include <WiFi.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
const char* reasonText(uint8_t r) {
switch (r) {
case 2: return "AUTH_EXPIRE (authentication expired)";
case 4: return "ASSOC_EXPIRE";
case 5: return "ASSOC_TOOMANY (router has no free client slots)";
case 6: return "NOT_AUTHED";
case 8: return "ASSOC_LEAVE (router disconnected us)";
case 15: return "4WAY_HANDSHAKE_TIMEOUT (usually wrong password)";
case 200: return "BEACON_TIMEOUT (signal lost)";
case 201: return "NO_AP_FOUND (network not visible)";
case 202: return "AUTH_FAIL (authentication failed)";
case 203: return "ASSOC_FAIL";
case 204: return "HANDSHAKE_TIMEOUT";
case 205: return "CONNECTION_FAIL";
default: return "other";
}
}
void onWiFiEvent(arduino_event_id_t event, arduino_event_info_t info) {
switch (event) {
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
Serial.println("Associated with the access point");
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: {
uint8_t r = info.wifi_sta_disconnected.reason;
Serial.printf("Disconnected, reason %u: %s\n", r, reasonText(r));
break;
}
default:
break;
}
}
void setup() {
Serial.begin(115200);
WiFi.onEvent(onWiFiEvent);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
}
void loop() {
}
Upload it and read the reason that appears in the Serial Monitor. The table below maps the common ones to a fix.
| Reason | What it means | Go to |
|---|---|---|
| 201 NO_AP_FOUND | The ESP32 cannot see that network name | Step 2 (name, band, range) |
| 15 / 202 / 204 | Password or security mode problem | Step 2 and Step 3 |
| 200 BEACON_TIMEOUT | Signal dropped or was too weak | Step 4 |
| 8 ASSOC_LEAVE, 5 ASSOC_TOOMANY | The router kicked it out or is full | Step 3 |
| Connects then loses link repeatedly | Power or signal | Steps 4 and 5 |
| Connected, but no internet or no names resolve | DHCP or DNS | Step 6 |
Step 2: The Four Most Common Causes (Check These First)
1. Wrong SSID or password. Case matters, spaces matter, and look-alike characters (zero and capital O, lowercase l and capital I) cause endless trouble. Copy the values from your router or phone settings. Reason 15 (handshake timeout) and 202 (auth failure) point here. A trailing space in the password string is invisible and fatal.
2. The network is 5 GHz only. The ESP32 supports 2.4 GHz only. If your router broadcasts the 5 GHz band under a separate name, connect to the 2.4 GHz name. If your router uses one combined name with band steering, the ESP32 normally still works, but turning band steering off or giving the 2.4 GHz band its own name often cures stubborn failures. Reason 201 with a network you can clearly see on your phone is the classic sign.
3. The network is out of range or invisible to the ESP32. Run a scan (see the WiFi tutorial) and check your network appears, with a signal better than about -75 dBm. Hidden networks need the exact name and sometimes a scan on the right channel.
4. Special characters or unusual names. Very long SSIDs, emoji or non-ASCII characters in the network name occasionally cause trouble. A quick test is to create a simple temporary network (for example from a phone hotspot) with a plain name and password. If that works, the issue is in the router's naming or security configuration, not your code.
A very useful experiment: try your phone's hotspot. If the ESP32 connects to the hotspot but not to your router, the code and hardware are fine and the router settings are the problem. On an iPhone, turn on Maximize Compatibility in the hotspot settings so it uses 2.4 GHz.
Step 3: Router Settings That Block the ESP32
| Setting | Problem | Fix |
|---|---|---|
| WPA3-only or strict WPA3 | Many ESP32 setups handle WPA2 best, and WPA3-only networks can fail | Use WPA2-PSK (AES) or a WPA2/WPA3 mixed mode |
| WPA/TKIP or old WEP | Insecure and often refused | Switch to WPA2 with AES |
| WPA2-Enterprise (username and login) | Needs special enterprise code | Use a separate normal WPA2 network for IoT devices |
| MAC address filtering | The router silently rejects unknown devices | Add the ESP32's MAC (print it with WiFi.macAddress()) or turn filtering off |
| Guest network or AP isolation | Device connects but cannot be reached | Disable client isolation, or use the main network |
| Maximum client limit reached | Reason 5, new devices refused | Remove old devices or raise the limit |
| Channel 12 to 14 | Some regions and chips handle high channels poorly | Set the 2.4 GHz channel to 1, 6 or 11 |
| 802.11n-only or 802.11ax-only | ESP32 is b/g/n | Allow 802.11b/g/n mixed mode |
| Captive portal networks (hotels, cafes) | Needs a browser login first | Not suitable without extra work |
Printing the ESP32 MAC address is a one-liner:
WiFi.mode(WIFI_STA);
Serial.println(WiFi.macAddress());
Changing router settings can disturb other devices, so change one thing at a time and test.
Step 4: Signal Strength
A device that connects but keeps dropping is very often a weak-signal device. Measure it:
Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
| RSSI | Meaning |
|---|---|
| Better than -60 dBm | Good |
| -60 to -70 dBm | Acceptable |
| -70 to -80 dBm | Marginal. Expect drops and slow replies |
| Worse than -80 dBm | Unreliable. Reason 200 (beacon timeout) is typical |
What helps, roughly in order of effect:
- Move the ESP32 or the router closer, or add an access point or mesh node nearby.
- Keep the antenna free. Metal enclosures, ground planes, batteries and thick walls hurt. On boards with a PCB antenna, keep the antenna end of the module clear of other parts.
- Use a board with an external antenna connector and a proper antenna for far-away installs.
- Pick a clear channel. Neighbors on the same 2.4 GHz channel add interference. Channels 1, 6 and 11 do not overlap.
- Keep it away from noise sources such as microwave ovens, motor drivers and USB 3 cables, which radiate in the 2.4 GHz band.
Some very small boards (certain ESP32-C3 mini boards with tiny antennas) connect more reliably if you lower the transmit power with WiFi.setTxPower(WIFI_POWER_8_5dBm); after WiFi.begin(). It sounds backwards, but on a few designs it stops the antenna and regulator from fighting each other. Treat it as a last-resort experiment.
Step 5: Power Problems Look Exactly Like WiFi Bugs
The WiFi radio draws brief current spikes of several hundred milliamps when it transmits. A weak USB port, a thin or long cable, a cheap charger or a dying battery makes the supply voltage sag, and the chip responds by resetting or dropping the link. The signs:
- The Serial Monitor shows "Brownout detector was triggered".
- The board reboots while connecting or while sending data.
- WiFi works with a short cable on a good charger and fails on a long cable.
Fixes: use a 5V supply rated 1A or more, a short, good-quality USB cable (many cheap ones have thin wires), and add a 470 µF to 1000 µF capacitor across the 5V and GND pins close to the board. Avoid powering the board from a laptop port through a long cable together with a motor or a relay, and never run servos or relays from the ESP32's own 3.3V. Do not disable the brownout detector to hide the symptom: it is warning you about a real problem.
The ESP32-CAM guide explains the same power issue in the context of cameras, where it is even more common.
Step 6: Connected, But Nothing Works (DHCP and DNS)
If the Serial Monitor shows Got IP but requests still fail, the network layer is the issue.
IP is 0.0.0.0. The ESP32 associated with the router but never received an address. The router's DHCP pool may be full, or DHCP is off. Free up addresses, enlarge the pool or reboot the router.
Can reach IP addresses but not names (getaddrinfo fails, HTTP returns -1). DNS is missing. This is the classic result of a static IP configured without DNS servers. Add the DNS arguments to WiFi.config(), as explained in the static IP guide.
Two devices fight and keep dropping. A duplicate IP address. Give each device a unique address or use DHCP reservations.
The device is reachable from one phone and not from another. The two are on different networks or one is on a guest network with isolation.
Test internet reachability from the ESP32 with a short HTTP request (GET and POST guide), and print WiFi.gatewayIP() and WiFi.dnsIP() to confirm they are sensible.
Step 7: It Works for Hours, Then Dies
Long-running failures need a different approach, because you cannot watch the Serial Monitor for three days.
Power saving. The ESP32 uses modem sleep between router beacons by default. On some routers this causes missed packets and slow or lost connections. Turn it off for always-powered devices:
WiFi.setSleep(false);
This costs a little more power but often fixes random latency and drops on mains-powered devices. Do not use it on battery projects. For those, see the deep sleep guide.
Router lease or reboot. After a router restart the ESP32 must notice the loss and reconnect. If your sketch waits forever in a loop that never checks the status, it stays dead. The next section handles this.
Memory leaks. A sketch that slowly loses heap eventually crashes. Print ESP.getFreeHeap() every few minutes. A number that keeps falling points to an object created in a loop and never released (a common culprit is forgetting http.end()).
A Reconnect Strategy That Actually Recovers
This pattern checks the connection regularly, retries with a growing delay, and restarts the chip as a last resort so a device in a remote location never stays offline.
#include <WiFi.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
const unsigned long CHECK_INTERVAL = 10000; // check every 10 s
const unsigned long RESTART_AFTER_MS = 600000; // restart after 10 min offline
unsigned long lastCheck = 0;
unsigned long offlineSince = 0;
unsigned long backoff = 5000;
unsigned long nextAttempt = 0;
void startWiFi() {
WiFi.disconnect(true);
delay(100);
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.persistent(false); // do not rewrite flash on every connect
WiFi.begin(ssid, password);
}
void maintainWiFi() {
if (millis() - lastCheck < CHECK_INTERVAL) return;
lastCheck = millis();
if (WiFi.status() == WL_CONNECTED) {
offlineSince = 0;
backoff = 5000;
return;
}
if (offlineSince == 0) offlineSince = millis();
Serial.printf("WiFi down for %lu s, retrying\n", (millis() - offlineSince) / 1000);
if (millis() >= nextAttempt) {
startWiFi();
nextAttempt = millis() + backoff;
backoff = min(backoff * 2, 60000UL);
}
if (millis() - offlineSince > RESTART_AFTER_MS) {
Serial.println("Offline too long, restarting");
ESP.restart();
}
}
void setup() {
Serial.begin(115200);
startWiFi();
}
void loop() {
maintainWiFi();
// your normal code here
}
The ideas matter more than the exact numbers: check periodically with millis(), back off between attempts so you do not hammer a router that is rebooting, and fall back to a full restart after a long outage. Combine it with the event-based reconnect from the WiFi tutorial if you want instant reaction as well.
For an even stronger safety net, enable the hardware watchdog so a hung sketch resets itself, and publish a heartbeat message (for example over MQTT) so you notice a silent device quickly.
Quick Reset Tricks
When a board is stuck in a strange state, these help:
- Erase stored WiFi settings.
WiFi.disconnect(true, true);clears saved credentials and settings, then reconnect with a freshWiFi.begin(). - Full flash erase. In the Arduino IDE choose Tools → Erase All Flash Before Sketch Upload → Enabled, upload once, then switch it back. This clears corrupted NVS data.
- Power-cycle the router (not just the ESP32). Routers can hold a stale record of a device.
- Try a different ESP32 board. Cheap clones vary, and a faulty antenna or regulator is rare but real.
- Isolate the problem. Run the bare connection sketch with nothing else attached. If it connects reliably, add your peripherals one at a time.
Checklist You Can Print
- Print the disconnect reason code.
- Is the network 2.4 GHz, visible in a scan, with RSSI better than -75 dBm?
- Are SSID and password exactly right (try a phone hotspot to rule out code)?
- Is the router using WPA2, with no MAC filtering, no client limit and no isolation?
- Is the power supply solid (1A or more, short cable, capacitor)?
- Does it get an IP, DNS and gateway?
- For long runs:
WiFi.setSleep(false)on mains power, reconnect logic and a restart fallback.
Frequently Asked Questions
Why does my ESP32 connect to my phone hotspot but not my router? Router security mode, band steering, MAC filtering or a WPA3-only setting. Use the checklist above.
Why does it say WL_NO_SSID_AVAIL? It cannot see the network. Usually the wrong name, a 5 GHz-only network or poor range.
How do I stop it disconnecting overnight? Disable modem sleep on mains power, add the reconnect strategy and check the power supply.
Should I use a static IP to fix disconnects? It can help with router DHCP quirks, but it does not fix signal or power problems. See the static IP guide.
How can I let users fix WiFi without re-flashing? Add a configuration portal with WiFiManager.