Why Your ESP8266 Resets When analogRead Runs During WiFi Traffic
You add one analogRead(A0) to a sketch that has been stable for days, and now the board reboots every few minutes. Always while WiFi is busy, never when you test the ADC alone on the bench. The ADC itself is rarely the culprit; the way you call it is.
The reset reason is the first clue, so read it before guessing.
Read the reset reason first
The chip tells you why it rebooted. Print it at the top of setup() and you can split the problem into two camps immediately.
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println(ESP.getResetInfo());
}
- "Software Watchdog" or "Hardware Watchdog" means your code (or the loop calling it) starved the WiFi stack.
- "Exception" with a stack dump means a bad pointer or a call from the wrong context.
- "Power on" or "External System" with no software cause points at the supply rail, not the code.
Match the cause to the reset reason before changing anything. The three have different fixes, and mixing them up wastes an evening.
Cause one: a tight loop that never yields
The ESP8266 runs your sketch cooperatively alongside the WiFi stack on one core. The stack only gets time when your code returns from loop() or calls delay() or yield(). A loop that spins on analogRead to build an average never gives that time back.
// Looks harmless, starves the radio
uint32_t sum = 0;
for (int i = 0; i < 5000; i++) {
sum += analogRead(A0);
}
Each read takes on the order of a hundred microseconds, so 5000 of them is half a second with no yield. Push that far enough and the software watchdog fires. I have not timed it on every core version, so measure your own with micros() rather than trusting my figure.
The fix is to sample a little at a time and hand control back.
const uint32_t SAMPLE_EVERY_MS = 100;
uint32_t lastSample = 0;
uint32_t sum = 0;
uint8_t count = 0;
void loop() {
uint32_t now = millis();
if (now - lastSample >= SAMPLE_EVERY_MS) {
lastSample = now;
sum += analogRead(A0);
if (++count == 16) {
Serial.println(sum / 16);
sum = 0;
count = 0;
}
}
yield();
}
One read per pass, no blocking, and the stack runs between them. The unsigned subtraction also survives millis() wrapping, which is a small bonus.
Cause two: the ADC is shared with the radio
Quick detour, because this is the odd part. The ESP8266 has a single ADC, and the SDK itself uses it internally, for example during RF calibration. Espressif's documentation for the fast read call, system_adc_read_fast, says it must not be used while WiFi is running. The Arduino analogRead wraps the normal call, which is safer, but the sharing is the reason readings can be noisy or odd while the radio is transmitting.
So two rules follow. Do not reach for the "fast" SDK call in a sketch that has WiFi up. And never call analogRead from an interrupt handler or a context where the stack might be mid-operation; set a flag there and read in loop().
Also check you are not mixing modes. ADC_MODE(ADC_VCC) makes the pin read the supply voltage, and analogRead(A0) then returns that rather than your sensor. That is a wrong-value bug, not a reset, but it is often found at the same time.
Cause three: the supply sags when the radio transmits
If the reset reason says power-on or looks like a brownout, forget the code. WiFi transmit bursts pull current in sharp spikes, and a weak USB cable or a cheap regulator drops the rail for a few milliseconds. Your ADC read just happens to be what you were doing at the time.
This also explains why the readings wander before it resets: the ADC reference moves with the rail. The checks are cheap:
- Put a 100 uF or larger capacitor across 3V3 and ground, close to the module.
- Try a different supply or a shorter, thicker cable.
- Watch the rail with a scope or a multimeter's min/max mode during a WiFi burst.
Another small trap: the ADC input range on a bare ESP8266 is 0 to 1 V. Many dev boards add a divider so 3.3 V is safe, but a bare module or a bad divider will happily feed it more than it likes.
A quick way to tell which one you have
Run the same sketch with WiFi switched off using WiFi.mode(WIFI_OFF). If the resets vanish and your sampling loop is long, it was starvation. If they vanish and the loop is short, suspect the supply. If they continue with WiFi off, the problem is elsewhere in your code, and the reset info will say where.
Then put WiFi back and keep every loop() pass short. The radio is not fragile; it just needs its turn.