From 83a76d4b483be1fa49194d90240f059a9bb01b6c Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 31 Mar 2026 16:01:15 -0600 Subject: [PATCH] chore: update tooling (mise, justfile, examples) Add mise.toml for tool version management, rename Justfile to justfile, add examples/justfile for running Python examples, and update docs. --- CONTRIBUTING.md | 9 +-- Cargo.lock | 4 +- Justfile | 133 ------------------------------------ README.md | 40 +++++------ examples/justfile | 29 ++++++++ justfile | 169 ++++++++++++++++++++++++++++++++++++++++++++++ mise.toml | 10 +++ 7 files changed, 235 insertions(+), 159 deletions(-) delete mode 100644 Justfile create mode 100644 examples/justfile create mode 100644 justfile create mode 100644 mise.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c077a2c..eab3fb14 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ DongLoRa is designed to make adding new boards easy: 1. Create `src/board/your_board.rs` implementing the `init()` function 2. Add a Cargo feature in `Cargo.toml` with the board's dependencies -3. Add the board's feature/target/chip to the `Justfile` boards list +3. Add the board's feature/target/chip to the `justfile` boards list 4. `build.rs` auto-discovers the new file and generates the module selector See existing board files (`heltec_v3.rs`, `rak_wisblock_4631.rs`) as templates. @@ -23,13 +23,14 @@ common tasks consume. No shared board code — each board is self-contained. ## Building and Testing ```sh -just check-all # Compile-check all boards (skips unavailable toolchains) +just setup # Install all tools and toolchains (one-time) +just check-all # Compile-check all boards just build-all # Build release firmware for all boards just clippy # Lint a specific board ``` -Xtensa boards (Heltec V3/V4) require the ESP toolchain via `espup`. -ARM boards (RAK 4631) build with stock `rustup`. +Tool versions (espup, espflash, probe-rs) are pinned in `mise.toml`. +`just setup` installs everything; individual commands also auto-install as needed. ## Commits diff --git a/Cargo.lock b/Cargo.lock index f43c4255..d86c4dee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1818,9 +1818,7 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucobs" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e019ee3a487897e4b704b5702deb9f2181f7fc210558c9fb5b18aacb12e3f7" +version = "0.3.1" [[package]] name = "ufmt-write" diff --git a/Justfile b/Justfile deleted file mode 100644 index 555e8f7b..00000000 --- a/Justfile +++ /dev/null @@ -1,133 +0,0 @@ -set shell := ["bash", "-c"] - -# Board definitions: feature target chip -# Xtensa targets use nightly cargo + esp rustc + -Zbuild-std=core. -# check-all gracefully skips boards whose toolchain isn't installed. -heltec_v3 := "heltec_v3 xtensa-esp32s3-none-elf esp32s3" -heltec_v4 := "heltec_v4 xtensa-esp32s3-none-elf esp32s3" -rak_wisblock_4631 := "rak_wisblock_4631 thumbv7em-none-eabihf nRF52840_xxAA" - -firmware_dir := "firmware" - -# All known boards -boards := "heltec_v3 heltec_v4 rak_wisblock_4631" - -# Build release firmware for all boards with available toolchains -build-all: - @trap 'exit 130' INT; \ - for board in {{boards}}; do \ - if just _can_build $board 2>/dev/null; then \ - echo "── building $board ──"; \ - just build $board || exit $?; \ - else \ - echo "── skipping $board (toolchain not available) ──"; \ - fi; \ - done - -# Check all boards that can build with the available toolchain -check-all: - @trap 'exit 130' INT; \ - for board in {{boards}}; do \ - if just _can_build $board 2>/dev/null; then \ - echo "── checking $board ──"; \ - just check $board || exit $?; \ - else \ - echo "── skipping $board (toolchain not available) ──"; \ - fi; \ - done - -# Check a single board compiles -check board: - @just _cargo {{board}} check - -# Run clippy on a single board -clippy board: - @just _cargo {{board}} "clippy" "-- -D warnings" - -# Build release firmware and copy to firmware/ with a readable name -build board profile="release": - @just _cargo {{board}} "build --{{profile}}" - @just _copy_firmware {{board}} {{profile}} - -# Build and flash a board (espflash for Xtensa, probe-rs for ARM) -flash board: - @just _require_flash_tool {{board}} - @just build {{board}} release - @read -r feat target chip <<< "$(just _info {{board}})"; \ - case "$target" in \ - xtensa-*) espflash flash "{{firmware_dir}}/donglora-{{board}}-release.elf" ;; \ - *) probe-rs run --chip $chip "{{firmware_dir}}/donglora-{{board}}-release.elf" ;; \ - esac - -# Show binary size for a release build -size board: - @just _cargo {{board}} "size --release" - -# ── Private helpers ─────────────────────────────────────────────────── - -# Run a cargo command for a board. -# Xtensa: nightly cargo + esp rustc (via RUSTC override) + -Zbuild-std=core -[private] -_cargo board cmd extra_args="": - @read -r feat target chip <<< "$(just _info {{board}})"; \ - env=""; extra=""; \ - case "$target" in xtensa-*) \ - just _require_esp_toolchain; \ - [ -f "$HOME/export-esp.sh" ] && . "$HOME/export-esp.sh"; \ - env="RUSTC=$(rustup which rustc --toolchain esp)"; extra="+nightly"; buildstd="-Zbuild-std=core,alloc";; \ - esac; \ - eval $env cargo $extra {{cmd}} --target "$target" --features "$feat" $buildstd {{extra_args}} - -# Check if a board's toolchain is available (silent, for build-all/check-all skipping) -[private] -_can_build board: - @read -r feat target chip <<< "$(just _info {{board}})"; \ - case "$target" in \ - xtensa-*) rustup toolchain list | grep -q "^esp" ;; \ - *) rustup target list --installed | grep -q "^$target$" || rustup target add "$target" >/dev/null 2>&1 ;; \ - esac - -# Verify the flash tool is installed for a board, with install instructions if not -[private] -_require_flash_tool board: - @read -r feat target chip <<< "$(just _info {{board}})"; \ - case "$target" in \ - xtensa-*) \ - if ! command -v espflash >/dev/null 2>&1; then \ - echo "error: espflash is required to flash ESP32 boards" >&2; \ - echo " install: cargo install espflash" >&2; \ - exit 1; \ - fi ;; \ - *) \ - if ! command -v probe-rs >/dev/null 2>&1; then \ - echo "error: probe-rs is required to flash ARM boards" >&2; \ - echo " install: cargo install probe-rs-tools" >&2; \ - exit 1; \ - fi ;; \ - esac - -# Verify the esp toolchain is installed, with install instructions if not -[private] -_require_esp_toolchain: - @if ! rustup toolchain list | grep -q "^esp"; then \ - echo "error: ESP toolchain not found" >&2; \ - echo " install:" >&2; \ - echo " cargo install espup" >&2; \ - echo " espup install" >&2; \ - exit 1; \ - fi - -[private] -_copy_firmware board profile: - @mkdir -p {{firmware_dir}} - @read -r feat target chip <<< "$(just _info {{board}})"; \ - src="target/$target/{{profile}}/donglora"; \ - dst="{{firmware_dir}}/donglora-{{board}}-{{profile}}"; \ - if [ -f "$src" ]; then cp "$src" "$dst.elf"; echo "→ $dst.elf"; fi - -[private] -_info name: - @if [ "{{name}}" == "heltec_v3" ]; then echo "{{heltec_v3}}"; \ - elif [ "{{name}}" == "heltec_v4" ]; then echo "{{heltec_v4}}"; \ - elif [ "{{name}}" == "rak_wisblock_4631" ]; then echo "{{rak_wisblock_4631}}"; \ - else echo "Unknown board: {{name}}" >&2; exit 1; fi diff --git a/README.md b/README.md index cdb9dea9..5d48ac29 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ and gets out of the way. No mesh logic, no protocol opinions, no config files. ## Quick Start 1. Get a [supported board](#supported-boards) -2. Install toolchain (see [Building](#building)) -3. Build and flash: `just flash heltec_v4` -4. Run an example: `uv run examples/simple_rx.py` +2. `just setup` (installs all tools and toolchains) +3. `just flash heltec_v4` +4. `just ex rx` 5. See packets ## Supported Boards @@ -34,41 +34,43 @@ specification with worked examples. ## Examples -All examples use [uv](https://docs.astral.sh/uv/) for dependency management: +Python dependencies are handled automatically — just run: + +```sh +just ex rx # receive packets +just ex tx # transmit a packet +just ex ping-pong --role tx # two-dongle ping-pong demo +just ex test-commands # exercise all DongLoRa commands +just ex bridge --mode server # LoRa bridge over TCP +just ex meshcore # MeshCore packet decoder +just ex run simple_rx # run any example by name +``` | Script | Description | |--------|-------------| -| [`all_commands.py`](examples/all_commands.py) | Exercise all 8 commands (Ping, SetConfig, GetConfig, StartRx, StopRx, Transmit, DisplayOn/Off) | | [`simple_rx.py`](examples/simple_rx.py) | Configure radio, receive and print packets | | [`simple_tx.py`](examples/simple_tx.py) | Transmit a single packet | | [`ping_pong.py`](examples/ping_pong.py) | Two-dongle demo (`--role tx` / `--role rx`) | +| [`all_commands.py`](examples/all_commands.py) | Exercise all 8 commands (Ping, SetConfig, GetConfig, StartRx, StopRx, Transmit, DisplayOn/Off) | | [`lora_bridge.py`](examples/lora_bridge.py) | Two-way LoRa bridge over TCP (works over Tailscale, WireGuard, etc.) | | [`meshcore/`](examples/meshcore/) | Full MeshCore packet decoder (advanced example) | ## Building -Requires [just](https://github.com/casey/just) and Rust. +Requires [just](https://github.com/casey/just), [mise](https://mise.jdx.dev/), and Rust. ```sh -just build-all # build firmware for all boards (skips unavailable toolchains) +just setup # install all tools and toolchains (one-time) +just build-all # build firmware for all boards just build heltec_v4 # build a specific board just check-all # compile-check only (no firmware output) just flash heltec_v4 # build + flash just clippy heltec_v4 # lint ``` -### Xtensa boards (Heltec V3/V4) - -Requires the [ESP toolchain](https://github.com/esp-rs/espup): - -```sh -cargo install espup -espup install --toolchain-version 1.82.0.3 -``` - -### ARM boards (RAK 4631) - -Stock Rust stable. Targets are installed automatically. +`just setup` handles everything: mise-managed tools (espup, espflash, +probe-rs), the ESP Xtensa toolchain, nightly rust-src, and ARM targets. +Individual build commands will also auto-install missing tools as needed.
Board Roadmap diff --git a/examples/justfile b/examples/justfile new file mode 100644 index 00000000..47be8cea --- /dev/null +++ b/examples/justfile @@ -0,0 +1,29 @@ +set shell := ["bash", "-c"] + +# Receive LoRa packets (Ctrl-C to stop) +rx *args: + @mise install && uv run {{source_directory()}}/simple_rx.py {{args}} + +# Transmit a single packet +tx *args: + @mise install && uv run {{source_directory()}}/simple_tx.py {{args}} + +# Two-dongle ping-pong demo (--role tx|rx) +ping-pong *args: + @mise install && uv run {{source_directory()}}/ping_pong.py {{args}} + +# Exercise all DongLoRa commands +test-commands *args: + @mise install && uv run {{source_directory()}}/all_commands.py {{args}} + +# Two-way LoRa bridge over TCP +bridge *args: + @mise install && uv run {{source_directory()}}/lora_bridge.py {{args}} + +# MeshCore packet decoder/monitor +meshcore *args: + @mise install && uv run {{source_directory()}}/meshcore/meshcore_rx.py {{args}} + +# Run any example by name (e.g. just ex run simple_rx) +run script *args: + @mise install && uv run {{source_directory()}}/{{script}}.py {{args}} diff --git a/justfile b/justfile new file mode 100644 index 00000000..8da7ed7c --- /dev/null +++ b/justfile @@ -0,0 +1,169 @@ +set shell := ["bash", "-c"] + +# Board definitions: feature target chip +# Xtensa targets use nightly cargo + esp rustc + -Zbuild-std=core. +# Tool versions are pinned in mise.toml; run `just setup` to install everything. +heltec_v3 := "heltec_v3 xtensa-esp32s3-none-elf esp32s3" +heltec_v4 := "heltec_v4 xtensa-esp32s3-none-elf esp32s3" +rak_wisblock_4631 := "rak_wisblock_4631 thumbv7em-none-eabihf nRF52840_xxAA" + +firmware_dir := "firmware" + +# All known boards +boards := "heltec_v3 heltec_v4 rak_wisblock_4631" + +# Install all required tools and toolchains +setup: + mise trust --yes + mise install + @# ESP Xtensa toolchain (not managed by mise) + @if ! rustup toolchain list | grep -q "^esp"; then \ + echo "ESP toolchain not found, installing via espup (this may take a while)..."; \ + espup install || { echo "error: espup install failed" >&2; exit 1; }; \ + fi + @# nightly rust-src for -Zbuild-std + @if ! rustup component list --toolchain nightly --installed 2>/dev/null | grep -q "^rust-src"; then \ + rustup component add rust-src --toolchain nightly; \ + fi + @# ARM target + @rustup target list --installed | grep -q "^thumbv7em-none-eabihf$" || rustup target add thumbv7em-none-eabihf + +# Build release firmware for all boards, installing toolchains as needed +build-all: + @trap 'exit 130' INT; \ + for board in {{boards}}; do \ + if just _can_build $board; then \ + echo "── building $board ──"; \ + just build $board || exit $?; \ + else \ + echo "── skipping $board (toolchain install failed) ──"; \ + fi; \ + done + +# Check all boards compile, installing toolchains as needed +check-all: + @trap 'exit 130' INT; \ + for board in {{boards}}; do \ + if just _can_build $board; then \ + echo "── checking $board ──"; \ + just check $board || exit $?; \ + else \ + echo "── skipping $board (toolchain install failed) ──"; \ + fi; \ + done + +# Check a single board compiles +check board: + @just _cargo {{board}} check + +# Run clippy on a single board +clippy board: + @just _cargo {{board}} "clippy" "-- -D warnings" + +# Build release firmware and copy to firmware/ with a readable name +build board profile="release": + @just _cargo {{board}} "build --{{profile}}" + @just _copy_firmware {{board}} {{profile}} + +# Build and flash a board (espflash for Xtensa, probe-rs for ARM) +flash board: + @just _ensure_tools + @just build {{board}} release + @read -r feat target chip <<< "$(just _info {{board}})"; \ + case "$target" in \ + xtensa-*) \ + port=$(just _find_port "303a:1001" "1209:5741"); \ + if [ -n "$port" ]; then \ + echo "Using $port"; \ + espflash flash -p "$port" "{{firmware_dir}}/donglora-{{board}}-release.elf"; \ + else \ + echo "No port found, falling back to espflash auto-detection..." >&2; \ + espflash flash "{{firmware_dir}}/donglora-{{board}}-release.elf"; \ + fi ;; \ + *) probe-rs run --chip $chip "{{firmware_dir}}/donglora-{{board}}-release.elf" ;; \ + esac + +# Show binary size for a release build +size board: + @just _cargo {{board}} "size --release" + +# Run host-side protocol unit tests +test: + DONGLORA_HOST_TEST=1 cargo test + +# Example scripts (just ex rx, just ex tx, just ex meshcore, ...) +mod ex 'examples/justfile' + +# ── Private helpers ─────────────────────────────────────────────────── + +# Install mise-managed tools if any are missing (fast no-op when current) +[private] +_ensure_tools: + @mise install + +# Run a cargo command for a board. +# Xtensa: nightly cargo + esp rustc (via RUSTC override) + -Zbuild-std=core +[private] +_cargo board cmd extra_args="": + @just _ensure_tools + @read -r feat target chip <<< "$(just _info {{board}})"; \ + env=""; extra=""; \ + case "$target" in xtensa-*) \ + just _require_esp_toolchain; \ + [ -f "$HOME/export-esp.sh" ] && . "$HOME/export-esp.sh"; \ + env="RUSTC=$(rustup which rustc --toolchain esp)"; extra="+nightly"; buildstd="-Zbuild-std=core,alloc";; \ + esac; \ + eval $env cargo $extra {{cmd}} --target "$target" --features "$feat" $buildstd {{extra_args}} + +# Ensure a board's toolchain is available, auto-installing if needed +[private] +_can_build board: + @read -r feat target chip <<< "$(just _info {{board}})"; \ + case "$target" in \ + xtensa-*) just _require_esp_toolchain ;; \ + *) rustup target list --installed | grep -q "^$target$" || rustup target add "$target" ;; \ + esac + +# Ensure the ESP Xtensa toolchain is installed (espup installed via mise) +[private] +_require_esp_toolchain: + @just _ensure_tools + @if ! rustup toolchain list | grep -q "^esp"; then \ + echo "ESP toolchain not found, installing via espup (this may take a while)..." >&2; \ + espup install || { echo "error: espup install failed" >&2; exit 1; }; \ + fi; \ + if ! rustup component list --toolchain nightly --installed 2>/dev/null | grep -q "^rust-src"; then \ + echo "Installing nightly rust-src (needed for -Zbuild-std)..." >&2; \ + rustup component add rust-src --toolchain nightly || { echo "error: failed to install rust-src" >&2; exit 1; }; \ + fi + +# Find a serial port matching any of the given VID:PID pairs (checked in order) +[private] +_find_port +vid_pids: + @for vidpid in {{vid_pids}}; do \ + vid="${vidpid%%:*}"; pid="${vidpid##*:}"; \ + for dev in /dev/ttyACM* /dev/ttyUSB*; do \ + [ -e "$dev" ] || continue; \ + info=$(udevadm info --query=property --name="$dev" 2>/dev/null) || continue; \ + dev_vid=$(echo "$info" | sed -n 's/^ID_VENDOR_ID=//p' | tr '[:upper:]' '[:lower:]'); \ + dev_pid=$(echo "$info" | sed -n 's/^ID_MODEL_ID=//p' | tr '[:upper:]' '[:lower:]'); \ + if [ "$dev_vid" = "$vid" ] && [ "$dev_pid" = "$pid" ]; then \ + echo "$dev"; exit 0; \ + fi; \ + done; \ + done + +[private] +_copy_firmware board profile: + @mkdir -p {{firmware_dir}} + @read -r feat target chip <<< "$(just _info {{board}})"; \ + src="target/$target/{{profile}}/donglora"; \ + dst="{{firmware_dir}}/donglora-{{board}}-{{profile}}"; \ + if [ -f "$src" ]; then cp "$src" "$dst.elf"; echo "→ $dst.elf"; fi + +[private] +_info name: + @if [ "{{name}}" == "heltec_v3" ]; then echo "{{heltec_v3}}"; \ + elif [ "{{name}}" == "heltec_v4" ]; then echo "{{heltec_v4}}"; \ + elif [ "{{name}}" == "rak_wisblock_4631" ]; then echo "{{rak_wisblock_4631}}"; \ + else echo "Unknown board: {{name}}" >&2; exit 1; fi diff --git a/mise.toml b/mise.toml new file mode 100644 index 00000000..34e64037 --- /dev/null +++ b/mise.toml @@ -0,0 +1,10 @@ +[tools] +rust = "1.93" +python = "3.14" +uv = "latest" +just = "latest" +jj = "latest" + +"cargo:espup" = "0.16" +"cargo:espflash" = "3" # 4.x has broken nix dependency on current stable Rust +"cargo:probe-rs-tools" = "0.31"