diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 59b181c..14fb69c 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,6 +1,5 @@ idf_component_register( SRCS "app_main.c" INCLUDE_DIRS "." - REQUIRES esp_wifi esp_timer nvs_flash fatfs - PRIV_REQUIRES wear_levelling + PRIV_REQUIRES esp_timer nvs_flash driver ieee802154 console fatfs wear_levelling esp_driver_usb_serial_jtag ) diff --git a/main/app_main.c b/main/app_main.c index f32a5c1..13ff8ed 100644 --- a/main/app_main.c +++ b/main/app_main.c @@ -1,7 +1,9 @@ /** - * 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 + * app_main.c - C-ITS Sniffer ESP32-C5 + * + * IEEE 802.15.4 Promiscuous Mode Sniffer + * Ringpuffer für JTAG-Auslesung von rohen Paketen + * Konsolencommands: status, read_pkts [n], chan , packets */ #include @@ -11,205 +13,412 @@ #include #include #include +#include +#include +#include #include "sdkconfig.h" #include "esp_err.h" #include "esp_log.h" -#include "esp_wifi.h" +#include "esp_log_write.h" #include "esp_timer.h" +#include "esp_console.h" +#include "esp_vfs.h" +#include "driver/usb_serial_jtag.h" +#include "driver/usb_serial_jtag_vfs.h" #include "nvs_flash.h" #include "esp_vfs_fat.h" -#include "esp_vfs.h" #include "wear_levelling.h" +#include "esp_ieee802154.h" +#include "esp_ieee802154_types.h" +#include "driver/gpio.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -// ==================== Typedefs ==================== - -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; - -// ==================== Constants ==================== - static const char *TAG = "CITS_SNIFFER"; -#define CITS_CHANNEL_0_MHZ 5900 -#define CSV_WRITE_INTERVAL_SEC (600) +static const char *TAG_CMD = "CMD"; +#define LED_PIN GPIO_NUM_28 +#define SNIFFER_CHANNEL 11 +#define CSV_INTERVAL_SEC 3600 -// ==================== CSV State ==================== +// Ringpuffer für rohe Pakete (für JTAG-Direct-Access) +#define PKT_RING_BUF_SIZE 128 +#define PKT_MAX_SIZE 160 +typedef struct { + uint8_t data[PKT_MAX_SIZE]; + uint16_t len; + int8_t rssi; + uint8_t lqi; + uint32_t ts_sec; + uint32_t ts_usec; +} raw_pkt_t; + +static raw_pkt_t pkt_ring[PKT_RING_BUF_SIZE]; +static volatile uint32_t pkt_ring_write = 0; +static volatile uint32_t pkt_ring_total = 0; +static bool pkt_ring_full = false; static FILE *csv_file = NULL; -static char current_filename[64]; -static time_t last_write_time = 0; +static char csv_filename[64]; +static time_t last_csv_time = 0; +static uint32_t packet_count = 0; +static wl_handle_t wl_handle = WL_INVALID_HANDLE; + +static volatile bool sniffer_active = false; +static volatile int8_t last_rssi = -127; + +// === CSV / Storage === static void write_csv_header(FILE *fp) { - fprintf(fp, "timestamp_sec,timestamp_usec,length,sender_mac,message_type,channel,rssi\n"); + fprintf(fp, "timestamp_sec,timestamp_usec,length,sender_mac,frame_type,channel,rssi,lqi\n"); } -static void write_csv_row(FILE *fp, cits_message_t *msg) +static void write_csv_row(FILE *fp, uint32_t ts_sec, uint32_t ts_usec, uint16_t len, + uint8_t *src_mac, uint8_t ftype, uint8_t ch, int16_t rssi, uint8_t lqi) { - 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); + char mac_str[24]; + snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + src_mac[7], src_mac[6], src_mac[5], src_mac[4], src_mac[3], src_mac[2], src_mac[1], src_mac[0]); + fprintf(fp, "%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%s,%u,%u,%d,%u\n", + ts_sec, ts_usec, len, mac_str, ftype, ch, rssi, lqi); } -static esp_err_t create_new_csv_file(void) +static esp_err_t create_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", + snprintf(csv_filename, sizeof(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 (csv_file != NULL) fclose(csv_file); - csv_file = fopen(current_filename, "w"); + csv_file = fopen(csv_filename, "w"); if (csv_file == NULL) { - ESP_LOGE(TAG, "CSV file erstellen fehlgeschlagen: %s", current_filename); + ESP_LOGE(TAG, "CSV create failed: %s", csv_filename); return ESP_FAIL; } write_csv_header(csv_file); fflush(csv_file); - ESP_LOGI(TAG, "Neue CSV-Datei: %s", current_filename); + ESP_LOGI(TAG, "CSV: %s", csv_filename); return ESP_OK; } -static void csv_store_cits_message(cits_message_t *msg) +static void store_pkt_raw(const uint8_t *data, uint16_t len, int8_t rssi, uint8_t lqi, + uint32_t ts_sec, uint32_t ts_usec) { - if (csv_file != NULL) { - write_csv_row(csv_file, msg); + uint32_t idx = pkt_ring_write; + pkt_ring[idx].len = (len > PKT_MAX_SIZE) ? PKT_MAX_SIZE : len; + memcpy(pkt_ring[idx].data, data, pkt_ring[idx].len); + pkt_ring[idx].rssi = rssi; + pkt_ring[idx].lqi = lqi; + pkt_ring[idx].ts_sec = ts_sec; + pkt_ring[idx].ts_usec = ts_usec; + pkt_ring_write = (pkt_ring_write + 1) % PKT_RING_BUF_SIZE; + pkt_ring_total++; + if (pkt_ring_total > PKT_RING_BUF_SIZE) pkt_ring_full = true; +} + +// === 802.15.4 Sniffer Callback === + +static void sniffer_rx_cb(uint8_t *data, esp_ieee802154_frame_info_t *info) +{ + if (!data || !info) return; + if (info->rssi < -100) return; + if (!sniffer_active) return; + + packet_count++; + last_rssi = info->rssi; + + uint16_t frame_ctrl = data[0] | (data[1] << 8); + uint8_t ftype = frame_ctrl & 0x07; + uint8_t addr_mode_src = (frame_ctrl >> 12) & 0x03; + + uint8_t src_mac[8] = {0}; + if (addr_mode_src == 1) { + src_mac[7] = data[2]; src_mac[6] = data[3]; + } else if (addr_mode_src == 2) { + memcpy(src_mac, &data[2], 8); + } + + uint32_t ts_sec = (uint32_t)(info->timestamp / 1000000); + uint32_t ts_usec = (uint32_t)(info->timestamp / 1000) % 1000000; + + // Raw-Paket im Ringpuffer speichern + store_pkt_raw(data, (uint16_t)(info->timestamp >> 8), info->rssi, info->lqi, ts_sec, ts_usec); + + if (csv_file) { + write_csv_row(csv_file, ts_sec, ts_usec, 0, src_mac, ftype, + info->channel, info->rssi, info->lqi); fflush(csv_file); } + + if (packet_count % 5 == 0) { + char mac_str[32]; + snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + src_mac[7], src_mac[6], src_mac[5], src_mac[4], src_mac[3], src_mac[2], + src_mac[1], src_mac[0]); + ESP_LOGI(TAG, "[Pkt %lu] RSSI=%d | LQI=%u | Ch=%u | Src=%s", + (unsigned long)packet_count, info->rssi, info->lqi, + info->channel, mac_str); + } } -// ==================== Promiscuous Callback ==================== - -static void sniffer_callback(void *buf, wifi_promiscuous_pkt_type_t type) +static esp_err_t init_sniffer(void) { - 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 > 65535) ? 65535 : pkt->rx_ctrl.sig_len, - .channel = (uint8_t)(CITS_CHANNEL_0_MHZ % 256), - .rssi = pkt->rx_ctrl.rssi, - }; - csv_store_cits_message(&msg); -} - -// ==================== Flash Storage ==================== - -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); + ESP_LOGI(TAG, "Starte 802.15.4 Stack..."); + esp_err_t ret = esp_ieee802154_enable(); if (ret != ESP_OK) { - ESP_LOGE(TAG, "FATFS Mount fehlgeschlagen: %s", esp_err_to_name(ret)); + ESP_LOGE(TAG, "esp_ieee802154_enable failed: %s", esp_err_to_name(ret)); return ret; } - ESP_LOGI(TAG, "FATFS storage gemountet"); + ESP_LOGI(TAG, "802.15.4 stack enabled"); + + ret = esp_ieee802154_set_channel(SNIFFER_CHANNEL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "channel set failed: %s", esp_err_to_name(ret)); + return ret; + } + ESP_LOGI(TAG, "Channel: %d", SNIFFER_CHANNEL); + + ret = esp_ieee802154_set_promiscuous(true); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "promiscuous failed: %s", esp_err_to_name(ret)); + return ret; + } + ESP_LOGI(TAG, "Promiscuous mode enabled"); + + esp_ieee802154_event_cb_list_t cb_list = { .rx_done_cb = sniffer_rx_cb }; + ret = esp_ieee802154_event_callback_list_register(cb_list); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "callback register failed: %s", esp_err_to_name(ret)); + return ret; + } + ESP_LOGI(TAG, "Rx callback registered"); + + sniffer_active = true; return ESP_OK; } -// ==================== WiFi Init (inline Block 1) ==================== - -static esp_err_t initialize_wifi(void) +static esp_err_t init_storage(void) { + static wl_handle_t wl; + const esp_vfs_fat_mount_config_t mcfg = { + .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", "storage", &mcfg, &wl); + if (ret == ESP_OK) { + wl_handle = wl; + ESP_LOGI(TAG, "FATFS storage mounted"); + } + return ret; +} + +// === Konsolencommands === + +static int cmd_status(int argc, char **argv) +{ + (void)argc; (void)argv; + ESP_LOGI(TAG_CMD, "== Status =="); + ESP_LOGI(TAG_CMD, "Packets: %lu", (unsigned long)packet_count); + ESP_LOGI(TAG_CMD, "CSV: %s", csv_filename); + ESP_LOGI(TAG_CMD, "Channel: %d", SNIFFER_CHANNEL); + ESP_LOGI(TAG_CMD, "Ring buffer: %lu/%d total, write_idx=%lu", + (unsigned long)pkt_ring_total, PKT_RING_BUF_SIZE, + (unsigned long)pkt_ring_write); + ESP_LOGI(TAG_CMD, "Last RSSI: %d", last_rssi); + ESP_LOGI(TAG_CMD, "Sniffer active: %s", sniffer_active ? "YES" : "NO"); + return 0; +} + +static int cmd_packets(int argc, char **argv) +{ + if (argc < 2) { ESP_LOGW(TAG_CMD, "Usage: packets "); return 1; } + int count = atoi(argv[1]); + ESP_LOGI(TAG_CMD, "Warte auf %d Pakete...", count); + uint32_t start = packet_count; + while (packet_count - start < count) vTaskDelay(pdMS_TO_TICKS(1000)); + ESP_LOGI(TAG_CMD, "%d Pakete empfangen!", count); + return 0; +} + +static int cmd_read_pkts(int argc, char **argv) +{ + int count = 10; + if (argc > 0) { + count = atoi(argv[0]); + if (count < 1) count = 1; + if (count > PKT_RING_BUF_SIZE) count = PKT_RING_BUF_SIZE; + } + + uint32_t total = pkt_ring_total; + if (total == 0) { + ESP_LOGI(TAG_CMD, "Keine Pakete empfangen."); + return 0; + } + + ESP_LOGI(TAG_CMD, "=== Ring Buffer Read (%d von %lu) ===", count, (unsigned long)total); + uint32_t idx = (pkt_ring_write == 0) ? PKT_RING_BUF_SIZE - 1 : pkt_ring_write - 1; + + for (int i = 0; i < count; i++) { + uint32_t read_total = pkt_ring_total; + if (pkt_ring_full && (read_total - (pkt_ring_write == 0 ? PKT_RING_BUF_SIZE : pkt_ring_write)) <= i) { + break; + } + if (!pkt_ring_full && i >= pkt_ring_total) break; + + raw_pkt_t *pkt = &pkt_ring[idx]; + char hex_str[72]; + hex_str[0] = 0; + for (int j = 0; j < pkt->len && j < 36; j++) { + snprintf(hex_str + strlen(hex_str), sizeof(hex_str) - strlen(hex_str), + "%02x ", pkt->data[j]); + } + ESP_LOGI(TAG_CMD, " [%d] len=%u RSSI=%d LQI=%u | %s", + i, pkt->len, pkt->rssi, pkt->lqi, hex_str); + idx = (idx == 0) ? PKT_RING_BUF_SIZE - 1 : idx - 1; + } + return 0; +} + +static int cmd_chan(int argc, char **argv) +{ + if (argc < 1) { + ESP_LOGW(TAG_CMD, "Usage: chan "); + return 1; + } + int ch = atoi(argv[0]); + if (ch < 11 || ch > 26) { + ESP_LOGW(TAG_CMD, "Channel muss 11-26 sein"); + return 1; + } + esp_err_t ret = esp_ieee802154_set_channel(ch); + if (ret == ESP_OK) { + ESP_LOGI(TAG_CMD, "Channel auf %d geändert", ch); + } else { + ESP_LOGE(TAG_CMD, "Channel change failed: %s", esp_err_to_name(ret)); + } + return 0; +} + +static void register_console_commands(void) +{ + esp_console_cmd_t cmd_status_def = { .command = "status", .help = "Show sniffer status", .func = cmd_status }; + esp_console_cmd_t cmd_packets_def = { .command = "packets", .help = "Wait for N packets", .func = cmd_packets }; + esp_console_cmd_t cmd_read_def = { .command = "read_pkts", .help = "Read raw packets from ring buffer", .func = cmd_read_pkts }; + esp_console_cmd_t cmd_ch_def = { .command = "chan", .help = "Change 802.15.4 channel", .func = cmd_chan }; + esp_console_register_help_command(); + esp_console_cmd_register(&cmd_status_def); + esp_console_cmd_register(&cmd_packets_def); + esp_console_cmd_register(&cmd_read_def); + esp_console_cmd_register(&cmd_ch_def); + ESP_LOGI(TAG, "Console commands registriert"); +} + +// === JTAG vprintf-Wrapper === +// Sendet Logs über JTAG-Verbindung zum Host +static volatile int jtag_connected = 0; +static TickType_t jtag_last_connect_tick = 0; + +static int jtag_vprintf(const char *fmt, va_list args) +{ + char buf[256]; + int len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len > 0 && len < 256) { + // Versuche non-blocking JTAG write + TickType_t now = xTaskGetTickCount(); + if (now - jtag_last_connect_tick < pdMS_TO_TICKS(1000)) { + // JTAG write with short timeout (100ms) + int wrote = usb_serial_jtag_write_bytes(buf, len, pdMS_TO_TICKS(100)); + if (wrote <= 0) { + jtag_connected = 0; + jtag_last_connect_tick = now; + } + } + } + return len; +} + +void app_main(void) +{ + ESP_LOGI(TAG, "== C-ITS SNIFFER ESP32-C5 =="); + ESP_LOGI(TAG, "Build: %s %s", __DATE__, __TIME__); + + // LED initialisieren + gpio_reset_pin(LED_PIN); + gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT); + gpio_set_level(LED_PIN, 0); + + // NVS initialisieren 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); + + // === USB-Serial/JTAG initialisieren === + ESP_LOGI(TAG, "Init USB-Serial/JTAG..."); + usb_serial_jtag_driver_config_t jtag_config = {0}; + ret = usb_serial_jtag_driver_install(&jtag_config); if (ret != ESP_OK) { - ESP_LOGE(TAG, "NVS Flash init fehlgeschlagen: %s", esp_err_to_name(ret)); - return ret; + ESP_LOGE(TAG, "usb_serial_jtag_driver_install failed: %s", esp_err_to_name(ret)); + return; + } + ret = usb_serial_jtag_vfs_register(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "usb_serial_jtag_vfs_register failed: %s", esp_err_to_name(ret)); + return; } - ESP_LOGI(TAG, "NVS Flash OK"); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); - ESP_ERROR_CHECK(esp_wifi_start()); + // Non-blocking JTAG write aktivieren + usb_serial_jtag_vfs_use_nonblocking(); + ESP_LOGI(TAG, "Non-blocking JTAG aktiv"); - ESP_LOGI(TAG, "WiFi initialisiert"); - return ESP_OK; -} + // Log-Ausgabe auf JTAG umleiten + esp_log_set_vprintf((vprintf_like_t)jtag_vprintf); -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)); + ESP_LOGI(TAG, "USB-Serial/JTAG initialisiert"); - 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)); + // Storage initialisieren + ESP_LOGI(TAG, "Init storage..."); + ESP_ERROR_CHECK(init_storage()); - ESP_LOGI(TAG, "Promiscuous Mode aktiv auf %u MHz", CITS_CHANNEL_0_MHZ); - return ESP_OK; -} + // Console commands registrieren + register_console_commands(); -// ==================== Main ==================== + // 802.15.4 Sniffer starten + ret = init_sniffer(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "802.15.4 init fehlgeschlagen: %s", esp_err_to_name(ret)); + ESP_LOGI(TAG, "Sniffer deaktiviert - nur Console verfügbar"); + } -void app_main(void) -{ - ESP_LOGI(TAG, "=== C-ITS SNIFFER FÜR ESP32-C5 ==="); - ESP_LOGI(TAG, "Starte..."); + // CSV-Datei erstellen + if (ret == ESP_OK) { + ESP_ERROR_CHECK(create_csv_file()); + last_csv_time = time(NULL); + } - // WiFi Setup - ESP_ERROR_CHECK(initialize_wifi()); - ESP_ERROR_CHECK(init_promiscuous()); + ESP_LOGI(TAG, "== SNIFFER LÄUFT =="); + ESP_LOGI(TAG, "Channel %d | Warte auf Pakete...", SNIFFER_CHANNEL); + ESP_LOGI(TAG, "Console: sende 'status', 'read_pkts [n]', 'chan ', 'packets '"); - // Flash Storage - 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; + // === Main Loop === + uint32_t tick = 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; + tick++; + gpio_set_level(LED_PIN, (tick % 2) ? 1 : 0); + if (tick % 100 == 0) { + ESP_LOGI(TAG, "tick=%lu | pkts=%lu | LED=%d", + (unsigned long)tick, (unsigned long)packet_count, (int)(tick%2)); + } + vTaskDelay(pdMS_TO_TICKS(1000)); + + if (sniffer_active && difftime(time(NULL), last_csv_time) >= CSV_INTERVAL_SEC) { + create_csv_file(); + last_csv_time = time(NULL); } - ESP_LOGI(TAG, "Laufzeit: %lu Min. | Warte auf C-ITS Pakete...", (unsigned long)last_report); } } diff --git a/main/block4_integration.c b/main/block4_integration.c index 62d90bf..2af20ee 100644 --- a/main/block4_integration.c +++ b/main/block4_integration.c @@ -18,6 +18,11 @@ #include "nvs_flash.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "esp_console.h" +#include "esp_vfs_dev_usb_serial_jtag.h" +#include "esp_log.h" +#include "arg_table3/arg_table3.h" +#include "driver/usb_serial_jtag.h" #include "block1_hardware_init.h" #include "block2_cits_parser.h" @@ -25,9 +30,51 @@ static const char *TAG = "CITS_SNIFFER"; +// Globale Puffer für JTAG-Direct-Auslesung +#define MAX_STORED_PACKETS 64 +#define MAX_PACKET_SIZE 2000 +typedef struct { + wifi_promiscuous_pkt_t pkt; + uint32_t timestamp; +} stored_packet_t; + +static stored_packet_t stored_packets[MAX_STORED_PACKETS]; +static volatile uint32_t packet_write_idx = 0; +static volatile uint32_t packet_count_total = 0; +static bool packets_available = false; + +/** + * Hilfsfunktion: HEX-Daten auf Console (JTAG) ausgeben + */ +static void hex_dump(const char *prefix, const uint8_t *data, int len) +{ + if (!prefix || !data || len <= 0) return; + + // Print header with prefix + char line[128]; + int offset = 0; + + ESP_LOGI(TAG, "%s [Len=%d]:", prefix, len); + + // First line: raw hex dump + for (int i = 0; i < len && i < 64; i++) { + if (i % 16 == 0) { + if (i > 0) { + ESP_LOGI(TAG, " %s", line); + } + offset = 0; + snprintf(line, sizeof(line), " %04x: ", i); + } + offset += snprintf(line + strlen(line), sizeof(line) - strlen(line), "%02x ", data[i]); + } + if (len > 0) { + ESP_LOGI(TAG, "%s", line); + } +} + /** * Sniffer Callback - Wird vom WiFi Promiscuous Mode aufgerufen - * Jede empfangene 802.11p Nachricht wird hier geloggt + * Jede empfangene 802.11p Nachricht wird hier geloggt UND gespeichert für JTAG-Auslesung */ static void sniffer_callback(void *recv_buf, wifi_promiscuous_pkt_type_t type) { @@ -44,9 +91,61 @@ static void sniffer_callback(void *recv_buf, wifi_promiscuous_pkt_type_t type) // RSSI aus dem Paket int8_t rssi = packet->rx_ctrl.rssi; + uint32_t ts = packet->rx_ctrl.timestamp; + uint16_t pkt_len = packet->rx_ctrl.sig_len; - ESP_LOGI(TAG, "[SNIFFER] 802.11p Paket empfangen: %d Bytes | RSSI: %d dBm", - packet->rx_ctrl.sig_len, rssi); + // Detailiertes Paket-Logging auf JTAG Console + ESP_LOGI(TAG, "[SNIFFER] === Paket #%lu ===", (unsigned long)packet_count_total); + ESP_LOGI(TAG, " Länge: %d Bytes | RSSI: %d dBm | Typ: %d | Chan: %d | Rate: %d", + pkt_len, rssi, type, + packet->rx_ctrl.channel, + packet->rx_ctrl.rate); + + // MAC Header Bytes ausgeben + ESP_LOGI(TAG, " MAC Header: %02x %02x %02x %02x %02x %02x %02x %02x", + packet->payload[0], packet->payload[1], + packet->payload[2], packet->payload[3], + packet->payload[4], packet->payload[5], + packet->payload[6], packet->payload[7]); + + // WAVE Header parsen + if (pkt_len >= 15) { + uint32_t pdu_len = packet->payload[0] | (packet->payload[1] << 8) | (packet->payload[2] << 16); + uint8_t sender_mac[6]; + memcpy(sender_mac, packet->payload + 3, 6); + uint32_t ts_field; + memcpy(&ts_field, packet->payload + 9, 4); + uint8_t priority = packet->payload[13]; + uint8_t pdu_type = packet->payload[14]; + + ESP_LOGI(TAG, " WAVE PDU Length: %u | Sender: %02X:%02X:%02X:%02X:%02X:%02X", + pdu_len, sender_mac[0], sender_mac[1], sender_mac[2], + sender_mac[3], sender_mac[4], sender_mac[5]); + ESP_LOGI(TAG, " WAVE Timestamp: %u | Priority: %u | PDU Type: %02X", + ts_field, priority, pdu_type); + + // C-ITS Payload wenn vorhanden + if (pkt_len > 15) { + ESP_LOGI(TAG, " C-ITS Payload (%d Bytes):", pkt_len - 15); + for (int i = 15; i < pkt_len && i < 47; i++) { + if ((i - 15) % 16 == 0) { + char hex_line[128]; + snprintf(hex_line, sizeof(hex_line), " %04x: ", i); + ESP_LOGI(TAG, "%s", hex_line); + } + ESP_LOG_BUFFER_HEX_LEVEL(TAG, packet->payload + i, 1, ESP_LOG_INFO); + } + } + } + + // Paket zum JTAG-Direct-Zugang speichern + if (packet_count_total < MAX_STORED_PACKETS) { + memcpy(&stored_packets[packet_write_idx].pkt, packet, sizeof(wifi_promiscuous_pkt_t)); + stored_packets[packet_write_idx].timestamp = ts; + packet_write_idx = (packet_write_idx + 1) % MAX_STORED_PACKETS; + } + packet_count_total++; + packets_available = true; // Verarbeite C-ITS Nachricht cits_process_packet(packet->payload, packet->rx_ctrl.sig_len, @@ -84,12 +183,131 @@ static esp_err_t setup_cits_sniffer(void) return ESP_OK; } +/** + * JTAG Direct Read - Gepackete Pakete auf Console (JTAG) ausgeben + */ +static int cmd_jtag_read(int argc, char **argv) +{ + int count = 10; // Default 10 Pakete + + if (argc > 0) { + count = atoi(argv[0]); + if (count < 1) count = 1; + if (count > MAX_STORED_PACKETS) count = MAX_STORED_PACKETS; + } + + if (!packets_available) { + ESP_LOGI(TAG, "Keine Pakete empfangen. Warte auf C-ITS Nachrichten..."); + return 0; + } + + ESP_LOGI(TAG, "[JTAG-READ] Empfangene Pakete (%d vom %lu insgesamt):", count, (unsigned long)packet_count_total); + + uint32_t idx = (packet_write_idx - 1 + MAX_STORED_PACKETS) % MAX_STORED_PACKETS; + int printed = 0; + + for (int i = 0; i < count; i++) { + if (printed >= packet_count_total) break; + + stored_packet_t *p = &stored_packets[idx]; + if (p->pkt.payload == NULL) { + idx = (idx - 1 + MAX_STORED_PACKETS) % MAX_STORED_PACKETS; + continue; + } + + uint16_t pkt_len = p->pkt.rx_ctrl.sig_len; + int8_t rssi = p->pkt.rx_ctrl.rssi; + + char mac_str[20]; + if (pkt_len >= 12) { + snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", + p->pkt.payload[0], p->pkt.payload[1], + p->pkt.payload[2], p->pkt.payload[3], + p->pkt.payload[4], p->pkt.payload[5]); + } else { + strcpy(mac_str, "N/A"); + } + + ESP_LOGI(TAG, "[Paket %d] L=%d RSSI=%d MAC=%s", + i + 1, pkt_len, rssi, mac_str); + ESP_LOGI(TAG, "[Paket %d] Raw: ", i + 1); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, p->pkt.payload, pkt_len < 64 ? pkt_len : 64, ESP_LOG_INFO); + + printed++; + idx = (idx - 1 + MAX_STORED_PACKETS) % MAX_STORED_PACKETS; + } + + return 0; +} + +/** + * Sniffer Status - Aktuelle Statistik ausgeben + */ +static int cmd_sniffer_status(int argc, char **argv) +{ + ESP_LOGI(TAG, "[Status] Gesamt empfangene Pakete: %lu", + (unsigned long)packet_count_total); + ESP_LOGI(TAG, "[Status] Gespeichert: %d / %d", + (packet_write_idx < MAX_STORED_PACKETS) ? + (MAX_STORED_PACKETS - packet_write_idx) : packet_write_idx, + MAX_STORED_PACKETS); + ESP_LOGI(TAG, "[Status] Pakete verfügbar: %s", packets_available ? "JA" : "NEIN"); + return 0; +} + +/** + * Kanal wechseln + */ +static int cmd_change_channel(int argc, char **argv) +{ + if (argc < 1) { + ESP_LOGI(TAG, "Nutzung: change_channel "); + return 1; + } + + uint32_t freq = atoi(argv[0]); + if (freq < 5800 || freq > 5900) { + ESP_LOGE(TAG, "Frequenz muss zwischen 5800 und 5900 MHz liegen"); + return 1; + } + + phy_11p_set(1, 0); + phy_change_channel(freq, 1, 0, 0); + ESP_LOGI(TAG, "Kanal auf %u MHz geändert", freq); + return 0; +} + +/** + * Sniffer starten (neu) + */ +static int cmd_start_sniffer(int argc, char **argv) +{ + ESP_LOGI(TAG, "Sniffer wird neu gestartet..."); + packets_available = false; + packet_write_idx = 0; + packet_count_total = 0; + + esp_err_t ret = start_promiscuous_mode(CITS_CHANNEL_0_MHZ); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Sniffer Start fehlgeschlagen"); + return 1; + } + + ESP_LOGI(TAG, "Sniffer läuft auf %u MHz", CITS_CHANNEL_0_MHZ); + return 0; +} + /** * Hauptfunktion */ void app_main(void) { - ESP_LOGI(TAG, "C-ITS Sniffer für ESP32-C5 startet..."); + // USB-Serial/JTAG Console initialisieren + usb_serial_jtag_driver_install(0, 0, 0); + esp_vfs_dev_usb_serial_jtag_set_console(); + + ESP_LOGI(TAG, "=== C-ITS Sniffer für ESP32-C5 startet ==="); + ESP_LOGI(TAG, "Console: USB-Serial/JTAG (115200 bps)"); // NVS initialisieren esp_err_t ret = init_nvs_flash(); @@ -112,10 +330,46 @@ void app_main(void) return; } + // Console Commands registrieren + const esp_console_cmd_t cmd_jtag_read_cmd = { + .command = "jtag_read", + .help = "Read captured packets via JTAG (optional count)", + .hint = NULL, + .func = &cmd_jtag_read, + }; + esp_console_cmd_register(&cmd_jtag_read_cmd); + + const esp_console_cmd_t cmd_status_cmd = { + .command = "sniffer_status", + .help = "Show sniffer statistics", + .hint = NULL, + .func = &cmd_sniffer_status, + }; + esp_console_cmd_register(&cmd_status_cmd); + + const esp_console_cmd_t cmd_chan_cmd = { + .command = "chan", + .help = "Change C-ITS channel (5800-5900 MHz)", + .hint = NULL, + .func = &cmd_change_channel, + }; + esp_console_cmd_register(&cmd_chan_cmd); + + const esp_console_cmd_t cmd_start_cmd = { + .command = "start_sniffer", + .help = "Restart sniffer", + .hint = NULL, + .func = &cmd_start_sniffer, + }; + esp_console_cmd_register(&cmd_start_cmd); + + register_cits_commands(); + // 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"); + ESP_LOGI(TAG, "Verwende USB-Serial/JTAG für Console/Ausgabe"); + ESP_LOGI(TAG, "Commands: jtag_read [count], sniffer_status, chan , start_sniffer"); // Zähler für Statistik uint32_t packet_count = 0; diff --git a/partitions_8M.csv b/partitions_8M.csv index 45e3c43..fff6e06 100644 --- a/partitions_8M.csv +++ b/partitions_8M.csv @@ -2,7 +2,5 @@ nvs, data, nvs, , 0x4000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, -factory, app, factory, , 0x3E0000, -ota_0, app, ota_0, , 0x3E0000, -ota_1, app, ota_1, , 0x3E0000, -storage, data, fat, , 0x200000, +factory, app, factory, , 0x3C0000, +storage, data, fat, , 0x400000, diff --git a/sdkconfig b/sdkconfig index fd81b7c..48ef3c7 100644 --- a/sdkconfig +++ b/sdkconfig @@ -645,14 +645,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set CONFIG_ESPTOOLPY_FLASHFREQ="80m" # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" # CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set CONFIG_ESPTOOLPY_BEFORE_RESET=y # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set @@ -666,13 +666,13 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # # Partition Table # -CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set # CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set # CONFIG_PARTITION_TABLE_TWO_OTA is not set # CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_8M.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_8M.csv" CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table @@ -723,6 +723,7 @@ CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y # # CONFIG_APPTRACE_DEST_JTAG is not set CONFIG_APPTRACE_DEST_NONE=y +# CONFIG_APPTRACE_DEST_UART0 is not set # CONFIG_APPTRACE_DEST_UART1 is not set # CONFIG_APPTRACE_DEST_UART2 is not set CONFIG_APPTRACE_DEST_UART_NONE=y @@ -1358,17 +1359,14 @@ CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y # CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 -CONFIG_ESP_CONSOLE_UART_DEFAULT=y -# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set +# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y # CONFIG_ESP_CONSOLE_UART_CUSTOM is not set # CONFIG_ESP_CONSOLE_NONE is not set -# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set -CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y -CONFIG_ESP_CONSOLE_UART=y -CONFIG_ESP_CONSOLE_UART_NUM=0 -CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 -CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_CONSOLE_UART_NUM=-1 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=3 CONFIG_ESP_INT_WDT=y CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 CONFIG_ESP_TASK_WDT_EN=y @@ -2370,13 +2368,11 @@ CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 CONFIG_MAIN_TASK_STACK_SIZE=3584 -CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_DEFAULT is not set # CONFIG_CONSOLE_UART_CUSTOM is not set # CONFIG_CONSOLE_UART_NONE is not set # CONFIG_ESP_CONSOLE_UART_NONE is not set -CONFIG_CONSOLE_UART=y -CONFIG_CONSOLE_UART_NUM=0 -CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_CONSOLE_UART_NUM=-1 CONFIG_INT_WDT=y CONFIG_INT_WDT_TIMEOUT_MS=300 CONFIG_TASK_WDT=y