You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 CAM(USB版)通过HardwareSerial无法获取NEO6M数据求助

ESP32 CAM读取NEO6M GPS数据故障调试

问题描述

使用带USB的ESP32 CAM通过HardwareSerial读取NEO6M GPS模块数据时无法正常获取数据,需要借助TinyGPS++将接收的数据格式化为易读内容,调试以下代码使其正常运行:

// libraries
#include "TinyGPS++.h"
#include "TimeLib.h"

HardwareSerial NEO6M(1); // tx, rx

// constants
#define CHAR_NUM 110
#define timeOffset 28800 // UTC-8

// variables
char latitude[11];
char longitude[11];
char Date[] = "00/00";
char Time[] = "00:00";
byte last_second, Second, Minute, Hour, Day, Month;
int Year;
 
// texts
char nameText[CHAR_NUM], alcoholLevelText[CHAR_NUM], locationText[CHAR_NUM], dateTimeText[CHAR_NUM];

// tinygps++ object
TinyGPSPlus gps;

void setup() {
  // serial monitor setup
  Serial.begin(9600);
  NEO6M.begin(9600, SERIAL_8N1, 14, 16); // TX/RX

  // check if serial monitor is available
  if (Serial.available()>0) {
    Serial.println("Serial monitor is available");
  } else {
    Serial.println("Serial monitor is not available!");
  }
  
}

void loop() {
  useNEO6M();
  
  Serial.println(locationText);
  Serial.println(dateTimeText);

  snprintf(locationText, sizeof(locationText), "Location: %s,%s",
           latitude, longitude);
  snprintf(dateTimeText, sizeof(dateTimeText), "Date: %s\nTime: %s",
           Date, Time);
           
  delay(1000);
}

void useNEO6M() {
  char latitudeTemp[11];
  char longitudeTemp[11];
  
  while (NEO6M.available() > 0) {
      gps.encode(NEO6M.read());
      Serial.println(latitudeTemp);
      dtostrf(gps.location.lat(), 9, 6, latitudeTemp);
      dtostrf(gps.location.lng(), 9, 6, longitudeTemp);

      if (gps.time.isValid()) {
        Second = gps.time.second();
        Minute = gps.time.minute();
        Hour = gps.time.hour();
      }

      if (gps.date.isValid()) {
        Day = gps.date.day() + 1;
        Month = gps.date.month();
        Year = gps.date.year();
      }
      
      if (last_second != gps.time.second()) {
        last_second = gps.time.second();

        setTime(Hour, Minute, Second, Day, Month, Year);

        adjustTime(timeOffset);
    
        changeLatitude(latitudeTemp);
        changeLongitude(longitudeTemp);
        changeTime();
        changeDate();
      }
      
  }
  
}

输出结果显示程序无法从NEO6M GPS模块获取数据。


调试方案

1. 修正串口初始化与引脚选择

ESP32 CAM的UART1默认引脚(TX=10、RX=9)可能与板载SD卡功能冲突,且你当前使用的14/16引脚需要确认硬件兼容性。另外必须确保GPS模块的TX接ESP32的RX,RX接ESP32的TX,接线反了会导致无数据。

修改后的串口初始化代码:

// 改用UART2,选择ESP32 CAM空闲引脚(示例:TX=17,RX=16,根据实际接线调整)
HardwareSerial NEO6M(2);

void setup() {
  Serial.begin(9600);
  // 初始化GPS串口,波特率匹配NEO6M默认的9600
  NEO6M.begin(9600, SERIAL_8N1, 16, 17);
  // 移除无效的串口监视器检测逻辑,Serial.available()仅检测是否有输入数据
  Serial.println("GPS reading started...");
}

2. 修复TinyGPS++解析逻辑

原代码在每次读取一个字节后立刻尝试获取经纬度,此时GPS还未完成NMEA语句解析,必然得到无效值。需先将所有串口数据喂给TinyGPS++,再判断数据有效性后更新内容。

修改useNEO6M()函数:

void useNEO6M() {
  // 先将所有可用串口数据喂给TinyGPS++解析
  while (NEO6M.available() > 0) {
    gps.encode(NEO6M.read());
  }

  // 仅当位置有效时更新经纬度
  if (gps.location.isValid()) {
    char latitudeTemp[11];
    char longitudeTemp[11];
    dtostrf(gps.location.lat(), 9, 6, latitudeTemp);
    dtostrf(gps.location.lng(), 9, 6, longitudeTemp);
    // 复制到全局变量(替换原changeLatitude/changeLongitude逻辑)
    strncpy(latitude, latitudeTemp, sizeof(latitude)-1);
    latitude[sizeof(latitude)-1] = '\0';
    strncpy(longitude, longitudeTemp, sizeof(longitude)-1);
    longitude[sizeof(longitude)-1] = '\0';
  }

  // 时间日期更新:仅当时间日期有效且秒数变化时更新
  if (gps.time.isValid() && gps.date.isValid()) {
    if (last_second != gps.time.second()) {
      last_second = gps.time.second();
      // 获取GPS原始时间
      Hour = gps.time.hour();
      Minute = gps.time.minute();
      Second = gps.time.second();
      Day = gps.date.day();
      Month = gps.date.month();
      Year = gps.date.year();

      // 调整为UTC+8时区(替代TimeLib的adjustTime)
      Hour = (Hour + 8) % 24;

      // 更新时间日期字符串(替换原changeTime/changeDate逻辑)
      snprintf(Time, sizeof(Time), "%02d:%02d:%02d", Hour, Minute, Second);
      snprintf(Date, sizeof(Date), "%02d/%02d/%04d", Month, Day, Year);
    }
  }
}

3. 调整输出顺序

原loop()中先打印再更新文本,导致输出滞后一次。需先更新内容再打印:

void loop() {
  useNEO6M();
  
  // 先构建输出文本,加入有效性判断
  snprintf(locationText, sizeof(locationText), "Location: %s,%s",
           gps.location.isValid() ? latitude : "Invalid",
           gps.location.isValid() ? longitude : "Invalid");
  snprintf(dateTimeText, sizeof(dateTimeText), "Date: %s\nTime: %s",
           gps.date.isValid() ? Date : "Invalid",
           gps.time.isValid() ? Time : "Invalid");
           
  Serial.println(locationText);
  Serial.println(dateTimeText);

  delay(1000);
}

4. 补充缺失的辅助函数(可选)

若坚持使用原代码中的changeLatitude等函数,需补充实现:

void changeLatitude(char* src) {
  strncpy(latitude, src, sizeof(latitude)-1);
  latitude[sizeof(latitude)-1] = '\0';
}

void changeLongitude(char* src) {
  strncpy(longitude, src, sizeof(longitude)-1);
  longitude[sizeof(longitude)-1] = '\0';
}

void changeTime() {
  snprintf(Time, sizeof(Time), "%02d:%02d:%02d", Hour, Minute, Second);
}

void changeDate() {
  snprintf(Date, sizeof(Date), "%02d/%02d", Month, Day);
}

5. 硬件排查要点

  • 供电:NEO6M必须使用3.3V供电,禁止用5V供电,否则会损坏模块或导致数据异常。
  • 接线:确认GPS模块的TX接ESP32的RX,RX接ESP32的TX,GND共地。
  • 定位环境:GPS需要开阔天空,室内无法定位,建议拿到室外测试。

内容的提问来源于stack exchange,提问作者Christian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:24:56