CITS Sniffer v2: ESP-IDF 5.5 Projektstruktur mit CMakeLists, sdkconfig, partitions_8M

This commit is contained in:
Clawdia
2026-05-18 14:19:34 +02:00
parent 657b2bc1ea
commit 9c1732c364
9 changed files with 5352 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
idf_component_register(
SRCS "app_main.c"
INCLUDE_DIRS "."
REQUIRES esp_wifi esp_timer nvs_flash fatfs
)
+218
View File
@@ -0,0 +1,218 @@
/**
* 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#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);
}
}
+171
View File
@@ -0,0 +1,171 @@
/**
* Block 1: Hardware Initialisierung für ESP32-C5 C-ITS Sniffer
*
* Ziel: WiFi Promiscuous Mode auf 802.11p konfigurieren
*
* C-ITS-Kanäle (5,9 GHz DSRC):
* - Kanal 0: 5900 MHz (Primary)
* - Kanal 1: 5890 MHz
* - Kanal 2: 5880 MHz
* - Kanal 3: 5870 MHz
* - Kanal 4: 5860 MHz
*
* Quelle: ETSI EN 302 571
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <sys/time.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_timer.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Externe Funktionen aus der OpenTrafficMap Firmware
extern void phy_11p_set(int enable, int unknown);
extern void phy_change_channel(int channel, int arg1, int arg2, int arg3);
static const char *TAG = "CITS_BLOCK1";
// C-ITS Konfiguration
#define CITS_CHANNEL_0_MHZ 5900 // Primary C-ITS channel
#define CITS_CHANNEL_1_MHZ 5890 // Alternate channel
#define CITS_CHANNEL_2_MHZ 5880 // Alternate channel
#define CITS_CHANNEL_3_MHZ 5870 // Alternate channel
/**
* WiFi Promiscuous Callback
*/
static void wifi_promiscuous_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type)
{
wifi_promiscuous_pkt_t *packet = (wifi_promiscuous_pkt_t *)recv_buf;
if (type != WIFI_PKT_MISC) {
return;
}
if (packet->rx_ctrl.rx_state) {
return;
}
uint64_t timestamp_us = packet->rx_ctrl.timestamp;
uint32_t sec = timestamp_us / 1000000U;
uint32_t usec = timestamp_us % 1000000U;
uint16_t payload_len;
#if CONFIG_SOC_WIFI_HE_SUPPORT
payload_len = packet->rx_ctrl.dump_len;
#else
payload_len = packet->rx_ctrl.sig_len - 4;
#endif
ESP_LOGD(TAG, "Received packet: %d bytes at %u.%06u", payload_len, sec, usec);
}
/**
* WiFi Initialisierung
*/
static esp_err_t initialize_wifi(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));
ESP_LOGI(TAG, "WiFi initialisiert");
return ESP_OK;
}
/**
* 802.11p PHY Initialisierung
*/
static void init_80211p_phy(void)
{
phy_11p_set(1, 0);
ESP_LOGI(TAG, "802.11p PHY aktiviert");
}
/**
* C-ITS Kanal wechseln
*/
static void set_cits_channel(uint32_t freq_mhz)
{
phy_change_channel(freq_mhz, 1, 0, 0);
ESP_LOGI(TAG, "Wechsle zu Kanal %u MHz", freq_mhz);
}
/**
* WiFi Promiscuous Mode starten
*/
static esp_err_t start_promiscuous_mode(uint32_t channel)
{
wifi_promiscuous_filter_t filter = {
.filter_mask = WIFI_PROMIS_FILTER_MASK_ALL
};
esp_wifi_set_promiscuous_filter(&filter);
esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb);
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
init_80211p_phy();
set_cits_channel(channel);
ESP_LOGI(TAG, "Promiscuous Mode gestartet auf %u MHz", channel);
return ESP_OK;
}
/**
* NVS Flash Initialisierung
*/
static esp_err_t init_nvs_flash(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
ESP_LOGI(TAG, "NVS Flash initialisiert");
return ESP_OK;
}
/**
* Test-Hilfe für manuelle Kanaländerung
*/
static int cmd_set_channel(int argc, char **argv)
{
if (argc < 2) {
printf("Nutzung: set_channel <freq_mhz>\n");
return 0;
}
uint32_t freq = atoi(argv[1]);
if (freq < 5800 || freq > 5900) {
printf("Frequenz muss zwischen 5800 und 5900 MHz liegen\n");
return 1;
}
set_cits_channel(freq);
printf("Kanal auf %u MHz gesetzt\n", freq);
return 0;
}
void register_cits_commands(void)
{
const esp_console_cmd_t cmd = {
.command = "set_channel",
.help = "Set C-ITS channel (5800-5900 MHz)",
.hint = NULL,
.func = &cmd_set_channel,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
+224
View File
@@ -0,0 +1,224 @@
/**
* Block 2: C-ITS Parser für ESP32-C5 C-ITS Sniffer
*
* Ziel: C-ITS DATENSATZ parsen und extrahieren
*
* WAVE/DSRC Paket Struktur (IEEE 1609.3):
* - MAC Header: 14 Bytes
* - LLC/SNAP: 8 Bytes
* - WAVE Short Message (WSM): 6 Bytes Header
* - C-ITS DATENSATZ (Payload)
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <sys/time.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "CITS_PARSER";
// WAVE Short Message Header Struktur
typedef struct __attribute__((packed)) {
uint8_t pduLength[3]; // PDU Länge (3 Bytes)
uint8_t senderId[6]; // Sender MAC Adresse
uint32_t timestamp; // Zeitstempel
uint8_t priority; // Priorität
uint8_t pduType; // PDU Typ
} wave_header_t;
// C-ITS Message Type (BSI Format)
typedef struct __attribute__((packed)) {
uint8_t msgId; // Message ID
uint8_t msgCnt; // Message Count
uint8_t sectionNum; // Section Number
uint8_t partId; // Part ID
uint8_t reserved[8]; // Reserviert
uint8_t payload[]; // Payload
} cits_bsi_header_t;
// C-ITS Message Types (ETSI TS 103 097)
#define CITS_MSGID_DECEVENT 0x01 // Dezisionsunterstützung
#define CITS_MSGID_DSECN 0x02 // Dekstruktive Umgebung
#define CITS_MSGID_DMM 0x03 // Dynamisches Map-Matching
#define CITS_MSGID_MAP 0x04 // Kartendaten
#define CITS_MSGID_MAPDATA 0x05 // Kartendaten
#define CITS_MSGID_MAPSELDATA 0x06 // Kartendaten Auswahl
#define CITS_MSGID_RSM 0x07 // Road Safety Message
#define CITS_MSGID_CAM 0x08 // Cooperative Awareness Message
#define CITS_MSGID_DENM 0x09 // Decentralized Environmental Notification
#define CITS_MSGID_SPA 0x0A // Signal Phase and Timing
#define CITS_MSGID_MAM 0x0B // Map Alignment Message
#define CITS_MSGID_VIT 0x0C // Vehicle Information Message
#define CITS_MSGID_VSL 0x0D // Variable Speed Limit
#define CITS_MSGID_SPA 0x0E // Signal Phase and Timing
#define CITS_MSGID_CSP 0x0F // Cross-Signal Phase
/**
* MAC Adresse als String formatieren
*/
static void format_mac(const uint8_t *mac, char *mac_str, size_t size)
{
snprintf(mac_str, size, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
/**
* WAVE Header parsen
*/
static esp_err_t parse_wave_header(const uint8_t *packet, wave_header_t *wave_hdr)
{
if (packet == NULL || wave_hdr == NULL) {
return ESP_ERR_INVALID_ARG;
}
memcpy(wave_hdr->pduLength, packet, 3);
memcpy(wave_hdr->senderId, packet + 3, 6);
memcpy(&wave_hdr->timestamp, packet + 9, 4);
wave_hdr->priority = packet[13];
wave_hdr->pduType = packet[14];
return ESP_OK;
}
/**
* C-ITS BSI Header parsen
*/
static esp_err_t parse_cits_bsi(const uint8_t *packet, cits_bsi_header_t *bsi_hdr)
{
if (packet == NULL || bsi_hdr == NULL) {
return ESP_ERR_INVALID_ARG;
}
bsi_hdr->msgId = packet[0];
bsi_hdr->msgCnt = packet[1];
bsi_hdr->sectionNum = packet[2];
bsi_hdr->partId = packet[3];
memcpy(bsi_hdr->reserved, packet + 4, 8);
return ESP_OK;
}
/**
* Nachrichtstyp identifizieren
*/
static const char* get_message_type(uint8_t msgId)
{
switch (msgId) {
case CITS_MSGID_DECEVENT: return "DECEVENT";
case CITS_MSGID_DSECN: return "DSECN";
case CITS_MSGID_DMM: return "DMM";
case CITS_MSGID_MAP: return "MAP";
case CITS_MSGID_MAPDATA: return "MAPDATA";
case CITS_MSGID_MAPSELDATA: return "MAPSELDATA";
case CITS_MSGID_RSM: return "RSM";
case CITS_MSGID_CAM: return "CAM";
case CITS_MSGID_DENM: return "DENM";
case CITS_MSGID_SPA: return "SPA";
case CITS_MSGID_MAM: return "MAM";
case CITS_MSGID_VIT: return "VIT";
case CITS_MSGID_VSL: return "VSL";
default: return "UNKNOWN";
}
}
/**
* C-ITS Nachricht verarbeiten
*/
static uint32_t packet_count = 0;
static void process_cits_packet(const uint8_t *packet, uint32_t length,
uint32_t timestamp_sec, uint32_t timestamp_usec)
{
if (packet == NULL || length < 20) {
return;
}
wave_header_t wave_hdr;
esp_err_t ret = parse_wave_header(packet, &wave_hdr);
if (ret != ESP_OK) {
return;
}
char mac_str[18];
format_mac(wave_hdr.senderId, mac_str, sizeof(mac_str));
packet_count++;
// Wichtige C-ITS Message Types sofort loggen
uint8_t msgId = 0;
if (length >= 23) {
msgId = packet[15]; // BSI Message ID
}
const char* msg_type = get_message_type(msgId);
ESP_LOGI(TAG, "[PACKET #%lu] C-ITS %s Nachricht empfangen!",
(unsigned long)packet_count, msg_type);
ESP_LOGI(TAG, " MAC: %s | PDU: %lu B | Kanal: %u MHz",
mac_str,
(unsigned long)(wave_hdr.pduLength[0] | wave_hdr.pduLength[1] << 8 | wave_hdr.pduLength[2] << 16),
CITS_CHANNEL_0_MHZ);
ESP_LOGI(TAG, " Zeit: %u.%06u | Priorität: %d",
timestamp_sec, timestamp_usec, wave_hdr.priority);
if (length >= 23) {
cits_bsi_header_t bsi_hdr;
if (parse_cits_bsi(packet + 15, &bsi_hdr) == ESP_OK) {
ESP_LOGI(TAG, " BSI: MsgID=%d | Count=%d | Section=%d | Part=%d",
bsi_hdr.msgId, bsi_hdr.msgCnt, bsi_hdr.sectionNum, bsi_hdr.partId);
}
}
// Speichere für CSV
cits_message_t csv_msg;
csv_msg.timestamp_sec = timestamp_sec;
csv_msg.timestamp_usec = timestamp_usec;
csv_msg.length = length;
memcpy(csv_msg.sender_id, wave_hdr.senderId, 6);
csv_msg.message_type = msgId;
csv_msg.channel = CITS_CHANNEL_0_MHZ;
csv_msg.rssi = 0; // Wird vom WiFi Layer bereitgestellt
csv_store_cits_message(&csv_msg);
}
/**
* C-ITS Parser initialisieren
*/
esp_err_t cits_parser_init(void)
{
ESP_LOGI(TAG, "C-ITS Parser initialisiert");
return ESP_OK;
}
/**
* C-ITS Parser beenden
*/
void cits_parser_deinit(void)
{
ESP_LOGI(TAG, "C-ITS Parser beendet");
}
/**
* Hauptverarbeitungsfunktion
* Diese wird von der sniffer_task aufgerufen
*/
void cits_process_packet(sniffer_packet_info_t *packet_info)
{
if (packet_info == NULL || packet_info->payload == NULL) {
return;
}
process_cits_packet(packet_info->payload, packet_info->length,
packet_info->seconds, packet_info->microseconds);
}
+163
View File
@@ -0,0 +1,163 @@
/**
* Block 3: CSV Writer für C-ITS Sniffer
*
* Erstellt CSV-Dateien mit empfangenen C-ITS Nachrichten
* Neue Datei alle 10 Minuten
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#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";
// CSV Datei Handle
static FILE *csv_file = NULL;
static char current_filename[64];
static time_t last_write_time = 0;
#define CSV_WRITE_INTERVAL_SEC (600) // 10 Minuten
/**
* C-ITS Message Header (BSI Format)
*/
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;
/**
* CSV Header schreiben
*/
static void write_csv_header(FILE *fp)
{
fprintf(fp, "timestamp_sec,timestamp_usec,length,sender_mac,message_type,channel,rssi\n");
}
/**
* CSV Zeile schreiben
*/
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);
}
/**
* 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);
// Dateinamen mit Zeitstempel
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, "Failed to create CSV file: %s", current_filename);
return ESP_FAIL;
}
write_csv_header(csv_file);
fflush(csv_file);
ESP_LOGI(TAG, "Created new CSV file: %s", current_filename);
return ESP_OK;
}
/**
* Prüfen ob neue Datei nötig (10 Minuten Intervall)
*/
static void check_new_csv_file(void)
{
time_t now;
time(&now);
if (difftime(now, last_write_time) >= CSV_WRITE_INTERVAL_SEC) {
create_new_csv_file();
last_write_time = now;
}
}
/**
* C-ITS Nachricht speichern
*
* @param msg Nachricht Daten
*/
void csv_store_cits_message(cits_message_t *msg)
{
if (csv_file == NULL) {
create_new_csv_file();
if (csv_file == NULL) {
return;
}
}
check_new_csv_file();
write_csv_row(csv_file, msg);
fflush(csv_file);
}
/**
* CSV System initialisieren
*/
esp_err_t csv_writer_init(void)
{
// SPI Flash Filesystem mounten
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, "Failed to mount FATFS (%s)", esp_err_to_name(ret));
return ret;
}
// Erste CSV Datei erstellen
return create_new_csv_file();
}
/**
* CSV System beenden
*/
void csv_writer_deinit(void)
{
if (csv_file != NULL) {
fclose(csv_file);
csv_file = NULL;
}
}
+130
View File
@@ -0,0 +1,130 @@
/**
* Block 4: Integration und Flashen der Firmware auf ESP32-C5
*
* Ziel: C-ITS Sniffer Firmware für ESP32-C5 compilieren und flashen
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "block1_hardware_init.h"
#include "block2_cits_parser.h"
#include "block3_csv_writer.h"
static const char *TAG = "CITS_SNIFFER";
/**
* Sniffer Callback - Wird vom WiFi Promiscuous Mode aufgerufen
* Jede empfangene 802.11p Nachricht wird hier geloggt
*/
static void sniffer_callback(void *recv_buf, wifi_promiscuous_pkt_type_t type)
{
wifi_promiscuous_pkt_t *packet = (wifi_promiscuous_pkt_t *)recv_buf;
// Nur MISC Typ für 802.11p verarbeiten
if (type != WIFI_PKT_MISC) {
return;
}
if (packet->payload == NULL || packet->rx_ctrl.sig_len < 20) {
return;
}
// RSSI aus dem Paket
int8_t rssi = packet->rx_ctrl.rssi;
ESP_LOGI(TAG, "[SNIFFER] 802.11p Paket empfangen: %d Bytes | RSSI: %d dBm",
packet->rx_ctrl.sig_len, rssi);
// Verarbeite C-ITS Nachricht
cits_process_packet(packet->payload, packet->rx_ctrl.sig_len,
packet->rx_ctrl.timestamp / 1000000U,
packet->rx_ctrl.timestamp % 1000000U);
}
/**
* C-ITS Sniffer aufsetzen
*/
static esp_err_t setup_cits_sniffer(void)
{
// WiFi Promiscuous Mode starten
esp_err_t ret = start_promiscuous_mode(CITS_CHANNEL_0_MHZ);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start promiscuous mode");
return ret;
}
// C-ITS Parser initialisieren
ret = cits_parser_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize C-ITS parser");
return ret;
}
// CSV Writer initialisieren
ret = csv_writer_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize CSV writer");
return ret;
}
ESP_LOGI(TAG, "C-ITS Sniffer Setup abgeschlossen");
return ESP_OK;
}
/**
* Hauptfunktion
*/
void app_main(void)
{
ESP_LOGI(TAG, "C-ITS Sniffer für ESP32-C5 startet...");
// NVS initialisieren
esp_err_t ret = init_nvs_flash();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize NVS");
return;
}
// WiFi initialisieren
ret = initialize_wifi();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize WiFi");
return;
}
// C-ITS Sniffer aufsetzen
ret = setup_cits_sniffer();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to setup C-ITS Sniffer");
return;
}
// Hauptschleife - C-ITS Nachrichten werden im Callback verarbeitet
ESP_LOGI(TAG, "C-ITS Sniffer läuft - Empfange DSRC/C-ITS Nachrichten auf %u MHz...",
CITS_CHANNEL_0_MHZ);
ESP_LOGI(TAG, "Jede empfangene Nachricht wird geloggt und in CSV gespeichert");
// Zähler für Statistik
uint32_t packet_count = 0;
uint32_t last_report = 0;
while (1) {
vTaskDelay(pdMS_TO_TICKS(60000)); // Alle 60 Sekunden Statistik
last_report++;
ESP_LOGI(TAG, "Laufzeit: %lu Min. | Warte auf C-ITS Nachrichten...",
(unsigned long)last_report);
}
}