Audit conducted at v1.5.2 (commit 688938d). Findings:
- 0 Critical / 0 High / 0 Medium / 2 Low / 1 Info
- Realistic attack surface = host transport + OTA RX; both traced
end-to-end with no exploitable defects found.
Hardening applied as part of audit follow-up:
- esp-println now uses default-features=false; output target is
selected per-board. UART-host boards (heltec_v3_uart,
elecrow_thinknode_m2, lilygo_tbeam classic) get `no-op` so
defmt/panic output cannot corrupt frames on UART0 (the host
protocol wire). Native-USB boards keep `auto` so espflash-monitor
still works.
- README documents the trust model: no host-transport auth, no
RF-regulatory enforcement, MAC exposed via USB iSerialNumber +
GET_INFO.mcu_uid (intentional, needed by donglora-mux for
multi-dongle disambiguation).
348 lines
12 KiB
TOML
348 lines
12 KiB
TOML
[workspace]
|
|
members = ["."]
|
|
|
|
[package]
|
|
name = "donglora"
|
|
version = "1.5.2"
|
|
edition = "2021"
|
|
build = "build.rs"
|
|
authors = ["Stephen Waits <steve@waits.net>"]
|
|
license = "MIT"
|
|
description = "Transparent LoRa radio over USB — plug in a board, talk LoRa from your host"
|
|
repository = "https://github.com/donglora/firmware"
|
|
homepage = "https://github.com/donglora/firmware"
|
|
keywords = ["lora", "radio", "embedded", "usb", "firmware"]
|
|
categories = ["embedded", "hardware-support", "no-std"]
|
|
|
|
[dependencies]
|
|
cfg-if = "1.0"
|
|
critical-section = "1.2"
|
|
defmt = "0.3"
|
|
embedded-hal = "1.0"
|
|
embedded-hal-async = "1.0"
|
|
embassy-embedded-hal = "0.6"
|
|
embassy-executor = "0.10"
|
|
embassy-sync = "0.8"
|
|
embassy-time = "0.5"
|
|
embassy-usb = { version = "0.6", features = ["defmt"] }
|
|
embassy-usb-driver = "0.2"
|
|
ucobs = { version = "0.3", features = ["legacy-msrv"] }
|
|
heapless = "0.9"
|
|
donglora-protocol = "1"
|
|
hsmc = { version = "0.6", default-features = false, features = ["embassy", "trace-defmt"] }
|
|
# Fork branch carrying CAD IRQ-clear / standby-after-CAD fixes awaiting upstream PRs.
|
|
# Switch back to crates.io once changes are upstreamed.
|
|
lora-phy = { git = "https://github.com/swaits/lora-rs.git", branch = "fixed", features = ["defmt-03"] }
|
|
portable-atomic = { version = "1", default-features = false, features = ["critical-section"] }
|
|
static_cell = "2.1"
|
|
|
|
# Display rendering
|
|
display-interface = "0.5"
|
|
embedded-graphics = "0.8"
|
|
ssd1306 = { version = "0.10", features = ["async"] }
|
|
# T114 ST7789 TFT (color, async SPI). Optional — only pulled in by the
|
|
# heltec_mesh_node_t114 feature. Mono OLED boards keep their existing
|
|
# display-interface + ssd1306/sh1106 stack and don't pay for these.
|
|
#
|
|
# `lcd-async` is an async fork of mipidsi: same model/builder shape but
|
|
# `init()` and pixel writes are `async fn`s. Combined with embassy-nrf's
|
|
# DMA-backed async Spim, the dashboard repaint yields the executor for
|
|
# the duration of the SPI transfer instead of busy-looping for ~65 ms,
|
|
# so the radio task isn't starved.
|
|
#
|
|
# embedded-hal-bus needs the `async` feature for `ExclusiveDevice` to
|
|
# implement `embedded_hal_async::spi::SpiDevice` (in addition to the
|
|
# blocking impl).
|
|
lcd-async = { version = "0.1", optional = true }
|
|
embedded-hal-bus = { version = "0.3", optional = true, features = ["async"] }
|
|
|
|
# Async IO traits and combinators
|
|
embedded-io-async = "0.7"
|
|
embassy-futures = "0.1"
|
|
|
|
# Xtensa ESP crates — requires esp toolchain (espup). The chip feature
|
|
# (esp32 / esp32s3) is selected per board via the board-level feature flag
|
|
# below, so the same dep lines support both classic ESP32 and ESP32-S3.
|
|
esp-backtrace = { version = "0.19", optional = true, features = [
|
|
"panic-handler",
|
|
"defmt",
|
|
] }
|
|
esp-hal = { version = "1.1", optional = true, features = [
|
|
"unstable",
|
|
] }
|
|
# `default-features = false` strips `auto` + `colors`. The output target
|
|
# (jtag-serial / uart / no-op / auto) is selected explicitly per board
|
|
# feature below. UART-host boards (heltec_v3_uart, elecrow_thinknode_m2,
|
|
# lilygo_tbeam) MUST use `no-op` — esp-println's `auto` falls back to
|
|
# UART0 on every ESP target here (no USB-Serial-JTAG host is ever
|
|
# attached to those boards), and UART0 IS the host protocol wire, so
|
|
# any defmt::error!() or panic backtrace would corrupt frames mid-flight.
|
|
# Native-USB boards keep `auto` so espflash-monitor still works in dev.
|
|
esp-println = { version = "0.17", optional = true, default-features = false, features = [
|
|
"critical-section",
|
|
"defmt-espflash",
|
|
] }
|
|
esp-rtos = { version = "0.3", optional = true, features = [
|
|
"embassy",
|
|
] }
|
|
# Emits the IDF app descriptor (`.flash.appdesc`) that espflash v4
|
|
# requires in every ESP binary. The chip subfeature is selected per
|
|
# board below alongside the other esp-* chip features.
|
|
esp-bootloader-esp-idf = { version = "0.5", optional = true, features = [
|
|
"defmt",
|
|
] }
|
|
|
|
# nRF52840 (RAK 4631)
|
|
cortex-m = { version = "0.7", optional = true, features = [
|
|
"critical-section-single-core",
|
|
] }
|
|
cortex-m-rt = { version = "0.7", optional = true }
|
|
defmt-rtt = { version = "0.4", optional = true }
|
|
embassy-nrf = { version = "0.10", optional = true, features = [
|
|
"nrf52840",
|
|
"gpiote",
|
|
"time-driver-rtc1",
|
|
"defmt",
|
|
# UICR pin-config flags — must match what the Adafruit nRF52 bootloader
|
|
# actually programmed into UICR on this board. Verified empirically on
|
|
# RAK4631 via `probe-rs read b32 0x10001200 4`:
|
|
# PSELRESET[0/1] = 0x12 (P0.18 connected as RESET — NOT gpio)
|
|
# NFCPINS = 0xFFFFFFFE (bit 0 = 0 → NFC pins as GPIO)
|
|
# So `nfc-pins-as-gpio` matches; `reset-pin-as-gpio` would NOT match
|
|
# (would request GPIO when UICR has RESET enabled). The bootloader
|
|
# uses NRESET for its physical button-reset path, so leaving the pin
|
|
# as RESET is the right state.
|
|
"nfc-pins-as-gpio",
|
|
] }
|
|
panic-probe = { version = "1.0", optional = true, features = ["print-defmt"] }
|
|
|
|
# RP2040 (Waveshare RP2040-LoRa)
|
|
embassy-rp = { version = "0.10", optional = true, features = [
|
|
"rp2040",
|
|
"time-driver",
|
|
"defmt",
|
|
] }
|
|
|
|
[build-dependencies]
|
|
minijinja = "2.18"
|
|
owo-colors = "4.3"
|
|
|
|
[features]
|
|
# Bridge features for hsmc's macro-expanded `#[cfg(feature = "...")]`
|
|
# attributes. Macros expand into the *call-site* crate, so any cfg the
|
|
# macro emits is evaluated against THIS crate's feature set, not hsmc's.
|
|
# That means enabling `hsmc/embassy` or `hsmc/trace-defmt` via the dep
|
|
# only activates them inside hsmc's own compilation — for the cfg gates
|
|
# inside expanded macro bodies to fire here, we need same-named features
|
|
# in this Cargo.toml. `tokio` is declared just to silence the macro's
|
|
# `#[cfg(feature = "tokio")]` checks.
|
|
#
|
|
# `trace-defmt` must be in every board feature so chart trace lines
|
|
# (entries, exits, transitions, etc.) actually compile in. Without it,
|
|
# hsmc 0.4's `__chart_observe!` macro expands to nothing for the defmt
|
|
# arm and you get silent statecharts.
|
|
embassy = []
|
|
tokio = []
|
|
trace-defmt = []
|
|
# When enabled, replaces the display task with a LED-based diagnostic
|
|
# blinker driven by `crate::debug_blink::CHECKPOINT`. Used for diagnosing
|
|
# the firmware on UART-only boards where defmt output would corrupt the
|
|
# host protocol on the same UART. The LED flashes N times then pauses —
|
|
# a human watches the pattern and reports N to determine where execution
|
|
# reached / stopped.
|
|
debug-checkpoint = []
|
|
# NOTE: chart auto-tracing in hsmc 0.4 emits `__chart_observe!` calls
|
|
# whose defmt arm is gated on `#[cfg(feature = "trace-defmt")]`. That
|
|
# cfg is evaluated against THIS crate (proc-macro cfg quirk above), so
|
|
# every board feature includes `trace-defmt` to keep the arm live. The
|
|
# `trace;` keyword in `statechart!` blocks is deprecated in 0.4 and
|
|
# no longer gates emission. Whether trace lines actually emit at
|
|
# runtime is still controlled by defmt's standard compile-time level
|
|
# filter (`DEFMT_LOG` env var) -- release builds with `DEFMT_LOG`
|
|
# unset compile the calls out to nothing.
|
|
# When enabled, the SH1106 OLED driver emits a defmt::info! line for
|
|
# every I2C transaction it issues — bytes, lengths, addresses. Used to
|
|
# debug the embassy_nrf::twim assertion panic at twim.rs:541 (cannot
|
|
# happen via the documented call path; instrumenting the SH1106 path
|
|
# captures the actual transaction sequence preceding the panic so we
|
|
# can root-cause). Off by default; opt in via
|
|
# `just flash-attach rak_wisblock_4631 debug-twim`.
|
|
debug-twim = []
|
|
heltec_v3 = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:esp-backtrace",
|
|
"dep:esp-bootloader-esp-idf",
|
|
"dep:esp-hal",
|
|
"dep:esp-println",
|
|
"dep:esp-rtos",
|
|
"esp-backtrace/esp32s3",
|
|
"esp-bootloader-esp-idf/esp32s3",
|
|
"esp-hal/esp32s3",
|
|
"esp-println/esp32s3",
|
|
# Native USB CDC-ACM via OTG_FS — UART0 (GPIO43/44) is dangling, so
|
|
# `auto`'s fallback target writes into the void: invisible but legal.
|
|
# Keeps espflash-monitor working in dev when USB-Serial-JTAG ROM mode
|
|
# is active before the firmware switches to OTG_FS.
|
|
"esp-println/auto",
|
|
"esp-rtos/esp32s3",
|
|
]
|
|
heltec_v3_uart = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:esp-backtrace",
|
|
"dep:esp-bootloader-esp-idf",
|
|
"dep:esp-hal",
|
|
"dep:esp-println",
|
|
"dep:esp-rtos",
|
|
"esp-backtrace/esp32s3",
|
|
"esp-bootloader-esp-idf/esp32s3",
|
|
"esp-hal/esp32s3",
|
|
"esp-println/esp32s3",
|
|
# UART0 IS the host protocol wire on this board. `no-op` discards all
|
|
# esp-println / defmt output so error-level logs or panic backtraces
|
|
# cannot corrupt frames mid-flight. Use `debug-checkpoint` for
|
|
# development diagnostics (LED-blink pattern instead of log bytes).
|
|
"esp-println/no-op",
|
|
"esp-rtos/esp32s3",
|
|
]
|
|
heltec_v4 = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:esp-backtrace",
|
|
"dep:esp-bootloader-esp-idf",
|
|
"dep:esp-hal",
|
|
"dep:esp-println",
|
|
"dep:esp-rtos",
|
|
"esp-backtrace/esp32s3",
|
|
"esp-bootloader-esp-idf/esp32s3",
|
|
"esp-hal/esp32s3",
|
|
"esp-println/esp32s3",
|
|
# Native USB CDC-ACM — same rationale as heltec_v3.
|
|
"esp-println/auto",
|
|
"esp-rtos/esp32s3",
|
|
]
|
|
elecrow_thinknode_m2 = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:esp-backtrace",
|
|
"dep:esp-bootloader-esp-idf",
|
|
"dep:esp-hal",
|
|
"dep:esp-println",
|
|
"dep:esp-rtos",
|
|
"esp-backtrace/esp32s3",
|
|
"esp-bootloader-esp-idf/esp32s3",
|
|
"esp-hal/esp32s3",
|
|
"esp-println/esp32s3",
|
|
# UART0 IS the host protocol wire — see heltec_v3_uart rationale.
|
|
"esp-println/no-op",
|
|
"esp-rtos/esp32s3",
|
|
]
|
|
lilygo_tbeam_supreme = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:esp-backtrace",
|
|
"dep:esp-bootloader-esp-idf",
|
|
"dep:esp-hal",
|
|
"dep:esp-println",
|
|
"dep:esp-rtos",
|
|
"esp-backtrace/esp32s3",
|
|
"esp-bootloader-esp-idf/esp32s3",
|
|
"esp-hal/esp32s3",
|
|
"esp-println/esp32s3",
|
|
# Native USB CDC-ACM — same rationale as heltec_v3.
|
|
"esp-println/auto",
|
|
"esp-rtos/esp32s3",
|
|
]
|
|
lilygo_tbeam = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:esp-backtrace",
|
|
"dep:esp-bootloader-esp-idf",
|
|
"dep:esp-hal",
|
|
"dep:esp-println",
|
|
"dep:esp-rtos",
|
|
"esp-backtrace/esp32",
|
|
"esp-bootloader-esp-idf/esp32",
|
|
"esp-hal/esp32",
|
|
"esp-println/esp32",
|
|
# ESP32 classic has no USB-Serial-JTAG peripheral — `auto` would
|
|
# unconditionally route to UART0 = host protocol wire. Use `no-op`.
|
|
"esp-println/no-op",
|
|
"esp-rtos/esp32",
|
|
]
|
|
rak_wisblock_4631 = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:cortex-m",
|
|
"dep:cortex-m-rt",
|
|
"cortex-m-rt/set-vtor",
|
|
"dep:defmt-rtt",
|
|
"dep:embassy-nrf",
|
|
"dep:panic-probe",
|
|
"embassy-executor/platform-cortex-m",
|
|
"embassy-executor/executor-thread",
|
|
]
|
|
wio_tracker_l1 = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:cortex-m",
|
|
"dep:cortex-m-rt",
|
|
"cortex-m-rt/set-vtor",
|
|
"dep:defmt-rtt",
|
|
"dep:embassy-nrf",
|
|
"dep:panic-probe",
|
|
"embassy-executor/platform-cortex-m",
|
|
"embassy-executor/executor-thread",
|
|
]
|
|
heltec_mesh_node_t114 = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:cortex-m",
|
|
"dep:cortex-m-rt",
|
|
"cortex-m-rt/set-vtor",
|
|
"dep:defmt-rtt",
|
|
"dep:embassy-nrf",
|
|
"dep:panic-probe",
|
|
"dep:lcd-async",
|
|
"dep:embedded-hal-bus",
|
|
"embassy-executor/platform-cortex-m",
|
|
"embassy-executor/executor-thread",
|
|
]
|
|
waveshare_rp2040_lora = [
|
|
"embassy",
|
|
"trace-defmt",
|
|
"dep:cortex-m",
|
|
"dep:cortex-m-rt",
|
|
"dep:defmt-rtt",
|
|
"dep:embassy-rp",
|
|
"dep:panic-probe",
|
|
"embassy-executor/platform-cortex-m",
|
|
"embassy-executor/executor-thread",
|
|
]
|
|
|
|
[lints.rust]
|
|
# hsmc 0.3's `statechart!` macro emits `#[cfg(feature = "journal")]`
|
|
# attributes (and others). check-cfg evaluates those in the CALLER's
|
|
# crate context, so unless donglora declares the same feature names
|
|
# (we don't — they're hsmc's, not ours), rustc lints them. Per hsmc's
|
|
# README, the documented fix is to allow the lint with an explicit
|
|
# check-cfg list so other unexpected cfgs still surface as warnings.
|
|
unexpected_cfgs = { level = "allow", check-cfg = [
|
|
'cfg(feature, values("tokio", "embassy", "journal", "trace-defmt", "trace-log", "trace-tracing"))',
|
|
] }
|
|
|
|
[lints.clippy]
|
|
undocumented_unsafe_blocks = "deny"
|
|
|
|
[profile.release]
|
|
codegen-units = 1
|
|
lto = "fat"
|
|
opt-level = 3 # "s"/"z" break Xtensa (l32r literal range exceeded)
|
|
panic = "abort"
|
|
# `strip = true` was killing defmt's `_defmt_version_` / `_defmt_encoding_`
|
|
# symbols, breaking RTT log decoding via probe-rs. Keep symbols + DWARF
|
|
# so probe-rs can show source locations for log lines.
|
|
strip = false
|
|
debug = 2
|