基于A9G与ESP8266 D1 Mini实现全自动GPS追踪器的代码修改方案
A9G+ESP8266 全自动GPS追踪代码修改方案
核心修改点
- 上电自动完成GSM、GPS初始化,无需手动输入指令
- 增加定位数据有效性校验,过滤空值、未定位成功的无效数据
- 采用非阻塞计时实现20分钟固定间隔发送,避免
delay()卡死串口通信逻辑 - 首次获取有效定位后自动发送第一条短信,之后循环按周期发送直到断电
- 保留原有手动调试指令功能,不影响串口调试
具体修改说明
- 新增全局变量
增加目标手机号宏定义、非阻塞计时变量、运行状态标记,替换原来硬编码的手机号、固定位置截取字符串的脆弱逻辑。 - setup阶段自动初始化
移除原有手动操作提示的冗余打印,增加A9G模块上电就绪等待时间,模块启动后自动开启GPS功能,完成短信模式配置。 - 优化定位数据解析逻辑
原代码固定从第33位截取字符串的逻辑容错性极差,不同固件版本返回长度差异会直接导致取数错误。修改后先匹配A9G定位返回的固定前缀+LOCATION:,再提取经纬度字段,同时校验字段长度、排除未定位时返回的空值/全0值。 - 非阻塞周期发送逻辑
用ESP8266内置的millis()函数做时间差计算,不会阻塞串口数据接收:- 首次拿到有效定位立刻触发短信发送,标记首条发送状态,记录发送时间戳
- 之后每轮循环判断当前时间与上次发送时间的差值,达到20分钟(1200000毫秒)就触发发送,更新时间戳
- 优化串口接收逻辑
缩短串口超时时间,避免readString()长时间等待阻塞主循环,每次发送短信后清空接收缓存,避免模块返回的指令应答干扰定位解析。
修改后完整代码
// GPS tracker with AI Thinker A9G module and ESP8266 D1 Mini #include <SoftwareSerial.h> #define rxPin D7 #define txPin D8 #define TARGET_PHONE "+86xxxxxxxxxxx" // 替换为接收短信的手机号,国内号码加+86前缀 #define SEND_INTERVAL 1200000 // 发送间隔,20分钟=1200000毫秒 SoftwareSerial A9modem(rxPin, txPin); String incomingData; String gpsData = ""; unsigned long lastSendTime = 0; bool firstSendDone = false; bool gpsStarted = false; void setup() { Serial.begin(115200); A9modem.begin(115200); A9modem.setTimeout(500); // 缩短串口超时,避免阻塞 Serial.println("Auto GPS Tracker Start"); // 等待A9G模块上电就绪 delay(3000); // 配置短信文本模式 A9modem.println("AT+CMGF=1\r"); delay(500); A9modem.println("AT+CNMI=2,2,0,0,0\r"); delay(500); // 自动启动GPS StartGPS(); gpsStarted = true; } void loop() { // 持续接收模块返回数据 ReceiveMessage(); // 解析有效定位数据 if (incomingData.startsWith("+LOCATION:")) { // 提取经纬度部分,跳过返回头 int locStartIndex = incomingData.indexOf(":") + 2; if (locStartIndex < incomingData.length()) { String tempLoc = incomingData.substring(locStartIndex); tempLoc.trim(); // 校验定位有效性:长度足够、不是无效的0,0返回 if (tempLoc.length() >= 15 && tempLoc != "0.000000,0.000000") { gpsData = tempLoc; Serial.print("Valid Location Got: "); Serial.println(gpsData); } } } // 自动发送逻辑 if (gpsData.length() > 0) { // 首次有效定位立刻发送 if (!firstSendDone) { SendMessage(); firstSendDone = true; lastSendTime = millis(); Serial.println("First location SMS sent"); } // 达到间隔周期发送 else if (millis() - lastSendTime >= SEND_INTERVAL) { SendMessage(); lastSendTime = millis(); Serial.println("Periodic location SMS sent"); } } // 保留原有手动调试功能 if (Serial.available() > 0) { switch(Serial.read()) { case 's': SendMessage(); break; case 'r': ReceiveMessage(); break; case 'c': MakeCall(); break; case 'h': HangupCall(); break; case 'l': ReadLocation(); break; case 'd': DisconnectGps(); break; } } // 定期请求定位数据,每2秒读一次 static unsigned long lastLocReq = 0; if (millis() - lastLocReq >= 2000 && gpsStarted) { ReadLocation(); lastLocReq = millis(); } } void ReceiveMessage() { incomingData = ""; if (A9modem.available() > 0) { incomingData = A9modem.readString(); Serial.print(incomingData); } } void SendMessage() { if (gpsData.length() == 0) { Serial.println("No valid location, skip send"); return; } A9modem.println("AT+CMGF=1"); delay(1000); A9modem.print("AT+CMGS=\""); A9modem.print(TARGET_PHONE); A9modem.println("\"\r"); delay(1000); String sms = "http://maps.google.com/maps?q=" + gpsData; A9modem.println(sms); delay(100); A9modem.println((char)26); // CTRL+Z结束短信输入 delay(1000); // 清空接收缓存避免返回数据干扰解析 while(A9modem.available()) A9modem.read(); } void MakeCall() { A9modem.print("ATD+"); A9modem.print(TARGET_PHONE); A9modem.println(";"); Serial.println("Calling"); delay(1000); } void HangupCall() { A9modem.println("ATH"); Serial.println("Hangup Call"); delay(1000); } void DisconnectGps() { A9modem.println("AT+GPS=0"); Serial.println("GPS Disconnected"); gpsStarted = false; delay(1000); } void StartGPS() { A9modem.println("AT+GPS=1"); Serial.println("GPS Started, waiting for satellite fix..."); delay(2000); } void ReadLocation() { A9modem.println("AT+LOCATION=2"); }
注意事项
- 冷启动首次搜星需要在室外开阔环境进行,室内、遮挡严重的区域可能数分钟都无法获取有效定位,属于正常现象
- 代码中20分钟间隔可通过修改
SEND_INTERVAL宏定义调整,单位为毫秒 - 若使用物联网卡,需确认卡已开通短信权限,且有足够余额
- A9G模块供电需保证5V/2A以上,供电不足会导致搜网、搜星失败甚至模块反复重启
内容的提问来源于stack exchange,提问作者Vetinari
相关产品推荐
相关产品推荐

