ZeroTrace AirLeak
Heartbeat Fields
Every diagnostic field in the 2-second heartbeat
The firmware emits a heartbeat every 2 seconds, regardless of capture mode. The heartbeat is a structured status record covering connection state, capture activity, memory, and timing.
The desktop's status bar reads from the heartbeat. If you're scripting against the unit, the heartbeat is what you should poll.
Field reference
Identity
| Field | Type | Meaning |
|---|---|---|
firmware | string | Version string (e.g. airleak-v1.4.2) |
build | string | Build date and tag |
mac | string | The unit's BLE MAC (also USB device ID) |
slot | integer | Active OTA slot (0 or 1) |
boot_count | integer | Successful boots since factory reset |
Capture state
| Field | Type | Meaning |
|---|---|---|
mode | enum | setup or monitor |
scan_state | enum | active, passive, or off |
ble_active_scanning | bool | Whether SCAN_REQs are being sent |
current_channel | integer | The WiFi channel currently being listened to (1–13) |
channel_dwell_ms | integer | Per-channel dwell time |
scan_window_ms | integer | BLE scan window |
scan_interval_ms | integer | BLE scan interval |
throttle_ms | integer | Event throttle window |
When mode=setup, scan_state=off and current_channel=0.
Activity counters
| Field | Type | Meaning |
|---|---|---|
devices | integer | Unique aggregated devices since session start |
events | integer | Total decoded events emitted |
events_throttled | integer | Events suppressed from USB stream by throttle |
wifi_events | integer | WiFi-side event count |
ble_events | integer | BLE-side event count |
alerts | integer | Total alerts fired since session start |
usb_dropped | integer | Events lost on USB transmit (queue full) |
drop_pct | float | usb_dropped / events as percent |
events_per_sec | float | Events emitted in last 1 s |
When events_throttled is much larger than events, the throttle is doing its job — most events are aggregated but not streamed. That's normal in dense environments.
Memory
| Field | Type | Meaning |
|---|---|---|
heap_free | integer | Free internal heap in bytes |
heap_free_pct | integer | Free internal heap as percent of total internal SRAM |
heap_min_ever | integer | Lowest free internal heap since boot (high-water-low) |
psram_free | integer | Free PSRAM in bytes |
psram_free_pct | integer | Free PSRAM as percent |
largest_block | integer | Largest contiguous internal heap block in bytes |
The status bar's Heap NK (X%) is heap_free / 1024 and heap_free_pct. Color coding:
>12%— green (healthy)7–12%— orange (under pressure)<7%— red (safe-mode armed)
largest_block matters under pressure: even with 30 KB free heap, if the largest contiguous block is only 3 KB, the unit can't allocate large queues and may stutter.
Timing
| Field | Type | Meaning |
|---|---|---|
uptime_ms | integer | Time since last boot, in milliseconds |
uptime_str | string | Human-readable: 1h 23m 45s, 2d 4h 12m, etc. |
time_since_last_event_ms | integer | Milliseconds since the last event was emitted |
time_since_last_alert_ms | integer | Milliseconds since the last alert was fired |
time_since_last_event_ms is useful for diagnosing "is anything happening." If it climbs above 30 s in Monitor mode, either the area is empty or the radio is wedged.
Health flags
| Field | Type | Meaning |
|---|---|---|
safe_mode | bool | True if the heap monitor has triggered safe mode |
wifi_healthy | bool | WiFi driver is responding to channel-hop commands |
ble_healthy | bool | BLE host is delivering scan callbacks |
usb_attached | bool | USB-CDC reports a host attached |
parser_healthy | bool | Parser task hasn't been stuck for >5 s |
aggregator_full | bool | True when device count = 768 (cap) |
If any of wifi_healthy / ble_healthy / parser_healthy is false, the desktop displays a red dot and surfaces a diagnostic in the status bar.
Per-task CPU snapshot
A small per-task CPU usage table is included for diagnostic purposes:
| Field | Meaning |
|---|---|
cpu_parser_pct | Parser task CPU usage |
cpu_usb_pct | USB stream task CPU usage |
cpu_nimble_pct | NimBLE host task CPU usage |
cpu_wifi_pct | WiFi capture task CPU usage |
cpu_idle_core0_pct | Idle time on core 0 |
cpu_idle_core1_pct | Idle time on core 1 |
These are rolling averages over the last second. Saturation (any task > 90 % consistently) means the throttle should be increased.
Aggregator state
| Field | Type | Meaning |
|---|---|---|
aggregator_size | integer | Number of devices currently in the aggregator |
aggregator_max | integer | Maximum the aggregator can hold (768) |
aggregator_evicted | integer | Total devices evicted (LRU) since boot |
fingerprint_groups | integer | Number of fingerprint-merged device groups |
A growing aggregator_evicted count in a long capture means devices were rotating fast enough to fill the aggregator. The desktop's library still has them via cross-session matching — eviction only affects the live in-firmware aggregator.
Channel histogram (last minute)
A compact representation of how many frames hit each channel in the last minute:
ch_hist: 1=12 2=8 3=4 4=2 5=3 6=58 7=2 8=4 9=5 10=8 11=124 12=1 13=2
Useful for spotting "channel 6 and 11 are busy, the rest are quiet." The dominant channels are usually the non-overlapping ones (1, 6, 11).
Heartbeat cadence
| Mode | Heartbeat interval | What's reported |
|---|---|---|
| Setup | 2 s | Identity + capture state + memory + uptime + flags |
| Monitor | 2 s | All of the above + activity counters + per-task CPU |
The cadence is constant — the heartbeat doesn't slow down or speed up under load. A heartbeat skipped is a sign of trouble (parser starved, USB queue saturated).
Reading heartbeats from the desktop
Open AirLeak → Settings → Device log. Filter for HB::
HB: mode=monitor scan=active devices=87 events=4421 ...
HB: mode=monitor scan=active devices=89 events=4533 ...
HB: mode=monitor scan=active devices=89 events=4631 ...
Each line is one heartbeat. The desktop's status bar parses these in real time.
Reading heartbeats from a script
If you connect to the unit's COM port directly (921600 baud, USB-CDC), you'll see lines prefixed:
HB:— heartbeatsEV:— events (when in Monitor)AL:— alerts (when in Monitor)DEV:— device dumps (on demand viaairleak-snapshot)LOG:— diagnostic log
Heartbeats arrive every 2 s on a steady cadence. The format is structured (key=value pairs) — easy to parse without a JSON dependency.
When heartbeats matter most
- Connection lost — heartbeat freezes; desktop dot turns gray
- Heap pressure —
heap_free_pctdrops; orange / red threshold - Stuck channel —
current_channeldoesn't advance;wifi_healthy=falsefollows - Stream backlog —
usb_droppedclimbs;drop_pct> 5 % - Aggregator full —
aggregator_full=true; evictions begin
Watch the heartbeat first when something feels wrong. It's the most informative single log line on the unit.
If you need real-time per-second updates instead of per-2-second, send airleak-status from the Command tab on a timer. It returns a single status frame in the heartbeat format, on demand.