Why Your ESP32 Weather Station Crashes After 24 Hours (and How to Fix It)

School ESP32 IoT projects rarely crash because of faulty silicon. They freeze because of dynamic heap memory fragmentation, blocking Wi-Fi reconnect loops, and unhandled hardware watchdogs.
If your classroom ESP32 weather station runs perfectly on the lab bench during period four but goes completely dark by the following morning, the silicon is not defective. Microcontrollers do not degrade over twenty-four hours. Instead, long-running embedded software almost always fails because of two quiet, cumulative problems: heap memory fragmentation from dynamic text operations, and unhandled network drops that trap the processor in an infinite blocking loop.
Solving these issues requires moving away from quick prototyping shortcuts and adopting standard embedded engineering practices: fixed-size memory buffers, non-blocking network state machines, and a hardware watchdog timer.
Culprit 1: Dynamic Strings and Heap Fragmentation
The ESP32 has roughly 320 KB of usable internal SRAM. In a weather station polling sensors once every ten seconds, code written with the standard Arduino String class allocates, resizes, and frees small blocks of memory thousands of times a day:
// The pattern that quietly breaks your project:
String payload = "{\"temp\": ";
payload += String(temperature);
payload += ", \"humidity\": ";
payload += String(humidity);
payload += "}";
http.POST(payload);Every time dynamic strings are created and concatenated inside loop(), the C++ runtime allocates space on the heap. Because other system background tasks (like the Wi-Fi and Bluetooth stacks) allocate memory at the same time, this continuous allocation and deallocation turns the heap into Swiss cheese. The total free memory might still report 150 KB, but the largest contiguous free block shrinks to a few hundred bytes. When the Wi-Fi stack or JSON serialiser suddenly requests an unbroken 2 KB block for a TCP packet, the allocation fails, returning a null pointer that triggers a silent kernel panic or an unhandled crash.
The Fix: Use fixed-size stack buffers and snprintf. Microcontroller firmware should allocate static or stack-based buffers whose size is known at compile time.
// Robust, zero-heap-allocation alternative:
char payload[128];
snprintf(payload, sizeof(payload),
"{\"temp\":%.2f,\"humidity\":%.2f}",
temperature, humidity);
http.POST((uint8_t*)payload, strlen(payload));Culprit 2: The School Wi-Fi Reconnect Loop
In school environments, Wi-Fi is rarely static. Access points re-key security tokens, DHCP leases expire, and local power cuts or load-shedding transitions briefly drop upstream routers even if the IoT station is running on a battery. Many introductory tutorials suggest reconnecting to Wi-Fi like this:
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
while (WiFi.status() != WL_CONNECTED) {
delay(500); // Fatal: blocks the processor indefinitely
}
}If the router reboots and takes two minutes to bring up the local subnet, this while loop freezes execution. Because FreeRTOS runs the Wi-Fi stack and system management tasks across dual cores, stalling the main loop can starve background watchdog tasks, leading to an immediate hardware reset or a permanent task lockup.
The Fix: Treat connectivity as a non-blocking state machine. Never stall execution waiting for a connection. If the Wi-Fi is down, log data locally to flash memory or an SD card and retry connection on a scheduled interval without halting sensor reads.
Culprit 3: Missing Watchdog Timers
No matter how clean your code is, outdoor sensors, electrical spikes from nearby compressors, or unresponsive I2C bus lines (especially the ubiquitous BME280 or DHT22 sensors) can pull an ESP32 into a hardware deadlock. If a sensor fails to release the I2C clock line, the standard wire library can hang forever.
A Task Watchdog Timer (TWDT) monitors critical loops. If the main loop fails to "feed" or reset the watchdog within a defined window (e.g. 8 seconds), the ESP32 automatically triggers a hard restart and recovers unattended.
#include <esp_task_wdt.h>
#define WDT_TIMEOUT_SECONDS 8
void setup() {
// Initialise watchdog for an 8-second timeout
esp_task_wdt_init(WDT_TIMEOUT_SECONDS, true);
esp_task_wdt_add(NULL); // Subscribe current thread
}
void loop() {
// Reset the watchdog timer each cycle
esp_task_wdt_reset();
readSensors();
sendTelemetry();
// Avoid long delay(); use non-blocking millis() checks
}A Note on Power Stability
Before assuming a bug is purely in software, inspect the hardware rail. An ESP32 draws brief current pulses up to 350–500 mA during Wi-Fi transmission bursts (RF calibration and packet bursts). If the board is powered from a thin USB lead connected to a laptop or a budget 5V wall plug, voltage drops below the 2.7V brownout threshold cause the internal brownout detector to trigger an abrupt restart.
- Place a 100 µF to 470 µF electrolytic capacitor across the
VINandGNDpins to absorb transient current spikes. - Ensure the brownout detector is enabled in your framework rather than suppressed, as undervoltage corrupts internal flash memory.
If you are building field-ready monitoring stations for environmental science or school grounds, our curriculum engineering at Sheen IoT provides modular hardware frameworks and production-ready firmware patterns that avoid these classic classroom pitfalls.



