PMS5003传感器搭配ESP8266出现校验和错误求助
解决PMS5003校验和错误及ESP8266通信冲突问题
核心问题分析
- 多软件串口并发冲突:Arduino的
SoftwareSerial是软件模拟实现,同一时刻只能处理一个串口的收发。ESP8266的高波特率AT指令通信会抢占CPU,导致PMS5003的串口数据丢包,引发校验和错误。 - PMS5003读取逻辑不完善:原代码仅检测起始字节
0x42就直接读取32字节,未确保完整帧到达,容易读入不完整数据。 - ESP8266通信流程冗余:每次发送数据都重新建立TCP连接,耗时且阻塞传感器读取流程。
具体修复方案
1. 优化软件串口使用,避免并发冲突
通过切换软件串口的活动状态,确保读取PMS5003时,ESP8266的串口暂时停止工作,读取完成后再恢复:
// 在loop中读取PMS5003前添加 espSerial.end(); pmsSerial.listen(); // 让SoftwareSerial专注监听PMS5003 // 读取PMS数据... pmsSerial.end(); espSerial.begin(115200); // 恢复ESP8266串口通信
2. 完善PMS5003数据读取逻辑
严格校验帧起始序列0x42+0x4D,确保完整32字节帧到达后再解析,同时优化字节序转换和校验和计算:
boolean readPMSdata(Stream *s) { // 等待起始字节0x42,清空无效数据 while (s->available()) { if (s->read() == 0x42) { // 验证第二个起始字节0x4D if (s->available() && s->read() == 0x4D) { uint8_t buffer[32]; buffer[0] = 0x42; buffer[1] = 0x4D; // 读取剩余30字节,确保全部读取成功 if (s->readBytes(buffer + 2, 30) != 30) { Serial.println("Frame incomplete"); return false; } // 计算校验和:从0x42到最后一个数据字节的总和 uint16_t sum = 0; for (uint8_t i = 0; i < 30; i++) { sum += buffer[i]; } // 转换字节序并解析到结构体 uint16_t buffer_u16[15]; for (uint8_t i = 0; i < 15; i++) { buffer_u16[i] = (buffer[2 + i*2] << 8) | buffer[2 + i*2 + 1]; } memcpy((void *)&data, (void *)buffer_u16, 30); // 校验和验证 if (sum != data.checksum) { Serial.println("Checksum failure"); return false; } return true; } } } return false; }
3. 优化ESP8266通信流程,减少阻塞
只在初始化时建立一次WiFi和TCP连接,后续复用连接发送数据,避免每次发送都重新连接:
void setup(){ Serial.begin(115200); espSerial.begin(115200); delay(500); // 初始化ESP8266网络,仅执行一次 espData("AT+RST", 1000, DEBUG); espData("AT+CWMODE=1", 1000, DEBUG); espData("AT+CWJAP=\""+ mySSID +"\",\""+ myPWD +"\"", 5000, DEBUG); // 延长WiFi连接超时 espData("AT+CIPMUX=1", 150, DEBUG); // 提前建立TCP连接,复用连接减少开销 espData("AT+CIPSTART=0,\"TCP\",\""+ myHOST +"\","+ myPORT, 2000, DEBUG); delay(1000); pmsSerial.begin(9600); } void loop(){ // 读取其他传感器数据... // 切换到PMS5003串口读取 espSerial.end(); pmsSerial.listen(); boolean pmsSuccess = readPMSdata(&pmsSerial); pmsSerial.end(); espSerial.begin(115200); if (pmsSuccess){ // 构造发送数据... // 复用已建立的TCP连接发送数据 espData("AT+CIPSEND=0," + String(sendData.length()), 150, DEBUG); if(espSerial.find(">")) { espSerial.println(sendData); delay(500); // 等待发送完成 } } // 控制更新间隔为5秒 delay(5000); }
4. 修复espData函数冗余代码
移除函数末尾无法执行的打印语句,优化调试输出:
String espData(String command, const int timeout, boolean debug) { Serial.print("AT Command ==> "); Serial.println(command); String response = ""; espSerial.println(command); long int time = millis(); while ( (time + timeout) > millis()) { while (espSerial.available()) { char c = espSerial.read(); response += c; } } if (debug) { Serial.println("Response: " + response); } return response; }
修改后的完整代码
#include <SoftwareSerial.h> SoftwareSerial espSerial(2, 3); #define DEBUG true #include <dht.h> #define dht_apin A1 dht DHT; int temp; int humi; int co2; SoftwareSerial pmsSerial (4,5); #include <Wire.h> int pm25; int pm251; String mySSID = "ssid"; String myPWD = "password"; String myAPI = "api key"; String myHOST = "api.thingspeak.com"; String myPORT = "80"; struct pms5003data { uint16_t framelen; uint16_t pm10_standard, pm25_standard, pm100_standard; uint16_t pm10_env, pm25_env, pm100_env; uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; uint16_t unused; uint16_t checksum; }; struct pms5003data data; boolean readPMSdata(Stream *s) { // 等待起始字节0x42,清空无效数据 while (s->available()) { if (s->read() == 0x42) { // 验证第二个起始字节0x4D if (s->available() && s->read() == 0x4D) { uint8_t buffer[32]; buffer[0] = 0x42; buffer[1] = 0x4D; // 读取剩余30字节,确保全部读取成功 if (s->readBytes(buffer + 2, 30) != 30) { Serial.println("Frame incomplete"); return false; } // 计算校验和 uint16_t sum = 0; for (uint8_t i = 0; i < 30; i++) { sum += buffer[i]; } // 转换字节序并解析到结构体 uint16_t buffer_u16[15]; for (uint8_t i = 0; i < 15; i++) { buffer_u16[i] = (buffer[2 + i*2] << 8) | buffer[2 + i*2 + 1]; } memcpy((void *)&data, (void *)buffer_u16, 30); // 校验和验证 if (sum != data.checksum) { Serial.println("Checksum failure"); return false; } return true; } } } return false; } String espData(String command, const int timeout, boolean debug) { Serial.print("AT Command ==> "); Serial.println(command); String response = ""; espSerial.println(command); long int time = millis(); while ( (time + timeout) > millis()) { while (espSerial.available()) { char c = espSerial.read(); response += c; } } if (debug) { Serial.println("Response: " + response); } return response; } void setup(){ Serial.begin(115200); espSerial.begin(115200); delay(500); // 初始化ESP8266网络 espData("AT+RST", 1000, DEBUG); espData("AT+CWMODE=1", 1000, DEBUG); espData("AT+CWJAP=\""+ mySSID +"\",\""+ myPWD +"\"", 5000, DEBUG); espData("AT+CIPMUX=1", 150, DEBUG); // 提前建立TCP连接,复用连接减少开销 espData("AT+CIPSTART=0,\"TCP\",\""+ myHOST +"\","+ myPORT, 2000, DEBUG); delay(1000); pmsSerial.begin(9600); } void loop(){ DHT.read11(dht_apin); temp = DHT.temperature; humi = DHT.humidity; co2 = analogRead(A0); // 切换到PMS5003串口读取数据,暂停ESP8266串口 espSerial.end(); pmsSerial.listen(); boolean pmsSuccess = readPMSdata(&pmsSerial); pmsSerial.end(); espSerial.begin(115200); if (pmsSuccess){ pm25 = data.particles_25um; String sendData = "GET /update?api_key="+ myAPI +"&field1=" + co2 + "&field2=" + String(pm25) + "&field3="+ String(temp) + "&field4=" + String(humi); Serial.println(sendData); // 复用已建立的TCP连接发送数据 espData("AT+CIPSEND=0," + String(sendData.length()), 150, DEBUG); if(espSerial.find(">")) { espSerial.println(sendData); delay(500); // 等待发送完成 } } // 控制更新间隔为5秒 delay(5000); }
内容的提问来源于stack exchange,提问作者astrid
相关产品推荐
相关产品推荐

