diff --git a/docs/adr/ADR-057-firmware-csi-build-guard.md b/docs/adr/ADR-057-firmware-csi-build-guard.md new file mode 100644 index 00000000..a602eabd --- /dev/null +++ b/docs/adr/ADR-057-firmware-csi-build-guard.md @@ -0,0 +1,82 @@ +# ADR-057: Firmware CSI Build Guard and sdkconfig.defaults + +| Field | Value | +|-------------|---------------------------------------------| +| **Status** | Accepted | +| **Date** | 2026-03-12 | +| **Authors** | ruv | +| **Issues** | #223, #238, #234, #210, #190 | + +## Context + +Multiple GitHub issues (#223, #238, #234, #210, #190) report firmware problems +that fall into two categories: + +1. **CSI not enabled at runtime** — The committed `sdkconfig` had + `# CONFIG_ESP_WIFI_CSI_ENABLED is not set` (line 1135), meaning users who + built from source or used pre-built binaries got the runtime error: + `E (6700) wifi:CSI not enabled in menuconfig!` + + Root cause: `sdkconfig.defaults.template` existed with the correct setting + (`CONFIG_ESP_WIFI_CSI_ENABLED=y`) but ESP-IDF only reads + `sdkconfig.defaults` — not `.template` suffixed files. No `sdkconfig.defaults` + file was committed. + +2. **Unsupported ESP32 variants** — Users attempting to use original ESP32 + (D0WD) and ESP32-C3 boards. The firmware targets ESP32-S3 only + (`CONFIG_IDF_TARGET="esp32s3"`, Xtensa architecture) and this was not + surfaced clearly enough in documentation or build errors. + +## Decision + +### Fix 1: Commit `sdkconfig.defaults` (not just template) + +Copy `sdkconfig.defaults.template` → `sdkconfig.defaults` so that ESP-IDF +applies the correct defaults (including `CONFIG_ESP_WIFI_CSI_ENABLED=y`) +automatically when `sdkconfig` is regenerated. + +### Fix 2: `#error` compile-time guard in `csi_collector.c` + +Add a preprocessor guard: + +```c +#ifndef CONFIG_ESP_WIFI_CSI_ENABLED +#error "CONFIG_ESP_WIFI_CSI_ENABLED must be set in sdkconfig." +#endif +``` + +This turns a confusing runtime crash into a clear compile-time error with +instructions on how to fix it. + +### Fix 3: Fix committed `sdkconfig` + +Change line 1135 from `# CONFIG_ESP_WIFI_CSI_ENABLED is not set` to +`CONFIG_ESP_WIFI_CSI_ENABLED=y`. + +## Consequences + +- **Positive**: New builds will always have CSI enabled. Users building from + source will get a clear compile error if CSI is somehow disabled. +- **Positive**: Pre-built release binaries will include CSI support. +- **Neutral**: Original ESP32 and ESP32-C3 remain unsupported. This is by + design — only ESP32-S3 has the CSI API surface we depend on. Future ADRs + may address multi-target support if demand warrants it. +- **Negative**: None identified. + +## Hardware Support Matrix + +| Variant | CSI Support | Firmware Target | Status | +|--------------|-------------|-----------------|---------------| +| ESP32-S3 | Yes | Yes | Supported | +| ESP32 (orig) | Partial | No | Unsupported | +| ESP32-C3 | Yes (IDF 5.1+) | No | Unsupported | +| ESP32-C6 | Yes | No | Unsupported | + +## Notes + +- ESP32-C3 and C6 use RISC-V architecture; a separate build target + (`idf.py set-target esp32c3`) would be needed. +- Original ESP32 has limited CSI (no STBC HT-LTF2, fewer subcarriers). +- Users on unsupported hardware can still write custom firmware using the + ADR-018 binary frame format (magic `0xC5110001`) for interop with the + Rust aggregator. diff --git a/firmware/esp32-csi-node/main/csi_collector.c b/firmware/esp32-csi-node/main/csi_collector.c index 69eb2982..0d73875c 100644 --- a/firmware/esp32-csi-node/main/csi_collector.c +++ b/firmware/esp32-csi-node/main/csi_collector.c @@ -21,6 +21,16 @@ #include "esp_timer.h" #include "sdkconfig.h" +/* ADR-057: Build-time guard — fail early if CSI is not enabled in sdkconfig. + * Without this, the firmware compiles but crashes at runtime with: + * "E (xxxx) wifi:CSI not enabled in menuconfig!" + * which is confusing for users flashing pre-built binaries. */ +#ifndef CONFIG_ESP_WIFI_CSI_ENABLED +#error "CONFIG_ESP_WIFI_CSI_ENABLED must be set in sdkconfig. " \ + "Run: idf.py menuconfig -> Component config -> Wi-Fi -> Enable WiFi CSI, " \ + "or copy sdkconfig.defaults.template to sdkconfig.defaults before building." +#endif + static const char *TAG = "csi_collector"; static uint32_t s_sequence = 0; diff --git a/firmware/esp32-csi-node/sdkconfig.defaults b/firmware/esp32-csi-node/sdkconfig.defaults new file mode 100644 index 00000000..49c4177a --- /dev/null +++ b/firmware/esp32-csi-node/sdkconfig.defaults @@ -0,0 +1,33 @@ +# ESP32-S3 CSI Node — Default SDK Configuration +# This file is applied automatically by idf.py when no sdkconfig exists. + +# Target: ESP32-S3 +CONFIG_IDF_TARGET="esp32s3" + +# Use custom partition table (8MB flash with OTA — ADR-045) +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_display.csv" + +# Flash configuration: 8MB (Quad SPI) +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +# Compiler optimization: optimize for size to reduce binary +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + +# Enable CSI (Channel State Information) in WiFi driver +CONFIG_ESP_WIFI_CSI_ENABLED=y + +# NVS encryption disabled by default (requires eFuse provisioning). +# Enable only after burning HMAC key to eFuse block. +# CONFIG_NVS_ENCRYPTION is not set + +# Disable unused features to reduce binary size +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_LOG_DEFAULT_LEVEL_INFO=y + +# LWIP: enable extended socket options for UDP multicast +CONFIG_LWIP_SO_RCVBUF=y + +# FreeRTOS: increase task stack for CSI processing +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 diff --git a/firmware/esp32-csi-node/sdkconfig.defaults.template b/firmware/esp32-csi-node/sdkconfig.defaults.template index be8a09a0..49c4177a 100644 --- a/firmware/esp32-csi-node/sdkconfig.defaults.template +++ b/firmware/esp32-csi-node/sdkconfig.defaults.template @@ -18,8 +18,9 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y # Enable CSI (Channel State Information) in WiFi driver CONFIG_ESP_WIFI_CSI_ENABLED=y -# Enable NVS encryption for secure credential storage -CONFIG_NVS_ENCRYPTION=y +# NVS encryption disabled by default (requires eFuse provisioning). +# Enable only after burning HMAC key to eFuse block. +# CONFIG_NVS_ENCRYPTION is not set # Disable unused features to reduce binary size CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y