swaits 16374ca241 refactor(radio): make every lora-phy operation cancel-safe via chart structure
Rewrite the Operational subtree of the radio chart so that every
cancel-unsafe lora-phy future (lora.rx() / lora.cad() / lora.tx() /
lora.init()) runs to natural completion, honoring lora-phy's documented
'must run to completion' contract.

Architectural pivot: every cancel-unsafe future is the body of a leaf-
state during whose host state declares NO outbound event handlers other
than events the during itself emits on natural completion. Per hsmc spec
§6, the chart cannot drop the during because no transition can fire.

RX subtree (RxSession super-state):
  - irq_pump (cancel-safe: wait_for_irq + get_irq_state + clear_irq_status)
    runs at RxSession scope, emits Dio1Asserted(IrqState) to chart
  - try_start_rx_hw chains prepare_for_rx -> start_rx so the chip is
    actually receiving when RxActive is entered (both single SPI ops)
  - RxActive's collect_rx_packet action does post-IRQ get_rx_result work
  - RxDraining's enter_standby cleanly stops RX before transitions out

TX subtree (TxSession super-state):
  - TxCadPrep / TxCadWait / TxBackoff / TxPrep / TxOnAir / TxFanout
    leaf states each owning one cancel-safe SPI op or one sandboxed
    cancel-unsafe await
  - lora.cad() / lora.tx() called AS-IS in states with zero non-self
    event handlers
  - chart timers (on(after Duration)) set context flags for wedge
    detection; the during checks the flag at its next cancel-safe
    wakeup and returns cleanly
  - mark_pending_reset action defers Reset events until natural
    completion (per spec §5 emit() semantics)

Removes lora_setup_with wrappers around lora.cad(), lora.tx(),
lora.init() -- those wrappers used embassy_time::with_timeout which
DROPS the wrapped future on timeout, the proximate cause of every
'lora_setup timeout: do_tx.cad' warn we've seen.

Keeps lora_setup around enter_standby / prepare_for_rx /
prepare_for_cad / prepare_for_tx -- those are short SPI sequences where
drop is documented-safe.

Documented latency budget: chart-cycle overhead <100us; regulatory
LBT (CAD-to-TX <50ms) well within budget.
2026-04-28 11:03:24 -06:00
2026-04-03 10:28:38 -06:00
2026-04-16 13:25:19 -06:00
2026-04-25 20:23:39 -06:00
2026-04-07 19:52:52 -06:00
2026-04-25 20:23:39 -06:00

DongLoRa Firmware

Embedded Rust firmware for DongLoRa boards. Exposes the LoRa radio over USB CDC-ACM using a COBS-framed binary protocol.

Building

just setup              # one-time toolchain install
just build heltec_v4    # build for a specific board
just flash heltec_v4    # build + flash
just check-all          # compile-check all boards
just clippy heltec_v4   # lint
just test               # host-side protocol unit tests

All commands are run from the firmware/ directory.

Supported Boards

Board Feature MCU Radio USB Target
Heltec V3 heltec_v3 ESP32-S3 SX1262 Native CDC-ACM xtensa-esp32s3-none-elf
Heltec V3 (UART) heltec_v3_uart ESP32-S3 SX1262 CP2102 bridge xtensa-esp32s3-none-elf
Heltec V4 heltec_v4 ESP32-S3 SX1262 Native CDC-ACM xtensa-esp32s3-none-elf
ELECROW ThinkNode-M2 elecrow_thinknode_m2 ESP32-S3 SX1262 CP2102 bridge xtensa-esp32s3-none-elf
LilyGo T-Beam lilygo_tbeam ESP32 SX1276 CP2102 bridge xtensa-esp32-none-elf
LilyGo T-Beam S3 Supreme lilygo_tbeam_supreme ESP32-S3 SX1262 Native CDC-ACM xtensa-esp32s3-none-elf
RAK WisBlock 4631 rak_wisblock_4631 nRF52840 SX1262 Native CDC-ACM thumbv7em-none-eabihf
Waveshare RP2040-LoRa waveshare_rp2040_lora RP2040 SX1262 Native CDC-ACM thumbv6m-none-eabi
Wio Tracker L1 wio_tracker_l1 nRF52840 SX1262 Native CDC-ACM thumbv7em-none-eabihf

Heltec V3 USB Note

The default heltec_v3 build uses native USB CDC-ACM, which requires a hardware modification to your board. Stock Heltec V3 boards route USB through a CP2102 bridge chip to UART — the ESP32-S3's native USB pins (GPIO19/GPIO20) are routed to header pins but not connected to the USB connector.

To enable native USB on a Heltec V3:

  1. Solder 0-ohm (or 22-ohm) resistors at pads R29 (D+) and R3 (D-)
  2. Disconnect the CP2102 from the USB connector's D+/D- lines (cut traces or remove the CP2102 bridge resistors)

After this mod, the board appears as /dev/ttyACM0 with the DongLoRa VID:PID (1209:5741), just like the V4 and nRF boards.

If you have a stock (unmodified) V3, use heltec_v3_uart instead:

just build heltec_v3_uart
just flash heltec_v3_uart

This builds firmware that communicates via the CP2102 bridge (appears as /dev/ttyUSB0).

Adding a Board

See src/board/PORTING.md.

Protocol

See PROTOCOL.md for the complete wire format specification.

S
Description
No description provided
Readme MIT
575 KiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 95.8%
Just 3.3%
RPC 0.5%
Jinja 0.2%
Linker Script 0.2%