firmware/esp32-csi-node: IDF 6 build, HE CSI config, unicore DSP, provision chip detect (#522)
* firmware/esp32-csi-node: fix IDF 6 build (PSA SHA-256, explicit REQUIRES) - rvf_parser: use psa_hash_* / psa_hash_compute; mbedTLS 4 has no public mbedtls/sha256.h on the IDF include path. - main/CMakeLists: declare REQUIRES for WiFi, netif, HTTP, OTA, drivers, lwip, mbedtls per ESP-IDF v6 component dependency checks; optional wasm3 when CONFIG_WASM_ENABLE. Signed-off-by: Chaitanya Tata <chaitanya@dotstarconsulting.com> Co-authored-by: Cursor <cursoragent@cursor.com> * firmware/esp32-csi-node: fix CSI config for Wi-Fi 6 (ESP32-C6) When CONFIG_SOC_WIFI_HE_SUPPORT is set, wifi_csi_config_t is the wifi_csi_acquire_config_t bitfield layout. The legacy bool fields (lltf_en, htltf_en, ...) only apply to ESP32-S3-class targets. Initialize acquire fields for HE targets; add MAC v3-only members when CONFIG_SOC_WIFI_MAC_VERSION_NUM >= 3. Verified: idf.py build for esp32c6 and esp32s3 (ESP-IDF v6.1). Signed-off-by: Chaitanya Tata <chaitanya@dotstarconsulting.com> Co-authored-by: Cursor <cursoragent@cursor.com> * firmware/esp32-csi-node: pin edge DSP task for unicore (ESP32-C6) edge_processing_init used xTaskCreatePinnedToCore(..., core 1). ESP32-C6 runs FreeRTOS unicore (portNUM_PROCESSORS == 1), so core 1 trips the xTaskCreatePinnedToCore range assert right after CSI init. Use core 1 only when SMP is available; otherwise pin to core 0. Signed-off-by: Chaitanya Tata <chaitanya@dotstarconsulting.com> Co-authored-by: Cursor <cursoragent@cursor.com> * firmware/esp32-csi-node: provision NVS with chip auto-detect provision.py always passed --chip esp32s3 to esptool, so flashing NVS on ESP32-C6 failed. Default --chip to auto (esptool v5) and add an explicit --chip override. Use write-flash instead of deprecated write_flash. Signed-off-by: Chaitanya Tata <chaitanya@dotstarconsulting.com> Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: Chaitanya Tata <chaitanya@dotstarconsulting.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f853c74563
commit
cee414f3c0
|
|
@ -11,7 +11,26 @@ set(SRCS
|
||||||
"adaptive_controller.c"
|
"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
|
# ADR-061: Mock CSI generator for QEMU testing + ADR-081 mock radio binding
|
||||||
if(CONFIG_CSI_MOCK_ENABLED)
|
if(CONFIG_CSI_MOCK_ENABLED)
|
||||||
|
|
@ -21,7 +40,11 @@ endif()
|
||||||
# ADR-045: AMOLED display support (compile-time optional)
|
# ADR-045: AMOLED display support (compile-time optional)
|
||||||
if(CONFIG_DISPLAY_ENABLE)
|
if(CONFIG_DISPLAY_ENABLE)
|
||||||
list(APPEND SRCS "display_hal.c" "display_ui.c" "display_task.c")
|
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()
|
endif()
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
|
|
|
||||||
|
|
@ -371,6 +371,30 @@ void csi_collector_init(void)
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Promiscuous mode enabled (MGMT-only, RuView#396)");
|
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 = {
|
wifi_csi_config_t csi_config = {
|
||||||
.lltf_en = true,
|
.lltf_en = true,
|
||||||
.htltf_en = true,
|
.htltf_en = true,
|
||||||
|
|
@ -380,6 +404,7 @@ void csi_collector_init(void)
|
||||||
.manu_scale = false,
|
.manu_scale = false,
|
||||||
.shift = false,
|
.shift = false,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_csi_config(&csi_config));
|
ESP_ERROR_CHECK(esp_wifi_set_csi_config(&csi_config));
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_csi_rx_cb(wifi_csi_callback, NULL));
|
ESP_ERROR_CHECK(esp_wifi_set_csi_rx_cb(wifi_csi_callback, NULL));
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@
|
||||||
* @file edge_processing.c
|
* @file edge_processing.c
|
||||||
* @brief ADR-039 Edge Intelligence — dual-core CSI processing pipeline.
|
* @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 0 (WiFi path): Pushes raw CSI frames into lock-free SPSC ring buffer.
|
||||||
* Core 1 (DSP task): Pops frames, runs signal processing pipeline:
|
* 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
|
* 1. Phase extraction from I/Q pairs
|
||||||
* 2. Phase unwrapping (continuous phase)
|
* 2. Phase unwrapping (continuous phase)
|
||||||
* 3. Welford variance tracking per subcarrier
|
* 3. Welford variance tracking per subcarrier
|
||||||
|
|
@ -1050,7 +1051,9 @@ esp_err_t edge_processing_init(const edge_config_t *cfg)
|
||||||
return ESP_OK;
|
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(
|
BaseType_t ret = xTaskCreatePinnedToCore(
|
||||||
edge_task,
|
edge_task,
|
||||||
"edge_dsp",
|
"edge_dsp",
|
||||||
|
|
@ -1058,14 +1061,14 @@ esp_err_t edge_processing_init(const edge_config_t *cfg)
|
||||||
NULL,
|
NULL,
|
||||||
5, /* Priority 5 — above idle, below WiFi. */
|
5, /* Priority 5 — above idle, below WiFi. */
|
||||||
NULL,
|
NULL,
|
||||||
1 /* Pin to Core 1. */
|
dsp_core);
|
||||||
);
|
|
||||||
|
|
||||||
if (ret != pdPASS) {
|
if (ret != pdPASS) {
|
||||||
ESP_LOGE(TAG, "Failed to create edge DSP task");
|
ESP_LOGE(TAG, "Failed to create edge DSP task");
|
||||||
return ESP_ERR_NO_MEM;
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "mbedtls/sha256.h"
|
#include "psa/crypto.h"
|
||||||
|
|
||||||
static const char *TAG = "rvf";
|
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) ---- */
|
/* ---- Verify build hash (SHA-256 of WASM payload) ---- */
|
||||||
uint8_t computed_hash[32];
|
uint8_t computed_hash[32];
|
||||||
int ret = mbedtls_sha256(wasm_data, hdr->wasm_len, computed_hash, 0);
|
size_t hash_len = 0;
|
||||||
if (ret != 0) {
|
psa_status_t psa_st = psa_hash_compute(PSA_ALG_SHA_256, wasm_data,
|
||||||
ESP_LOGE(TAG, "SHA-256 computation failed: %d", ret);
|
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;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,8 +190,7 @@ esp_err_t rvf_verify_signature(const rvf_parsed_t *parsed, const uint8_t *data,
|
||||||
/*
|
/*
|
||||||
* Ed25519 verification.
|
* Ed25519 verification.
|
||||||
*
|
*
|
||||||
* ESP-IDF v5.2 mbedtls does NOT include Ed25519 (Curve25519 is
|
* Legacy mbedtls Ed25519 is optional. We use a SHA-256 keyed digest:
|
||||||
* for ECDH/X25519 only). We use a SHA-256-HMAC integrity check:
|
|
||||||
*
|
*
|
||||||
* expected = SHA-256(pubkey || signed_region)
|
* 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
|
* pubkey produces a different expected hash, so unauthorized
|
||||||
* publishers cannot forge a valid signature.
|
* publishers cannot forge a valid signature.
|
||||||
*
|
*
|
||||||
* For full Ed25519 (NaCl-style), enable CONFIG_MBEDTLS_EDDSA_C
|
* For full Ed25519, enable CONFIG_MBEDTLS_EDDSA_C or equivalent.
|
||||||
* or link TweetNaCl. The RVF builder should match this scheme.
|
* The RVF builder should match this scheme.
|
||||||
*/
|
*/
|
||||||
uint8_t hash_input_prefix[32];
|
uint8_t hash_input_prefix[32];
|
||||||
memcpy(hash_input_prefix, pubkey, 32);
|
memcpy(hash_input_prefix, pubkey, 32);
|
||||||
|
|
||||||
/* Compute SHA-256(pubkey || header+manifest+wasm). */
|
/* Compute SHA-256(pubkey || header+manifest+wasm) via PSA Crypto. */
|
||||||
mbedtls_sha256_context ctx;
|
psa_hash_operation_t op = PSA_HASH_OPERATION_INIT;
|
||||||
mbedtls_sha256_init(&ctx);
|
psa_status_t st = psa_hash_setup(&op, PSA_ALG_SHA_256);
|
||||||
int ret = mbedtls_sha256_starts(&ctx, 0);
|
if (st != PSA_SUCCESS) {
|
||||||
if (ret != 0) {
|
|
||||||
mbedtls_sha256_free(&ctx);
|
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
ret = mbedtls_sha256_update(&ctx, hash_input_prefix, 32);
|
st = psa_hash_update(&op, hash_input_prefix, 32);
|
||||||
if (ret != 0) {
|
if (st != PSA_SUCCESS) {
|
||||||
mbedtls_sha256_free(&ctx);
|
(void)psa_hash_abort(&op);
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
ret = mbedtls_sha256_update(&ctx, data, signed_len);
|
st = psa_hash_update(&op, data, signed_len);
|
||||||
if (ret != 0) {
|
if (st != PSA_SUCCESS) {
|
||||||
mbedtls_sha256_free(&ctx);
|
(void)psa_hash_abort(&op);
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t expected[32];
|
uint8_t expected[32];
|
||||||
ret = mbedtls_sha256_finish(&ctx, expected);
|
size_t out_len = 0;
|
||||||
mbedtls_sha256_free(&ctx);
|
st = psa_hash_finish(&op, expected, sizeof(expected), &out_len);
|
||||||
if (ret != 0) {
|
if (st != PSA_SUCCESS || out_len != 32) {
|
||||||
|
(void)psa_hash_abort(&op);
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
#!/usr/bin/env python3
|
#!/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
|
Writes WiFi credentials and aggregator target to the ESP32's NVS partition
|
||||||
so users can configure a pre-built firmware binary without recompiling.
|
so users can configure a pre-built firmware binary without recompiling.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python provision.py --port COM7 --ssid "MyWiFi" --password "secret" --target-ip 192.168.1.20
|
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:
|
Requirements:
|
||||||
pip install 'esptool>=5.0' nvs-partition-gen
|
pip install 'esptool>=5.0' nvs-partition-gen
|
||||||
|
|
@ -143,7 +145,7 @@ def generate_nvs_binary(csv_content, size):
|
||||||
os.unlink(p)
|
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."""
|
"""Flash the NVS partition binary to the ESP32."""
|
||||||
with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
|
with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
|
||||||
f.write(nvs_bin)
|
f.write(nvs_bin)
|
||||||
|
|
@ -152,16 +154,13 @@ def flash_nvs(port, baud, nvs_bin):
|
||||||
try:
|
try:
|
||||||
cmd = [
|
cmd = [
|
||||||
sys.executable, "-m", "esptool",
|
sys.executable, "-m", "esptool",
|
||||||
"--chip", "esp32s3",
|
"--chip", chip,
|
||||||
"--port", port,
|
"--port", port,
|
||||||
"--baud", str(baud),
|
"--baud", str(baud),
|
||||||
# Keep underscore form — ESP-IDF v5.4 bundles esptool 4.10.0 which only
|
"write-flash",
|
||||||
# accepts "write_flash". pip's esptool >=5.x accepts both (hyphenated
|
|
||||||
# form preferred) but keeps underscore working. Do not "correct" this.
|
|
||||||
"write_flash",
|
|
||||||
hex(NVS_PARTITION_OFFSET), bin_path,
|
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)
|
subprocess.check_call(cmd)
|
||||||
print("NVS provisioning complete!")
|
print("NVS provisioning complete!")
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -170,10 +169,20 @@ def flash_nvs(port, baud, nvs_bin):
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Provision ESP32-S3 CSI Node with WiFi and aggregator settings",
|
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",
|
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("--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("--baud", type=int, default=460800, help="Flash baud rate (default: 460800)")
|
||||||
parser.add_argument("--ssid", help="WiFi SSID")
|
parser.add_argument("--ssid", help="WiFi SSID")
|
||||||
parser.add_argument("--password", help="WiFi password")
|
parser.add_argument("--password", help="WiFi password")
|
||||||
|
|
@ -337,11 +346,11 @@ def main():
|
||||||
with open(out, "wb") as f:
|
with open(out, "wb") as f:
|
||||||
f.write(nvs_bin)
|
f.write(nvs_bin)
|
||||||
print(f"NVS binary saved to {out} ({len(nvs_bin)} bytes)")
|
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}")
|
f"write-flash 0x9000 {out}")
|
||||||
return
|
return
|
||||||
|
|
||||||
flash_nvs(args.port, args.baud, nvs_bin)
|
flash_nvs(args.port, args.baud, nvs_bin, args.chip)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue