基于微雪ESP32-S3-Touch-LCD-1.47,基于ESP-IDF 5.5.x,该代码目的适配ATGM和UBLOX部分GPS模块,可以热切换115200、38400波特率,实测115200波特率下可以实现5Hz下的NMEA语句持续平稳透传,10Hz下不太稳定。
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/ringbuf.h"
#include "driver/uart.h"
#include "driver/usb_serial_jtag.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "lwip/sockets.h"
#include "esp_timer.h"
/* ================== 配置常量 ================== */
#define TAG "GPS_BRIDGE"
// 物理串口
#define GPS_UART_NUM UART_NUM_1
#define GPS_TX_PIN 43
#define GPS_RX_PIN 44
// 缓存大小
#define UART_BUF_SIZE 2048
#define RINGBUF_SIZE (16 * 1024)
// 网络配置
#define TCP_PORT 8080
#define WIFI_SSID "ESP32S3_GPS"
#define WIFI_PASS "12345678"
// GPS参数
static const int baud_list[] = {115200, 38400};
#define BAUD_NUM (sizeof(baud_list) / sizeof(baud_list[0]))
#define DETECT_TIMEOUT_MS 1500 // 每个波特率停留尝试时间
#define LOSS_TIMEOUT_SEC 5 // 5秒无NMEA数据则判定丢失
/* ================== 全局句柄 ================== */
static RingbufHandle_t uart_rb;
static SemaphoreHandle_t sock_mutex;
static SemaphoreHandle_t uart_mutex;
static int client_sock = -1;
static int current_baud_idx = 0;
static bool baud_locked = false;
/* ================== 工具函数 ================== */
// 安全地重新初始化 UART
static void uart_reinit(int baudrate)
{
xSemaphoreTake(uart_mutex, portMAX_DELAY);
if (uart_is_driver_installed(GPS_UART_NUM)) {
uart_driver_delete(GPS_UART_NUM);
}
uart_config_t cfg = {
.baud_rate = baudrate,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// 安装驱动:缓冲区设为 UART_BUF_SIZE 的 2 倍
uart_driver_install(GPS_UART_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(GPS_UART_NUM, &cfg);
uart_set_pin(GPS_UART_NUM, GPS_TX_PIN, GPS_RX_PIN, -1, -1);
ESP_LOGW(TAG, "UART re-init: %d baud, scanning for GPS...", baudrate);
xSemaphoreGive(uart_mutex);
}
// 协议特征检测
static bool is_valid_nmea(const uint8_t *buf, int len)
{
if (len < 6) return false;
// NMEA 标准报文必须以 $ 开头
if (buf[0] != '$') return false;
// 检查常见的 NMEA 类型标识符
if (memmem(buf, len, "GGA", 3) ||
memmem(buf, len, "RMC", 3) ||
memmem(buf, len, "GSA", 3) ||
memmem(buf, len, "GSV", 3)) {
return true;
}
return false;
}
/* ================== 核心任务 ================== */
/**
* 任务1: UART 接收与波特率自动匹配
* 优先级: 高 (10)
*/
static void gps_rx_task(void *arg)
{
static uint8_t rx_buf[UART_BUF_SIZE];
int64_t last_baud_switch = 0;
int64_t last_valid_data = 0;
uart_reinit(baud_list[current_baud_idx]);
last_baud_switch = esp_timer_get_time();
while (1) {
// 使用锁保护 UART 读取过程
xSemaphoreTake(uart_mutex, portMAX_DELAY);
int len = uart_read_bytes(GPS_UART_NUM, rx_buf, sizeof(rx_buf), pdMS_TO_TICKS(20));
xSemaphoreGive(uart_mutex);
int64_t now = esp_timer_get_time();
if (len > 0) {
if (is_valid_nmea(rx_buf, len)) {
last_valid_data = now;
if (!baud_locked) {
baud_locked = true;
ESP_LOGI(TAG, ">>> GPS Locked @ %d baud <<<", baud_list[current_baud_idx]);
}
}
// 只有锁定了正确的波特率才转发数据,过滤扫描时的乱码
if (baud_locked) {
xRingbufferSend(uart_rb, rx_buf, len, 0);
}
}
/* 状态机切换逻辑 */
if (!baud_locked) {
// 未锁定时:超时轮换波特率
if ((now - last_baud_switch) / 1000 > DETECT_TIMEOUT_MS) {
current_baud_idx = (current_baud_idx + 1) % BAUD_NUM;
uart_reinit(baud_list[current_baud_idx]);
last_baud_switch = now;
}
} else {
// 已锁定时:长时间无有效 NMEA 信号判定为丢失
if ((now - last_valid_data) / 1000000 > LOSS_TIMEOUT_SEC) {
ESP_LOGE(TAG, "GPS signal lost, restarting scan...");
baud_locked = false;
last_baud_switch = now;
}
}
}
}
/**
* 任务2: 数据多路分发 (UART -> USB & WiFi)
* 优先级: 中 (6)
*/
static void tx_dispatcher_task(void *arg)
{
size_t len;
uint8_t *data;
while (1) {
// 从环形缓冲区提取数据,最大等待
data = (uint8_t *)xRingbufferReceive(uart_rb, &len, portMAX_DELAY);
if (!data) continue;
// 1. 发送到物理 USB (CDC)
if (usb_serial_jtag_is_connected()) {
usb_serial_jtag_write_bytes(data, len, 0);
}
// 2. 发送到 TCP 客户端
if (xSemaphoreTake(sock_mutex, 0) == pdTRUE) {
if (client_sock != -1) {
// 使用非阻塞发送,防止 WiFi 拥塞卡住串口读写
int sent = send(client_sock, data, len, MSG_DONTWAIT);
if (sent < 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) {
ESP_LOGW(TAG, "TCP send error, client might be dead");
}
}
xSemaphoreGive(sock_mutex);
}
vRingbufferReturnItem(uart_rb, data);
}
}
/**
* 任务3: 反向路径 USB -> UART
*/
static void usb_rx_task(void *arg)
{
static uint8_t buf[512];
while (1) {
int len = usb_serial_jtag_read_bytes(buf, sizeof(buf), pdMS_TO_TICKS(10));
if (len > 0 && baud_locked) {
xSemaphoreTake(uart_mutex, portMAX_DELAY);
uart_write_bytes(GPS_UART_NUM, (char *)buf, len);
xSemaphoreGive(uart_mutex);
}
}
}
/**
* 任务4: TCP 服务端 (WiFi -> UART)
*/
static void tcp_server_task(void *arg)
{
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(TCP_PORT),
.sin_addr.s_addr = htonl(INADDR_ANY)
};
int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr));
listen(listen_sock, 1);
static uint8_t buf[512];
while (1) {
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sock = accept(listen_sock, (struct sockaddr *)&client, &len);
if (sock < 0) continue;
xSemaphoreTake(sock_mutex, portMAX_DELAY);
if (client_sock != -1) close(client_sock);
client_sock = sock;
xSemaphoreGive(sock_mutex);
ESP_LOGI(TAG, "New TCP client connected");
while (1) {
int r = recv(sock, buf, sizeof(buf), 0);
if (r <= 0) break;
if (baud_locked) {
xSemaphoreTake(uart_mutex, portMAX_DELAY);
uart_write_bytes(GPS_UART_NUM, (char *)buf, r);
xSemaphoreGive(uart_mutex);
}
}
ESP_LOGI(TAG, "TCP client disconnected");
xSemaphoreTake(sock_mutex, portMAX_DELAY);
if (client_sock == sock) client_sock = -1;
close(sock);
xSemaphoreGive(sock_mutex);
}
}
/* ================== 系统初始化 ================== */
static void wifi_init_softap(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t ap_cfg = {
.ap = {
.ssid = WIFI_SSID,
.ssid_len = strlen(WIFI_SSID),
.password = WIFI_PASS,
.channel = 1,
.max_connection = 4,
.authmode = WIFI_AUTH_WPA2_PSK
}
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_cfg));
ESP_ERROR_CHECK(esp_wifi_start());
}
void app_main(void)
{
// 1. 初始化存储
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 2. 初始化同步组件与缓冲区
uart_rb = xRingbufferCreate(RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
sock_mutex = xSemaphoreCreateMutex();
uart_mutex = xSemaphoreCreateMutex();
// 3. 初始化 USB 驱动
usb_serial_jtag_driver_config_t usb_cfg = {
.rx_buffer_size = 2048,
.tx_buffer_size = 2048
};
usb_serial_jtag_driver_install(&usb_cfg);
// 4. 初始化 WiFi
wifi_init_softap();
// 5. 创建任务集群
// 核心 0 处理实时性最强的串口 IO
xTaskCreatePinnedToCore(gps_rx_task, "gps_rx", 4096, NULL, 10, NULL, 0);
xTaskCreatePinnedToCore(usb_rx_task, "usb_rx", 4096, NULL, 5, NULL, 0);
// 核心 1 处理数据分发和网络服务
xTaskCreatePinnedToCore(tx_dispatcher_task, "tx_dis", 4096, NULL, 6, NULL, 1);
xTaskCreatePinnedToCore(tcp_server_task, "tcp_srv", 4096, NULL, 4, NULL, 1);
ESP_LOGI(TAG, "Bridge System Started. Use TCP 192.168.4.1:8080 or USB JTAG Serial.");
}

发表回复