/** * Block 3: CSV Writer für C-ITS Sniffer * * Erstellt CSV-Dateien mit empfangenen C-ITS Nachrichten * auf der SD-Karte (FATFS storage partition). * Neue CSV-Datei alle 10 Minuten. * * Dateiname: /storage/cits_YYYYMMDD_HHMMSS.csv * Format: timestamp_sec,timestamp_usec,length,sender_mac,message_type,channel,rssi */ #include #include #include #include #include #include #include #include "sdkconfig.h" #include "esp_err.h" #include "esp_log.h" #include "esp_vfs_fat.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG = "CSV_WRITER"; /* ---------- C-ITS Message Record (für CSV) ---------- */ typedef struct { uint32_t timestamp_sec; uint32_t timestamp_usec; uint16_t length; uint8_t sender_id[6]; // MAC Address uint8_t message_type; // BSI Message Type uint8_t channel; // Kanal int16_t rssi; // Signal Stärke } cits_message_t; /* ---------- Globale CSV-Zustand ---------- */ static FILE *g_csv_file = NULL; static char g_csv_filename[64]; static time_t g_last_csv_time = 0; #define CSV_ROLLOVER_SEC 600 // 10 Minuten /* ---------- CSV Header schreiben ---------- */ static void write_csv_header(FILE *fp) { fprintf(fp, "timestamp_sec,timestamp_usec,length,sender_mac,message_type,channel,rssi\r\n"); } /* ---------- CSV Zeile schreiben ---------- */ static void write_csv_row(FILE *fp, const cits_message_t *msg) { char mac_str[18]; snprintf(mac_str, sizeof(mac_str), "%02X:%02X:%02X:%02X:%02X:%02X", msg->sender_id[0], msg->sender_id[1], msg->sender_id[2], msg->sender_id[3], msg->sender_id[4], msg->sender_id[5]); fprintf(fp, "%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%s,%" PRIu8 ",%" PRIu8 ",%d\r\n", msg->timestamp_sec, msg->timestamp_usec, msg->length, mac_str, msg->message_type, msg->channel, msg->rssi); } /* ---------- Neue CSV-Datei erstellen ---------- */ static esp_err_t create_new_csv_file(void) { time_t now; struct tm *tm_info; time(&now); tm_info = localtime(&now); snprintf(g_csv_filename, sizeof(g_csv_filename), "/storage/cits_%04d%02d%02d_%02d%02d%02d.csv", tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday, tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec); if (g_csv_file != NULL) { fclose(g_csv_file); g_csv_file = NULL; } g_csv_file = fopen(g_csv_filename, "a"); if (g_csv_file == NULL) { ESP_LOGE(TAG, "CSV file create failed: %s (errno=%d)", g_csv_filename, (int)errno); return ESP_FAIL; } write_csv_header(g_csv_file); fflush(g_csv_file); ESP_LOGI(TAG, "CSV file created: %s", g_csv_filename); return ESP_OK; } /* ---------- Prüfen ob Rollover nötig (10 Min) ---------- */ static void check_csv_rollover(void) { time_t now; time(&now); if (difftime(now, g_last_csv_time) >= CSV_ROLLOVER_SEC) { if (g_csv_file) { fclose(g_csv_file); g_csv_file = NULL; } esp_err_t ret = create_new_csv_file(); if (ret == ESP_OK) { g_last_csv_time = now; } } } /* ---------- C-ITS Nachricht speichern ---------- */ void csv_store_cits_message(const cits_message_t *msg) { if (!msg) return; if (g_csv_file == NULL) { create_new_csv_file(); if (g_csv_file == NULL) return; } check_csv_rollover(); write_csv_row(g_csv_file, msg); fflush(g_csv_file); } /* ---------- CSV System initialisieren (FATFS mount) ---------- */ esp_err_t csv_writer_init(void) { ESP_LOGI(TAG, "CSV Writer: Mount FATFS storage partition..."); esp_vfs_fat_mount_config_t mount_config = { .max_files = 4, .format_if_mount_failed = true, .allocation_unit_size = 16 * 1024, }; esp_err_t ret = esp_vfs_fat_spiflash_mount_rw_wl( "/storage", // VFS mount point "storage", // FATFS partition label &mount_config, NULL // wl_handle nicht benötigt ); if (ret != ESP_OK) { ESP_LOGE(TAG, "FATFS mount failed: %s", esp_err_to_name(ret)); return ret; } ESP_LOGI(TAG, "FATFS storage mounted at /storage"); // Erste CSV-Datei erstellen ret = create_new_csv_file(); if (ret != ESP_OK) { ESP_LOGE(TAG, "Erste CSV-Datei konnte nicht erstellt werden"); return ret; } g_last_csv_time = time(NULL); return ESP_OK; } /* ---------- CSV System beenden ---------- */ void csv_writer_deinit(void) { if (g_csv_file != NULL) { fflush(g_csv_file); fclose(g_csv_file); g_csv_file = NULL; ESP_LOGI(TAG, "CSV file closed"); } }