Debugging WiFi Power Saving Interference on ESP Chips

July 2, 2024

While working with ESP chips—specifically the ESP32 and ESP8266—I ran into some frustrating issues tied to their built-in WiFi power-saving features. These chips run FreeRTOS and include a WiFi module that can be configured for different power-saving modes, which is super useful—until it isn’t.

The Problem

By default, the global variable esp_wifi_set_ps is set to WIFI_PS_MIN_MODEM. This setting enables modem sleep mode, meaning the WiFi radio goes to sleep when it's idle to save power. That sounds great in theory, but I started noticing unreliable data transmissions under certain conditions.

Specifically, if the delay between sending payloads fell within a weird "sweet spot"—around 60 to 90 times any integer above 2—it seemed to mess with the FreeRTOS scheduler. The scheduler would sometimes think the modem was idle and prematurely enter sleep mode, causing inconsistent and unreliable results.

My Solution

After some digging (and a bit of frustration), I found that disabling the power-saving feature solved the problem. I did this by setting:

esp_wifi_set_ps(WIFI_PS_OFF);

This completely turns off modem sleep mode. Yes, it increases power consumption (I saw that clearly in my power measurements), but the reliability boost was totally worth it—especially when I kept the delay between payloads over 200 milliseconds.

What Changed

The difference was night and day. Before making this change, round-trip times were all over the place. After, everything smoothed out, and the system behaved reliably even under tricky conditions. I documented all of this in some charts, comparing performance before and after the change.

Extra Help

One thing that really helped me crack this was a discussion I found on Reddit. The ESP32 MQTT performance variance thread gave me a helpful nudge in the right direction, proving once again how valuable the online maker community can be.

Final Thoughts

This issue (and its fix) is actually noted in the ESP8266 RTOS SDK documentation, but it’s easy to overlook if you're not specifically hunting for it. Hopefully, sharing my experience helps someone else save some debugging time!