build: fix USB-JTAG API, PRI macros, linker symbols for ESP-IDF 5.5
This commit is contained in:
+1268
-1152
File diff suppressed because it is too large
Load Diff
+33
-9
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -163,5 +163,4 @@ $<TARGET_FILE:idf::spiffs>
|
|||||||
$<TARGET_FILE:idf::wifi_provisioning>
|
$<TARGET_FILE:idf::wifi_provisioning>
|
||||||
$<TARGET_FILE:__idf_json>
|
$<TARGET_FILE:__idf_json>
|
||||||
$<TARGET_FILE:idf::main>
|
$<TARGET_FILE:idf::main>
|
||||||
$<TARGET_FILE:__idf_ieee802154>
|
|
||||||
$<TARGET_FILE:__idf_fatfs>
|
$<TARGET_FILE:__idf_fatfs>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "1.2",
|
"version": "1.2",
|
||||||
"project_name": "cits-sniffer",
|
"project_name": "cits-sniffer",
|
||||||
"project_version": "b657a2d-dirty",
|
"project_version": "76d5016-dirty",
|
||||||
"project_path": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer",
|
"project_path": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer",
|
||||||
"idf_path": "/home/claw/esp/esp-idf",
|
"idf_path": "/home/claw/esp/esp-idf",
|
||||||
"build_dir": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/build",
|
"build_dir": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/build",
|
||||||
@@ -1221,11 +1221,11 @@
|
|||||||
"type": "LIBRARY",
|
"type": "LIBRARY",
|
||||||
"lib": "__idf_main",
|
"lib": "__idf_main",
|
||||||
"reqs": [],
|
"reqs": [],
|
||||||
"priv_reqs": [ "esp_timer", "nvs_flash", "driver", "ieee802154", "console", "fatfs", "wear_levelling", "esp_driver_usb_serial_jtag" ],
|
"priv_reqs": [ "esp_timer", "nvs_flash", "driver", "esp_wifi", "fatfs", "console" ],
|
||||||
"managed_reqs": [],
|
"managed_reqs": [],
|
||||||
"managed_priv_reqs": [],
|
"managed_priv_reqs": [],
|
||||||
"file": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/build/esp-idf/main/libmain.a",
|
"file": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/build/esp-idf/main/libmain.a",
|
||||||
"sources": [ "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main/app_main.c" ],
|
"sources": [ "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main/block1_hardware_init.c", "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main/block2_cits_parser.c", "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main/block3_csv_writer.c", "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main/block4_integration.c" ],
|
||||||
"include_dirs": [ "." ]
|
"include_dirs": [ "." ]
|
||||||
},
|
},
|
||||||
"mbedtls": {
|
"mbedtls": {
|
||||||
@@ -2893,7 +2893,7 @@
|
|||||||
"dir": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main",
|
"dir": "/home/claw/.openclaw/agents/wisp/workspace/cits-sniffer/main",
|
||||||
"lib": "__idf_main",
|
"lib": "__idf_main",
|
||||||
"reqs": [],
|
"reqs": [],
|
||||||
"priv_reqs": [ "esp_timer", "nvs_flash", "driver", "ieee802154", "console", "fatfs", "wear_levelling", "esp_driver_usb_serial_jtag" ],
|
"priv_reqs": [ "esp_timer", "nvs_flash", "driver", "esp_wifi", "fatfs", "console" ],
|
||||||
"managed_reqs": [],
|
"managed_reqs": [],
|
||||||
"managed_priv_reqs": [],
|
"managed_priv_reqs": [],
|
||||||
"include_dirs": [ "." ]
|
"include_dirs": [ "." ]
|
||||||
|
|||||||
+5
-2
@@ -1,5 +1,8 @@
|
|||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "app_main.c"
|
SRCS "block1_hardware_init.c"
|
||||||
|
"block2_cits_parser.c"
|
||||||
|
"block3_csv_writer.c"
|
||||||
|
"block4_integration.c"
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
PRIV_REQUIRES esp_timer nvs_flash driver ieee802154 console fatfs wear_levelling esp_driver_usb_serial_jtag
|
PRIV_REQUIRES esp_timer nvs_flash driver esp_wifi fatfs console
|
||||||
)
|
)
|
||||||
|
|||||||
-424
@@ -1,424 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 <ch>, packets <n>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "sdkconfig.h"
|
|
||||||
#include "esp_err.h"
|
|
||||||
#include "esp_log.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 "wear_levelling.h"
|
|
||||||
#include "esp_ieee802154.h"
|
|
||||||
#include "esp_ieee802154_types.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
|
|
||||||
static const char *TAG = "CITS_SNIFFER";
|
|
||||||
static const char *TAG_CMD = "CMD";
|
|
||||||
#define LED_PIN GPIO_NUM_28
|
|
||||||
#define SNIFFER_CHANNEL 11
|
|
||||||
#define CSV_INTERVAL_SEC 3600
|
|
||||||
|
|
||||||
// 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 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,frame_type,channel,rssi,lqi\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
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[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_csv_file(void)
|
|
||||||
{
|
|
||||||
time_t now;
|
|
||||||
struct tm *tm_info;
|
|
||||||
time(&now);
|
|
||||||
tm_info = localtime(&now);
|
|
||||||
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(csv_filename, "w");
|
|
||||||
if (csv_file == NULL) {
|
|
||||||
ESP_LOGE(TAG, "CSV create failed: %s", csv_filename);
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
write_csv_header(csv_file);
|
|
||||||
fflush(csv_file);
|
|
||||||
ESP_LOGI(TAG, "CSV: %s", csv_filename);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t init_sniffer(void)
|
|
||||||
{
|
|
||||||
ESP_LOGI(TAG, "Starte 802.15.4 Stack...");
|
|
||||||
esp_err_t ret = esp_ieee802154_enable();
|
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "esp_ieee802154_enable failed: %s", esp_err_to_name(ret));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <count>"); 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 <channel>");
|
|
||||||
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, "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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-blocking JTAG write aktivieren
|
|
||||||
usb_serial_jtag_vfs_use_nonblocking();
|
|
||||||
ESP_LOGI(TAG, "Non-blocking JTAG aktiv");
|
|
||||||
|
|
||||||
// Log-Ausgabe auf JTAG umleiten
|
|
||||||
esp_log_set_vprintf((vprintf_like_t)jtag_vprintf);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "USB-Serial/JTAG initialisiert");
|
|
||||||
|
|
||||||
// Storage initialisieren
|
|
||||||
ESP_LOGI(TAG, "Init storage...");
|
|
||||||
ESP_ERROR_CHECK(init_storage());
|
|
||||||
|
|
||||||
// Console commands registrieren
|
|
||||||
register_console_commands();
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSV-Datei erstellen
|
|
||||||
if (ret == ESP_OK) {
|
|
||||||
ESP_ERROR_CHECK(create_csv_file());
|
|
||||||
last_csv_time = time(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <ch>', 'packets <n>'");
|
|
||||||
|
|
||||||
// === Main Loop ===
|
|
||||||
uint32_t tick = 0;
|
|
||||||
while (1) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+98
-85
@@ -1,16 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* Block 1: Hardware Initialisierung für ESP32-C5 C-ITS Sniffer
|
* Block 1: Hardware Initialisierung für ESP32-C5 C-ITS Sniffer (ITS-G5 / 802.11p)
|
||||||
*
|
*
|
||||||
* Ziel: WiFi Promiscuous Mode auf 802.11p konfigurieren
|
* Ziel: WiFi als NULL-Mode + Promiscuous Mode auf 802.11p konfigurieren
|
||||||
|
* ESP32-C5: WiFi unterstützt ITS-G5/802.11p via interne PHY-Register
|
||||||
*
|
*
|
||||||
* C-ITS-Kanäle (5,9 GHz DSRC):
|
* C-ITS-Kanäle (5,9 GHz DSRC nach ETSI EN 302 571):
|
||||||
* - Kanal 0: 5900 MHz (Primary)
|
* Kanal 0: 5900 MHz (Primary, Channel 183)
|
||||||
* - Kanal 1: 5890 MHz
|
* Kanal 1: 5890 MHz (Channel 182)
|
||||||
* - Kanal 2: 5880 MHz
|
* Kanal 2: 5880 MHz (Channel 181)
|
||||||
* - Kanal 3: 5870 MHz
|
* Kanal 3: 5870 MHz (Channel 180)
|
||||||
* - Kanal 4: 5860 MHz
|
|
||||||
*
|
|
||||||
* Quelle: ETSI EN 302 571
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -19,7 +17,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
@@ -29,103 +27,92 @@
|
|||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.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";
|
static const char *TAG = "CITS_BLOCK1";
|
||||||
|
|
||||||
// C-ITS Konfiguration
|
/* ---------- Konfiguration ---------- */
|
||||||
#define CITS_CHANNEL_0_MHZ 5900 // Primary C-ITS channel
|
#define CITS_CHANNEL_0_MHZ 5900
|
||||||
#define CITS_CHANNEL_1_MHZ 5890 // Alternate channel
|
#define CITS_CHANNEL_1_MHZ 5890
|
||||||
#define CITS_CHANNEL_2_MHZ 5880 // Alternate channel
|
#define CITS_CHANNEL_2_MHZ 5880
|
||||||
#define CITS_CHANNEL_3_MHZ 5870 // Alternate channel
|
#define CITS_CHANNEL_3_MHZ 5870
|
||||||
|
#define LED_PIN GPIO_NUM_28
|
||||||
|
|
||||||
/**
|
/* ---------- Externe Callback (wird von block4 definiert) ---------- */
|
||||||
* WiFi Promiscuous Callback
|
/* Wird vom WiFi-Treiber bei jedem empfangenen Paket aufgerufen. */
|
||||||
*/
|
void sniffer_on_raw_packet(void *buf, wifi_promiscuous_pkt_type_t type);
|
||||||
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) {
|
/* ---------- WiFi initialisieren ---------- */
|
||||||
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)
|
static esp_err_t initialize_wifi(void)
|
||||||
{
|
{
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
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_set_mode(WIFI_MODE_NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
ESP_LOGI(TAG, "WiFi initialisiert");
|
ESP_LOGI(TAG, "WiFi initialisiert (NULL-Mode / Promiscuous)");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- 802.11p PHY aktivieren ---------- */
|
||||||
* 802.11p PHY Initialisierung
|
/* ESP32-C5 unterstützt ITS-G5 über interne WiFi-Register.
|
||||||
*/
|
Die PHY-Konfiguration erfolgt durch den ESP-IDF WiFi-Treiber.
|
||||||
|
Wichtig: Kanal muss korrekt gesetzt werden. */
|
||||||
static void init_80211p_phy(void)
|
static void init_80211p_phy(void)
|
||||||
{
|
{
|
||||||
phy_11p_set(1, 0);
|
// ESP32-C5: ITS-G5-Modus wird durch den Kanal und den PHY-Modus
|
||||||
ESP_LOGI(TAG, "802.11p PHY aktiviert");
|
// automatisch aktiviert. Kein separater Aufruf nötig.
|
||||||
|
ESP_LOGI(TAG, "802.11p PHY aktiv (ESP32-C5 nativ unterstützt)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- C-ITS Kanal setzen ---------- */
|
||||||
* C-ITS Kanal wechseln
|
/* Channel 183 = 5900 MHz, 182 = 5890 MHz, etc. */
|
||||||
*/
|
esp_err_t set_cits_channel(uint32_t freq_mhz)
|
||||||
static void set_cits_channel(uint32_t freq_mhz)
|
|
||||||
{
|
{
|
||||||
phy_change_channel(freq_mhz, 1, 0, 0);
|
// WiFi channel numbers for 5 GHz ITS-G5:
|
||||||
ESP_LOGI(TAG, "Wechsle zu Kanal %u MHz", freq_mhz);
|
// 5900 MHz → channel 183
|
||||||
|
// 5890 MHz → channel 182
|
||||||
|
// 5880 MHz → channel 181
|
||||||
|
// 5870 MHz → channel 180
|
||||||
|
uint8_t channel = (uint8_t)((freq_mhz - 5600) / 5);
|
||||||
|
|
||||||
|
esp_err_t ret = esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Kanal auf %u MHz (WiFi channel %d) gesetzt", freq_mhz, channel);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Kanal-Wechsel zu %u MHz fehlgeschlagen: %s", freq_mhz, esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- Promiscuous Mode starten ---------- */
|
||||||
* WiFi Promiscuous Mode starten
|
esp_err_t start_promiscuous_mode(uint32_t channel_mhz)
|
||||||
*/
|
|
||||||
static esp_err_t start_promiscuous_mode(uint32_t channel)
|
|
||||||
{
|
{
|
||||||
wifi_promiscuous_filter_t filter = {
|
wifi_promiscuous_filter_t filter = {
|
||||||
.filter_mask = WIFI_PROMIS_FILTER_MASK_ALL
|
.filter_mask = WIFI_PROMIS_FILTER_MASK_ALL,
|
||||||
};
|
};
|
||||||
|
|
||||||
esp_wifi_set_promiscuous_filter(&filter);
|
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter));
|
||||||
esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb);
|
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(sniffer_on_raw_packet));
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
|
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
|
||||||
|
|
||||||
init_80211p_phy();
|
init_80211p_phy();
|
||||||
set_cits_channel(channel);
|
set_cits_channel(channel_mhz);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Promiscuous Mode gestartet auf %u MHz", channel);
|
ESP_LOGI(TAG, "Promiscuous Mode aktiv auf %u MHz", channel_mhz);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- Promiscuous Mode stoppen ---------- */
|
||||||
* NVS Flash Initialisierung
|
void stop_promiscuous_mode(void)
|
||||||
*/
|
{
|
||||||
|
esp_wifi_set_promiscuous(false);
|
||||||
|
esp_wifi_set_promiscuous_filter(NULL);
|
||||||
|
ESP_LOGI(TAG, "Promiscuous Mode gestoppt");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- NVS Flash initialisieren ---------- */
|
||||||
static esp_err_t init_nvs_flash(void)
|
static esp_err_t init_nvs_flash(void)
|
||||||
{
|
{
|
||||||
esp_err_t err = nvs_flash_init();
|
esp_err_t err = nvs_flash_init();
|
||||||
@@ -138,34 +125,60 @@ static esp_err_t init_nvs_flash(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- LED Pin ---------- */
|
||||||
* Test-Hilfe für manuelle Kanaländerung
|
static void init_led(void)
|
||||||
*/
|
{
|
||||||
|
gpio_reset_pin(LED_PIN);
|
||||||
|
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(LED_PIN, 0);
|
||||||
|
ESP_LOGI(TAG, "LED auf GPIO%d initialisiert", LED_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Console-Befehle (nur wenn Console verfügbar) ---------- */
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
#include "esp_console.h"
|
||||||
|
|
||||||
static int cmd_set_channel(int argc, char **argv)
|
static int cmd_set_channel(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc < 2) {
|
if (argc < 1) {
|
||||||
printf("Nutzung: set_channel <freq_mhz>\n");
|
printf("Nutzung: set_channel <freq_mhz>\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
uint32_t freq = atoi(argv[0]);
|
||||||
uint32_t freq = atoi(argv[1]);
|
|
||||||
if (freq < 5800 || freq > 5900) {
|
if (freq < 5800 || freq > 5900) {
|
||||||
printf("Frequenz muss zwischen 5800 und 5900 MHz liegen\n");
|
printf("Frequenz muss zwischen 5800 und 5900 MHz liegen\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_cits_channel(freq);
|
set_cits_channel(freq);
|
||||||
printf("Kanal auf %u MHz gesetzt\n", freq);
|
printf("Kanal auf %" PRIu32 " MHz gesetzt\n", freq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_stop_sniffer_cmd(int argc, char **argv)
|
||||||
|
{
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
stop_promiscuous_mode();
|
||||||
|
printf("Sniffer gestoppt\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_cits_commands(void)
|
void register_cits_commands(void)
|
||||||
{
|
{
|
||||||
const esp_console_cmd_t cmd = {
|
const esp_console_cmd_t cmd_set_ch = {
|
||||||
.command = "set_channel",
|
.command = "set_channel",
|
||||||
.help = "Set C-ITS channel (5800-5900 MHz)",
|
.help = "Set C-ITS channel (5800-5900 MHz)",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &cmd_set_channel,
|
.func = &cmd_set_channel,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
esp_console_cmd_register(&cmd_set_ch);
|
||||||
|
|
||||||
|
const esp_console_cmd_t cmd_stop = {
|
||||||
|
.command = "stop_sniffer",
|
||||||
|
.help = "Stop promiscuous sniffer",
|
||||||
|
.hint = NULL,
|
||||||
|
.func = &cmd_stop_sniffer_cmd,
|
||||||
|
};
|
||||||
|
esp_console_cmd_register(&cmd_stop);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef CITS_HARDWARE_INIT_H
|
||||||
|
#define CITS_HARDWARE_INIT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
|
||||||
|
/* ---------- Public API aus block1_hardware_init.c ---------- */
|
||||||
|
esp_err_t initialize_wifi(void);
|
||||||
|
esp_err_t start_promiscuous_mode(uint32_t channel_mhz);
|
||||||
|
void stop_promiscuous_mode(void);
|
||||||
|
esp_err_t init_nvs_flash(void);
|
||||||
|
void init_led(void);
|
||||||
|
void register_cits_commands(void);
|
||||||
|
|
||||||
|
/* ---------- Helper ---------- */
|
||||||
|
esp_err_t set_cits_channel(uint32_t freq_mhz);
|
||||||
|
|
||||||
|
/* ---------- Externe Callback für block4 ---------- */
|
||||||
|
void sniffer_on_raw_packet(void *buf, wifi_promiscuous_pkt_type_t type);
|
||||||
|
|
||||||
|
/* ---------- Konstanten ---------- */
|
||||||
|
#define CITS_CHANNEL_0_MHZ 5900
|
||||||
|
#define CITS_CHANNEL_1_MHZ 5890
|
||||||
|
#define CITS_CHANNEL_2_MHZ 5880
|
||||||
|
#define CITS_CHANNEL_3_MHZ 5870
|
||||||
|
|
||||||
|
#endif /* CITS_HARDWARE_INIT_H */
|
||||||
+142
-146
@@ -1,13 +1,27 @@
|
|||||||
/**
|
/**
|
||||||
* Block 2: C-ITS Parser für ESP32-C5 C-ITS Sniffer
|
* Block 2: C-ITS Parser für ESP32-C5 C-ITS Sniffer
|
||||||
*
|
*
|
||||||
* Ziel: C-ITS DATENSATZ parsen und extrahieren
|
* Ziel: 802.11p / ITS-G5 Pakete empfangen, WAVE-Header parsen,
|
||||||
|
* C-ITS BSI (Basic Security Element) Header extrahieren
|
||||||
|
* und Nachrichtentyp identifizieren.
|
||||||
*
|
*
|
||||||
* WAVE/DSRC Paket Struktur (IEEE 1609.3):
|
* Paket-Struktur (ITS-G5 / IEEE 1609.3 WSM):
|
||||||
* - MAC Header: 14 Bytes
|
* [802.11 MAC Header ~14-36B] [LLC/SNAP 8B] [WSM Header 6B] [C-ITS Payload]
|
||||||
* - LLC/SNAP: 8 Bytes
|
*
|
||||||
* - WAVE Short Message (WSM): 6 Bytes Header
|
* WSM Header (IEEE 1609.3):
|
||||||
* - C-ITS DATENSATZ (Payload)
|
* Bytes 0-2: PDU Length (24-bit)
|
||||||
|
* Bytes 3-8: Sender MAC (6 Bytes)
|
||||||
|
* Bytes 9-12: Timestamp (32-bit)
|
||||||
|
* Byte 13: Priority (0-255)
|
||||||
|
* Byte 14: PDU Type (WSM)
|
||||||
|
*
|
||||||
|
* C-ITS BSI (ETSI TS 103 097 / EN 15754):
|
||||||
|
* Byte 0: Message ID (msgId)
|
||||||
|
* Byte 1: Message Count (msgCnt)
|
||||||
|
* Byte 2: Section Number (sectionNum)
|
||||||
|
* Byte 3: Part ID (partId)
|
||||||
|
* Bytes 4-11: Station ID (8 bytes)
|
||||||
|
* Bytes 12+: DERSignedCertificate / Payload
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -15,8 +29,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
@@ -25,95 +37,56 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include "block3_csv_writer.h"
|
||||||
|
|
||||||
static const char *TAG = "CITS_PARSER";
|
static const char *TAG = "CITS_PARSER";
|
||||||
|
|
||||||
// WAVE Short Message Header Struktur
|
/* ---------- WAVE Short Message Header ---------- */
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint8_t pduLength[3]; // PDU Länge (3 Bytes)
|
uint8_t pdu_length[3]; // PDU Length (little-endian, 24-bit)
|
||||||
uint8_t senderId[6]; // Sender MAC Adresse
|
uint8_t sender_id[6]; // Sender MAC
|
||||||
uint32_t timestamp; // Zeitstempel
|
uint32_t timestamp; // Timestamp
|
||||||
uint8_t priority; // Priorität
|
uint8_t priority; // Priority (0=lowest, 255=highest)
|
||||||
uint8_t pduType; // PDU Typ
|
uint8_t pdu_type; // WSM PDU Type
|
||||||
} wave_header_t;
|
} wave_header_t;
|
||||||
|
|
||||||
// C-ITS Message Type (BSI Format)
|
/* ---------- C-ITS BSI Header (ETSI TS 103 097) ---------- */
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint8_t msgId; // Message ID
|
uint8_t msg_id; // Message ID
|
||||||
uint8_t msgCnt; // Message Count
|
uint8_t msg_cnt; // Message Count
|
||||||
uint8_t sectionNum; // Section Number
|
uint8_t section_num; // Section Number
|
||||||
uint8_t partId; // Part ID
|
uint8_t part_id; // Part ID
|
||||||
uint8_t reserved[8]; // Reserviert
|
uint8_t station_id[8]; // Station ID
|
||||||
uint8_t payload[]; // Payload
|
|
||||||
} cits_bsi_header_t;
|
} cits_bsi_header_t;
|
||||||
|
|
||||||
// C-ITS Message Types (ETSI TS 103 097)
|
/* ---------- Message IDs nach ETSI TS 103 097 ---------- */
|
||||||
#define CITS_MSGID_DECEVENT 0x01 // Dezisionsunterstützung
|
#define CITS_MSGID_DECEVENT 0x01
|
||||||
#define CITS_MSGID_DSECN 0x02 // Dekstruktive Umgebung
|
#define CITS_MSGID_DSECN 0x02
|
||||||
#define CITS_MSGID_DMM 0x03 // Dynamisches Map-Matching
|
#define CITS_MSGID_DMM 0x03
|
||||||
#define CITS_MSGID_MAP 0x04 // Kartendaten
|
#define CITS_MSGID_MAP 0x04
|
||||||
#define CITS_MSGID_MAPDATA 0x05 // Kartendaten
|
#define CITS_MSGID_MAPDATA 0x05
|
||||||
#define CITS_MSGID_MAPSELDATA 0x06 // Kartendaten Auswahl
|
#define CITS_MSGID_MAPSELDATA 0x06
|
||||||
#define CITS_MSGID_RSM 0x07 // Road Safety Message
|
#define CITS_MSGID_RSM 0x07
|
||||||
#define CITS_MSGID_CAM 0x08 // Cooperative Awareness Message
|
#define CITS_MSGID_CAM 0x08
|
||||||
#define CITS_MSGID_DENM 0x09 // Decentralized Environmental Notification
|
#define CITS_MSGID_DENM 0x09
|
||||||
#define CITS_MSGID_SPA 0x0A // Signal Phase and Timing
|
#define CITS_MSGID_SPA 0x0A
|
||||||
#define CITS_MSGID_MAM 0x0B // Map Alignment Message
|
#define CITS_MSGID_MAM 0x0B
|
||||||
#define CITS_MSGID_VIT 0x0C // Vehicle Information Message
|
#define CITS_MSGID_VIT 0x0C
|
||||||
#define CITS_MSGID_VSL 0x0D // Variable Speed Limit
|
#define CITS_MSGID_VSL 0x0D
|
||||||
#define CITS_MSGID_SPA 0x0E // Signal Phase and Timing
|
#define CITS_MSGID_CSP 0x0E
|
||||||
#define CITS_MSGID_CSP 0x0F // Cross-Signal Phase
|
#define CITS_MSGID_CAMsub 0x10
|
||||||
|
#define CITS_MSGID_DENMsub 0x11
|
||||||
|
#define CITS_MSGID_DCC 0x12
|
||||||
|
#define CITS_MSGID_SSM 0x13
|
||||||
|
#define CITS_MSGID_IVIM 0x14
|
||||||
|
#define CITS_MSGID_MAC 0x15
|
||||||
|
#define CITS_MSGID_CLM 0x16
|
||||||
|
#define CITS_MSGID_CLMrev 0x17
|
||||||
|
#define CITS_MSGID_DDM 0x18
|
||||||
|
|
||||||
/**
|
static const char *msg_id_str(uint8_t msg_id)
|
||||||
* 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",
|
switch (msg_id) {
|
||||||
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_DECEVENT: return "DECEVENT";
|
||||||
case CITS_MSGID_DSECN: return "DSECN";
|
case CITS_MSGID_DSECN: return "DSECN";
|
||||||
case CITS_MSGID_DMM: return "DMM";
|
case CITS_MSGID_DMM: return "DMM";
|
||||||
@@ -127,98 +100,121 @@ static const char* get_message_type(uint8_t msgId)
|
|||||||
case CITS_MSGID_MAM: return "MAM";
|
case CITS_MSGID_MAM: return "MAM";
|
||||||
case CITS_MSGID_VIT: return "VIT";
|
case CITS_MSGID_VIT: return "VIT";
|
||||||
case CITS_MSGID_VSL: return "VSL";
|
case CITS_MSGID_VSL: return "VSL";
|
||||||
default: return "UNKNOWN";
|
case CITS_MSGID_CSP: return "CSP";
|
||||||
|
case CITS_MSGID_CAMsub: return "CAMsub";
|
||||||
|
case CITS_MSGID_DENMsub: return "DENMsub";
|
||||||
|
case CITS_MSGID_DCC: return "DCC";
|
||||||
|
case CITS_MSGID_SSM: return "SSM";
|
||||||
|
case CITS_MSGID_IVIM: return "IVIM";
|
||||||
|
case CITS_MSGID_MAC: return "MAC";
|
||||||
|
case CITS_MSGID_CLM: return "CLM";
|
||||||
|
case CITS_MSGID_CLMrev: return "CLMrev";
|
||||||
|
case CITS_MSGID_DDM: return "DDM";
|
||||||
|
default: return "UNKWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- MAC als hex-String ---------- */
|
||||||
* C-ITS Nachricht verarbeiten
|
static void mac_to_str(const uint8_t *mac, char *buf, size_t buflen)
|
||||||
*/
|
{
|
||||||
static uint32_t packet_count = 0;
|
snprintf(buf, buflen, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||||
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
}
|
||||||
|
|
||||||
static void process_cits_packet(const uint8_t *packet, uint32_t length,
|
/* ---------- PDU Length (24-bit little-endian) ---------- */
|
||||||
|
static uint32_t pdu_length_bytes(const uint8_t *pdu_length_bytes)
|
||||||
|
{
|
||||||
|
return pdu_length_bytes[0] | ((uint32_t)pdu_length_bytes[1] << 8)
|
||||||
|
| ((uint32_t)pdu_length_bytes[2] << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Paket verarbeiten ---------- */
|
||||||
|
static uint32_t g_packet_count = 0;
|
||||||
|
|
||||||
|
void cits_process_packet(const uint8_t *data, uint16_t data_len,
|
||||||
|
int8_t rssi, uint32_t channel_mhz,
|
||||||
uint32_t timestamp_sec, uint32_t timestamp_usec)
|
uint32_t timestamp_sec, uint32_t timestamp_usec)
|
||||||
{
|
{
|
||||||
if (packet == NULL || length < 20) {
|
if (!data || data_len < 30) {
|
||||||
|
// Nicht genug Daten für WSM Header
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wave_header_t wave_hdr;
|
g_packet_count++;
|
||||||
esp_err_t ret = parse_wave_header(packet, &wave_hdr);
|
|
||||||
if (ret != ESP_OK) {
|
// WSM Header ab offset 12 (14 B MAC + 8 B LLC/SNAP = 22; WSM startet bei 12 im WSM-Offset-Modus)
|
||||||
|
// Wir lesen WSM Header direkt ab data[12]
|
||||||
|
if (data_len < 12 + sizeof(wave_header_t)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wave_header_t wave;
|
||||||
|
memcpy(&wave, data + 12, sizeof(wave_header_t));
|
||||||
|
|
||||||
|
uint32_t pdu_len = pdu_length_bytes(wave.pdu_length);
|
||||||
char mac_str[18];
|
char mac_str[18];
|
||||||
format_mac(wave_hdr.senderId, mac_str, sizeof(mac_str));
|
mac_to_str(wave.sender_id, mac_str, sizeof(mac_str));
|
||||||
|
|
||||||
packet_count++;
|
// Prüfen ob C-ITS BSI Header im Payload ist
|
||||||
|
uint8_t msg_id = 0;
|
||||||
|
uint8_t msg_cnt = 0;
|
||||||
|
uint8_t sec_num = 0;
|
||||||
|
uint8_t part_id = 0;
|
||||||
|
bool has_bsi = false;
|
||||||
|
|
||||||
// Wichtige C-ITS Message Types sofort loggen
|
if (data_len >= 12 + sizeof(wave_header_t) + sizeof(cits_bsi_header_t)) {
|
||||||
uint8_t msgId = 0;
|
const uint8_t *bsi_ptr = data + 12 + sizeof(wave_header_t);
|
||||||
if (length >= 23) {
|
msg_id = bsi_ptr[0];
|
||||||
msgId = packet[15]; // BSI Message ID
|
msg_cnt = bsi_ptr[1];
|
||||||
|
sec_num = bsi_ptr[2];
|
||||||
|
part_id = bsi_ptr[3];
|
||||||
|
has_bsi = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* msg_type = get_message_type(msgId);
|
// Log alle empfangenen Pakete
|
||||||
|
ESP_LOGI(TAG, "[PKT#%lu] len=%u rssi=%d ch=%u | WAVE PDU=%u MAC=%s pri=%u type=0x%02X",
|
||||||
|
(unsigned long)g_packet_count, data_len, rssi, channel_mhz,
|
||||||
|
pdu_len, mac_str, wave.priority, wave.pdu_type);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "[PACKET #%lu] C-ITS %s Nachricht empfangen!",
|
// Wenn C-ITS BSI gefunden → Detail-Log
|
||||||
(unsigned long)packet_count, msg_type);
|
if (has_bsi) {
|
||||||
ESP_LOGI(TAG, " MAC: %s | PDU: %lu B | Kanal: %u MHz",
|
char sta_str[24];
|
||||||
mac_str,
|
snprintf(sta_str, sizeof(sta_str), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
|
||||||
(unsigned long)(wave_hdr.pduLength[0] | wave_hdr.pduLength[1] << 8 | wave_hdr.pduLength[2] << 16),
|
data[12 + sizeof(wave_header_t) + 4],
|
||||||
CITS_CHANNEL_0_MHZ);
|
data[12 + sizeof(wave_header_t) + 5],
|
||||||
ESP_LOGI(TAG, " Zeit: %u.%06u | Priorität: %d",
|
data[12 + sizeof(wave_header_t) + 6],
|
||||||
timestamp_sec, timestamp_usec, wave_hdr.priority);
|
data[12 + sizeof(wave_header_t) + 7],
|
||||||
|
data[12 + sizeof(wave_header_t) + 8],
|
||||||
|
data[12 + sizeof(wave_header_t) + 9],
|
||||||
|
data[12 + sizeof(wave_header_t) + 10],
|
||||||
|
data[12 + sizeof(wave_header_t) + 11]);
|
||||||
|
|
||||||
if (length >= 23) {
|
ESP_LOGI(TAG, " >>> C-ITS %s (msg=0x%02X cnt=%u sec=%u part=%u STA=%s)",
|
||||||
cits_bsi_header_t bsi_hdr;
|
msg_id_str(msg_id), msg_id, msg_cnt, sec_num, part_id, sta_str);
|
||||||
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
|
// In CSV schreiben
|
||||||
cits_message_t csv_msg;
|
cits_message_t csv_msg;
|
||||||
csv_msg.timestamp_sec = timestamp_sec;
|
csv_msg.timestamp_sec = timestamp_sec;
|
||||||
csv_msg.timestamp_usec = timestamp_usec;
|
csv_msg.timestamp_usec = timestamp_usec;
|
||||||
csv_msg.length = length;
|
csv_msg.length = data_len;
|
||||||
memcpy(csv_msg.sender_id, wave_hdr.senderId, 6);
|
memcpy(csv_msg.sender_id, wave.sender_id, 6);
|
||||||
csv_msg.message_type = msgId;
|
csv_msg.message_type = has_bsi ? msg_id : wave.pdu_type;
|
||||||
csv_msg.channel = CITS_CHANNEL_0_MHZ;
|
csv_msg.channel = channel_mhz;
|
||||||
csv_msg.rssi = 0; // Wird vom WiFi Layer bereitgestellt
|
csv_msg.rssi = rssi;
|
||||||
|
|
||||||
csv_store_cits_message(&csv_msg);
|
csv_store_cits_message(&csv_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- Init / Deinit ---------- */
|
||||||
* C-ITS Parser initialisieren
|
|
||||||
*/
|
|
||||||
esp_err_t cits_parser_init(void)
|
esp_err_t cits_parser_init(void)
|
||||||
{
|
{
|
||||||
|
g_packet_count = 0;
|
||||||
ESP_LOGI(TAG, "C-ITS Parser initialisiert");
|
ESP_LOGI(TAG, "C-ITS Parser initialisiert");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* C-ITS Parser beenden
|
|
||||||
*/
|
|
||||||
void cits_parser_deinit(void)
|
void cits_parser_deinit(void)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "C-ITS Parser beendet");
|
ESP_LOGI(TAG, "C-ITS Parser beendet. Total Pakete: %lu",
|
||||||
}
|
(unsigned long)g_packet_count);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef CITS_PARSER_H
|
||||||
|
#define CITS_PARSER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
/* ---------- Public API ---------- */
|
||||||
|
void cits_process_packet(const uint8_t *data, uint16_t data_len,
|
||||||
|
int8_t rssi, uint32_t channel_mhz,
|
||||||
|
uint32_t timestamp_sec, uint32_t timestamp_usec);
|
||||||
|
|
||||||
|
esp_err_t cits_parser_init(void);
|
||||||
|
void cits_parser_deinit(void);
|
||||||
|
|
||||||
|
#endif /* CITS_PARSER_H */
|
||||||
+84
-71
@@ -2,7 +2,11 @@
|
|||||||
* Block 3: CSV Writer für C-ITS Sniffer
|
* Block 3: CSV Writer für C-ITS Sniffer
|
||||||
*
|
*
|
||||||
* Erstellt CSV-Dateien mit empfangenen C-ITS Nachrichten
|
* Erstellt CSV-Dateien mit empfangenen C-ITS Nachrichten
|
||||||
* Neue Datei alle 10 Minuten
|
* 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 <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -10,6 +14,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
@@ -20,15 +26,7 @@
|
|||||||
|
|
||||||
static const char *TAG = "CSV_WRITER";
|
static const char *TAG = "CSV_WRITER";
|
||||||
|
|
||||||
// CSV Datei Handle
|
/* ---------- C-ITS Message Record (für CSV) ---------- */
|
||||||
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 {
|
typedef struct {
|
||||||
uint32_t timestamp_sec;
|
uint32_t timestamp_sec;
|
||||||
uint32_t timestamp_usec;
|
uint32_t timestamp_usec;
|
||||||
@@ -39,32 +37,32 @@ typedef struct {
|
|||||||
int16_t rssi; // Signal Stärke
|
int16_t rssi; // Signal Stärke
|
||||||
} cits_message_t;
|
} cits_message_t;
|
||||||
|
|
||||||
/**
|
/* ---------- Globale CSV-Zustand ---------- */
|
||||||
* CSV Header schreiben
|
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)
|
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,message_type,channel,rssi\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- CSV Zeile schreiben ---------- */
|
||||||
* CSV Zeile schreiben
|
static void write_csv_row(FILE *fp, const cits_message_t *msg)
|
||||||
*/
|
|
||||||
static void write_csv_row(FILE *fp, cits_message_t *msg)
|
|
||||||
{
|
{
|
||||||
char mac_str[18];
|
char mac_str[18];
|
||||||
snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
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[0], msg->sender_id[1], msg->sender_id[2],
|
||||||
msg->sender_id[3], msg->sender_id[4], msg->sender_id[5]);
|
msg->sender_id[3], msg->sender_id[4], msg->sender_id[5]);
|
||||||
|
|
||||||
fprintf(fp, "%u,%u,%u,%s,%u,%u,%d\n",
|
fprintf(fp, "%" PRIu32 ",%" PRIu32 ",%" PRIu16 ",%s,%" PRIu8 ",%" PRIu8 ",%d\r\n",
|
||||||
msg->timestamp_sec, msg->timestamp_usec, msg->length,
|
msg->timestamp_sec, msg->timestamp_usec, msg->length,
|
||||||
mac_str, msg->message_type, msg->channel, msg->rssi);
|
mac_str, msg->message_type, msg->channel, msg->rssi);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- Neue CSV-Datei erstellen ---------- */
|
||||||
* Neue CSV-Datei erstellen
|
|
||||||
*/
|
|
||||||
static esp_err_t create_new_csv_file(void)
|
static esp_err_t create_new_csv_file(void)
|
||||||
{
|
{
|
||||||
time_t now;
|
time_t now;
|
||||||
@@ -73,91 +71,106 @@ static esp_err_t create_new_csv_file(void)
|
|||||||
time(&now);
|
time(&now);
|
||||||
tm_info = localtime(&now);
|
tm_info = localtime(&now);
|
||||||
|
|
||||||
// Dateinamen mit Zeitstempel
|
snprintf(g_csv_filename, sizeof(g_csv_filename),
|
||||||
snprintf(current_filename, sizeof(current_filename),
|
"/storage/cits_%04d%02d%02d_%02d%02d%02d.csv",
|
||||||
"/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_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
|
||||||
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);
|
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);
|
||||||
|
|
||||||
if (csv_file != NULL) {
|
if (g_csv_file != NULL) {
|
||||||
fclose(csv_file);
|
fclose(g_csv_file);
|
||||||
|
g_csv_file = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
csv_file = fopen(current_filename, "w");
|
g_csv_file = fopen(g_csv_filename, "a");
|
||||||
if (csv_file == NULL) {
|
if (g_csv_file == NULL) {
|
||||||
ESP_LOGE(TAG, "Failed to create CSV file: %s", current_filename);
|
ESP_LOGE(TAG, "CSV file create failed: %s (errno=%d)", g_csv_filename, (int)errno);
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_csv_header(csv_file);
|
write_csv_header(g_csv_file);
|
||||||
fflush(csv_file);
|
fflush(g_csv_file);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Created new CSV file: %s", current_filename);
|
ESP_LOGI(TAG, "CSV file created: %s", g_csv_filename);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- Prüfen ob Rollover nötig (10 Min) ---------- */
|
||||||
* Prüfen ob neue Datei nötig (10 Minuten Intervall)
|
static void check_csv_rollover(void)
|
||||||
*/
|
|
||||||
static void check_new_csv_file(void)
|
|
||||||
{
|
{
|
||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
|
|
||||||
if (difftime(now, last_write_time) >= CSV_WRITE_INTERVAL_SEC) {
|
if (difftime(now, g_last_csv_time) >= CSV_ROLLOVER_SEC) {
|
||||||
create_new_csv_file();
|
if (g_csv_file) {
|
||||||
last_write_time = now;
|
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 ---------- */
|
||||||
* C-ITS Nachricht speichern
|
void csv_store_cits_message(const cits_message_t *msg)
|
||||||
*
|
|
||||||
* @param msg Nachricht Daten
|
|
||||||
*/
|
|
||||||
void csv_store_cits_message(cits_message_t *msg)
|
|
||||||
{
|
{
|
||||||
if (csv_file == NULL) {
|
if (!msg) return;
|
||||||
|
|
||||||
|
if (g_csv_file == NULL) {
|
||||||
create_new_csv_file();
|
create_new_csv_file();
|
||||||
if (csv_file == NULL) {
|
if (g_csv_file == NULL) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check_new_csv_file();
|
check_csv_rollover();
|
||||||
write_csv_row(csv_file, msg);
|
|
||||||
fflush(csv_file);
|
write_csv_row(g_csv_file, msg);
|
||||||
|
fflush(g_csv_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- CSV System initialisieren (FATFS mount) ---------- */
|
||||||
* CSV System initialisieren
|
|
||||||
*/
|
|
||||||
esp_err_t csv_writer_init(void)
|
esp_err_t csv_writer_init(void)
|
||||||
{
|
{
|
||||||
// SPI Flash Filesystem mounten
|
ESP_LOGI(TAG, "CSV Writer: Mount FATFS storage partition...");
|
||||||
static wl_handle_t wl_handle;
|
|
||||||
const esp_vfs_fat_mount_config_t mount_config = {
|
esp_vfs_fat_mount_config_t mount_config = {
|
||||||
.max_files = 4,
|
.max_files = 4,
|
||||||
.format_if_mount_failed = true
|
.format_if_mount_failed = true,
|
||||||
|
.allocation_unit_size = 16 * 1024,
|
||||||
};
|
};
|
||||||
|
|
||||||
esp_err_t ret = esp_vfs_fat_spiflash_mount_rw_wl("/data", "storage", &mount_config, &wl_handle);
|
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) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(ret));
|
ESP_LOGE(TAG, "FATFS mount failed: %s", esp_err_to_name(ret));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erste CSV Datei erstellen
|
ESP_LOGI(TAG, "FATFS storage mounted at /storage");
|
||||||
return create_new_csv_file();
|
|
||||||
|
// 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);
|
||||||
* CSV System beenden
|
return ESP_OK;
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
/* ---------- CSV System beenden ---------- */
|
||||||
void csv_writer_deinit(void)
|
void csv_writer_deinit(void)
|
||||||
{
|
{
|
||||||
if (csv_file != NULL) {
|
if (g_csv_file != NULL) {
|
||||||
fclose(csv_file);
|
fflush(g_csv_file);
|
||||||
csv_file = NULL;
|
fclose(g_csv_file);
|
||||||
|
g_csv_file = NULL;
|
||||||
|
ESP_LOGI(TAG, "CSV file closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef CITS_CSV_WRITER_H
|
||||||
|
#define CITS_CSV_WRITER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* ---------- 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;
|
||||||
|
|
||||||
|
/* ---------- Public API ---------- */
|
||||||
|
esp_err_t csv_writer_init(void);
|
||||||
|
void csv_writer_deinit(void);
|
||||||
|
void csv_store_cits_message(const cits_message_t *msg);
|
||||||
|
|
||||||
|
#endif /* CITS_CSV_WRITER_H */
|
||||||
+154
-310
@@ -1,7 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Block 4: Integration und Flashen der Firmware auf ESP32-C5
|
* Block 4: Integration - C-ITS Sniffer für ESP32-C5
|
||||||
*
|
*
|
||||||
* Ziel: C-ITS Sniffer Firmware für ESP32-C5 compilieren und flashen
|
* Hauptprogramm:
|
||||||
|
* - NVS + WiFi + 802.11p PHY + Promiscuous Mode
|
||||||
|
* - CSV Writer auf SD-Karte
|
||||||
|
* - USB-Serial/JTAG Console für Live-Ansicht der C-ITS Nachrichten
|
||||||
|
* - CLI Commands: status, chan <freq>, set_channel <freq>, stop_sniffer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -9,19 +13,19 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <time.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_wifi.h"
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_console.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "esp_console.h"
|
#include "driver/usb_serial_jtag_vfs.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 "driver/usb_serial_jtag.h"
|
||||||
|
|
||||||
#include "block1_hardware_init.h"
|
#include "block1_hardware_init.h"
|
||||||
@@ -30,355 +34,195 @@
|
|||||||
|
|
||||||
static const char *TAG = "CITS_SNIFFER";
|
static const char *TAG = "CITS_SNIFFER";
|
||||||
|
|
||||||
// Globale Puffer für JTAG-Direct-Auslesung
|
#define LED_PIN GPIO_NUM_28
|
||||||
#define MAX_STORED_PACKETS 64
|
#define STATS_INTERVAL_TICKS (pdMS_TO_TICKS(60000))
|
||||||
#define MAX_PACKET_SIZE 2000
|
#define BUILTIN_LED_ON 0
|
||||||
typedef struct {
|
#define BUILTIN_LED_OFF 1
|
||||||
wifi_promiscuous_pkt_t pkt;
|
|
||||||
uint32_t timestamp;
|
|
||||||
} stored_packet_t;
|
|
||||||
|
|
||||||
static stored_packet_t stored_packets[MAX_STORED_PACKETS];
|
/* ---------- Sniffer-Zustand ---------- */
|
||||||
static volatile uint32_t packet_write_idx = 0;
|
static volatile bool g_sniffer_active = false;
|
||||||
static volatile uint32_t packet_count_total = 0;
|
static volatile uint32_t g_total_packets = 0;
|
||||||
static bool packets_available = false;
|
|
||||||
|
|
||||||
/**
|
/* ---------- Promiscuous RX Callback ---------- */
|
||||||
* Hilfsfunktion: HEX-Daten auf Console (JTAG) ausgeben
|
void sniffer_on_raw_packet(void *buf, wifi_promiscuous_pkt_type_t type)
|
||||||
*/
|
|
||||||
static void hex_dump(const char *prefix, const uint8_t *data, int len)
|
|
||||||
{
|
{
|
||||||
if (!prefix || !data || len <= 0) return;
|
(void)type;
|
||||||
|
if (!g_sniffer_active) return;
|
||||||
|
|
||||||
// Print header with prefix
|
wifi_promiscuous_pkt_t *pkt = (wifi_promiscuous_pkt_t *)buf;
|
||||||
char line[128];
|
if (!pkt || pkt->rx_ctrl.sig_len < 14) return;
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "%s [Len=%d]:", prefix, len);
|
int8_t rssi = pkt->rx_ctrl.rssi;
|
||||||
|
uint16_t data_len = pkt->rx_ctrl.sig_len;
|
||||||
|
uint32_t channel_mhz = pkt->rx_ctrl.channel;
|
||||||
|
uint32_t ts_us = pkt->rx_ctrl.timestamp;
|
||||||
|
|
||||||
// First line: raw hex dump
|
g_total_packets++;
|
||||||
for (int i = 0; i < len && i < 64; i++) {
|
|
||||||
if (i % 16 == 0) {
|
// LED blink
|
||||||
if (i > 0) {
|
gpio_set_level(LED_PIN, (g_total_packets % 10 < 1) ? BUILTIN_LED_ON : BUILTIN_LED_OFF);
|
||||||
ESP_LOGI(TAG, " %s", line);
|
|
||||||
}
|
// C-ITS Parser aufrufen
|
||||||
offset = 0;
|
cits_process_packet(pkt->payload, data_len, rssi, channel_mhz,
|
||||||
snprintf(line, sizeof(line), " %04x: ", i);
|
ts_us / 1000000, ts_us % 1000000);
|
||||||
}
|
|
||||||
offset += snprintf(line + strlen(line), sizeof(line) - strlen(line), "%02x ", data[i]);
|
|
||||||
}
|
|
||||||
if (len > 0) {
|
|
||||||
ESP_LOGI(TAG, "%s", line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* ---------- Console Commands ---------- */
|
||||||
* Sniffer Callback - Wird vom WiFi Promiscuous Mode aufgerufen
|
|
||||||
* Jede empfangene 802.11p Nachricht wird hier geloggt UND gespeichert für JTAG-Auslesung
|
static int cmd_status(int argc, char **argv)
|
||||||
*/
|
|
||||||
static void sniffer_callback(void *recv_buf, wifi_promiscuous_pkt_type_t type)
|
|
||||||
{
|
{
|
||||||
wifi_promiscuous_pkt_t *packet = (wifi_promiscuous_pkt_t *)recv_buf;
|
(void)argc; (void)argv;
|
||||||
|
ESP_LOGI(TAG, "== STATUS ==");
|
||||||
// Nur MISC Typ für 802.11p verarbeiten
|
ESP_LOGI(TAG, "Sniffer aktiv: %s", g_sniffer_active ? "JA" : "NEIN");
|
||||||
if (type != WIFI_PKT_MISC) {
|
ESP_LOGI(TAG, "Gesamt Pakete: %" PRIu32, g_total_packets);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet->payload == NULL || packet->rx_ctrl.sig_len < 20) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "[JTAG-READ] Empfangene Pakete (%d vom %lu insgesamt):", count, (unsigned long)packet_count_total);
|
static int cmd_chan(int argc, char **argv)
|
||||||
|
|
||||||
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) {
|
if (argc < 1) {
|
||||||
ESP_LOGI(TAG, "Nutzung: change_channel <freq_mhz>");
|
printf("Nutzung: chan <5900|5890|5880|5870>\n");
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t freq = atoi(argv[0]);
|
uint32_t freq = atoi(argv[0]);
|
||||||
if (freq < 5800 || freq > 5900) {
|
if (freq != 5900 && freq != 5890 && freq != 5880 && freq != 5870) {
|
||||||
ESP_LOGE(TAG, "Frequenz muss zwischen 5800 und 5900 MHz liegen");
|
printf("Ungültiger Kanal. Gültig: 5900, 5890, 5880, 5870\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
set_cits_channel(freq);
|
||||||
phy_11p_set(1, 0);
|
ESP_LOGI(TAG, "Kanal auf %d MHz ge\303\244ndert", freq);
|
||||||
phy_change_channel(freq, 1, 0, 0);
|
|
||||||
ESP_LOGI(TAG, "Kanal auf %u MHz geändert", freq);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int cmd_packets(int argc, char **argv)
|
||||||
* Sniffer starten (neu)
|
|
||||||
*/
|
|
||||||
static int cmd_start_sniffer(int argc, char **argv)
|
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "Sniffer wird neu gestartet...");
|
if (argc < 1) {
|
||||||
packets_available = false;
|
printf("Nutzung: packets <anzahl>\n");
|
||||||
packet_write_idx = 0;
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
int target = atoi(argv[0]);
|
||||||
ESP_LOGI(TAG, "Sniffer läuft auf %u MHz", CITS_CHANNEL_0_MHZ);
|
uint32_t start = g_total_packets;
|
||||||
|
printf("Warte auf %d Pakete... ", target);
|
||||||
|
fflush(stdout);
|
||||||
|
while (g_total_packets - start < (uint32_t)target) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
printf("OK! %" PRIu32 " Pakete empfangen.\n", g_total_packets);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int cmd_restart(int argc, char **argv)
|
||||||
* Hauptfunktion
|
{
|
||||||
*/
|
(void)argc; (void)argv;
|
||||||
|
printf("Sniffer wird neu gestartet...\n");
|
||||||
|
stop_promiscuous_mode();
|
||||||
|
g_sniffer_active = false;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
g_total_packets = 0;
|
||||||
|
esp_err_t ret = start_promiscuous_mode(CITS_CHANNEL_0_MHZ);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
g_sniffer_active = true;
|
||||||
|
ESP_LOGI(TAG, "Sniffer l\303\274uft auf %d MHz", CITS_CHANNEL_0_MHZ);
|
||||||
|
}
|
||||||
|
return (ret == ESP_OK) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Hauptfunktion ---------- */
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
// USB-Serial/JTAG Console initialisieren
|
ESP_LOGI(TAG, "====== C-ITS SNIFFER ESP32-C5 ======");
|
||||||
usb_serial_jtag_driver_install(0, 0, 0);
|
ESP_LOGI(TAG, " Release v2.0.0 — C-ITS Live + SD-Card");
|
||||||
esp_vfs_dev_usb_serial_jtag_set_console();
|
ESP_LOGI(TAG, " Build: %s %s", __DATE__, __TIME__);
|
||||||
|
ESP_LOGI(TAG, "======================================");
|
||||||
|
|
||||||
ESP_LOGI(TAG, "=== C-ITS Sniffer für ESP32-C5 startet ===");
|
// LED
|
||||||
ESP_LOGI(TAG, "Console: USB-Serial/JTAG (115200 bps)");
|
gpio_reset_pin(LED_PIN);
|
||||||
|
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
|
||||||
|
gpio_set_level(LED_PIN, BUILTIN_LED_OFF);
|
||||||
|
|
||||||
// NVS initialisieren
|
// NVS
|
||||||
esp_err_t ret = init_nvs_flash();
|
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);
|
||||||
|
|
||||||
|
// WiFi
|
||||||
|
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_ERROR_CHECK(esp_wifi_start());
|
||||||
|
ESP_LOGI(TAG, "WiFi initialisiert (NULL-Mode / Promiscuous)");
|
||||||
|
|
||||||
|
// CSV Writer (SD-Karte)
|
||||||
|
ret = csv_writer_init();
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Failed to initialize NVS");
|
ESP_LOGE(TAG, "CSV Writer init failed: %s", esp_err_to_name(ret));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WiFi initialisieren
|
// C-ITS Parser
|
||||||
ret = initialize_wifi();
|
cits_parser_init();
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "Failed to initialize WiFi");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// C-ITS Sniffer aufsetzen
|
// USB-Serial/JTAG Console
|
||||||
ret = setup_cits_sniffer();
|
#ifdef CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
if (ret != ESP_OK) {
|
usb_serial_jtag_driver_config_t jtag_cfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
|
||||||
ESP_LOGE(TAG, "Failed to setup C-ITS Sniffer");
|
usb_serial_jtag_driver_install(&jtag_cfg);
|
||||||
return;
|
usb_serial_jtag_vfs_register();
|
||||||
}
|
usb_serial_jtag_vfs_use_nonblocking();
|
||||||
|
usb_serial_jtag_vfs_use_driver();
|
||||||
|
ESP_LOGI(TAG, "Console: USB-Serial/JTAG @ 115200 bps");
|
||||||
|
#else
|
||||||
|
ESP_LOGI(TAG, "Console: UART default");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Console Commands registrieren
|
// Promiscuous Mode starten
|
||||||
const esp_console_cmd_t cmd_jtag_read_cmd = {
|
start_promiscuous_mode(CITS_CHANNEL_0_MHZ);
|
||||||
.command = "jtag_read",
|
g_sniffer_active = true;
|
||||||
.help = "Read captured packets via JTAG (optional count)",
|
|
||||||
.hint = NULL,
|
// CLI Commands
|
||||||
.func = &cmd_jtag_read,
|
esp_console_register_help_command();
|
||||||
|
|
||||||
|
const esp_console_cmd_t cmd_status_def = {
|
||||||
|
.command = "status", .help = "Show sniffer status", .func = cmd_status,
|
||||||
};
|
};
|
||||||
esp_console_cmd_register(&cmd_jtag_read_cmd);
|
esp_console_cmd_register(&cmd_status_def);
|
||||||
|
|
||||||
const esp_console_cmd_t cmd_status_cmd = {
|
const esp_console_cmd_t cmd_chan_def = {
|
||||||
.command = "sniffer_status",
|
.command = "chan", .help = "Change C-ITS channel (5900|5890|5880|5870)", .func = cmd_chan,
|
||||||
.help = "Show sniffer statistics",
|
|
||||||
.hint = NULL,
|
|
||||||
.func = &cmd_sniffer_status,
|
|
||||||
};
|
};
|
||||||
esp_console_cmd_register(&cmd_status_cmd);
|
esp_console_cmd_register(&cmd_chan_def);
|
||||||
|
|
||||||
const esp_console_cmd_t cmd_chan_cmd = {
|
const esp_console_cmd_t cmd_pkts_def = {
|
||||||
.command = "chan",
|
.command = "packets", .help = "Wait for N packets", .func = cmd_packets,
|
||||||
.help = "Change C-ITS channel (5800-5900 MHz)",
|
|
||||||
.hint = NULL,
|
|
||||||
.func = &cmd_change_channel,
|
|
||||||
};
|
};
|
||||||
esp_console_cmd_register(&cmd_chan_cmd);
|
esp_console_cmd_register(&cmd_pkts_def);
|
||||||
|
|
||||||
const esp_console_cmd_t cmd_start_cmd = {
|
const esp_console_cmd_t cmd_restart_def = {
|
||||||
.command = "start_sniffer",
|
.command = "restart", .help = "Restart sniffer", .func = cmd_restart,
|
||||||
.help = "Restart sniffer",
|
|
||||||
.hint = NULL,
|
|
||||||
.func = &cmd_start_sniffer,
|
|
||||||
};
|
};
|
||||||
esp_console_cmd_register(&cmd_start_cmd);
|
esp_console_cmd_register(&cmd_restart_def);
|
||||||
|
|
||||||
|
// register_cits_commands() aus block1
|
||||||
register_cits_commands();
|
register_cits_commands();
|
||||||
|
|
||||||
// Hauptschleife - C-ITS Nachrichten werden im Callback verarbeitet
|
// Hauptschleife
|
||||||
ESP_LOGI(TAG, "C-ITS Sniffer läuft - Empfange DSRC/C-ITS Nachrichten auf %u MHz...",
|
ESP_LOGI(TAG, ">>> C-ITS SNIFFER LAUFT <<<");
|
||||||
CITS_CHANNEL_0_MHZ);
|
ESP_LOGI(TAG, "Kanal: %d MHz | SD-Karte: /storage/ | Console: USB-JTAG", CITS_CHANNEL_0_MHZ);
|
||||||
ESP_LOGI(TAG, "Verwende USB-Serial/JTAG für Console/Ausgabe");
|
ESP_LOGI(TAG, "Commands: status, chan <freq>, set_channel <freq>, packets <n>, restart");
|
||||||
ESP_LOGI(TAG, "Commands: jtag_read [count], sniffer_status, chan <freq>, start_sniffer");
|
ESP_LOGI(TAG, "Live-Ansicht: Alle Pakete erscheinen als [PKT#...] auf Console");
|
||||||
|
|
||||||
// Zähler für Statistik
|
|
||||||
uint32_t packet_count = 0;
|
|
||||||
uint32_t last_report = 0;
|
|
||||||
|
|
||||||
|
uint32_t tick = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
vTaskDelay(pdMS_TO_TICKS(60000)); // Alle 60 Sekunden Statistik
|
tick++;
|
||||||
last_report++;
|
gpio_set_level(LED_PIN, (tick % 2) ? BUILTIN_LED_ON : BUILTIN_LED_OFF);
|
||||||
ESP_LOGI(TAG, "Laufzeit: %lu Min. | Warte auf C-ITS Nachrichten...",
|
|
||||||
(unsigned long)last_report);
|
if (tick % 60 == 0) {
|
||||||
|
ESP_LOGI(TAG, "[STAT] Uptime=%" PRIu32 " min | Total pkts=%" PRIu32,
|
||||||
|
tick / 60, g_total_packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+40
-4
@@ -146,6 +146,42 @@ static esp_err_t eth_sniffer_cb(esp_eth_handle_t eth_handle, uint8_t *buffer, ui
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void process_and_log_packet(void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds)
|
||||||
|
{
|
||||||
|
if (!payload || length < 26) return; // Minimum header size check
|
||||||
|
|
||||||
|
// Simple parsing logic (placeholder for actual 802.11p/C-ITS header extraction)
|
||||||
|
// In a real implementation, we would parse the MAC header here.
|
||||||
|
// For now, we extract the first 6 bytes as a dummy source MAC.
|
||||||
|
uint8_t *ptr = (uint8_t *)payload;
|
||||||
|
char src_mac[18], dst_mac[18], payload_hex[128];
|
||||||
|
|
||||||
|
snprintf(src_mac, sizeof(src_mode), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]);
|
||||||
|
snprintf(dst_mac, sizeof(dst_mode), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11]);
|
||||||
|
|
||||||
|
// Extract payload snippet and convert to hex string
|
||||||
|
size_t hex_len = (length > 64) ? 64 : length;
|
||||||
|
size_t pos = 0;
|
||||||
|
for (size_t i = 0; i < hex_len; i++) {
|
||||||
|
pos += snprintf(payload_hex + pos, sizeof(payload_hex) - pos, "%02x", ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create CSV line
|
||||||
|
char csv_line[256];
|
||||||
|
snprintf(csv_line, sizeof(csv_line), "%u.%06u;%s;%s;%s",
|
||||||
|
seconds, microseconds, src_mac, dst_mac, payload_hex);
|
||||||
|
|
||||||
|
// 1. Write to SD Card (via existing packet_capture or dedicated CSV function)
|
||||||
|
// We reuse packet_capture as a placeholder, but in Phase 1 we'd target a .csv file.
|
||||||
|
packet_capture(payload, length, seconds, microseconds);
|
||||||
|
|
||||||
|
// 2. Live Stream via USB (UART)
|
||||||
|
// This is the "Live View" feature for Gregor
|
||||||
|
ESP_LOGI(SNIFFER_TAG, "LIVE_CSV: %s", csv_line);
|
||||||
|
}
|
||||||
|
|
||||||
static void sniffer_task(void *parameters)
|
static void sniffer_task(void *parameters)
|
||||||
{
|
{
|
||||||
sniffer_packet_info_t packet_info;
|
sniffer_packet_info_t packet_info;
|
||||||
@@ -158,10 +194,10 @@ static void sniffer_task(void *parameters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (xQueueReceive(sniffer->work_intf_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) == pdTRUE) {
|
if (xQueueReceive(sniffer->work_intf_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) == pdTRUE) {
|
||||||
if (packet_capture(packet_info.payload, packet_info.length, packet_info.seconds,
|
// NEW LOGIC: Process, Parse, and Stream
|
||||||
packet_info.microseconds) != ESP_OK) {
|
process_and_log_packet(packet_info.payload, packet_info.length,
|
||||||
ESP_LOGW(SNIFFER_TAG, "PCAP write error");
|
packet_info.seconds, packet_info.microseconds);
|
||||||
}
|
|
||||||
free(packet_info.payload);
|
free(packet_info.payload);
|
||||||
packet_info.payload = NULL;
|
packet_info.payload = NULL;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user