ZeroTrace AirLeak
Safe Mode
How and when the firmware self-protects under heap pressure
Safe mode is a self-protective state the firmware enters when free internal heap drops dangerously low. It's designed to keep the unit responsive and reachable instead of crashing under extreme load. Everything below is from src/pipeline/heap_monitor.cpp.
What the heap monitor watches
A dedicated task samples free internal heap once per second and tracks the lowest value seen. It acts on three thresholds:
| Threshold | Free heap | What happens |
|---|---|---|
| Soft warning | below ~28 KB | A warn diagnostic is recorded |
| Safe-mode arm | below ~25 KB | Safe mode arms |
| Clear | at or above ~40 KB | Safe mode clears |
The clear threshold is well above the arm threshold (hysteresis), so the unit doesn't flap in and out of safe mode around a single value.
What safe mode does
When free heap drops below the arm threshold, the firmware:
- Sets
safe_mode = true(visible instate.read, so the app can show it). - Stops admitting new devices into the aggregator, existing devices keep updating, but new MACs aren't inserted while heap is critical. This is what frees pressure.
If heap stays below the arm threshold for three consecutive samples (~3 seconds), the monitor escalates:
- Force-stops the BLE scanner to stop the inflow entirely, and increments
safe_mode_triggers. - Records a
safe_modeevent to the on-flash diagnostic log (diag.read).
When heap recovers above the clear threshold, safe mode clears, and if the scanner was force-stopped it is re-armed automatically so a transient dip can't leave the unit silently not scanning until reboot.
The BLE-only firmware has no WiFi capture to suspend and no USB stream to throttle. Safe mode acts on the two levers it has: stop admitting new devices, and (if pressure persists) pause the scanner until heap recovers.
When it happens in real life
Safe mode is rare in normal use. You're most likely to see it in a very dense RF environment (a packed conference hall or transit hub) where new MACs arrive faster than the aggregator's working set can absorb.
What you lose during safe mode
- New devices stop appearing while it's armed, devices already in the table keep updating.
- If the scanner was force-stopped, capture pauses entirely until heap recovers, then resumes on its own.
No persistent state is lost, safe mode is a brake, not a reset.
Reading safe-mode state
state.read exposes it directly:
safe_mode, currently armedsafe_mode_triggers, how many times it has escalated since bootlowest_heap, how close to the edge the unit has gotten
The on-flash diagnostic log keeps safe_mode_armed / safe_mode / safe_mode_cleared / capture_resumed entries with timestamps, so you can review pressure after the fact via diag.read.
How to recover
If safe mode triggers repeatedly:
- Move to a less dense area, or briefly switch to Setup to let the working set drain, then back to Monitor.
- Power-cycle if the unit seems wedged, settings and license persist.
If it triggers immediately on every boot even in a quiet area, capture the diagnostic log (diag.read from the app) and contact support.
Why safe mode instead of crash
Without it, a heap-exhausted ESP32-S3 would fail an allocation, panic, watchdog-reset, and lose all in-RAM state plus the live session. Safe mode keeps the unit alive and reachable, sheds whatever caused the pressure, and resumes when memory is healthy.
Persistent safe-mode escalation means the environment is denser than the unit's working set can hold. Capture in a less crowded spot, or cycle through Setup to drain, then resume Monitor.