diff --git a/components/mcs_telemetry/mcs_telemetry.c b/components/mcs_telemetry/mcs_telemetry.c index f978a63..24d6c66 100644 --- a/components/mcs_telemetry/mcs_telemetry.c +++ b/components/mcs_telemetry/mcs_telemetry.c @@ -302,30 +302,40 @@ esp_err_t mcs_telemetry_to_json(char *json_buffer, size_t buffer_len, const char return ESP_ERR_INVALID_ARG; } +#define MCS_JSON_TAIL_RESERVE 4 /* room for "]\0" and safety */ + if (buffer_len <= MCS_JSON_TAIL_RESERVE) { + return ESP_ERR_NO_MEM; + } + uint32_t now_ms = esp_timer_get_time() / 1000; int written = snprintf(json_buffer, buffer_len, "{\"device_id\":\"%s\",\"timestamp\":%lu,\"total_frames\":%lu,\"devices\":[", device_id ? device_id : "unknown", now_ms, s_stats.total_frames_captured); - if (written < 0 || written >= buffer_len) { + if (written < 0 || (size_t)written >= buffer_len) { return ESP_ERR_NO_MEM; } - int offset = written; + size_t offset = (size_t)written; bool first = true; - for (int i = 0; i < MCS_TELEMETRY_MAX_DEVICES && offset < buffer_len - 100; i++) { + for (int i = 0; i < MCS_TELEMETRY_MAX_DEVICES; i++) { mcs_device_telemetry_t *dev = &s_stats.devices[i]; if (dev->sample_count == 0) continue; + size_t space = buffer_len - offset - MCS_JSON_TAIL_RESERVE; + if (space < 2) break; + if (!first) { - written = snprintf(json_buffer + offset, buffer_len - offset, ","); + written = snprintf(json_buffer + offset, space, ","); if (written < 0) break; - offset += written; + offset += (size_t)((written < (int)space) ? written : (space - 1)); + space = buffer_len - offset - MCS_JSON_TAIL_RESERVE; + if (space < 2) break; } first = false; - written = snprintf(json_buffer + offset, buffer_len - offset, + written = snprintf(json_buffer + offset, space, "{\"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\"," "\"mcs\":%u,\"ss\":%u,\"rssi\":%d," "\"channel\":%u,\"bandwidth\":%u," @@ -338,15 +348,22 @@ esp_err_t mcs_telemetry_to_json(char *json_buffer, size_t buffer_len, const char dev->avg_phy_rate_kbps); if (written < 0) break; - offset += written; + offset += (size_t)((written < (int)space) ? written : (space - 1)); } - written = snprintf(json_buffer + offset, buffer_len - offset, "]}"); - if (written < 0) { - return ESP_ERR_NO_MEM; + { + size_t tail_space = buffer_len - offset; + if (tail_space < 3) { + return ESP_ERR_NO_MEM; + } + written = snprintf(json_buffer + offset, tail_space, "]}"); + if (written < 0 || (size_t)written >= tail_space) { + return ESP_ERR_NO_MEM; + } } return ESP_OK; +#undef MCS_JSON_TAIL_RESERVE } uint32_t mcs_calculate_phy_rate_ax(uint8_t mcs, uint8_t ss, mcs_bandwidth_t bandwidth, bool sgi) { diff --git a/components/sd_card/sd_card.c b/components/sd_card/sd_card.c index 9736db9..c649c16 100644 --- a/components/sd_card/sd_card.c +++ b/components/sd_card/sd_card.c @@ -42,6 +42,7 @@ #include #include #include +#include #include // Pin definitions for SparkFun microSD Transflash Breakout @@ -255,22 +256,27 @@ esp_err_t sd_card_write_file(const char *filename, const void *data, size_t len, snprintf(full_path, sizeof(full_path), "%s%s%s", s_mount_point, (filename[0] == '/') ? "" : "/", filename); - const char *mode = append ? "a" : "w"; - FILE *f = fopen(full_path, mode); - if (f == NULL) { + int flags = O_WRONLY | O_CREAT; + if (append) { + flags |= O_APPEND; + } else { + flags |= O_TRUNC; + } + int fd = open(full_path, flags, 0644); + if (fd < 0) { ESP_LOGE(TAG, "Failed to open file for writing: %s", full_path); return ESP_FAIL; } - size_t written = fwrite(data, 1, len, f); - fclose(f); + ssize_t written = write(fd, data, len); + close(fd); - if (written != len) { - ESP_LOGE(TAG, "Failed to write all data: wrote %zu of %zu bytes", written, len); + if (written < 0 || (size_t)written != len) { + ESP_LOGE(TAG, "Failed to write all data: wrote %zd of %zu bytes", (ssize_t)written, len); return ESP_FAIL; } - ESP_LOGD(TAG, "Wrote %zu bytes to %s", written, full_path); + ESP_LOGD(TAG, "Wrote %zu bytes to %s", (size_t)written, full_path); return ESP_OK; } diff --git a/components/status_led/status_led.c b/components/status_led/status_led.c index 6dd6d09..a0593c4 100644 --- a/components/status_led/status_led.c +++ b/components/status_led/status_led.c @@ -43,6 +43,7 @@ static led_strip_handle_t s_led_strip = NULL; static bool s_is_rgb = false; static int s_gpio_pin = -1; static volatile led_state_t s_current_state = LED_STATE_NO_CONFIG; +static volatile bool s_capture_active = false; static void set_color(uint8_t r, uint8_t g, uint8_t b) { if (s_is_rgb && s_led_strip) { @@ -69,8 +70,13 @@ static void led_task(void *arg) { case LED_STATE_CONNECTED: set_color(0, 25, 0); vTaskDelay(pdMS_TO_TICKS(1000)); break; - case LED_STATE_MONITORING: - set_color(0, 0, 50); vTaskDelay(pdMS_TO_TICKS(1000)); + case LED_STATE_MONITORING: /* Blink blue only when frames being captured */ + if (s_capture_active) { + set_color(0, 0, toggle ? 50 : 0); toggle = !toggle; + vTaskDelay(pdMS_TO_TICKS(300)); + } else { + set_color(0, 0, 10); vTaskDelay(pdMS_TO_TICKS(1000)); /* Dim solid: monitor on, no capture */ + } break; case LED_STATE_TRANSMITTING: set_color(toggle ? 50 : 0, 0, toggle ? 50 : 0); toggle = !toggle; @@ -116,6 +122,6 @@ void status_led_init(int gpio_pin, bool is_rgb_strip) { xTaskCreate(led_task, "led_task", 2048, NULL, 5, NULL); } -// ... Setters/Getters ... void status_led_set_state(led_state_t state) { s_current_state = state; } led_state_t status_led_get_state(void) { return s_current_state; } +void status_led_set_capture_active(bool active) { s_capture_active = active; } diff --git a/components/status_led/status_led.h b/components/status_led/status_led.h index a9fedc1..c5b2e5b 100644 --- a/components/status_led/status_led.h +++ b/components/status_led/status_led.h @@ -70,6 +70,12 @@ void status_led_set_state(led_state_t state); */ led_state_t status_led_get_state(void); +/** + * @brief Set capture-active flag (frames being captured in monitor mode) + * Used with LED_STATE_MONITORING: blink blue only when capture_active is true. + */ +void status_led_set_capture_active(bool active); + #ifdef __cplusplus } #endif diff --git a/components/wifi_controller/wifi_controller.c b/components/wifi_controller/wifi_controller.c index 42df98f..ce74377 100644 --- a/components/wifi_controller/wifi_controller.c +++ b/components/wifi_controller/wifi_controller.c @@ -91,6 +91,7 @@ static void monitor_frame_callback(const wifi_frame_info_t *frame, const uint8_t (void)payload; (void)len; s_monitor_frame_count++; + status_led_set_capture_active(true); if (frame->retry && frame->duration_id > 5000) { log_collapse_event((float)frame->duration_id, frame->rssi, frame->retry); } @@ -102,8 +103,13 @@ static void monitor_stats_task(void *arg) { (void)arg; static char json_buf[FIWI_TELEMETRY_JSON_BUF_SIZE]; uint32_t flush_count = 0; + uint32_t last_frame_count = 0; while (1) { vTaskDelay(pdMS_TO_TICKS(10000)); + if (s_monitor_frame_count == last_frame_count) { + status_led_set_capture_active(false); + } + last_frame_count = s_monitor_frame_count; wifi_collapse_stats_t stats; if (wifi_monitor_get_stats(&stats) == ESP_OK) { ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry: %.2f%%, Avg NAV: %u us ---", @@ -113,9 +119,12 @@ static void monitor_stats_task(void *arg) { /* Write MCS telemetry to fiwi-telemetry on SD card (default on monitor start) */ if (sd_card_is_ready() && mcs_telemetry_to_json(json_buf, sizeof(json_buf), "esp32") == ESP_OK) { size_t len = strlen(json_buf); - if (len > 0 && sd_card_write_file(FIWI_TELEMETRY_FILE, json_buf, len, false) == ESP_OK) { - flush_count++; - ESP_LOGD(TAG, "fiwi-telemetry flushed (#%lu)", (unsigned long)flush_count); + if (len > 0) { + json_buf[len] = '\n'; /* NDJSON: one object per line */ + if (sd_card_write_file(FIWI_TELEMETRY_FILE, json_buf, len + 1, true) == ESP_OK) { + flush_count++; + ESP_LOGD(TAG, "fiwi-telemetry flushed (#%lu)", (unsigned long)flush_count); + } } } } @@ -212,6 +221,7 @@ esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel, wifi_bandwidth_t bw) { vTaskDelay(pdMS_TO_TICKS(500)); esp_wifi_set_mode(WIFI_MODE_NULL); + status_led_set_capture_active(false); if (wifi_monitor_init(channel, monitor_frame_callback) != ESP_OK) { ESP_LOGE(TAG, "Failed to init monitor mode"); return ESP_FAIL; @@ -257,6 +267,7 @@ esp_err_t wifi_ctl_switch_to_sta(void) { } if (s_monitor_enabled) { + status_led_set_capture_active(false); mcs_telemetry_stop(); wifi_monitor_stop(); s_monitor_enabled = false;