Skip to content

feat(companion): full capture of dropped frames for observer apps - #29

Merged
Elektr0Vodka merged 2 commits into
mainfrom
Feat/full-capture
Oct 19, 2026
Merged

Elektr0Vodka merged 2 commits into
mainfrom
Feat/full-capture

Conversation

@Elektr0Vodka

Copy link
Copy Markdown
Owner

What

This adds Full capture: an opt-in way for an observer app to also get the frames the companion drops today.

  • Command 0xE0 [flags]. The flags are 0x01 for over-long frames, 0x02 for damaged frames and 0x04 for drop notices. The reply is [0x00][supported flags], and unknown bits get ERR_CODE_ILLEGAL_ARG. Stock firmware answers 0xE0 with an error, so an app can tell whether the feature is there.
  • Push 0xC0. The layout is [tag][index<<4|count][status][snr_x4][rssi][total_len][chunk]. A frame that does not fit one 176-byte push is split in two, so it works over BLE too. Statuses:
    • 0: a valid frame of 174-255 bytes. These never fit the 0x88 RX log push, which is limited to 173 bytes of raw data. A frame of up to 173 bytes still goes out as 0x88 only.
    • 1 to 3: a CRC error, a damaged header or another read error. RadioLib fills the buffer before it reports the error. The wrapper now keeps those bytes in two slots, the oldest overwritten, instead of discarding them. They never reach tryParsePacket().
    • 4: a drop notice, the frames lost to a full ESP32 receive queue since the last notice, at most once a second.
  • The setting lives in RAM only. It is cleared on reboot and when no app is connected; the app sends it again on every connect.
  • Nothing extra is transmitted on RF.

Why

RTFM-EV wants to act as a complete observer, for other firmwares' traffic and for link debugging. Its receiver side is Elektr0Vodka/RTFM-EV#366 and uses exactly this wire format.

How

  • examples/companion_radio/FullCapture.{h,cpp} is a pure encoder, built into native with tests in test/test_full_capture.
  • FullCaptureHook.cpp holds the MyMesh side: the command, the long push from logRxRaw, the damaged push from a new logRxBad, and the reset and drop notices in loop(). It follows the CompanionFilterHook.cpp pattern.
  • The seams have defaults that do nothing, so other roles and radios are unchanged:
    • mesh::Radio::recvBad() and getRxQueueDrops();
    • Dispatcher::logRxBad(), called only when recvRaw() returned nothing and a damaged frame is waiting;
    • RadioLibWrapper keeps the damaged frames under the existing radio mutex. This costs about 530 bytes of RAM.
  • The wire format is in docs/companion_protocol.md (section 10); changelog_ev.md has an entry.

Tested

  • pio test -e native: 102 test cases passed, including the 8 new test_full_capture cases.
  • pio run -e Heltec_v3_companion_radio_usb_tcp: success (RAM 33.9%, flash 48.8%).
  • pio run -e RAK_4631_companion_radio_usb: success (RAM 26.9%, flash 68.1%). This checks the polled path without the drain task.
  • No warnings in the touched files in either build.
  • native_kiss_modem errors on this Windows machine (0xC0000139, a DLL entry point). The same happens on clean origin/main, and this change does not touch it.

Not tested

  • On hardware. The next step is to flash a Heltec V3 and, from a second radio, send a 200-byte packet and a frame with a broken CRC, with RTFM-EV (#366) set to Full capture on.
  • The other PR matrix envs (Heltec V4, V4 TFT, Wio Tracker L1 BLE, Heltec V2 BLE) were not built locally; CI builds them.

Opt-in command 0xE0 makes the companion also push (0xC0) what it drops
today: valid frames of 174-255 bytes that do not fit the 0x88 RX log
push, frames received with a CRC or header error (now kept by the
RadioLib wrapper in two slots instead of discarded), and on ESP32 a
count of frames lost to a full receive queue. A frame that does not
fit one 176-byte push is split in two.

The setting is RAM only and cleared when no app is connected, so an
observer such as RTFM-EV sends it on every connect. A valid frame of up
to 173 bytes still goes out as 0x88 only. Nothing extra is transmitted
on RF. The encoder is pure and has native tests.
@Elektr0Vodka
Elektr0Vodka merged commit 363ea49 into main Oct 19, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant