/** * app_main.c - C-ITS Sniffer für ESP32-C5 (LilyGO T-Dongle-C5) * * Kombiniert alle Blöcke zu einer lauffähigen ESP-IDF Anwendung */ #include #include #include #include #include #include #include "sdkconfig.h" #include "esp_err.h" #include "esp_log.h" #include "esp_wifi.h" #include "esp_timer.h" #include "nvs_flash.h" #include "esp_vfs_fat.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" // ==================== Forward Declarations ==================== // Block 1: Hardware extern esp_err_t initialize_wifi(void); extern esp_err_t start_promiscuous_mode(uint32_t channel); extern esp_err_t init_nvs_flash(void); extern void register_cits_commands(void); // Block 2: Parser typedef struct { uint32_t timestamp_sec; uint32_t timestamp_usec; uint16_t length; uint8_t sender_id[6]; uint8_t message_type; uint8_t channel; int16_t rssi; } cits_message_t; extern esp_err_t cits_parser_init(void); extern void cits_process_packet(void *packet_info); // Block 3: CSV Writer extern esp_err_t csv_writer_init(void); extern void csv_store_cits_message(cits_message_t *msg); extern void csv_writer_deinit(void); // ==================== Constants ==================== static const char *TAG = "CITS_SNIFFER"; #define CITS_CHANNEL_0_MHZ 5900 // ==================== External PHY Functions ==================== // Diese kommen vom ESP-IDF / proprietären WiFi-Layer // ==================== CSV File Handle ==================== static FILE *csv_file = NULL; static char current_filename[64]; static time_t last_write_time = 0; #define CSV_WRITE_INTERVAL_SEC (600) static void write_csv_header(FILE *fp) { fprintf(fp, "timestamp_sec,timestamp_usec,length,sender_mac,message_type,channel,rssi\n"); } static void write_csv_row(FILE *fp, 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, "%u,%u,%u,%s,%u,%u,%d\n", msg->timestamp_sec, msg->timestamp_usec, msg->length, mac_str, msg->message_type, msg->channel, msg->rssi); } static esp_err_t create_new_csv_file(void) { time_t now; struct tm *tm_info; time(&now); tm_info = localtime(&now); snprintf(current_filename, sizeof(current_filename), "/data/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 (csv_file != NULL) fclose(csv_file); csv_file = fopen(current_filename, "w"); if (csv_file == NULL) { ESP_LOGE(TAG, "CSV file erstellen fehlgeschlagen: %s", current_filename); return ESP_FAIL; } write_csv_header(csv_file); fflush(csv_file); ESP_LOGI(TAG, "Neue CSV-Datei: %s", current_filename); return ESP_OK; } // ==================== Promiscuous Callback ==================== static void sniffer_callback(void *buf, wifi_promiscuous_pkt_type_t type) { wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)buf; if (type != WIFI_PKT_MISC) return; if (pkt->rx_ctrl.rssi < -100) return; // Rauschen filtern uint32_t timestamp_us = pkt->rx_ctrl.timestamp; uint32_t sec = timestamp_us / 1000000U; uint32_t usec = timestamp_us % 1000000U; // Logge Paket-Metadaten ESP_LOGI(TAG, "[Paket] RSSI=%d dBm | CH=%u MHz | Len=%u | TS=%u.%06u", pkt->rx_ctrl.rssi, CITS_CHANNEL_0_MHZ, pkt->rx_ctrl.sig_len, sec, usec); // Speichere in CSV (nur Header-Daten, kein Parser) cits_message_t msg = { .timestamp_sec = sec, .timestamp_usec = usec, .length = pkt->rx_ctrl.sig_len, .channel = CITS_CHANNEL_0_MHZ, .rssi = pkt->rx_ctrl.rssi, }; csv_store_cits_message(&msg); } // ==================== Partition Table ==================== // partition.csv content embedded for flash: // # ESP-ADF Partition Table // # Name, Type, SubType, Offset, Size, Flags // nvs, data, nvs, 0x9000, 0x4000, // phy_init, data, phy, 0xd000, 0x1000, // factory, app, factory, 0x10000, 0x3E0000, // storage, data, fat, , 0x200000, // ==================== Init Functions ==================== static esp_err_t setup_flash_storage(void) { static wl_handle_t wl_handle; const esp_vfs_fat_mount_config_t mount_config = { .max_files = 4, .format_if_mount_failed = true, }; esp_err_t ret = esp_vfs_fat_spiflash_mount_rw_wl("/data", "storage", &mount_config, &wl_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "FATFS Mount fehlgeschlagen: %s", esp_err_to_name(ret)); return ret; } ESP_LOGI(TAG, "FATFS storage gemountet"); return ESP_OK; } static esp_err_t init_promiscuous(void) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); wifi_promiscuous_filter_t filter = { .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL }; ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(sniffer_callback)); ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); ESP_LOGI(TAG, "Promiscuous Mode aktiv auf %u MHz", CITS_CHANNEL_0_MHZ); return ESP_OK; } // ==================== Main ==================== void app_main(void) { ESP_LOGI(TAG, "=== C-ITS SNIFFER FÜR ESP32-C5 ==="); ESP_LOGI(TAG, "Starte..."); // NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { nvs_flash_erase(); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ESP_LOGI(TAG, "NVS Flash OK"); // WiFi Setup ESP_ERROR_CHECK(initialize_wifi()); ESP_ERROR_CHECK(init_promiscuous()); // CSV Writer ESP_ERROR_CHECK(setup_flash_storage()); ESP_ERROR_CHECK(create_new_csv_file()); last_write_time = time(NULL); ESP_LOGI(TAG, "=== C-ITS SNIFFER LÄUFT ==="); ESP_LOGI(TAG, "Erwarte C-ITS/DSRC Pakete auf %u MHz...", CITS_CHANNEL_0_MHZ); uint32_t last_report = 0; while (1) { vTaskDelay(pdMS_TO_TICKS(60000)); last_report++; time_t now; time(&now); if (difftime(now, last_write_time) >= CSV_WRITE_INTERVAL_SEC) { create_new_csv_file(); last_write_time = now; } ESP_LOGI(TAG, "Laufzeit: %lu Min. | Warte auf C-ITS Pakete...", (unsigned long)last_report); } }