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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:05:10