218 lines
6.7 KiB
C
218 lines
6.7 KiB
C
/**
|
|
* 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 <inttypes.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, "%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%s,%" PRIu8 ",%" PRIu8 ",%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=%" PRIu16 " | TS=%" PRIu32 ".%06" PRIu32 "",
|
|
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 = (uint8_t)(CITS_CHANNEL_0_MHZ % 256),
|
|
.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);
|
|
}
|
|
}
|