From 898c536eac0416501cfc91acc52dde0c94ebf41e Mon Sep 17 00:00:00 2001 From: ruv Date: Tue, 2 Jun 2026 09:57:19 +0200 Subject: [PATCH] =?UTF-8?q?fix(firmware):=20capture=20DATA=20frames=20on?= =?UTF-8?q?=20display-less=20boards=20=E2=80=94=20#893/#866/#897?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-built binaries set a MGMT-only promiscuous filter (WIFI_PROMIS_FILTER_MASK_MGMT) as the #396 workaround — DATA-frame interrupt load races the QSPI display's SPI traffic against the SPI-flash cache and crashes Core 0 in wDev_ProcessFiq. But MGMT-only fires the CSI callback only on sparse management frames, so on the common DISPLAY-LESS boards (DevKitC-1, T7-S3, N8R8) CSI yield collapses to 0 pps under real traffic (#521) — the node looks dead despite being on the network, which is the root cause of most "can't reproduce / it's fake" reports (#804/#37). A board with no AMOLED panel has no QSPI/SPI-flash contention, so it can safely capture DATA frames. After the boot-time display probe runs: - display present -> keep MGMT-only (preserve #396 crash protection) - no display -> upgrade filter to MGMT|DATA (restore CSI yield) Implementation (runtime-gated, no boot reorder): - display_task.c: s_display_active flag + display_is_active() accessor, set true only when the panel is detected and the display task starts. - csi_collector.c: csi_collector_enable_data_capture() re-sets the promiscuous filter to MGMT|DATA. - main.c: after display_task_start(), if !display_is_active() (or display support not compiled in), upgrade the filter. Build-verified on BOTH targets: esp32c6 (headless path) and esp32s3 (display path, display_task.c compiled) — Project build complete, RC 0. Needs on-hardware confirmation that yield recovers and no #396 crash. --- firmware/esp32-csi-node/main/csi_collector.c | 17 +++++++++++++++++ firmware/esp32-csi-node/main/csi_collector.h | 13 +++++++++++++ firmware/esp32-csi-node/main/display_task.c | 9 +++++++++ firmware/esp32-csi-node/main/display_task.h | 10 ++++++++++ firmware/esp32-csi-node/main/main.c | 15 +++++++++++++++ 5 files changed, 64 insertions(+) diff --git a/firmware/esp32-csi-node/main/csi_collector.c b/firmware/esp32-csi-node/main/csi_collector.c index 484b40cc..56ae21f7 100644 --- a/firmware/esp32-csi-node/main/csi_collector.c +++ b/firmware/esp32-csi-node/main/csi_collector.c @@ -637,6 +637,23 @@ static void hop_timer_cb(void *arg) csi_hop_next_channel(); } +void csi_collector_enable_data_capture(void) +{ + /* MGMT-only (RuView#396) starves the CSI callback on display-less boards + * (RuView#521/#893): beacons alone are sparse, yield collapses to 0 pps. + * Without a display there is no QSPI/SPI-flash cache contention with the + * DATA-frame interrupt load, so capture DATA frames too. */ + wifi_promiscuous_filter_t filt = { + .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA, + }; + esp_err_t err = esp_wifi_set_promiscuous_filter(&filt); + if (err == ESP_OK) { + ESP_LOGI(TAG, "CSI filter upgraded to MGMT+DATA (no display, RuView#893)"); + } else { + ESP_LOGW(TAG, "Failed to enable DATA-frame CSI capture: %s", esp_err_to_name(err)); + } +} + void csi_collector_start_hop_timer(void) { if (s_hop_count <= 1) { diff --git a/firmware/esp32-csi-node/main/csi_collector.h b/firmware/esp32-csi-node/main/csi_collector.h index dfb3f20e..92f43105 100644 --- a/firmware/esp32-csi-node/main/csi_collector.h +++ b/firmware/esp32-csi-node/main/csi_collector.h @@ -90,6 +90,19 @@ void csi_hop_next_channel(void); */ void csi_collector_start_hop_timer(void); +/** + * Upgrade the promiscuous filter to capture DATA frames in addition to MGMT + * (RuView#893/#521). + * + * Called on display-less boards: the MGMT-only filter (the #396 display-crash + * workaround set in csi_collector_init) only fires the CSI callback on sparse + * management frames, so yield collapses to 0 pps under real traffic and the + * node looks dead. A board with no AMOLED panel has no QSPI/SPI-flash cache + * contention, so it can safely capture DATA frames — restoring abundant CSI. + * Display boards keep MGMT-only to avoid the #396 crash. + */ +void csi_collector_enable_data_capture(void); + /** * Inject an NDP (Null Data Packet) frame for sensing. * diff --git a/firmware/esp32-csi-node/main/display_task.c b/firmware/esp32-csi-node/main/display_task.c index 9d834edc..02102497 100644 --- a/firmware/esp32-csi-node/main/display_task.c +++ b/firmware/esp32-csi-node/main/display_task.c @@ -9,6 +9,14 @@ #include "display_task.h" #include "sdkconfig.h" +/* Set true once an AMOLED panel is detected and the display task starts. + * Defined outside the CONFIG_DISPLAY_ENABLE guard so display_is_active() + * exists on headless builds too (where it stays false → CSI captures DATA + * frames; see RuView#893). */ +static bool s_display_active = false; + +bool display_is_active(void) { return s_display_active; } + #if CONFIG_DISPLAY_ENABLE #include @@ -162,6 +170,7 @@ esp_err_t display_task_start(void) ESP_LOGI(TAG, "Display task started (Core %d, priority %d, %d fps)", DISP_TASK_CORE, DISP_TASK_PRIORITY, DISP_FPS_LIMIT); + s_display_active = true; return ESP_OK; } diff --git a/firmware/esp32-csi-node/main/display_task.h b/firmware/esp32-csi-node/main/display_task.h index b5af7060..2b20f8d7 100644 --- a/firmware/esp32-csi-node/main/display_task.h +++ b/firmware/esp32-csi-node/main/display_task.h @@ -7,6 +7,7 @@ #define DISPLAY_TASK_H #include "esp_err.h" +#include #ifdef __cplusplus extern "C" { @@ -22,6 +23,15 @@ extern "C" { */ esp_err_t display_task_start(void); +/** + * @return true once an AMOLED panel has been detected and the display task + * is running; false on headless boards (no panel, or built without display + * support). Used to choose the CSI promiscuous filter (RuView#893): a board + * with no display has no QSPI/SPI-flash contention, so it can safely capture + * DATA frames for proper CSI yield instead of starving on MGMT-only. + */ +bool display_is_active(void); + #ifdef __cplusplus } #endif diff --git a/firmware/esp32-csi-node/main/main.c b/firmware/esp32-csi-node/main/main.c index ef576890..536bb95f 100644 --- a/firmware/esp32-csi-node/main/main.c +++ b/firmware/esp32-csi-node/main/main.c @@ -410,6 +410,21 @@ void app_main(void) } #endif + /* RuView#893/#521: the MGMT-only promiscuous filter (set in + * csi_collector_init as the #396 display-crash workaround) starves the CSI + * callback on display-less boards — yield collapses to 0 pps and the node + * looks dead despite being on the network. Now that the display probe has + * run, boards with no AMOLED panel (no QSPI/SPI-flash cache contention) + * upgrade the filter to capture DATA frames too, restoring CSI yield. */ +#ifdef CONFIG_DISPLAY_ENABLE + bool has_display = display_is_active(); /* runtime panel probe result */ +#else + bool has_display = false; /* display support not compiled in */ +#endif + if (!has_display) { + csi_collector_enable_data_capture(); + } + ESP_LOGI(TAG, "CSI streaming active → %s:%d (edge_tier=%u, OTA=%s, WASM=%s, mmWave=%s, swarm=%s, adapt=%s)", g_nvs_config.target_ip, g_nvs_config.target_port, g_nvs_config.edge_tier,