compiles on targets esp32, esp32-s3, and esp32-c5

This commit is contained in:
Bob 2025-12-07 19:57:41 -08:00
parent 7924da11c7
commit 0e2cea8da7
3 changed files with 155 additions and 110 deletions

View File

@ -11,15 +11,15 @@
#include <assert.h>
#include <inttypes.h>
// --- 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,15 +96,21 @@ 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) {
// Process all bytes received in this batch
for (int i = 0; i < len; i++) {
uint8_t data = d_buf[i];
if (data == '\n') {
line[pos] = '\0';
@ -120,6 +124,7 @@ static void gps_task(void* arg) {
next_pps_gps_second = gps_time + 1;
xSemaphoreGive(sync_mutex);
// Wait a bit to ensure PPS has likely fired if it was going to
vTaskDelay(pdMS_TO_TICKS(300));
xSemaphoreTake(sync_mutex, portMAX_DELAY);
@ -165,10 +170,13 @@ static void gps_task(void* arg) {
}
}
}
}
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);
}

View File

@ -1,40 +1,36 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#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);

View File

@ -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, "========================================");