Commit Graph
27 Commits
Author SHA1 Message Date
swaits 481712930c chore: release 1.4.0 2026-04-25 20:23:39 -06:00
swaits d262abe853 feat(radio): adaptive LBT — scale CAD retries to current LoRa config
The CAD pre-TX loop previously ran up to 10 retries with a fixed 10 ms
sleep — roughly one max-length packet only at SF7/BW500. At SF10/BW125
the 100 ms total was ~4% of one packet; at SF12/BW125 LBT was a no-op.

Now the per-retry sleep is sized to one time-on-air of an assumed-peer
80-byte packet under the active config, cached on RadioContext and
recomputed on every SET_CONFIG. Retries scale as
clamp(floor(BUDGET/T)+1, 1, 10) with BUDGET=2.5 s, so fast configs
still get up to 10 retries while SF11/12 degrade to single-CAD-and-bail
(logged at config-apply). Sleeps are jittered ±20% per iteration to
decorrelate simultaneously-backing-off contenders.

The ToA math, CR→{1..4} mapping, retry-count derivation, and jitter
helper live in a pure src/lbt.rs module so they're host-testable via
DONGLORA_HOST_TEST=1 cargo test (13 tests).

Validated on-device: 20 iterations of the AI Bot test flow that
previously saw ~2 ChannelBusy errors now see zero at the same SF/BW.
2026-04-25 20:15:44 -06:00
swaits 23ddc15557 fix(radio): consume upstream lora-phy pre-command BUSY check
Pick up the proper fix from our fork at swaits/lora-rs@fix/sx126x-busy-race:
move the ready check from *after* to *before* each SPI op per the
Semtech manuals (SX1261/2 §8.3.1, LR1121 UM §3 / §3.5, LR1110 DS
§1.2.5) and rename InterfaceVariant::wait_on_busy to wait_until_ready.

Firmware side is surgical: since the upstream fix keeps the
GenericSx126xInterfaceVariant::new signature at 5 args (no DelayNs
threading), there is nothing to change in the four nRF / ESP / RP2040
board files beyond a one-line comment rename (wait_on_busy ->
wait_until_ready) in the BUSY-pullup comment on the two nRF boards.

Wire in the [patch.crates-io] override pointing at ../../lora-rs/lora-phy,
bump the dep spec to the pre-release 3.0.2-alpha that matches the fork,
and enable the defmt-03 feature that upstream moved behind a gate
between 3.0.1 and the fix branch.

TODO.md rewritten to track the new approach: PR body template with
verbatim citations from all three Semtech chip manuals (SX1261/2,
LR1121 UM, LR1110 DS) plus the SX1276 pin table confirming the family
has no BUSY line.

No change to firmware behavior beyond picking up the upstream fix,
which takes first-TX-after-boot success from ~50% to 20/20 on the
Seeed Wio Tracker L1 repro board.
2026-04-24 12:44:01 -06:00
swaits 0fbd8412c2 chore: release 1.3.1 2026-04-23 18:25:50 -06:00
swaits a2c611841b chore: release 1.3.0 2026-04-23 12:50:03 -06:00
swaits a1b92356b3 feat(boards): LilyGo T-Beam (classic) support
First classic-ESP32 (non-S3) board in the firmware, and first SX127x
radio. ESP32 + SX1276 + SSD1306 OLED + AXP192 PMIC, CP2102 USB-UART
bridge. Pin map verified from MeshCore variants/lilygo_tbeam_SX1276.

The AXP192 gates power to the LoRa chip and OLED. Its I²C bus is
shared with the display, so the firmware builds I²C0 in blocking mode
first for the PMIC init, then converts the same handle to async and
hands it to the display task — avoids double-consuming the peripheral.

Lora-phy's Sx127x support drives DIO0 as the sole IRQ (SX127x has no
BUSY line); the chip runs from a crystal, tx_boost routes +20 dBm
through PA_BOOST.

The `esp-hal` / `esp-rtos` / `esp-backtrace` / `esp-println` deps have
been restructured so the chip flavour (esp32 vs esp32s3) is selected
at the per-board feature level instead of baked into the dep lines.
This keeps the existing S3 boards building on esp32s3 while the new
T-Beam feature selects classic esp32 via the same dependency set.

LoRaBoard gains supported_sf_bitmap() / supported_bw_bitmap() methods
(with SX126x defaults) so the T-Beam can report SF6–SF12 via GET_INFO
— the SX1276 has no SF5. radio_chip_id() reports 0x0011 per
donglora-protocol RadioChipId::Sx1276. freq_range_hz widens to the
SX1276 datasheet span of 137 MHz–1020 MHz.

New modules:
- src/hal/esp32.rs           — classic-ESP32 MCU primitives
- src/board/esp32.rs         — Sx127x radio + SSD1306 display pipe
- src/board/lilygo_tbeam.rs  — the board file
- src/driver/axp.rs          — AXP192 init added alongside AXP2101
2026-04-23 10:42:42 -06:00
swaits 7fca5fcb50 feat(boards): LilyGo T-Beam S3 Supreme support
ESP32-S3 + SX1262 + SH1106 OLED + AXP2101 PMIC, native USB-C.

The AXP2101 PMIC gates power to the LoRa chip and the OLED. Without
its init sequence (ALDO3 = 3.3 V for LoRa, ALDO1 = 3.3 V for OLED)
the SX1262 never responds on SPI and radio init fails.

Pin map verified against MeshCore's variants/lilygo_tbeam_supreme_SX1262.
PMIC lives on a dedicated I²C1 bus (SDA=42, SCL=41) separate from the
SH1106 on I²C0 (SDA=17, SCL=18); I²C1 is used blocking for a one-shot
init and then dropped. TCXO at 1.6 V via DIO3.

New src/driver/axp.rs holds the AXP2101 init sequence — kept minimal
(only the LoRa + OLED rails), since DongLoRa is a LoRa-over-USB bridge
and every other rail (GPS, BME280, IMU, SD, batt ADC) is out of scope.
2026-04-23 10:29:48 -06:00
swaits 6867439044 chore: release 1.2.1 2026-04-22 23:59:44 -06:00
swaits 0033bbb941 chore: release 1.2.0 2026-04-22 20:50:11 -06:00
swaits 42a6b1142d feat(display): OLED dims a few seconds after last activity
Splash holds bright for 10 s on (re-)entry, then drops to dim while
LearnMore/Info keep alternating. The dashboard stays bright for 3 s
after the last packet or mode change, then dims in place without
disturbing the Listening/Transmitting state or the 1 s sparkline
timer. Cross-leaf TX↔RX hops stay bright throughout — `set_dim` is
fired by the 3 s timer handler as an action rather than as a sibling
`exit:` action, so mode-change leaf-crossings no longer flash
dim→bright on every swap.

Two driver families (`ssd1306::Ssd1306Async` and the in-tree
`Sh1106`) wrapped behind a single `DisplayBrightness` trait in
`src/driver/mod.rs`; the board trait's `DisplayDriver` associated
type now requires it. Dim/bright contrast values centralized as
`CONTRAST_BRIGHT` / `CONTRAST_DIM` constants — tune in one place.

`On.on(CmdReset)` now targets `LearnMore` directly rather than the
ancestor `Splash`. Under hsmc 0.2 up-transition semantics, targeting
an already-active ancestor unwinds only below it without re-descent;
the USB-disconnect reset path was previously relying on the old
re-enter-and-descend behavior and would have parked at `Splash`
with a dead alternation timer.

Bumps `hsmc` 0.1.0 → 0.2 for the up-transition semantic fix.
2026-04-22 19:38:42 -06:00
swaits 027ac5eb04 chore: release 1.1.0 2026-04-22 11:47:42 -06:00
swaits b0ce0a7aca chore: release 1.0.0 2026-04-22 09:32:06 -06:00
swaits a0b15ddcaa chore: release v0.5.0 2026-04-16 13:30:52 -06:00
swaits 388d07e90f refactor: complete hsmc conversion and add debug-checkpoint feature
Finishes the hsmc migration that was staged across the prior two
commits:

- Radio task is now a single `machine.run().await`; all I/O lives in
  state-scoped `during:` activities (`next_command`, `next_packet`,
  `perform_tx`). The old `push/drain` helpers, the imperative
  top-level `select`, `handle_cmd`, `handle_rx_result`, and
  `execute_transmit` are gone. `StartingRx` / `ResumingRx` intermediate
  states model the host-visible difference between a responding and a
  silent RX re-arm.

- Host/USB task is now an hsmc statechart (Disconnected/Connected)
  with entry/exit actions handling display wake, radio StopRx, and
  display reset — replacing the open-coded `was_connected` edge
  detector. The `protocol_loop` keeps its imperative read path because
  `read_packet` blocks.

- New `debug-checkpoint` cargo feature ships a dedicated
  `debug_blink.rs` task and a shared atomic counter that code along
  the boot/dispatch/TX path bumps at notable points. A ladder-latching
  write policy preserves the deepest TX phase reached on the first
  attempt until reboot — diagnostic output for UART-only boards where
  defmt would corrupt the host protocol. Zero footprint when the
  feature is off.

- Silence a stray `clippy::unit_arg` on `led_off` / `led_blip` in the
  display task for boards with `LedDriver = ()`.
2026-04-16 13:28:44 -06:00
swaits 596bc3ed96 refactor: drive display via hsmc statechart
Replaces the ad-hoc display loop with a declarative hsmc statechart
(Off/On.{Splash.{LearnMore,Info}, Dashboard.{Listening,Transmitting}})
and swaps the Radio→Display channel from a RadioStatus watch to a
typed RadioEvent queue. The radio task now emits discrete mode and
packet events instead of publishing full status snapshots; the
display consumes them through the statechart, which also handles the
5 s splash alternation and 1 s sparkline tick via `on(after …)` /
`on(every …)` transitions.

Also:
- Add hsmc dep and embassy/tokio feature shims (hsmc proc macro
  evaluates `cfg(feature = "embassy")` in this crate).
- Fix USB disconnect path in `host/usb.rs` so the DTR branch always
  runs on read error (was early-continuing, starving display/radio
  reset).
2026-04-16 13:26:04 -06:00
swaits 272abceedb refactor: consume donglora-protocol as a path dep
Pulls the wire protocol types and COBS framing out of firmware and into
the sibling ../protocol crate so client-rs, mux-rs, and bridge can share
them. Firmware's src/protocol.rs is now a thin re-export shim preserving
every crate::protocol::\* call site; src/host/framing.rs drops the
in-tree CobsDecoder / cobs_encode_response and re-exports the codec,
keeping only route_command (which needs embassy channels).

All seven board features still build clean, cargo fmt clean, cargo test
green.
2026-04-16 13:25:20 -06:00
swaits fd4e180879 chore: release v0.4.0 2026-04-11 22:35:33 -06:00
swaits 8b549d49a9 feat: add ELECROW ThinkNode-M2 board support
ESP32-S3 + SX1262 + 1.3" SH1106 OLED + CP2102 UART bridge +
1000 mAh Li-Po. Pin map verified against Meshtastic's
variants/esp32s3/ELECROW-ThinkNode-M2. DIO3 TCXO at 3.3 V (vs 1.8 V
on Heltec), VEXT_ENABLE active HIGH (opposite polarity), dedicated
LORA_POWER_EN on GPIO48 sequenced before SPI init. Reuses the
existing async SH1106 driver at I2C address 0x3C.

Refactors hal::esp32s3::{init_spi,init_i2c} and board::esp32s3::init_radio
to accept generic GPIO bounds so ESP32-S3 boards with different pin
assignments can share the helpers. init_radio now takes a
TcxoCtrlVoltage parameter instead of hardcoding Ctrl1V8.

Incidental cleanups needed to make `just check-all` green:

- Replace the core::mem::forget(output) pattern with let _foo = ...;
  current esp-hal makes Output non-Drop, so forget is a no-op that
  trips clippy's forget_non_drop lint.

- Switch justfile xtensa builds from `cargo +nightly` with a RUSTC
  override to `cargo +esp` so the compiler and rust-src versions stay
  aligned. Public nightly's rust-src had drifted two months ahead of
  the esp fork and broke -Zbuild-std on core.
2026-04-11 22:29:04 -06:00
swaits 7184610eea feat: migrate to embassy-executor 0.10
Bump embassy-executor 0.9 -> 0.10 and embassy-rp 0.9 -> 0.10 from crates.io.
Patch esp-hal, esp-rtos, esp-backtrace, esp-println from esp-rs/esp-hal main
branch (esp-rtos 0.2.0 on crates.io pins embassy-executor ^0.9).

API migrations:
- embassy-executor: arch-cortex-m -> platform-cortex-m feature rename
- embassy-executor: SpawnToken now returned as Result from task functions
- esp-hal main: efuse::Efuse::mac_address() -> efuse::base_mac_address()
- esp-hal main: esp_rtos::start() now requires SoftwareInterrupt<0>
- embassy-rp 0.10: SPI constructor requires DMA interrupt bindings

Also fixes UART display stuck on splash (sent Reset instead of On at boot)
and switches just check to clippy -D warnings.
2026-04-11 02:34:04 -06:00
swaits 46822c182c feat: add Waveshare RP2040-LoRa board support
First RP2040 board and third MCU family (after ESP32-S3 and nRF52840).

Hardware: RP2040 + SX1262 radio, 850-930 MHz HF, PE4259 antenna switch,
LED on GPIO25, no OLED, USB-C via FPC ribbon cable to adapter board.

New files:
- src/hal/rp2040.rs: MCU primitives (SPI bus, USB driver, flash unique ID MAC)
- src/board/waveshare_rp2040_lora.rs: board implementation
- ld/rp2040-memory.x: linker memory layout

Also fixes cortex-m-rt/set-vtor (moved from global dep to per-board feature,
since Cortex-M0+ has no VTOR register) and makes build.rs memory.x selection
target-aware.
2026-04-10 16:05:08 -06:00
swaitsandClaude Opus 4.6 de38278d7b fix: resolve display stuck after disconnect and radio task deadlock
- Set disconnected flag on DisplayCommand::Reset to block stale radio
  status updates from re-rendering dashboard over splash screen
- Send StopRx to radio when USB DTR drops so radio stops generating
  packets after host disconnects
- Use try_send for unsolicited responses (RxPacket, error recovery,
  StopRx Ok) to prevent radio task deadlock on full response channel
- Fall back to LED-only event loop when display init fails instead of
  returning early and orphaning the LED

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 19:49:15 -06:00
swaits 0c9094dcbd release: firmware v0.2.0
Major architecture reorganization, Wio Tracker L1 support, LED blink
on packet activity, native USB for Heltec V3 (hardware mod required).
See CHANGELOG.md for full details.
2026-04-07 12:46:58 -06:00
swaits 41b5775268 refactor: unify SimpleLed via embedded-hal, remove Option<L> from BoardParts
- Added embedded-hal 1.0 as direct dep (already transitive via embedded-hal-async)
- SimpleLed<P: OutputPin> now generic over any HAL — single definition in driver/
- Removed duplicate SimpleLed from board/esp32s3.rs and board/wio_tracker_l1.rs
- Changed BoardParts.led from Option<L> to L — boards with no LED use ()
- Display task calls led.set_rgb() unconditionally (no-op for () boards)
- RAK board: led: () instead of led: None
2026-04-07 12:40:55 -06:00
swaits 6db287c241 feat: native USB CDC-ACM support for Heltec V3
The default heltec_v3 feature now uses native USB OTG (same as V4),
appearing as /dev/ttyACM0 with the DongLoRa VID:PID (1209:5741).
This requires a hardware mod: solder R29/R3 and disconnect the CP2102
from the USB data lines.

Stock (unmodified) V3 boards should use the new heltec_v3_uart feature
which preserves the original UART-over-CP2102 behavior (/dev/ttyUSB0).

- heltec_v3: switched from UART to USB OTG
- heltec_v3_uart: new feature, UART fallback for stock boards
- Removed #[cfg] gates on UsbParts/UartParts (both always defined)
- Updated README with hardware mod instructions and board table
- Updated justfile with heltec_v3_uart board definition
2026-04-07 10:44:25 -06:00
swaits 0ae1c84085 WIP: cleanup a bit, strip binaries 2026-04-06 22:30:45 -06:00
swaits 38684482e8 WIP: functional checkpoint! 2026-04-06 21:15:35 -06:00
swaits bb588c93ce chore: massive repo reorganization 2026-04-03 10:28:38 -06:00