Commit Graph
48 Commits
Author SHA1 Message Date
swaits 6867439044 chore: release 1.2.1 v1.2.1 2026-04-22 23:59:44 -06:00
swaits bcfe736b30 fix(radio): preserve RX session across transient failures
Extends the v1.1.0 "SET_CONFIG during RX auto-re-arms" invariant to
every failure path that can fire while a client has RX active:

* `Receiving.on(RxFailed)` now transitions to `ResumingRx` instead of
  `Idle`. A transient `next_packet` error re-arms the chip rather than
  ending the session.
* `TransmittingFromRx.on(TransmitFailed)` now transitions to
  `ResumingRx` instead of `Idle`. TX failure during RX still responds
  `TX_FAILED` to the caller, then returns to RX — matching the shape
  of the adjacent `ChannelBusy` handler, which already did this.
* `ResumingRx.on(StartRxFailed)` is now a bounded retry (5 attempts,
  25 ms backoff) rather than an immediate drop to `Idle`. Only after
  the retry budget is exhausted does the chip actually land in `Idle`,
  at which point it's genuinely wedged and needs reconfiguring.

Observable effect: the OLED dashboard no longer bounces to Splash
when a client (e.g. ai-bot) TXes while bridges are receiving.
Previously any single hiccup in the RX/TX/ResumeRX cycle would
announce `RadioEvent::Idle`, bouncing the OLED, and the radio would
sit in Idle with no client aware enough to re-issue RX_START —
stranding the session. The invariant should have held for the whole
RX window between `CMD_RX_START` and `CMD_RX_STOP`; now it does.

Implementation notes:

* Two new internal `RadioInput` variants: `ResumeRxRetry` drives the
  `ResumingRx` self-transition; `ResumeRxExhausted` breaks out to
  Idle after too many failed tries.
* New `resume_retries: u8` field on `RadioContext`, reset to 0 on
  every successful `StartRxOk`, bumped by `bump_resume_retries`.
* Constants `MAX_RESUME_RETRIES = 5` and `RESUME_RX_RETRY_BACKOFF_MS
  = 25` at the top of `radio.rs`.
* Removed the dead `announce_rx_failed` helper — its only caller was
  the now-deleted `Receiving.on(RxFailed) => Idle` edge.

No behavior change for the three paths where `Idle` remains the
correct landing state: initial `CmdRxStart` failure in `StartingRx`,
`CmdRxStop` in `Receiving`, and all three `TransmittingFromIdle`
outcomes (where RX wasn't running to begin with).
2026-04-22 23:59:12 -06:00
swaits 0033bbb941 chore: release 1.2.0 v1.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 fa1eeceb7c fix(boards): Elecrow ThinkNode-M2 uses CH340K, not CP2102
The shipping revision of this board has a WCH CH340K USB-UART bridge
(1a86:7522), not a Silicon Labs CP2102 as the original board-module
comment claimed. Update the doc header, and add the CH340K +
CH340-standard VID:PIDs to the `just flash` port list so we stop
falling through to espflash auto-detect on these boards.
2026-04-22 19:32:34 -06:00
swaits 027ac5eb04 chore: release 1.1.0 v1.1.0 2026-04-22 11:47:42 -06:00
swaits b7d9b37bb0 feat: auto-resume RX across SET_CONFIG (protocol v1.1 §3.5)
Inside state Receiving, override the parent Configured state chart handler on(ConfigApplied) => Idle with on(ConfigApplied) => ResumingRx. ResumingRx entry (try_start_rx_hw) re-enables RX on the chip with the new modulation once apply_set_config has finished the SPI write + calibration, and transitions back to Receiving on StartRxOk.


Net effect: SET_CONFIG issued while the radio is in continuous RX no longer kicks the radio (and therefore the OLED) out to idle. The RX ring is still cleared by apply_set_config; callers never need to re-issue RX_START after SET_CONFIG.


This matches donglora-protocol 1.1.0 §3.5 and §6.3 normative behavior.
2026-04-22 11:47:14 -06:00
swaits b0ce0a7aca chore: release 1.0.0 v1.0.0 2026-04-22 09:32:06 -06:00
swaits 2a3c2be060 feat!: migrate firmware to DongLoRa Protocol v2 wire format
Wire-incompatible break with the 0.x protocol. Every host-side path (UART, USB CDC, framing, radio dispatcher, display update channel) speaks the v2 wire format defined in PROTOCOL.md.

* Replace the host framing path with streaming FrameDecoder + encode_frame from donglora-protocol, matching the new (type_id, tag, payload) shape.

* Update src/channel.rs and src/host/{uart,usb,framing}.rs for the new frame semantics: per-request tag echo, CRC checked in the decoder, async ERR fan-out on tag 0.

* Rework src/radio.rs to emit RX / TX_DONE / OK / ERR messages in v2 shape, including RxOrigin, TxDonePayload, and SetConfigResult codes.

* Update src/protocol.rs glue to re-export v2 types from donglora-protocol and drop the legacy 0.x shim surface.

* Refresh PROTOCOL.md to the v2 normative spec (kept in sync with the donglora-protocol crate).

* Minor display and main-loop cleanups needed for the new radio event shape.
2026-04-22 09:32:06 -06:00
swaits a0b15ddcaa chore: release v0.5.0 v0.5.0 v0.5.to 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 c6ef9559ec refactor: drive radio task via hsmc statechart
Replaces the imperative Mode enum + if-branches in the radio loop
with a declarative hsmc statechart mirroring the display:

  Radio
  ├── Unconfigured
  └── Configured
      ├── Idle
      ├── Receiving
      └── Transmitting
          ├── FromIdle
          └── FromRx

The `TransmittingFrom{Idle,Rx}` substates encode "where to return after
TX" as state identity, killing the `was_receiving` bookkeeping. Entry
actions announce `RadioEvent`s on the display channel; RF I/O stays in
the task loop because `rx_once` blocks indefinitely and cannot live
inside an action. Radio-init failure runs a Ping-only degraded loop
without ever instantiating the machine.
2026-04-16 13:26:23 -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 f9582c0b94 style: cargo fmt 2026-04-16 13:25:19 -06:00
swaits 649c29218c docs: add BLE soft-mesh multiplexer design doc 2026-04-16 13:24:37 -06:00
swaits fd4e180879 chore: release v0.4.0 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.
v0.3.0
2026-04-10 16:05:08 -06:00
swaitsandClaude Opus 4.6 2b7aacdb99 docs: fix stale path reference in changelog
clients/python/ → donglora on PyPI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:46:12 -06:00
swaitsandClaude Opus 4.6 cabc8c1cc0 feat: add espflash save-image for Xtensa boards
Generate .bin firmware images for ESP32 boards alongside .elf, matching
the existing .hex/.uf2 flow for nRF52840 boards.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:34:25 -06:00
swaits 0cbf59765c docs: add missing LICENSE file 2026-04-07 19:52:52 -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>
v0.2.1
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.
v0.2.0
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 f9117d71c8 fix: Wio Tracker LED on P1.01, move SimpleLed to board layer
Per the Wio Tracker L1 schematic, it has a green LED on P1.01.
Moved SimpleLed from driver/ to board layer (esp32s3.rs and
wio_tracker_l1.rs) since the impl needs HAL-specific Output types.
Removed driver/simple_led.rs — each board layer defines its own.
RAK WisBlock remains LedDriver=() (no user LED on the core module).
2026-04-07 12:20:20 -06:00
swaits 3bb41da223 fix: V4 also has plain LED on GPIO35, not WS2812 on GPIO38
Per the official V4 schematic (HTIT-WB32LAF_V4.3.pdf), the V4 has
an orange LED on GPIO35 with a series resistor — same setup as V3.
GPIO38 is GNSS_RX on V4, not an LED. Neither V3 nor V4 has a WS2812.

- V4: switched from WS2812/GPIO38 to SimpleLed/GPIO35
- Removed ws2812.rs driver (no current board uses addressable LEDs)
- All ESP boards now use the same SimpleLed driver on GPIO35
2026-04-07 11:59:05 -06:00
swaits eb880d03f6 fix: switch WS2812 driver to blocking RMT transmit
Async RMT had peripheral lifetime issues causing silent failures.
Blocking transmit is ~31µs per update (negligible) and avoids the
async complexity. Also removes the ManuallyDrop/unsafe workaround.
2026-04-07 11:51:18 -06:00
swaits 239e72d0a8 fix: prevent RMT peripheral drop on V4, log WS2812 errors
The Rmt struct was being dropped after extracting channel0, which
likely disabled the RMT peripheral. Use ManuallyDrop to keep it alive.
Also log WS2812 transmit errors instead of silently discarding them.
2026-04-07 11:48:34 -06:00
swaits 6558350aee fix: short 50ms LED blink, RX brightness scaled by SNR
- LED blink is now 50ms (was waiting for 1s timer tick)
- RX blink: green brightness scaled by SNR (-20dB→dim, +15dB→bright)
- TX blink: fixed red brightness
- Blink is self-contained (on→50ms delay→off) instead of timer-based
- V3 white LED: any non-zero = on, so SNR scaling is invisible but
  V4 WS2812 will show green intensity varying with signal quality
2026-04-07 11:34:51 -06:00
swaits 019198b636 fix: V3 has white LED on GPIO35, not WS2812B on GPIO38
The Heltec V3 has a plain white LED on GPIO35 (simple GPIO, not
addressable). Only the V4 has a WS2812B RGB LED on GPIO38.

- Created driver/simple_led.rs for single-color GPIO LEDs
- V3/V3-UART boards: SimpleLed on GPIO35 (white blink on RX/TX)
- V4 board: unchanged (WS2812 on GPIO38, green/red colors)
- nRF boards: unchanged (no LED, type LedDriver = ())
2026-04-07 11:30:16 -06:00
swaits 5ac4b73739 feat: RGB LED support — blinks green on RX, red on TX
- Added RgbLed trait with impl on () for boards without LED
- Added WS2812 driver (driver/ws2812.rs) using ESP32-S3 RMT peripheral
  - 80MHz clock, 12.5ns resolution, 24-bit GRB encoding
  - Zero extra crate dependencies
- Heltec V3/V3-UART/V4: WS2812B on GPIO38 via RMT channel 0
- RAK WisBlock/Wio Tracker: type LedDriver = (), led = None (no-op)
- Display task: green blink on RX packet, red blink on TX packet
- LED auto-off on timer tick (~1s), off when display is off/reset
- LoRaBoard trait gains type LedDriver: RgbLed associated type
- BoardParts gains led: Option<L> field
2026-04-07 11:18:30 -06:00
swaits 2b9e236f32 feat: Heltec V3 native USB, remove all dead_code suppressions
- heltec_v3 now uses USB OTG (requires hardware mod: solder R29/R3)
- heltec_v3_uart added as fallback for stock boards
- Removed every #[allow(dead_code)] — fixed root causes instead:
  - Comm types (UsbParts/UartParts) moved to individual board files
  - USB/UART init removed from hal (boards call esp-hal directly)
  - SH1106 driver gated on boards that use it
  - UART task sends DisplayCommand::Reset at startup (variant now used)
  - ErrorCode::CrcError removed (SX1262 drops bad-CRC silently)
- check/clippy now use --release (no more Xtensa dev-mode warnings)
- Updated README with hardware mod docs and board table
2026-04-07 10:52:58 -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 07651dce44 fix: display stuck on splash for UART boards (disconnected flag)
The display task started with disconnected=true, waiting for a
DisplayCommand::On that the USB task sends on DTR connect. The UART
task has no DTR, so the display never left the "disconnected" state
and ignored all radio status updates. Start connected instead — the
USB task will send Reset on actual disconnect.
2026-04-07 10:27:27 -06:00
swaits 18b509344c refactor: build system auto-discovery, updated docs
- build.rs discovers boards by content (implements LoRaBoard), not exclusion lists
- build.rs auto-discovers helper modules by scanning board files for use super::<helper>
- Jinja template dynamically generates cfg gates for helpers
- Updated PORTING.md for new architecture (hal/, driver/, BoardParts, associated types)
- Zero manual template editing needed when adding boards to existing MCU families
2026-04-07 09:54:32 -06:00
swaits ecc451affe refactor: LoRaBoard trait with full associated types + BoardParts
- LoRaBoard trait now has associated types: RadioParts, CommParts, DisplayParts, DisplayDriver
- into_parts() is on the trait (enforced by compiler, not implicit convention)
- BoardParts<R, C, D> struct replaces tuple return from into_parts()
- mac_address() stays on trait, MAC also in BoardParts for convenience
- Removed mac field from DisplayParts (display task calls Board::mac_address())
- DisplayDriver associated type requires DrawTarget<Color = BinaryColor>
2026-04-07 09:50:50 -06:00
swaits 7a54c4dff3 refactor: board-owned display init, zero cfg in application code
- Each board now exports DisplayDriver type and create_display() async fn
- ESP32-S3 boards share SSD1306 create_display in board/esp32s3.rs
- RAK WisBlock has its own SSD1306 create_display (different I2C type)
- Wio Tracker has SH1106 create_display (different display IC)
- Removed all cfg_if from display.rs — calls board::create_display()
- Removed display init retry logic (single attempt, return None on failure)
- ZERO board-specific #[cfg(feature)] outside board/, host/mod.rs, and hal/
2026-04-07 09:46:25 -06:00
swaits 622684cdde refactor: flatten radio.rs and display.rs, zero warnings
- Flattened radio/ directory to radio.rs (was just one file)
- Flattened display/ to display.rs (combined task + rendering as inline module)
- Moved SH1106 display driver reference from display/ to driver/ (done in Phase 2)
- Suppressed dead_code warnings for conditional USB/UART types in hal
- All boards: zero warnings, zero cfg in application code
2026-04-07 09:43:46 -06:00
swaits 2c852b9f45 refactor: unified host/ communication module, cfg-free run()
- Created host/ module with internal cfg dispatch (USB vs UART)
- Moved usb/task.rs → host/usb.rs, uart/task.rs → host/uart.rs
- Moved protocol_io.rs → host/framing.rs
- Renamed usb_task/uart_task to host_task (uniform name)
- Removed all host-comm cfg from main.rs
- The run() function now has ZERO board-specific conditional compilation
- Deleted old usb/, uart/, protocol_io.rs modules
2026-04-07 09:40:59 -06:00
swaits fb334118fd refactor: extract hal/ and driver/ modules
- Created hal/esp32s3.rs with MCU primitives (SPI, I2C, USB, UART, timer, MAC)
- Created hal/nrf52840.rs with shared nRF boilerplate (interrupts, bus wrapping, VBUS, MAC)
- Moved SH1106 OLED driver from display/ to driver/ (clear separation from app code)
- Board esp32s3.rs now delegates MCU init to hal, keeps only peripheral wiring (RadioParts)
- nRF board files use hal for shared StaticCell patterns, mac_address, interrupt bindings
- Board files still own pin assignments and peripheral construction
2026-04-07 09:38:56 -06:00
swaits 8bdaa72bc3 refactor: deduplicate USB/UART protocol framing
- Made protocol_io.rs compile for all boards (not just heltec_v3)
- Renamed FrameAccumulator → CobsDecoder, encode_response → cobs_encode_response
- Refactored USB task to use shared CobsDecoder and route_command from protocol_io
- Removed ~80 lines of duplicated COBS framing and command routing from usb/task.rs
- Single canonical MAX_FRAME definition in protocol_io.rs
2026-04-07 09:24:59 -06:00
swaits 6e8699610a WIP: fix firmware filenaming 2026-04-07 08:18:28 -06:00
swaits 73878b792f WIP: get Heltec V3 working 2026-04-07 07:58:57 -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 v0.1.0 2026-04-03 10:28:38 -06:00