ESP32 实战教程:用 PMS5003 做 PM2.5 空气质量监测站

本文按“先跑通、再测量、最后工程化”的顺序整理。不同开发板、模块批次和库版本可能存在差异,请以手中硬件的丝印、数据手册与实际串口日志为准。

ESP32 实战教程:用 PMS5003 做 PM2.5 空气质量监测站 系统架构

一、为什么值得做

PMS5003 内部用风扇吸入空气,通过激光散射估算颗粒物浓度。它会通过串口输出一整帧数据,不是简单地吐出一个 PM2.5 数字。学习它的价值在于:你会完整走一遍二进制协议解析、校验、异常帧丢弃和趋势记录。空气质量变化通常是分钟级的,没必要每收到一帧就让网页疯狂刷新,设备也不需要表现得比灰尘更焦虑。

这次不追求把所有功能一次堆满。先完成一个最小闭环:硬件能初始化、原始数据有变化、异常时能明确报错;随后再加入标定、滤波和状态机;最后才连接网页、MQTT 或本地存储。这个顺序看起来慢一点,实际会少掉很多“到底是线错了、库错了,还是页面错了”的集体猜谜。

二、硬件清单与接线重点

准备 ESP32、PMS5003 转接线和稳定 5 V 电源。传感器风扇启动时需要足够电流,不建议从边缘能力不足的 3.3 V 引脚供电。将传感器 TX 接 ESP32 的 UART RX,若需要主动/休眠控制再连接 RX、SET 和 RESET。进风口与出风口不能贴着外壳,安装方向和开孔会直接影响采样。

第一次接线建议使用短线,在断电状态下逐根核对,并记录开发板型号、GPIO 编号、模块供电电压和接口地址。不要只写“接到 SDA”,因为换一块板后默认 SDA 可能不同。电机、舵机、风扇和继电器等负载应使用合适的独立电源,GPIO 只负责信号,不能兼职当小型发电站。

三、工作原理

串口帧通常包含帧头、帧长度、标准颗粒物浓度、大气环境浓度、颗粒计数、保留字段和校验和。程序先寻找连续帧头字节,再读取声明长度,最后对前面字节求和并与帧尾校验值比较。只有帧长合理、校验正确的数据才能进入业务层。这样即使串口从半帧开始、偶尔丢字节,也不会把两个字节拼成一次离谱的雾霾事件。

ESP32 实战教程:用 PMS5003 做 PM2.5 空气质量监测站 数据流程

把系统分成五层会更容易排错:电气连接、驱动通信、原始数据、业务换算、显示与联网。每层都留一个可观察点,例如电源电压、初始化返回值、原始帧、换算结果和页面状态。上层失败时先确认下层证据,不要页面上没有数字就立刻换三套库。

四、第一次实验与标定

先在通风稳定的室内运行至少十几分钟,记录风扇稳定后的读数。用香烟或明火做测试既不健康也容易污染传感器,可以在远处轻抖纸巾或开窗观察趋势,但不要把粉尘直接灌进风道。消费级颗粒物传感器适合趋势和提醒,不应在没有比对与校准的情况下替代法规监测设备。

标定数据应与设备配置一起保存,并记录时间、参考物、环境和固件版本。更换模块、改变机械结构或升级算法后应重新验证。只记一个最终系数而不记原始数据,下一次发现偏差时很难判断是传感器老化、结构变化,还是当初计算时手滑。

五、最小可运行代码

下面的代码用于验证关键链路。安装对应库后,根据自己的开发板修改引脚、地址和参数:

HardwareSerial pms(2);
uint8_t frame[32];

bool readFrame() {
  while (pms.available() >= 32) {
    if (pms.read() != 0x42 || pms.peek() != 0x4D) continue;
    frame[0] = 0x42;
    pms.readBytes(frame + 1, 31);
    uint16_t sum = 0;
    for (int i = 0; i < 30; ++i) sum += frame[i];
    uint16_t expected = (frame[30] << 8) | frame[31];
    return sum == expected;
  }
  return false;
}

void setup() {
  Serial.begin(115200);
  pms.begin(9600, SERIAL_8N1, 16, 17);
}

void loop() {
  if (readFrame()) {
    uint16_t pm25 = (frame[12] << 8) | frame[13];
    Serial.printf("PM2.5=%u ug/m3\n", pm25);
  }
}

示例故意保留串口输出,因为学习阶段最重要的是能看到系统正在做什么。代码跑通后再逐步拆分模块,不能因为最终产品没有串口窗口,就在开发阶段也把所有线索关掉。

六、数据处理与状态设计

建议保留原始一秒数据,但页面只显示 10 秒中位数和一分钟平均值。状态中增加有效帧数、校验失败数、最后成功时间和风扇运行状态。如果连续多次没有有效帧,应显示“传感器离线”,而不是继续展示昨天最后一个看起来很安详的数字。

建议为设备统一设计四种状态:INIT 表示初始化,RUNNING 表示数据有效,DEGRADED 表示部分功能仍可用,ERROR 表示需要人工处理。每次状态变化记录原因和时间。错误恢复要有间隔,不能每个循环都重新初始化外设,否则日志会被刷满,设备也只是在高频重复同一个错误。

七、常见问题与排错顺序

最常见问题是供电不足、RX/TX 接反、UART 引脚选错和读取时没有重新同步帧头。不要假设一次 readBytes() 永远能拿到完整帧,正式程序应使用环形缓冲区和超时。传感器安装在厨房油烟、加湿器雾气或强气流附近时,读数可能明显偏高。

排错时按以下顺序进行:先测供电和地线,再确认引脚与接口参数;然后只运行最小示例,打印原始数据;最后才恢复网页和自动控制。每次只改一个变量,并保留失败时的原始输出。最怕的是接线、库版本和业务代码同时修改,最终虽然偶然成功,却不知道是哪一步真正起了作用。

八、从演示变成长期运行设备

连续运行要考虑风扇寿命、积尘与维护周期。若使用休眠模式节能,应预留唤醒后的稳定时间,不能醒来立刻读一帧又睡。远程平台需要明确单位、平均周期与数据有效性,报警最好采用持续超标而不是单点越线。

程序结构上,可以把驱动、数据模型、业务状态机、网络和存储拆开。采样任务只负责产生带时间戳的有效数据,网页负责读取快照,日志任务负责批量落盘。任何网络故障都不应阻止本地采样,任何页面刷新也不应改变控制周期。配置保存在 NVS 时加入默认值、范围检查和版本迁移。

九、建议完成的测试清单

  1. 冷启动、复位和断电重启后,设备能否进入明确状态;
  2. 外设拔掉、接错或返回异常值时,程序是否会卡死;
  3. 连续运行两小时,内存、温度、供电和日志是否稳定;
  4. 阈值附近反复变化时,输出是否存在抖动或频繁动作;
  5. 更换同型号模块后,原标定是否仍然适用;
  6. Wi-Fi 断开、网页关闭或存储满时,本地核心功能是否继续工作;
  7. 对涉及人体、机械运动和大电流的项目,保护措施是否独立于软件存在。

建议建立实验表,至少记录原始值、处理值、单位、采样周期、错误次数、供电方式和测试结论。遇到偶发问题时,再增加启动次数和运行时长。工程经验常常不是来自“成功一次”,而是来自知道失败出现在哪个边界。

十、还能怎么扩展

可以把 PM2.5 与 BME680 的温湿度、气体趋势放到同一页面,形成更完整的环境站;再接入 MQTT、Home Assistant 或电子纸看板。多个房间部署时统一设备编号、采样周期和校准记录,才能进行有意义的横向比较。

扩展功能时仍然遵循一次增加一层的原则。先证明新模块独立工作,再接入现有状态机;先保存原始值,再绘制漂亮曲线。这样教程不仅能展示结果,也能给读者留下一条可以照着走的排错路线。

十一、总结

本项目的重点不是某一行库函数,而是把硬件、数据、状态和异常组合成一个可解释的小系统。能读取只是起点,能标定、能发现错误、能恢复、能留下日志,才算真正完成。实际制作后,建议继续补充接线照片、串口截图和一次真实失败记录,这些往往比“运行成功”四个字更有参考价值。

参考资料

打赏

如果这篇学习记录对你有帮助,欢迎扫码支持后续整理。

打赏码

打赏支持:王庆岩

posted on 2026-10-06 00:30  潇洒石头  阅读(13)  评论(0)    收藏  举报

导航