diff --git a/firmware/esp32-csi-node/main/CMakeLists.txt b/firmware/esp32-csi-node/main/CMakeLists.txt index 6f0930a53..62d7d1896 100644 --- a/firmware/esp32-csi-node/main/CMakeLists.txt +++ b/firmware/esp32-csi-node/main/CMakeLists.txt @@ -11,7 +11,26 @@ set(SRCS "adaptive_controller.c" ) -set(REQUIRES "") +# ESP-IDF v6+: headers must resolve via explicit REQUIRES (no implicit deps). +set(REQUIRES + esp_wifi + esp_netif + esp_event + nvs_flash + app_update + esp_http_server + esp_http_client + esp_app_format + esp_timer + esp_pm + esp_driver_uart + esp_driver_gpio + esp_driver_spi + esp_driver_i2c + driver + lwip + mbedtls +) # ADR-061: Mock CSI generator for QEMU testing + ADR-081 mock radio binding if(CONFIG_CSI_MOCK_ENABLED) @@ -21,7 +40,11 @@ endif() # ADR-045: AMOLED display support (compile-time optional) if(CONFIG_DISPLAY_ENABLE) list(APPEND SRCS "display_hal.c" "display_ui.c" "display_task.c") - set(REQUIRES esp_lcd esp_lcd_touch lvgl) + list(APPEND REQUIRES esp_lcd esp_lcd_touch lvgl) +endif() + +if(CONFIG_WASM_ENABLE) + list(APPEND REQUIRES wasm3) endif() idf_component_register( diff --git a/firmware/esp32-csi-node/main/csi_collector.c b/firmware/esp32-csi-node/main/csi_collector.c index c8d5eb7de..3d1987b48 100644 --- a/firmware/esp32-csi-node/main/csi_collector.c +++ b/firmware/esp32-csi-node/main/csi_collector.c @@ -356,6 +356,30 @@ void csi_collector_init(void) ESP_LOGI(TAG, "Promiscuous mode enabled (MGMT-only, RuView#396)"); +#if CONFIG_SOC_WIFI_HE_SUPPORT + /* Wi-Fi 6 targets (e.g. ESP32-C6): wifi_csi_config_t is wifi_csi_acquire_config_t + * (bitfields), not the legacy 802.11n bool layout used on ESP32-S3. */ + wifi_csi_config_t csi_config; + memset(&csi_config, 0, sizeof(csi_config)); + csi_config.enable = 1U; + csi_config.acquire_csi_legacy = 1U; + csi_config.acquire_csi_ht20 = 1U; + csi_config.acquire_csi_ht40 = 1U; + csi_config.acquire_csi_su = 1U; + csi_config.acquire_csi_mu = 1U; + csi_config.acquire_csi_dcm = 1U; + csi_config.acquire_csi_beamformed = 1U; +#if CONFIG_SOC_WIFI_MAC_VERSION_NUM >= 3 + csi_config.acquire_csi_force_lltf = 1U; + csi_config.acquire_csi_vht = 1U; + csi_config.acquire_csi_he_stbc_mode = ESP_CSI_ACQUIRE_STBC_SAMPLE_HELTFS; + csi_config.val_scale_cfg = 0U; +#else + csi_config.acquire_csi_he_stbc = ESP_CSI_ACQUIRE_STBC_SAMPLE_HELTFS; + csi_config.val_scale_cfg = 0U; +#endif + csi_config.dump_ack_en = 0U; +#else wifi_csi_config_t csi_config = { .lltf_en = true, .htltf_en = true, @@ -365,6 +389,7 @@ void csi_collector_init(void) .manu_scale = false, .shift = false, }; +#endif ESP_ERROR_CHECK(esp_wifi_set_csi_config(&csi_config)); ESP_ERROR_CHECK(esp_wifi_set_csi_rx_cb(wifi_csi_callback, NULL)); diff --git a/firmware/esp32-csi-node/main/edge_processing.c b/firmware/esp32-csi-node/main/edge_processing.c index 94680e528..9f88ddb26 100644 --- a/firmware/esp32-csi-node/main/edge_processing.c +++ b/firmware/esp32-csi-node/main/edge_processing.c @@ -2,8 +2,9 @@ * @file edge_processing.c * @brief ADR-039 Edge Intelligence — dual-core CSI processing pipeline. * - * Core 0 (WiFi task): Pushes raw CSI frames into lock-free SPSC ring buffer. - * Core 1 (DSP task): Pops frames, runs signal processing pipeline: + * Core 0 (WiFi path): Pushes raw CSI frames into lock-free SPSC ring buffer. + * Second core when present (DSP task): pops frames, runs signal processing pipeline. + * On unicore targets (e.g. ESP32-C6), the DSP task is pinned to core 0. * 1. Phase extraction from I/Q pairs * 2. Phase unwrapping (continuous phase) * 3. Welford variance tracking per subcarrier @@ -1050,7 +1051,9 @@ esp_err_t edge_processing_init(const edge_config_t *cfg) return ESP_OK; } - /* Start DSP task on Core 1. */ + /* Pin DSP off WiFi's preferred core when SMP; else core 0 only (ESP32-C6). */ + const BaseType_t dsp_core = (portNUM_PROCESSORS > 1) ? (BaseType_t)1 : (BaseType_t)0; + BaseType_t ret = xTaskCreatePinnedToCore( edge_task, "edge_dsp", @@ -1058,14 +1061,14 @@ esp_err_t edge_processing_init(const edge_config_t *cfg) NULL, 5, /* Priority 5 — above idle, below WiFi. */ NULL, - 1 /* Pin to Core 1. */ - ); + dsp_core); if (ret != pdPASS) { ESP_LOGE(TAG, "Failed to create edge DSP task"); return ESP_ERR_NO_MEM; } - ESP_LOGI(TAG, "Edge DSP task created on Core 1 (stack=8192, priority=5)"); + ESP_LOGI(TAG, "Edge DSP task created on core %d (stack=8192, priority=5)", + (int)dsp_core); return ESP_OK; } diff --git a/firmware/esp32-csi-node/main/rvf_parser.c b/firmware/esp32-csi-node/main/rvf_parser.c index d5fec4218..aa57a7f8b 100644 --- a/firmware/esp32-csi-node/main/rvf_parser.c +++ b/firmware/esp32-csi-node/main/rvf_parser.c @@ -10,7 +10,7 @@ #include #include "esp_log.h" -#include "mbedtls/sha256.h" +#include "psa/crypto.h" static const char *TAG = "rvf"; @@ -125,9 +125,13 @@ esp_err_t rvf_parse(const uint8_t *data, uint32_t data_len, rvf_parsed_t *out) /* ---- Verify build hash (SHA-256 of WASM payload) ---- */ uint8_t computed_hash[32]; - int ret = mbedtls_sha256(wasm_data, hdr->wasm_len, computed_hash, 0); - if (ret != 0) { - ESP_LOGE(TAG, "SHA-256 computation failed: %d", ret); + size_t hash_len = 0; + psa_status_t psa_st = psa_hash_compute(PSA_ALG_SHA_256, wasm_data, + hdr->wasm_len, computed_hash, + sizeof(computed_hash), &hash_len); + if (psa_st != PSA_SUCCESS || hash_len != 32) { + ESP_LOGE(TAG, "SHA-256 computation failed: psa=%d len=%u", + (int)psa_st, (unsigned)hash_len); return ESP_FAIL; } @@ -186,8 +190,7 @@ esp_err_t rvf_verify_signature(const rvf_parsed_t *parsed, const uint8_t *data, /* * Ed25519 verification. * - * ESP-IDF v5.2 mbedtls does NOT include Ed25519 (Curve25519 is - * for ECDH/X25519 only). We use a SHA-256-HMAC integrity check: + * Legacy mbedtls Ed25519 is optional. We use a SHA-256 keyed digest: * * expected = SHA-256(pubkey || signed_region) * @@ -196,35 +199,34 @@ esp_err_t rvf_verify_signature(const rvf_parsed_t *parsed, const uint8_t *data, * pubkey produces a different expected hash, so unauthorized * publishers cannot forge a valid signature. * - * For full Ed25519 (NaCl-style), enable CONFIG_MBEDTLS_EDDSA_C - * or link TweetNaCl. The RVF builder should match this scheme. + * For full Ed25519, enable CONFIG_MBEDTLS_EDDSA_C or equivalent. + * The RVF builder should match this scheme. */ uint8_t hash_input_prefix[32]; memcpy(hash_input_prefix, pubkey, 32); - /* Compute SHA-256(pubkey || header+manifest+wasm). */ - mbedtls_sha256_context ctx; - mbedtls_sha256_init(&ctx); - int ret = mbedtls_sha256_starts(&ctx, 0); - if (ret != 0) { - mbedtls_sha256_free(&ctx); + /* Compute SHA-256(pubkey || header+manifest+wasm) via PSA Crypto. */ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t st = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (st != PSA_SUCCESS) { return ESP_FAIL; } - ret = mbedtls_sha256_update(&ctx, hash_input_prefix, 32); - if (ret != 0) { - mbedtls_sha256_free(&ctx); + st = psa_hash_update(&op, hash_input_prefix, 32); + if (st != PSA_SUCCESS) { + (void)psa_hash_abort(&op); return ESP_FAIL; } - ret = mbedtls_sha256_update(&ctx, data, signed_len); - if (ret != 0) { - mbedtls_sha256_free(&ctx); + st = psa_hash_update(&op, data, signed_len); + if (st != PSA_SUCCESS) { + (void)psa_hash_abort(&op); return ESP_FAIL; } uint8_t expected[32]; - ret = mbedtls_sha256_finish(&ctx, expected); - mbedtls_sha256_free(&ctx); - if (ret != 0) { + size_t out_len = 0; + st = psa_hash_finish(&op, expected, sizeof(expected), &out_len); + if (st != PSA_SUCCESS || out_len != 32) { + (void)psa_hash_abort(&op); return ESP_FAIL; } diff --git a/firmware/esp32-csi-node/provision.py b/firmware/esp32-csi-node/provision.py index d6a0e2f0a..578ae945d 100644 --- a/firmware/esp32-csi-node/provision.py +++ b/firmware/esp32-csi-node/provision.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 """ -ESP32-S3 CSI Node Provisioning Script +ESP32 CSI node provisioning (ESP32-S3, ESP32-C6, other targets). Writes WiFi credentials and aggregator target to the ESP32's NVS partition so users can configure a pre-built firmware binary without recompiling. Usage: python provision.py --port COM7 --ssid "MyWiFi" --password "secret" --target-ip 192.168.1.20 + python provision.py --port /dev/ttyUSB0 --chip esp32c6 --ssid "..." \\ + --password "..." --target-ip 192.168.1.20 Requirements: pip install 'esptool>=5.0' nvs-partition-gen @@ -143,7 +145,7 @@ def generate_nvs_binary(csv_content, size): os.unlink(p) -def flash_nvs(port, baud, nvs_bin): +def flash_nvs(port, baud, nvs_bin, chip): """Flash the NVS partition binary to the ESP32.""" with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f: f.write(nvs_bin) @@ -152,16 +154,13 @@ def flash_nvs(port, baud, nvs_bin): try: cmd = [ sys.executable, "-m", "esptool", - "--chip", "esp32s3", + "--chip", chip, "--port", port, "--baud", str(baud), - # Keep underscore form — ESP-IDF v5.4 bundles esptool 4.10.0 which only - # accepts "write_flash". pip's esptool >=5.x accepts both (hyphenated - # form preferred) but keeps underscore working. Do not "correct" this. - "write_flash", + "write-flash", hex(NVS_PARTITION_OFFSET), bin_path, ] - print(f"Flashing NVS partition ({len(nvs_bin)} bytes) to {port}...") + print(f"Flashing NVS partition ({len(nvs_bin)} bytes) to {port} (chip={chip})...") subprocess.check_call(cmd) print("NVS provisioning complete!") finally: @@ -170,10 +169,20 @@ def flash_nvs(port, baud, nvs_bin): def main(): parser = argparse.ArgumentParser( - description="Provision ESP32-S3 CSI Node with WiFi and aggregator settings", - epilog="Example: python provision.py --port COM7 --ssid MyWiFi --password secret --target-ip 192.168.1.20", + description="Provision CSI node NVS (WiFi + aggregator); works on S3, C6, etc.", + epilog=( + "Example: python provision.py --port COM7 --ssid MyWiFi --password secret " + "--target-ip 192.168.1.20\n" + "ESP32-C6: same, or pass --chip esp32c6 if auto-detect fails " + "(default chip is auto for esptool v5+)." + ), ) parser.add_argument("--port", required=True, help="Serial port (e.g. COM7, /dev/ttyUSB0)") + parser.add_argument( + "--chip", + default="auto", + help="esptool target: auto (default), esp32s3, esp32c6, ... (must match connected chip)", + ) parser.add_argument("--baud", type=int, default=460800, help="Flash baud rate (default: 460800)") parser.add_argument("--ssid", help="WiFi SSID") parser.add_argument("--password", help="WiFi password") @@ -337,11 +346,11 @@ def main(): with open(out, "wb") as f: f.write(nvs_bin) print(f"NVS binary saved to {out} ({len(nvs_bin)} bytes)") - print(f"Flash manually: python -m esptool --chip esp32s3 --port {args.port} " + print(f"Flash manually: python -m esptool --chip {args.chip} --port {args.port} " f"write-flash 0x9000 {out}") return - flash_nvs(args.port, args.baud, nvs_bin) + flash_nvs(args.port, args.baud, nvs_bin, args.chip) if __name__ == "__main__":