Commit Graph
19 Commits
Author SHA1 Message Date
swaits 9560349547 feat: switch ucobs to published crate 0.2, note upgrade blockers
Switch ucobs from local path dependency to crates.io v0.2. Add note
documenting that embassy-executor 0.10 and ucobs 0.3 are blocked on
the ESP Xtensa toolchain supporting Rust >= 1.93 stable.
2026-03-30 20:46:08 -06:00
swaits b34ca99fd4 feat: upgrade all dependencies to latest versions
Major version bumps:
- defmt 0.3 → 1.0
- esp-hal 0.22 → 1.0 (full API migration)
- esp-hal-embassy 0.5 → esp-rtos 0.2 (replacement crate)
- esp-backtrace 0.14 → 0.18
- esp-println 0.12 → 0.16
- embassy-nrf 0.2 → 0.10
- embassy-embedded-hal 0.2 → 0.6
- embassy-sync 0.6 → 0.8
- embassy-time 0.3 → 0.5
- embassy-usb 0.3 → 0.6
- embassy-usb-driver 0.1 → 0.2
- embedded-io-async 0.6 → 0.7
- heapless 0.8 → 0.9
- panic-probe 0.3 → 1.0
- minijinja 2.0 → 2.18
- owo-colors 4.0 → 4.3

embassy-executor stays at 0.9 (latest with Xtensa support via esp-rtos).
Updated ESP toolchain from 1.82 to 1.93.
2026-03-30 12:59:05 -06:00
swaits 4c6cab0bfe refactor: replace donglora-cobs with external ucobs crate 2026-03-30 11:27:00 -06:00
swaits d7bb127d92 test: proptest property testing + cargo-fuzz fuzzing for donglora-cobs 2026-03-30 10:57:57 -06:00
swaits 1fc86f413a test: exhaustive COBS interop tests against corncobs, porting guide, README
- 8 new interop tests cross-validating our COBS encoder/decoder against
  corncobs (including 500 random payloads). Total: 39 tests.
- New: src/board/PORTING.md — step-by-step guide to adding a new board
- New: crates/donglora-cobs/README.md — documents design choice and testing
- Updated README with lora_bridge.py example
2026-03-30 08:55:48 -06:00
swaits 2633daf437 refactor: LoRaBoard trait for compile-time board completeness verification
Every board's Board struct now implements the LoRaBoard trait, which
enforces that NAME, TX_POWER_RANGE, init(), and mac_address() are all
provided. Missing any of these is a compile error — no "good intentions"
needed.

The trait verifies metadata and initialization. Tasks use the board's
concrete types directly (Embassy tasks must be concrete). Board-specific
type aliases (RadioDriver, UsbDriver, DisplayI2c) and struct definitions
(RadioParts, UsbParts, DisplayParts) remain in each board file.

- New: src/board/traits.rs (LoRaBoard trait definition)
- Modified: all 3 board files (implement trait, remove standalone constants)
- Modified: main.rs (import trait for init)
- Modified: radio/task.rs (use Board::TX_POWER_RANGE via trait)
- Modified: display/task.rs (use Board::NAME via trait)
- Modified: build.rs (exclude traits.rs from board discovery)
- Removed: embedded-hal-async direct dep (not needed for trait approach)
2026-03-30 08:44:39 -06:00
swaits 65c43cda23 feat: donglora-cobs workspace crate with 31 spec-compliance tests
Extract COBS encoder/decoder into a separate no_std workspace crate
(crates/donglora-cobs) with comprehensive tests against the Cheshire &
Baker 1999 IEEE/ACM specification and Wikipedia canonical vectors.

Tests cover: empty input, single/multiple zeros, block boundaries at
254/255 bytes, alternating patterns, the Ping regression case (tag 0x00),
round-trip verification, and error cases (truncation, unexpected zeros).

Replace inline cobs_decode() in usb/task.rs with the tested crate.
Replace external cobs dependency with workspace crate.
Fix encoder bug at 254-byte block boundary (spurious trailing code byte).
2026-03-29 21:33:08 -06:00
swaits 1646564bc1 refactor: replace postcard/serde with fixed-size LE wire format
Drop postcard and serde dependencies entirely. All integers are now
fixed-width little-endian on the wire — no varints, no zigzag encoding.
A developer sending tx_power_dbm=14 sees 0x0E on the wire. No surprises.

- RadioConfig: 10 bytes fixed (freq:4 LE, bw:1, sf:1, cr:1, sync:2 LE, pwr:1)
- Commands/Responses: tag byte + fixed-size fields
- Vec<u8>: 2-byte LE length prefix + raw bytes
- Option<T>: 1-byte (0=None, 1=Some) + T
- COBS framing preserved (just byte-stuffing, no encoding tricks)
- Manual COBS decode replaces postcard's CobsAccumulator
- Removes ~15KB from firmware binary (no serde monomorphization)
2026-03-29 20:42:58 -06:00
swaitsandClaude Opus 4.6 4e7e6d2167 feat: USB OTG CDC-ACM with custom VID:PID, disconnect UX
Switch all ESP32-S3 boards from UsbSerialJtag to USB OTG CDC-ACM via
embassy-usb. The USB device stack is now shared code — boards only
provide the platform-specific embassy-usb Driver.

- Custom VID:PID 1209:5741 (open-source VID, "WA" PID)
- CDC-ACM class with proper device enumeration
- COBS+postcard protocol over CDC-ACM packet read/write
- Concurrent USB device loop + protocol loop via join()
- On disconnect: sends DisplayCommand::Reset to show "Waiting for host..."
- Justfile: espflash no longer uses --monitor (OTG steals PHY)
- Test script updated to detect by new VID:PID

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:56 -06:00
swaitsandClaude Opus 4.6 102ce417ff fix: ack SetConfig/StartRx/StopRx, improve display layout
Protocol:
- SetConfig, StartRx, StopRx now send Response::Ok on success
  (host was timing out waiting for acks that never came)

Display:
- Full frequency precision: 910.525MHz not 910.5MHz
- Full bandwidth labels: 62.5kHz not 62k
- RSSI/SNR row shows units (dBm, dB)
- Separator line moved down 3px to give RSSI/SNR breathing room
- Sparkline is now time-based: each bar = 1 second, scrolls left,
  showing ~1 minute of RSSI history. Empty slots = no packets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:56 -06:00
swaitsandClaude Opus 4.6 b6fd85495a feat: implement USB serial with COBS-framed postcard protocol
Wire up UsbSerialJtag on ESP32-S3 boards for host communication:
- Read COBS-framed postcard commands from host via serial
- Route DisplayOn/Off to display task, everything else to radio
- Serialize responses back to host as COBS frames
- Use postcard::CobsAccumulator for chunked deserialization
- Concurrent read/write via embassy select

RAK nRF52840 USB (embassy-usb CDC-ACM) is still a stub — different
init path, will be implemented separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:55 -06:00
swaitsandClaude Opus 4.6 f1df675363 refactor: rename project to DongLoRa
Rebrand from lora-dongle to DongLoRa (portmanteau of dongle + LoRa).
Lowercase donglora in Cargo package name, firmware filenames, and code.
Splash screen shows "DongLoRa" in 9x15 bold.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:55 -06:00
swaitsandClaude Opus 4.6 7d20e3ef97 fix: resolve ESP32-S3 runtime crash, match official esp-hal examples
Root causes found and fixed:
- USB OTG init stole the PHY from USB-Serial-JTAG (the only USB on V4)
- executor-thread feature conflicted with esp-hal-embassy's executor
- Linker flags needed -Wl, prefix for GCC-to-LD passthrough
- Missing exception-handler feature on esp-backtrace
- Missing alloc in build-std
- Missing LTO and codegen-units=1 in release profile
- Vext on V4 is active LOW (datasheet correct, Arduino lib was wrong)

Now matches the official esp-hal v0.22.0 examples exactly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:55 -06:00
swaitsandClaude Opus 4.6 da37f6aa8a fix: resolve ESP32-S3 linker errors, all boards produce firmware
Three fixes:
- Add .cargo/config.toml with -Tlinkall.x for xtensa target (resolves
  ROM functions, interrupt handlers, memory layout)
- Add integrated-timers feature to embassy-executor for ESP32 boards
  (resolves _embassy_time_schedule_wake)
- Add `use esp_println as _` to pull in defmt global_logger (was being
  stripped as dead code because nothing referenced the crate)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:55 -06:00
swaitsandClaude Opus 4.6 8649b2ec46 fix: get Heltec V3 (ESP32-S3) building with esp toolchain
Use nightly cargo + esp rustc (RUSTC override) + -Zbuild-std=core for
Xtensa targets. This avoids the esp cargo's inability to parse modern
crate manifests (edition 2024) while still using esp's LLVM with full
Xtensa support.

Pin proc-macro-crate to 3.2.0 to avoid transitive deps requiring
rustc 1.85+.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:54 -06:00
swaitsandClaude Opus 4.6 3102fc747a feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:54 -06:00
swaits 7064110293 feat: restore Heltec V3, add firmware output, support all common boards
Reverts the removal of heltec_v3 — it's a major board. Xtensa builds
use the esp toolchain (+esp via espup); check-all gracefully skips
boards whose toolchain isn't installed.

Build artifacts now land in firmware/ with readable names like
lora-dongle-rak_4631-release.elf instead of being buried in target/.

DESIGN.md updated with "support all common boards" principle (#10)
and corrected toolchain policy (#11).
2026-03-29 20:42:54 -06:00
swaits 0e9315280b refactor: drop Heltec V3, enforce standard toolchain, remove dead code
Heltec V3 (ESP32-S3) requires the espup Xtensa toolchain fork, which
violates the standard-toolchain-only principle. Removed until upstream
Rust supports Xtensa targets via stock rustup.

Also removes all #[allow(dead_code)] band-aids — unused items are
deleted rather than suppressed. DESIGN.md updated with standard
toolchain principle (#10) and peripheral model (input/telemetry slots).
2026-03-26 15:29:58 -06:00
swaits c30f480c34 feat: initial lora-dongle firmware scaffold
Transparent LoRa-over-USB dongle firmware supporting Heltec V3 (ESP32-S3)
and RAK 4631 (nRF52840) boards. Actor-model architecture with typed
channels between USB, radio, and display tasks.

- Protocol: Command/Response enums with COBS+postcard framing design
- Board abstraction: auto-discovered via build.rs Jinja2 codegen
- Radio task: SX1262 state machine (Idle → Receiving ↔ Transmitting)
- Display task: optional SSD1306 dashboard via StatusWatch
- RAK 4631: fully wired SPI/USB/I2C peripherals, clippy-clean
- Heltec V3: structure in place, awaits espup Xtensa toolchain
2026-03-26 15:16:42 -06:00