Skip to content

Commit b56b0b0

Browse files
committed
Update .gitignore, CHANGELOG, and ROADMAP for April 2026 stack upgrade
1 parent 8d58a09 commit b56b0b0

10 files changed

Lines changed: 29 additions & 32 deletions

.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ Cargo.lock
1010
.claudeignore
1111
.codex/
1212
CLAUDE.md
13-
inbox/
14-
outbox/
15-
archive/
13+
/inbox/
14+
/outbox/
15+
/archive/
1616

1717
# Local cargo config (use .cargo/config.toml.dist as template)
1818
.cargo/config.toml

CHANGELOG.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
### Added
1616

17-
- **OTA MVP** — three new crates for end-to-end firmware update on ESP32-C3 (and ESP32-C6 / ESP32 for the bare-metal stack), aligned with `docs/adr/011-ota-crate-hosting-and-transport.md` and `docs/features/ota-mvp-v1.md`. All public APIs are explicitly experimental for MVP; stabilization is owned by the future `ota-library` feature.
17+
- **OTA MVP** — three new crates for end-to-end firmware update on ESP32-C3 (and ESP32-C6 / ESP32 for the bare-metal stack), aligned with `docs/adr/011-ota-crate-hosting-and-transport.md` and `docs/features/archive/ota-mvp-v1.md`. All public APIs are explicitly experimental for MVP; stabilization is owned by the future `ota-library` feature.
1818
- `ota-pure` (new crate, **experimental API**) — platform-independent, `no_std`, host-tested. Surface: `Version` semver parser (`u16` components, `Display`, `Ord`), `StreamingVerifier` (chunk-fed SHA-256 over `sha2 = { default-features = false }`), `bytes_to_hex` / `hex_to_bytes` fixed-size helpers (returns `heapless::String<64>`), `ImageMetadata` sidecar parser (`.bin.sha256` + `.bin.version`), backend-neutral `OtaState` enum (`Idle → Downloading → Verifying → Writing → SwapPending → Booted`) with `next_state()`, and `OtaError` with the 8 MVP variants (`ServerUnreachable`, `DownloadFailed { status: u16 }`, `DownloadTimeout`, `ChecksumMismatch`, `VersionInvalid`, `FlashWriteFailed`, `PartitionNotFound`, `InsufficientSpace`). `DownloadFailed { status: 0 }` is reserved as a sentinel for protocol-shape rejections from the bare-metal HTTP client. 37 host unit tests.
1919
- `rustyfarian-esp-idf-ota` (new crate, **experimental API**, blocking) — ESP-IDF std, lifted from `rustyfarian-beekeeper/src/ota/`. Surface: `OtaSession::new(config)`, `fetch_and_apply(url, &expected_sha256)`, `mark_valid()`, `rollback()`. Wraps `EspOta` / `EspOtaUpdate` and `EspHttpConnection`; streams download → SHA-256 verify (`StreamingVerifier`) → flash → swap in one pass without holding the full image in RAM. Strips RFC 3986 userinfo from URLs before logging (no credential leakage to `espflash monitor`). HTTPS rejected at MVP scope per ADR 011 (`ota-hardened` will revisit).
2020
- `rustyfarian-esp-hal-ota` (new crate, **experimental API**, async-only) — bare-metal `no_std`, built fresh against `esp_bootloader_esp_idf::OtaUpdater` over `esp-storage` and `embassy-net::TcpSocket`. Surface: `EspHalOtaManager::new(config, FLASH<'d>)`, `async fetch_and_apply(socket, url, &expected_sha256)`, `mark_valid()`, `rollback()`. Carries an internal hand-rolled HTTP/1.1 GET parser (per ADR 011 §2): accepts only `HTTP/1.1 200 OK` with exactly one valid `Content-Length`; rejects redirects, `Transfer-Encoding: chunked`/`identity`, missing or duplicate `Content-Length`, non-`1*DIGIT` numeric values (incl. leading `+`/`-`), whitespace before colon, oversized bodies, and short reads. Chip features `esp32c3` (MVP), `esp32c6`, `esp32`; stack features `unstable`, `rt`, `embassy`. Host stub mirrors the wifi-crate pattern (typecheck-only). 29 parser unit tests.
2121
- Workspace: `sha2 = { version = "0.10", default-features = false }`, `esp-storage = "=0.9.0"`, `embedded-storage = "0.3"` added to `[workspace.dependencies]`; `embedded-svc = "0.29"` declared (aligning with `esp-idf-svc 0.52`).
2222
- Justfile: `check-ota-pure`, `test-ota`, `check-ota-idf`, `check-ota-hal`, `check-ota-hal-embassy`, `test-ota-hal` recipes; `test` aggregate extended.
2323
- CI (`.github/workflows/rust.yml`): host tests for `ota-pure` and `rustyfarian-esp-hal-ota --no-default-features` added to the "Test pure crates" block.
2424
- Build scripts: `scripts/detect-port.sh` narrows `espflash`'s auto-detect to USB serial devices (`usbmodem*`/`usbserial*` on macOS, `ttyUSB*`/`ttyACM*` on Linux) so paired Bluetooth ports stop hijacking the probe; used by `flash.sh`, `just run`, `just monitor`, and `just erase-flash`. `ESPFLASH_PORT=…` still wins when set explicitly
25-
- `rustyfarian-esp-hal-wifi`: `embassy` Cargo feature + `WiFiManager::init_async()` returning an `AsyncWifiHandle { controller, stack, runner }` wired into an `embassy-net` stack with automatic DHCPv4 (`AsyncWifiHandle::wait_for_ip().await` awaits the first lease). Originally landed alongside a synchronous `WiFiManager::init` path that drove `smoltcp` directly; that sync path was removed later in this same release cycle when the stack moved to `esp-radio 0.18` (see the breaking-change entry below). The `embassy` feature is now the only supported Wi-Fi path on bare-metal — see `docs/features/embassy-feature-flag-v1.md` and `docs/features/wifi-manager-async-v1.md`
25+
- `rustyfarian-esp-hal-wifi`: `embassy` Cargo feature + `WiFiManager::init_async()` returning an `AsyncWifiHandle { controller, stack, runner }` wired into an `embassy-net` stack with automatic DHCPv4 (`AsyncWifiHandle::wait_for_ip().await` awaits the first lease). Originally landed alongside a synchronous `WiFiManager::init` path that drove `smoltcp` directly; that sync path was removed later in this same release cycle when the stack moved to `esp-radio 0.18` (see the breaking-change entry below). The `embassy` feature is now the only supported Wi-Fi path on bare-metal — see `docs/features/archive/embassy-feature-flag-v1.md` and `docs/features/archive/wifi-manager-async-v1.md`
2626
- `rustyfarian-esp-hal-wifi`: `hal_c3_connect_async` example — first async bare-metal Wi-Fi demo on ESP32-C3, uses `#[esp_rtos::main]` with two spawned tasks (`wifi_task` for association + reconnection, `net_task` for the embassy-net runner), prints the DHCP-assigned IP and idles asynchronously (see `docs/features/hal-c3-connect-async-example-v1.md`)
2727
- Build scripts: `scripts/build-example.sh` now appends the `embassy` feature automatically for any `hal_*_async*` example
2828
- Justfile: `check-wifi-hal-embassy` recipe that verifies the `embassy` feature compiles for ESP32-C6 (`riscv32imac-unknown-none-elf`) and ESP32-C3 (`riscv32imc-unknown-none-elf`)
2929
- `espnow-pure`: `PeerTracker` — heartbeat-based peer liveness tracker with online/offline transition detection, extracted from rustbox-rgb-puzzle brain firmware
3030
- `espnow-pure`: `ScanConfig::with_probe_timeout()` and `DEFAULT_PROBE_TIMEOUT` (100 ms) — per-channel probe timeout is now configurable
3131
- `espnow-pure`: `ScanConfig::with_burst_timeout()` and `DEFAULT_BURST_TIMEOUT` (3 s) — bounds total time the radio spends at boosted TX power during peer discovery
32-
- `wifi-pure`: `TxPowerLevel` enum (`Lowest`, `Low`, `Medium`, `High`, `Max`) with `to_quarter_dbm()` mapping to ESP-IDF quarter-dBm values; `WiFiConfig::with_tx_power()` builder method (see `docs/features/wifi-radio-power-config-v1.md`)
32+
- `wifi-pure`: `TxPowerLevel` enum (`Lowest`, `Low`, `Medium`, `High`, `Max`) with `to_quarter_dbm()` mapping to ESP-IDF quarter-dBm values; `WiFiConfig::with_tx_power()` builder method (see `docs/features/archive/wifi-radio-power-config-v1.md`)
3333
- `rustyfarian-esp-idf-wifi`: applies `TxPowerLevel` via `esp_wifi_set_max_tx_power()` after Wi-Fi start
3434
- `rustyfarian-esp-hal-wifi`: stores `TxPowerLevel` config; logs a warning that `esp-radio` does not expose a TX power API on bare-metal targets (true on both 0.17 and 0.18 — the radio default applies)
3535
- `rustyfarian-esp-idf-espnow`: `scan_for_peer()` auto-bursts TX power to maximum during channel scanning, restores previous level after scan completes
36-
- `espnow-pure`: `command` module — `CommandFrame<'a>` zero-copy parser, `SystemCommand` enum (`Ping`, `SelfTest`, `Identify`), tag range helpers, and response payload builders for the ESP-NOW Peripheral Command Framework (see `docs/features/espnow-peripheral-command-framework-v1.md`)
36+
- `espnow-pure`: `command` module — `CommandFrame<'a>` zero-copy parser, `SystemCommand` enum (`Ping`, `SelfTest`, `Identify`), tag range helpers, and response payload builders for the ESP-NOW Peripheral Command Framework (see `docs/features/archive/espnow-peripheral-command-framework-v1.md`)
3737
- `rustyfarian-esp-hal-wifi`: `ActiveLowLed<P>` adapter — implements `StatusLed` with inverted polarity for onboard LEDs wired active-low (e.g. ESP32-C3 Super Mini GPIO8)
3838
- `rustyfarian-esp-hal-wifi`: `hal_c3_connect_async_led` example — async Wi-Fi connect with spawned `led_task` that blinks the onboard GPIO8 LED during connection, holds steady once IP acquired; uses `AtomicBool` for task coordination
3939
- `rustyfarian-esp-hal-wifi`: `hal_c6_connect_async_led` example — async Wi-Fi connect with spawned `led_task` that pulses the onboard WS2812 RGB LED (GPIO8) blue via `PulseEffect` during connection, holds dim green once connected
4040
- Build scripts: `build-example.sh` and `flash.sh` auto-detect `rustyfarian-esp-hal-ws2812` feature for `hal_c6_*_led*` examples
41-
- Justfile: `check-wifi-hal-embassy` recipe that verifies the `embassy` feature compiles for ESP32-C6 (`riscv32imac-unknown-none-elf`) and ESP32-C3 (`riscv32imc-unknown-none-elf`)
42-
- `espnow-pure`: `PeerTracker` — heartbeat-based peer liveness tracker with online/offline transition detection, extracted from rustbox-rgb-puzzle brain firmware
4341
- `rustyfarian-esp-hal-wifi`: `EspHalWifiManager` with real `WifiDriver` implementation using `esp-radio 0.17.0` for bare-metal ESP32-C3/C6 (ADR 006 Phase 5); `hal_c3_connect` and `hal_c6_connect` examples
4442
- `rustyfarian-network-pure`: `status_colors` module with shared LED colour palette (`BOOT`, `WIFI_CONNECTING`, `MQTT_CONNECTING`, `CONNECTED`, `ERROR`, `OFFLINE`)
4543
- `rustyfarian-esp-idf-mqtt`: `MqttBuilder::build_and_wait()` with `StatusLed` support for visual boot feedback (cyan pulse while connecting, green on success, red on timeout)
@@ -61,7 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6159

6260
### Changed
6361

64-
- **April 2026 esp-hal stack wave** — coordinated workspace-wide bump of every bare-metal crate to exact pins (`Cargo.toml` `[workspace.dependencies]`): `esp-hal 1.0.0``=1.1.0`, `esp-rtos 0.2.0``=0.3.0`, `esp-radio 0.17.0``=0.18.0`, `esp-bootloader-esp-idf 0.4.0``=0.5.0`, `esp-alloc 0.9.0``=0.10.0`, `esp-println 0.16.1``=0.17.0`, `esp-backtrace 0.18.1``=0.19.0`; embassy ecosystem `embassy-executor 0.9``=0.10.0`, `embassy-net 0.7``=0.8.0`, `embassy-time 0.5``=0.5.1`, `embassy-sync` newly pinned at `=0.8.0`. `smoltcp` is now exact-pinned at `=0.12.0` but is no longer a direct dependency of `rustyfarian-esp-hal-wifi``esp-radio 0.18` removed the `smoltcp` feature in favour of `embassy-net-driver`. Coordinated with `rustyfarian-ws2812`'s April 2026 wave (see `docs/features/esp-hal-stack-upgrade-april-2026-v1.md`)
62+
- **April 2026 esp-hal stack wave** — coordinated workspace-wide bump of every bare-metal crate to exact pins (`Cargo.toml` `[workspace.dependencies]`): `esp-hal 1.0.0``=1.1.0`, `esp-rtos 0.2.0``=0.3.0`, `esp-radio 0.17.0``=0.18.0`, `esp-bootloader-esp-idf 0.4.0``=0.5.0`, `esp-alloc 0.9.0``=0.10.0`, `esp-println 0.16.1``=0.17.0`, `esp-backtrace 0.18.1``=0.19.0`; embassy ecosystem `embassy-executor 0.9``=0.10.0`, `embassy-net 0.7``=0.8.0`, `embassy-time 0.5``=0.5.1`, `embassy-sync` newly pinned at `=0.8.0`. `smoltcp` is now exact-pinned at `=0.12.0` but is no longer a direct dependency of `rustyfarian-esp-hal-wifi``esp-radio 0.18` removed the `smoltcp` feature in favour of `embassy-net-driver`. Coordinated with `rustyfarian-ws2812`'s April 2026 wave (see `docs/features/archive/esp-hal-stack-upgrade-april-2026-v1.md`)
6563
- ws2812 cross-repo dependencies (`led-effects`, `rustyfarian-esp-idf-ws2812`, `rustyfarian-esp-hal-ws2812`) re-pinned from `tag = "v0.4.0"` to `tag = "v0.5.0"` — v0.5.0 is the April 2026 wave release that ships the matching exact pins for `esp-hal`/`esp-rtos`/`esp-radio`, so the resolved feature graph stays unified with this workspace's bare-metal stack
6664
- **BREAKING** `rustyfarian-esp-hal-wifi`: API renames flowing through from `esp-radio 0.18``WifiDevice``Interface`, `ModeConfig::Client(ClientConfig)``Config::Station(StationConfig)`, `Interfaces.sta``.station`, `WifiEvent::StaDisconnected``StationDisconnected`, `WifiError::Disconnected` is now a tuple variant carrying `DisconnectedStationInfo`, `controller.is_connected()` returns `bool` directly (not `Result`), `controller.connect()`/`disconnect()` are async-only (`connect_async`/`disconnect_async`), `controller.wait_for_event(StaDisconnected)` becomes `wait_for_disconnect_async`, `esp_radio::wifi::new()` is now `(WIFI, ControllerConfig)` (the radio init parameter is gone — radio init is implicit)
6765
- **BREAKING** `rustyfarian-esp-hal-wifi`: `set_config` is now idempotent in `esp-radio 0.18` and implicitly starts the controller and initiates association — the explicit `start()`/`connect()` calls that existed in 0.17 are no longer needed (or available)

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ lora-modulation = "0.1"
3737
sx126x = "0.3"
3838

3939
# Bare-metal (esp-hal) dependencies — exact pins for the April 2026 release wave
40-
# (coordinated with rustyfarian-ws2812; see docs/features/esp-hal-stack-upgrade-april-2026-v1.md).
40+
# (coordinated with rustyfarian-ws2812; see docs/features/archive/esp-hal-stack-upgrade-april-2026-v1.md).
4141
# These crates ship as a monorepo wave; mixing minor versions across them produces
4242
# feature-unification conflicts in the Wi-Fi `embassy-net-driver` + `esp-alloc` graph.
4343
esp-hal = { version = "=1.1.0" }

docs/ROADMAP.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Roadmap
22

3-
*Last updated: April 2026*
3+
*Last updated: May 2026*
44

55
The bare-metal stack is now aligned on the April 2026 esp-hal wave (`esp-hal 1.1.0` / `esp-radio 0.18.0` / `esp-rtos 0.3.0` / embassy 0.10), hardware-validated on ESP32-C3 and ESP32-C6.
66
The bare-metal Wi-Fi surface is now async-only — `esp-radio 0.18` removed direct `smoltcp` integration and made the controller async-only, so `WiFiManager::init_async` + `AsyncWifiHandle` is the single public path.
7+
The OTA MVP three-crate triad (`ota-pure`, `rustyfarian-esp-idf-ota`, `rustyfarian-esp-hal-ota`) has landed on `main` per [ADR 011](adr/011-ota-crate-hosting-and-transport.md) and [`docs/features/archive/ota-mvp-v1.md`](features/archive/ota-mvp-v1.md); all public APIs are explicitly marked experimental and remain unreleased until v0.2.0.
78
TTN v3 LoRa validation remains blocked on hardware.
8-
Next milestone: release v0.2.0 with the accumulated post-0.1.0 features (including this upgrade), then deliver the OTA MVP three-crate triad — design locked by [ADR 011](adr/011-ota-crate-hosting-and-transport.md) and [`docs/features/ota-mvp-v1.md`](features/ota-mvp-v1.md), unblocked by the April 2026 stack landing.
9+
Next milestone: cut v0.2.0 bundling the accumulated post-0.1.0 features (April 2026 stack upgrade, async Wi-Fi, OTA MVP, command framework, power-save, ESP-NOW channel scanning).
910

1011
```mermaid
1112
%%{init: {
@@ -25,12 +26,7 @@ Next milestone: release v0.2.0 with the accumulated post-0.1.0 features (includi
2526
timeline
2627
title rustyfarian-network Roadmap
2728
28-
Ready : Wi-Fi Radio Power Config v1 — TX power levels, power-save enum, auto-burst during discovery (feature-doc)
29-
30-
Near term : Release v0.2.0 — EspHalWifiManager, async Wi-Fi on the April 2026 esp-hal wave, status_colors, non-blocking publish, power save, ESP-NOW channel scanning, command framework
31-
: OTA MVP Stage 1 — ota-pure (Version, StreamingVerifier, ImageMetadata, OtaState, OtaError) host-tested
32-
: OTA MVP Stage 2 — rustyfarian-esp-idf-ota (OtaSession::fetch_and_apply / mark_valid / rollback) on ESP32-C3 via EspOta + EspHttpConnection
33-
: OTA MVP Stage 3 — rustyfarian-esp-hal-ota async over embassy-net + esp_bootloader_esp_idf::OtaUpdater with strict internal HTTP/1.1 GET (ADR 011)
29+
Near term : Release v0.2.0 — EspHalWifiManager, async Wi-Fi on the April 2026 esp-hal wave, OTA MVP triad, status_colors, non-blocking publish, power save, ESP-NOW channel scanning, command framework
3430
3531
Mid term : Phase 5 — TTN v3 EU868 OTAA validation (blocked on hardware)
3632
: LoRa post-adoption backlog — builder pattern, CRC-32, hardware driver, state machine
@@ -64,7 +60,8 @@ timeline
6460
- Wi-Fi Radio Power Config v1 — `TxPowerLevel` enum, `with_tx_power()` builder, ESP-IDF `esp_wifi_set_max_tx_power()`, ESP-NOW auto-burst during scanning
6561
- ESP-NOW Peripheral Command Framework v1 — `CommandFrame` zero-copy parser, `SystemCommand` enum (Ping/SelfTest/Identify), response helpers in `espnow-pure`
6662
- WiFiManager LED integration for esp-hal — `ActiveLowLed<P>` adapter, `hal_c3_connect_async_led` and `hal_c6_connect_async_led` examples (StatusLed support matching ESP-IDF; the synchronous `init_with_led` was later removed when the stack moved to esp-radio 0.18 — LED feedback now wires via spawned tasks alongside `init_async`)
67-
- esp-hal Stack Upgrade — April 2026 wave: workspace exact-pinned to `esp-hal 1.1.0`, `esp-rtos 0.3.0`, `esp-radio 0.18.0`, `esp-bootloader-esp-idf 0.5.0`, `esp-alloc 0.10.0`, `esp-println 0.17.0`, `esp-backtrace 0.19.0`, `embassy-executor 0.10.0`, `embassy-net 0.8.0`, `embassy-time 0.5.1`, `embassy-sync 0.8.0`, `smoltcp 0.12.0`. `rustyfarian-esp-hal-wifi` collapsed to async-only (`WiFiManager::init_async` + `AsyncWifiHandle`) — sync surface and direct `smoltcp` integration removed (BREAKING for the bare-metal Wi-Fi consumers; `embassy` feature is now effectively required). Hardware-validated on ESP32-C3-DevKitM-1 and ESP32-C6-DevKitC-1; LoRa example builds clean for ESP32-S3 (Phase 5 hardware run separate). Tooling: `scripts/detect-port.sh` filters espflash's auto-detect to USB serial devices on macOS. See `docs/features/esp-hal-stack-upgrade-april-2026-v1.md`.
63+
- esp-hal Stack Upgrade — April 2026 wave: workspace exact-pinned to `esp-hal 1.1.0`, `esp-rtos 0.3.0`, `esp-radio 0.18.0`, `esp-bootloader-esp-idf 0.5.0`, `esp-alloc 0.10.0`, `esp-println 0.17.0`, `esp-backtrace 0.19.0`, `embassy-executor 0.10.0`, `embassy-net 0.8.0`, `embassy-time 0.5.1`, `embassy-sync 0.8.0`, `smoltcp 0.12.0`. `rustyfarian-esp-hal-wifi` collapsed to async-only (`WiFiManager::init_async` + `AsyncWifiHandle`) — sync surface and direct `smoltcp` integration removed (BREAKING for the bare-metal Wi-Fi consumers; `embassy` feature is now effectively required). Hardware-validated on ESP32-C3-DevKitM-1 and ESP32-C6-DevKitC-1; LoRa example builds clean for ESP32-S3 (Phase 5 hardware run separate). Tooling: `scripts/detect-port.sh` filters espflash's auto-detect to USB serial devices on macOS. See `docs/features/archive/esp-hal-stack-upgrade-april-2026-v1.md`.
64+
- OTA MVP — three-crate dual-stack firmware update: `ota-pure` (no_std, host-tested), `rustyfarian-esp-idf-ota` (blocking, ESP-IDF), `rustyfarian-esp-hal-ota` (async, bare-metal via `embassy-net` + `esp_bootloader_esp_idf::OtaUpdater`). Streaming SHA-256 verify, partition swap, rollback; strict internal HTTP/1.1 GET parser. All public APIs explicitly experimental (locked by [ADR 011](adr/011-ota-crate-hosting-and-transport.md) and [`docs/features/archive/ota-mvp-v1.md`](features/archive/ota-mvp-v1.md)).
6865

6966
</details>
7067

@@ -87,7 +84,7 @@ timeline
8784

8885
### OTA MVP — Three-Crate Dual-Stack Firmware Update
8986

90-
Locked by [ADR 011](adr/011-ota-crate-hosting-and-transport.md) and detailed in [`docs/features/ota-mvp-v1.md`](features/ota-mvp-v1.md).
87+
Locked by [ADR 011](adr/011-ota-crate-hosting-and-transport.md) and detailed in [`docs/features/archive/ota-mvp-v1.md`](features/archive/ota-mvp-v1.md).
9188
Requested by `rustyfarian-ferriswheel-demo` (sibling repo).
9289
All public APIs are explicitly marked experimental for the MVP; stabilization is owned by the future `ota-library` feature.
9390

File renamed without changes.

0 commit comments

Comments
 (0)