From 0e2cea8da7a50beb314ccf9af0fd9f664529253b Mon Sep 17 00:00:00 2001 From: Bob Date: Sun, 7 Dec 2025 19:57:41 -0800 Subject: [PATCH] compiles on targets esp32, esp32-s3, and esp32-c5 --- components/gps_sync/gps_sync.c | 159 ++++++++++++++++----------------- components/gps_sync/gps_sync.h | 30 +++---- main/main.c | 76 +++++++++++++--- 3 files changed, 155 insertions(+), 110 deletions(-) diff --git a/components/gps_sync/gps_sync.c b/components/gps_sync/gps_sync.c index eaf2648..bbb6bad 100644 --- a/components/gps_sync/gps_sync.c +++ b/components/gps_sync/gps_sync.c @@ -11,15 +11,15 @@ #include #include -// --- SAFE WIRING FOR ESP32-C5 --- -#define GPS_UART_NUM UART_NUM_1 -#define GPS_RX_PIN GPIO_NUM_23 -#define GPS_TX_PIN GPIO_NUM_24 -#define PPS_GPIO GPIO_NUM_25 +static const char *TAG = "GPS_SYNC"; + +// --- INTERNAL MACROS --- #define GPS_BAUD_RATE 9600 #define UART_BUF_SIZE 1024 -static const char *TAG = "GPS_SYNC"; +// --- GLOBAL STATE --- +// MUST be defined before gps_task uses it +static uart_port_t gps_uart_num = UART_NUM_1; // GPS sync state static int64_t monotonic_offset_us = 0; @@ -28,8 +28,6 @@ static volatile time_t next_pps_gps_second = 0; static bool gps_has_fix = false; static bool use_gps_for_logs = false; static SemaphoreHandle_t sync_mutex; - -// Force update flag (defaults to true so boot-up snaps immediately) static volatile bool force_sync_update = true; // PPS interrupt - captures exact monotonic time at second boundary @@ -98,77 +96,87 @@ void gps_force_next_update(void) { // GPS processing task static void gps_task(void* arg) { + // Buffer for UART reads (more efficient than reading 1 byte at a time) + uint8_t d_buf[64]; char line[128]; int pos = 0; - static int log_counter = 0; // NEW: Counter to throttle logs + static int log_counter = 0; // Counter to throttle logs while (1) { - uint8_t data; - int len = uart_read_bytes(GPS_UART_NUM, &data, 1, 100 / portTICK_PERIOD_MS); + // Read up to 64 bytes with a 100ms timeout + int len = uart_read_bytes(gps_uart_num, d_buf, sizeof(d_buf), pdMS_TO_TICKS(100)); if (len > 0) { - if (data == '\n') { - line[pos] = '\0'; + // Process all bytes received in this batch + for (int i = 0; i < len; i++) { + uint8_t data = d_buf[i]; - struct tm gps_tm; - bool valid; - if (parse_gprmc(line, &gps_tm, &valid)) { - if (valid) { - time_t gps_time = mktime(&gps_tm); + if (data == '\n') { + line[pos] = '\0'; - xSemaphoreTake(sync_mutex, portMAX_DELAY); - next_pps_gps_second = gps_time + 1; - xSemaphoreGive(sync_mutex); + struct tm gps_tm; + bool valid; + if (parse_gprmc(line, &gps_tm, &valid)) { + if (valid) { + time_t gps_time = mktime(&gps_tm); - vTaskDelay(pdMS_TO_TICKS(300)); + xSemaphoreTake(sync_mutex, portMAX_DELAY); + next_pps_gps_second = gps_time + 1; + xSemaphoreGive(sync_mutex); - xSemaphoreTake(sync_mutex, portMAX_DELAY); - if (last_pps_monotonic > 0) { - int64_t gps_us = (int64_t)next_pps_gps_second * 1000000LL; - int64_t new_offset = gps_us - last_pps_monotonic; + // Wait a bit to ensure PPS has likely fired if it was going to + vTaskDelay(pdMS_TO_TICKS(300)); - if (monotonic_offset_us == 0 || force_sync_update) { - monotonic_offset_us = new_offset; + xSemaphoreTake(sync_mutex, portMAX_DELAY); + if (last_pps_monotonic > 0) { + int64_t gps_us = (int64_t)next_pps_gps_second * 1000000LL; + int64_t new_offset = gps_us - last_pps_monotonic; - if (force_sync_update) { - ESP_LOGW(TAG, "GPS sync SNAP: Offset forced to %lld us", monotonic_offset_us); - force_sync_update = false; - log_counter = 0; // Ensure we see the log immediately after a snap + if (monotonic_offset_us == 0 || force_sync_update) { + monotonic_offset_us = new_offset; + + if (force_sync_update) { + ESP_LOGW(TAG, "GPS sync SNAP: Offset forced to %lld us", monotonic_offset_us); + force_sync_update = false; + log_counter = 0; // Ensure we see the log immediately after a snap + } + } else { + // Low-pass filter: 90% old + 10% new + monotonic_offset_us = (monotonic_offset_us * 9 + new_offset) / 10; } - } else { - // Low-pass filter: 90% old + 10% new - monotonic_offset_us = (monotonic_offset_us * 9 + new_offset) / 10; - } - gps_has_fix = true; + gps_has_fix = true; - // LOGGING THROTTLE: Only print every 60th update (approx 60 seconds) - if (log_counter == 0) { - ESP_LOGI(TAG, "GPS sync: %04d-%02d-%02d %02d:%02d:%02d, offset=%lld us", - gps_tm.tm_year + 1900, gps_tm.tm_mon + 1, gps_tm.tm_mday, - gps_tm.tm_hour, gps_tm.tm_min, gps_tm.tm_sec, - monotonic_offset_us); - log_counter = 60; + // LOGGING THROTTLE: Only print every 60th update (approx 60 seconds) + if (log_counter == 0) { + ESP_LOGI(TAG, "GPS sync: %04d-%02d-%02d %02d:%02d:%02d, offset=%lld us", + gps_tm.tm_year + 1900, gps_tm.tm_mon + 1, gps_tm.tm_mday, + gps_tm.tm_hour, gps_tm.tm_min, gps_tm.tm_sec, + monotonic_offset_us); + log_counter = 60; + } + log_counter--; } - log_counter--; + xSemaphoreGive(sync_mutex); + } else { + gps_has_fix = false; } - xSemaphoreGive(sync_mutex); - } else { - gps_has_fix = false; } - } - pos = 0; - } else if (pos < sizeof(line) - 1) { - line[pos++] = data; + pos = 0; + } else if (pos < sizeof(line) - 1) { + line[pos++] = data; + } } } } } -void gps_sync_init(bool use_gps_log_timestamps) { +void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps) { ESP_LOGI(TAG, "Initializing GPS sync"); + // 1. Store the UART port for the task to use + gps_uart_num = config->uart_port; use_gps_for_logs = use_gps_log_timestamps; // Ensure we start with a forced update @@ -176,12 +184,12 @@ void gps_sync_init(bool use_gps_log_timestamps) { if (use_gps_log_timestamps) { ESP_LOGI(TAG, "ESP_LOG timestamps: GPS time in seconds.milliseconds format"); - // Override vprintf to add decimal point to timestamps esp_log_set_vprintf(gps_log_vprintf); } sync_mutex = xSemaphoreCreateMutex(); + // 2. Configure UART uart_config_t uart_config = { .baud_rate = GPS_BAUD_RATE, .data_bits = UART_DATA_8_BITS, @@ -191,26 +199,34 @@ void gps_sync_init(bool use_gps_log_timestamps) { .source_clk = UART_SCLK_DEFAULT, }; - ESP_ERROR_CHECK(uart_driver_install(GPS_UART_NUM, UART_BUF_SIZE, 0, 0, NULL, 0)); - ESP_ERROR_CHECK(uart_param_config(GPS_UART_NUM, &uart_config)); - ESP_ERROR_CHECK(uart_set_pin(GPS_UART_NUM, GPS_TX_PIN, GPS_RX_PIN, - UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_driver_install(config->uart_port, UART_BUF_SIZE, 0, 0, NULL, 0)); + ESP_ERROR_CHECK(uart_param_config(config->uart_port, &uart_config)); + // 3. Set Pins (Dynamic Configuration) + ESP_ERROR_CHECK(uart_set_pin(config->uart_port, + config->tx_pin, + config->rx_pin, + UART_PIN_NO_CHANGE, + UART_PIN_NO_CHANGE)); + + // 4. Configure PPS GPIO gpio_config_t io_conf = { .intr_type = GPIO_INTR_POSEDGE, .mode = GPIO_MODE_INPUT, - .pin_bit_mask = (1ULL << PPS_GPIO), + .pin_bit_mask = (1ULL << config->pps_pin), .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, }; ESP_ERROR_CHECK(gpio_config(&io_conf)); - ESP_ERROR_CHECK(gpio_install_isr_service(0)); - ESP_ERROR_CHECK(gpio_isr_handler_add(PPS_GPIO, pps_isr_handler, NULL)); + // 5. Install ISR + gpio_install_isr_service(0); + ESP_ERROR_CHECK(gpio_isr_handler_add(config->pps_pin, pps_isr_handler, NULL)); xTaskCreate(gps_task, "gps_task", 4096, NULL, 5, NULL); - ESP_LOGI(TAG, "GPS sync initialized (RX=GPIO%d, PPS=GPIO%d)", GPS_RX_PIN, PPS_GPIO); + ESP_LOGI(TAG, "GPS sync initialized (UART=%d, RX=%d, TX=%d, PPS=%d)", + config->uart_port, config->rx_pin, config->tx_pin, config->pps_pin); } gps_timestamp_t gps_get_timestamp(void) { @@ -250,24 +266,16 @@ bool gps_is_synced(void) { // ---------------- LOGGING SYSTEM INTERCEPTION ---------------- -// We now only return standard system time (ms) to ESP-IDF. -// We do NOT return GPS time here because it overflows 32 bits. uint32_t gps_log_timestamp(void) { return (uint32_t)(esp_timer_get_time() / 1000ULL); } -// Intercepts the log line string before it is printed. -// It detects the timestamp `(1234)` which is monotonic ms, -// and mathematically converts it to `(+17544234.123)` GPS sec.ms int gps_log_vprintf(const char *fmt, va_list args) { static char buffer[512]; - - // Format the message into our buffer int ret = vsnprintf(buffer, sizeof(buffer), fmt, args); assert(ret >= 0); if (use_gps_for_logs) { - // Look for timestamp pattern: "I (", "W (", etc. char *timestamp_start = NULL; for (int i = 0; buffer[i] != '\0' && i < sizeof(buffer) - 20; i++) { if ((buffer[i] == 'I' || buffer[i] == 'W' || buffer[i] == 'E' || @@ -281,44 +289,33 @@ int gps_log_vprintf(const char *fmt, va_list args) { if (timestamp_start) { char *timestamp_end = strchr(timestamp_start, ')'); if (timestamp_end) { - // Parse the MONOTONIC ms that ESP-IDF put there uint32_t monotonic_log_ms = 0; if (sscanf(timestamp_start, "%lu", &monotonic_log_ms) == 1) { - char reformatted[512]; size_t prefix_len = timestamp_start - buffer; memcpy(reformatted, buffer, prefix_len); int decimal_len = 0; if (gps_has_fix) { - // MATH: Calculate GPS time based on the log's monotonic time int64_t log_mono_us = (int64_t)monotonic_log_ms * 1000; int64_t log_gps_us = log_mono_us + monotonic_offset_us; - - // Split into Seconds and Milliseconds uint64_t gps_sec = log_gps_us / 1000000; uint32_t gps_ms = (log_gps_us % 1000000) / 1000; - decimal_len = snprintf(reformatted + prefix_len, sizeof(reformatted) - prefix_len, "+%" PRIu64 ".%03lu", gps_sec, gps_ms); } else { - // No fix: just show monotonic nicely uint32_t sec = monotonic_log_ms / 1000; uint32_t ms = monotonic_log_ms % 1000; decimal_len = snprintf(reformatted + prefix_len, sizeof(reformatted) - prefix_len, "*%lu.%03lu", sec, ms); } - - // Copy the rest of the message (from the closing parenthesis onwards) strcpy(reformatted + prefix_len + decimal_len, timestamp_end); - return printf("%s", reformatted); } } } } - return printf("%s", buffer); } diff --git a/components/gps_sync/gps_sync.h b/components/gps_sync/gps_sync.h index 9348a17..665d6a1 100644 --- a/components/gps_sync/gps_sync.h +++ b/components/gps_sync/gps_sync.h @@ -1,40 +1,36 @@ #pragma once #include +#include #include +#include "driver/gpio.h" +#include "driver/uart.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" +typedef struct { + uart_port_t uart_port; + gpio_num_t tx_pin; + gpio_num_t rx_pin; + gpio_num_t pps_pin; +} gps_sync_config_t; + typedef struct { int64_t monotonic_us; // Microseconds - never jumps backward int64_t monotonic_ms; // Milliseconds - for easier logging int64_t gps_us; // GPS UTC time in microseconds int64_t gps_ms; // GPS UTC time in milliseconds - struct timespec mono_ts; // POSIX timespec (for clock_gettime) + struct timespec mono_ts; // POSIX timespec bool synced; // true if GPS has valid fix } gps_timestamp_t; -// Initialize GPS sync system -// If use_gps_log_timestamps is true, ESP_LOGI/ESP_LOGW/etc will use GPS time -// with visual indicators: -// I (+1733424645.234) TAG: message <-- + indicates GPS synced -// I (*1.234) TAG: message <-- * indicates not synced (monotonic) -void gps_sync_init(bool use_gps_log_timestamps); +void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps); -// FORCE UPDATE: Ignore the low-pass filter for the next valid GPS fix. -// This snaps the time offset immediately to the new value. -// Useful on boot or if you detect a massive time discrepancy. void gps_force_next_update(void); - -// Get current timestamp (with both us and ms) gps_timestamp_t gps_get_timestamp(void); - -// Get millisecond timestamp using clock_gettime int64_t gps_get_monotonic_ms(void); - -// Check if GPS is synced bool gps_is_synced(void); -// Internal functions (called automatically by ESP-IDF - don't call directly) +// Internal logging hooks uint32_t gps_log_timestamp(void); int gps_log_vprintf(const char *fmt, va_list args); diff --git a/main/main.c b/main/main.c index e4a9a0a..c339caa 100644 --- a/main/main.c +++ b/main/main.c @@ -26,14 +26,40 @@ #include "wifi_monitor.h" #include "gps_sync.h" +// --- BOARD CONFIGURATION --- +// --- Hardware Configuration --- +#if defined(CONFIG_IDF_TARGET_ESP32S3) + // ESP32-S3 Specific Wiring + #define RGB_LED_GPIO 48 // Standard S3 DevKit RGB pin + #define GPS_TX_PIN GPIO_NUM_5 + #define GPS_RX_PIN GPIO_NUM_4 + #define GPS_PPS_PIN GPIO_NUM_6 + +#elif defined(CONFIG_IDF_TARGET_ESP32C5) + // ESP32-C5 Specific Wiring + #define RGB_LED_GPIO 27 + #define GPS_TX_PIN GPIO_NUM_24 + #define GPS_RX_PIN GPIO_NUM_23 + #define GPS_PPS_PIN GPIO_NUM_25 + +#elif defined(CONFIG_IDF_TARGET_ESP32) + // ESP32 (Original) Specific Wiring + // Note: ESP32 has no GPIO 24. GPIO 8 causes flash crash. + #define RGB_LED_GPIO 2 // Often onboard Blue LED (valid GPIO) + #define GPS_TX_PIN GPIO_NUM_17 // Standard UART2 TX + #define GPS_RX_PIN GPIO_NUM_16 // Standard UART2 RX + #define GPS_PPS_PIN GPIO_NUM_4 + +#else + // Fallback / Other Chips (C6, etc.) + #define RGB_LED_GPIO 8 + #define GPS_TX_PIN GPIO_NUM_24 + #define GPS_RX_PIN GPIO_NUM_23 + #define GPS_PPS_PIN GPIO_NUM_25 +#endif + static const char *TAG = "MAIN"; -// --- Hardware Configuration --- -#if CONFIG_IDF_TARGET_ESP32C5 - #define RGB_LED_GPIO 27 -#else - #define RGB_LED_GPIO 8 -#endif // --- WiFi Operation Mode --- typedef enum { @@ -146,24 +172,38 @@ static void csi_cb(void *ctx, wifi_csi_info_t *info) { ESP_LOGI("CSI", "Captured %lu CSI packets", (unsigned long)s_csi_packet_count); } } - static void wifi_enable_csi_once(void) { if (s_csi_enabled) return; vTaskDelay(pdMS_TO_TICKS(2000)); - wifi_csi_config_t csi_cfg; - memset(&csi_cfg, 0, sizeof(csi_cfg)); - csi_cfg.enable = true; + + // Initialize with defaults (safe for all chips) + wifi_csi_config_t csi_cfg = { 0 }; + +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) + // These fields ONLY exist on Xtensa-based chips (S3, S2, Original) + // The ESP32-C5/C6 (RISC-V) hardware handles this automatically/internally. + csi_cfg.lltf_en = true; + csi_cfg.htltf_en = true; + csi_cfg.stbc_htltf2_en = true; + csi_cfg.ltf_merge_en = true; + csi_cfg.channel_filter_en = true; + csi_cfg.manu_scale = false; + csi_cfg.shift = false; +#endif ESP_LOGI("CSI", "Configuring CSI..."); if (esp_wifi_set_csi_config(&csi_cfg) != ESP_OK) { ESP_LOGE("CSI", "Failed to set CSI config"); return; } + if (esp_wifi_set_csi_rx_cb(csi_cb, NULL) != ESP_OK) { ESP_LOGE("CSI", "Failed to set CSI callback"); return; } + + // Explicit enable call if (esp_wifi_set_csi(true) != ESP_OK) { ESP_LOGE("CSI", "Failed to enable CSI"); return; @@ -755,8 +795,20 @@ void app_main(void) { // 4. Initialize GPS (Enable GPS-timestamped logs) ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Initializing GPS sync..."); - gps_sync_init(true); // true = GPS timestamps for ESP_LOG - ESP_LOGI(TAG, "GPS initialized"); + + // CONFIGURATION STRUCT: Maps the correct pins based on the chip type + const gps_sync_config_t gps_cfg = { + .uart_port = UART_NUM_1, + .tx_pin = GPS_TX_PIN, + .rx_pin = GPS_RX_PIN, + .pps_pin = GPS_PPS_PIN, + }; + + // Pass the config and enable GPS timestamps in logs + gps_sync_init(&gps_cfg, true); + + ESP_LOGI(TAG, "GPS initialized on UART1 (TX:%d, RX:%d, PPS:%d)", + GPS_TX_PIN, GPS_RX_PIN, GPS_PPS_PIN); ESP_LOGI(TAG, " - Waiting for GPS lock..."); ESP_LOGI(TAG, " - Timestamps: (*) = not synced, (+) = GPS synced"); ESP_LOGI(TAG, "========================================");