Arduino Leonardo运行数轮循环后卡顿问题:原因及解决方法
嘿,看起来你的GPS追踪器遇到了典型的串口资源冲突或者阻塞问题!前几轮正常之后卡顿,大概率是SoftwareSerial的局限性或者代码里的阻塞操作搞的鬼,我给你拆解几个可能的原因和解决办法:
可能的原因&解决办法
1. SoftwareSerial拖垮了系统资源
SoftwareSerial是用模拟IO实现的串口,本质上靠CPU不断轮询引脚,而且不能同时收发,还会临时禁用其他中断——这就麻烦了:当你的HardwareSerial(GPS)在输出数据时,SoftwareSerial(GPRS)的操作可能会打断它,或者反过来导致数据堆积,最后系统因为处理不过来直接卡死。
解决办法:
- 如果你的板子有多余的硬件串口(比如Arduino Mega有3个额外的HardwareSerial),直接把GPRS接到硬件串口上,彻底摆脱SoftwareSerial的限制。
- 必须用SoftwareSerial的话,绝对不要在GPRS操作时用
delay(),改用millis()做非阻塞计时,同时尽量把波特率降到9600(高波特率下SoftwareSerial的误差极大,容易丢数据)。
2. 串口缓冲区溢出导致崩溃
GPS模块会不断输出NMEA语句,GPRS模块也会返回AT指令响应,如果你的代码没有及时读取这些数据,串口缓冲区会被填满,最后溢出导致系统卡死——前几轮数据少还能撑住,多了就崩了。
解决办法:
- 在
loop()里优先处理串口数据,不管你用不用,都要及时读取:
// 处理GPS数据,避免缓冲区溢出 while(gpsSerial.available()) { gps.processData(); // 或者直接用read()清空缓冲区 } // 处理GPRS响应,避免堆积 while(gprsSerial.available()) { char c = gprsSerial.read(); // 可以打印出来调试,或者直接忽略 }
- 绝对不要在循环里创建对象或者分配大量内存,比如把所有变量定义在全局或者
setup()里,避免内存泄漏。
3. 供电不足导致模块罢工
GPS和GPRS都是耗电大户,尤其是GPRS发送数据时瞬间电流能到几百毫安。前几次发送可能电源还能扛住,之后电压被拉低,模块要么重启要么直接罢工,系统自然就卡顿了。
解决办法:
- 换个功率足够的电源:比如5V/2A以上的适配器,或者用带升压模块的锂电池(比如3.7V锂电池升到5V,输出电流至少1A)。
- 在模块的电源引脚旁加滤波电容:1000uF的电解电容+0.1uF的陶瓷电容,能缓解电压波动带来的不稳定。
4. 阻塞式操作卡死了系统
如果你的代码里用了delay(INTERVAL)或者GPRS的阻塞式发送函数(比如sendData()一直等响应),系统会在这段时间里完全停住,串口数据没人处理,堆积多了就崩了。
解决办法:
- 把所有
delay()换成millis()的非阻塞计时,像这样:
unsigned long previousMillis = 0; const long interval = INTERVAL; void loop() { unsigned long currentMillis = millis(); if(currentMillis - previousMillis >= interval) { previousMillis = currentMillis; // 这里执行发送数据的操作 } // 其余时间处理串口数据 }
- 调用GPRS发送函数时,不要一直等待响应,先检查模块是否就绪,或者设置超时时间,避免无限阻塞。
优化后的代码示例
这里给你改了个非阻塞的版本,你可以参考:
#include <GPRS_Shield_Arduino.h> #include <TroykaGPS.h> #include <SoftwareSerial.h> #define IMEI "你的IMEI号" #define INTERVAL 5000 // 发送间隔(毫秒) #define GPRS_APN "你的运营商APN" #define GPRS_USER "" // 大部分APN不需要用户名密码 #define GPRS_PWD "" // 硬件串口接GPS(Mega用Serial1,Uno如果用Serial要注意和USB串口冲突) HardwareSerial gpsSerial(1); TroykaGPS gps(gpsSerial); // SoftwareSerial接GPRS,选支持中断的引脚(Uno的2、3脚) SoftwareSerial gprsSerial(2, 3); GPRS gprs(gprsSerial); unsigned long lastSendTime = 0; void setup() { Serial.begin(9600); gpsSerial.begin(9600); gprsSerial.begin(9600); // 等待GPS定位 Serial.println("等待GPS定位..."); while(!gps.waitForFix(10000)) { Serial.println("未定位,继续等待..."); } Serial.println("GPS定位成功!"); // 连接GPRS Serial.println("连接GPRS..."); if(gprs.attachGPRS(GPRS_APN, GPRS_USER, GPRS_PWD)) { Serial.println("GPRS连接成功!"); } else { Serial.println("GPRS连接失败,请检查APN设置!"); } } void loop() { unsigned long now = millis(); // 优先处理串口数据,避免缓冲区溢出 while(gpsSerial.available()) { gps.processData(); } while(gprsSerial.available()) { char c = gprsSerial.read(); Serial.print(c); // 打印GPRS响应方便调试 } // 非阻塞式发送数据 if(now - lastSendTime >= INTERVAL) { lastSendTime = now; if(gps.isFixed()) { // 组装GPS数据 String gpsData = String(gps.getLatitude(), 6) + "," + String(gps.getLongitude(), 6) + "," + String(gps.getAltitude()); Serial.println("准备发送数据: " + gpsData); // 发送到服务器(替换成你的服务器IP和端口) if(gprs.sendTCP("192.168.1.100", 8080, gpsData)) { Serial.println("数据发送成功!"); } else { Serial.println("发送失败,稍后重试..."); } } else { Serial.println("GPS未定位,跳过发送"); } } }
内容的提问来源于stack exchange,提问作者Ilya Savitsky
相关产品推荐
相关产品推荐

