Files
ZephCore/docs/adr/0001-esp32-boot-and-flash-layout.md
liquidraverandClaude Opus 5.5 9fc1eaf18a docs: publish the system design and architecture decision records
DESIGN.md is the reconciled high-level design after the 2026-09 restructure:
system context, roles, build-time composition, layers and dependency rules
(with the known exceptions), threading and event loop, end-to-end flows,
cross-cutting concerns and the upstream alignment policy. docs/adr/ holds the
nine decision records (ESP32 boot layout, S3 native USB, overlay precedence,
BLE privacy, Zephyr main pin, SX126x duty cycle and RX-busy gating, adaptive
contention window, storage backend, WiFi companion boards).
ARCHITECTURE.md stays the detailed component reference and links both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:01:28 +02:00

69 lines
4.4 KiB
Markdown

# ADR 0001 — ESP32 boot: simple boot for plain builds, MCUboot for OTA builds and releases
Status: accepted (recorded 2026-09-25)
## Context
**ESP32 flash** — Zephyr uses `CONFIG_ESP_SIMPLE_BOOT` by default (no MCUBoot). The standard build produces a
self-contained `zephyr.bin` that the ESP32 ROM bootloader loads directly from `0x0`. Just `west flash` — no
`--sysbuild`, no ceremony, works on a bare chip:
```bash
west build -b <board>/esp32s3/procpu zephcore --pristine
west flash --esp-device COMX
```
**Exception — WiFi AP OTA** (`wifi_ota.conf`): the HTTP OTA updater writes to MCUBoot slot1 and requires
MCUBoot. When building with `wifi_ota.conf`, add `--sysbuild` and flash once to seat MCUBoot:
```bash
west build -b <board>/esp32s3/procpu zephcore --pristine --sysbuild -- \
-DEXTRA_CONF_FILE="boards/common/wifi_ota.conf"
west flash --esp-device COMX
```
Subsequent OTA-capable builds still need `--sysbuild`; plain builds without `wifi_ota.conf` do not.
**`repeater.conf` pulls `wifi_ota.conf` in by itself on S3/C-series boards, so ESP32 repeater builds always need
`--sysbuild`.** The ESP32 auto-include block in `CMakeLists.txt` appends it whenever the platform is `esp32`, the
build is classified as repeater (from the user conf *names* — `repeater.conf` — or
`-DCONFIG_ZEPHCORE_ROLE_REPEATER`; checked against Kconfig after `find_package`), and the board is not a classic
`/esp32/` one. Without `--sysbuild` the app still comes out built *for* the MCUboot slot
(`CONFIG_BOOTLOADER_MCUBOOT=y`, `CONFIG_FLASH_LOAD_OFFSET=0x10000`) but is never signed: `west flash` writes it to
`0x10000`, leaves `0x0` untouched, and whatever MCUboot is already there rejects the unsigned header. The symptom
is not a build or flash error — both report success and the hash verifies — but the board stops enumerating USB
entirely, and only the BOOT button gets it back. Companion builds on the same boards really are simple-boot
(`CONFIG_ESP_SIMPLE_BOOT=y`, load offset `0x0`), which is why this bites only when switching a board to the
repeater role.
**GitHub Release artifacts diverge from the plain-build default.** `build.sh` always builds S3/C-series boards
(`heltec_wifi_lora32_v3/v4/v43/v4_r8`, `station_g2`, `xiao_esp32s3`, `xiao_esp32c6`, `lilygo_tlora_c6`,
`heltec_wireless_tracker`, `thinknode_m9`) with `--sysbuild`, regardless of `wifi_ota.conf` — `sysbuild.conf` at
the repo root forces `SB_CONFIG_BOOTLOADER_MCUBOOT=y` whenever `--sysbuild` is passed. So the published release
for these boards is always an MCUboot layout (MCUboot @ `0x0`, signed app @ `0x10000`), never the simple-boot
`zephyr.bin`. (The app slot is at `0x10000` — right after the 64 KB MCUboot region, matching the
Arduino/ESP-IDF app offset; it was `0x20000` before 2026-07-09. The flash map lives in per-board
`boards/esp32/<board>/partitions.overlay`, fed to **both** the app and the MCUboot image via
`sysbuild/CMakeLists.txt` so their slots match. Before that split, `board.overlay` reached only the app, so
MCUboot used the upstream base table and looked for OTA images at a different `image-1` address than the app
wrote them to, silently reverting every WiFi-OTA update on the slot-customizing boards.) Only `-merged.bin`
(MCUboot + signed app, esptool `merge-bin`) is published for them — the signed app alone isn't bootable
standalone and isn't shipped (see [GH #42](https://github.com/liquidraver/ZephCore/issues/42): publishing it as a
"plain .bin" caused bricked boards when flashed like classic-ESP32's self-contained image). Classic ESP32 boards
(T-Beam, PICO-D4 — chip name `esp32`, not `esp32s3`/`esp32c*`) still use simple-boot and ship a genuinely
self-contained plain `.bin` at the `0x1000` ROM bootloader offset. Flashing `-merged.bin` via
`west flash`/`esptool write_flash 0x0 <file>` only touches MCUboot + the app slot — it does not reach
`storage_partition`/`lfs_partition` (identity, prefs, contacts, BLE bonds), so it's safe for routine updates as
well as first flash.
## Decision
Plain local builds of S3/C-series companions stay simple-boot; repeater builds and every release build use
MCUboot (`--sysbuild`), with one per-board `partitions.overlay` shared by app and MCUboot.
## Consequences
- A repeater build without `--sysbuild` bricks USB silently (see above): always pass it.
- Releases publish only `-merged.bin` for S3/C-series boards.
- `sysbuild.conf` sets `SB_CONFIG_BOOT_SIGNATURE_TYPE_NONE` (the RSA default overflows MCUboot's 64 KB slot).