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,