ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ESP32-S3驱动JW01 CO2传感器:UART通讯与供电避坑实践

ESP32-S3驱动JW01 CO2传感器:UART通讯与供电避坑实践 最近在给家里搭一个室内空气质量监测的小项目核心需求很明确实时看到 CO₂ 浓度方便判断要不要开窗通风。主控我选了 ESP32-S3传感器选了 JW01 这个便宜的 CO₂ 模块整体成本压得很低。本以为串口接上就能出数结果没想到在供电上反复折腾踩了好几个坑才稳定运行。这篇博客就把整个过程中拆解出的选型思路、传感器协议、供电问题的前因后果、以及完整的 Arduino 代码都分享出来给准备用 ESP32-S3 或类似开发板驱动串口传感器的朋友做个参考。这个项目适合谁如果你手里有 ESP32、ESP8266、Arduino 这类板子想接一个 UART 输出的气体传感器或者类似模块被“读数异常”“一开 WiFi 就重启”“串口乱码”这种问题困扰过那这篇文章应该能帮你少走不少弯路。我会把供电、共地、电平、协议解析这几块讲透代码也是可以直接抄作业的完整版。1. 项目背景与选型分析1.1 为什么选 ESP32-S3 作为主控最初我考虑过 Arduino Uno、ESP8266 和 ESP32-S3 三个方案。Arduino Uno 上手简单但板载资源太弱后期想加 WiFi 上报、OLED 显示或者本地存储都得外挂一堆模块板上资源很快就会见底。ESP8266 价格便宜也能联网但只有一个 UART 外设引脚也相对紧张用起来不够从容。ESP32-S3 在这个场景里优势很明显双核 240MHz 的 Xtensa 处理器处理串口数据绰绰有余板载 WiFi 和 BLE方便后续把数据推送到 MQTT 或者 Home AssistantUART 外设充足可以专门分一个串口给传感器不影响 USB 调试口。另外 ESP32-S3 的 3.3V 逻辑电平和常见传感器模块的通信电平更匹配硬件层面省掉不少麻烦。还有一点很实用ESP32-S3 的 Arduino 生态已经非常成熟开发板支持包在 Arduino IDE 里一键安装写代码的方式和传统 Arduino 完全一致。对一个做原型验证的项目来说这种低门槛的工具链意味着更快的迭代速度。1.2 为什么传感器选择 JW01CO₂ 传感器的选型市面上常见的大致分三类进口高精度模块、国产 NDIR 模块、国产低成本电化学/固体电解质模块。JW01 属于最后一类单价通常只有几十块钱和 MH-Z19 这种动辄上百的红外模块相比价格优势巨大。JW01 的输出方式是 UART 串口主动上报数据不需要像 I2C 那样去查询和时序控制代码逻辑非常直观。模块本身支持 400~5000ppm 的量程日常室内监测完全够用。虽然它的精度和响应速度不如高端 NDIR 方案但对于“看个趋势、决定开不开窗”的需求来说这个性价比非常划算。我理解很多玩家会纠结“便宜的传感器数据准不准”这个问题。实际使用中我发现只要供电稳定、预热充分、并且校准好初始环境JW01 的数据稳定性还是可以的。当然如果你要做严格的科研采集或者医疗级监测那就别省这个钱直接上 SCD30 或者 MH-Z19 这类更高端的模块。但做 DIY 环境监测JW01 完全是够格的。1.3 项目的整体功能规划最初我只想做一个简单的 CO₂ 读取设备把数据打印到串口监视器里。但随着项目推进我对功能做了个规划基础功能通过 UART 读取 JW01 数据解析出 CO₂ 浓度并显示。扩展功能用 ESP32-S3 的 WiFi 将数据发送到 MQTT 服务器配合 Home Assistant 做室内环境自动化比如 CO₂ 浓度超过 1000ppm 时自动提醒开窗或启动新风。人机交互预留 OLED 屏幕插座后期可以实时显示浓度曲线。这套规划的好处是每一步都在基础硬件上做增量不用换板子。ESP32-S3 的资源完全撑得起这些扩展这也是我坚持选它的原因之一。2. JW01 通信基础与接线方案2.1 模组引脚定义JW01 模块通常引出 4 个引脚分别是 VCC、GND、TX、RX。不同厂家生产的模块引脚顺序可能略有区别但功能基本一致。接线前我特意检查了模块上丝印标注和配套文档避免把电源和信号线接错。VCC电源正极常见标称 5V。GND电源地也是通信参考地。TX传感器数据发送端接主控的 RX。RX传感器数据接收端接主控的 TX这个引脚一般用不到因为 JW01 默认主动上报。我用万用表量了一下模块正常工作时 VCC 和 GND 之间的电压波纹发现如果直接用 3.3V 供电波纹明显偏大不说传感器输出的字节流也会变得不稳定。所以后来我坚持用 5V 供电这个问题后面整个“供电踩坑实录”章节会详细展开。2.2 串口数据帧解析JW01 的串口参数固定为 9600bps、8 位数据、无校验位、1 位停止位也就是常见的 9600 8N1。模块上电后以一定间隔主动向上位机发送数据帧不需要发送任何指令。我手里这块模块的帧长度为 9 字节结构如下字节序号值含义00xFF固定帧头10x04命令码表示数据帧20x03气体类型标识这里代表 CO₂3CO₂ 浓度高字节浓度值高位4CO₂ 浓度低字节浓度值低位5~7保留字节可以忽略8校验字节前 8 个字节累加和的低 8 位其中 CO₂ 浓度的计算方式为浓度值 第3字节 × 256 第4字节校验方法很典型把第 0 到第 7 字节累加取结果的低 8 位和第 8 字节比较相等则说明这一帧数据有效。需要注意不同批次或不同厂家生产的 JW01 帧结构可能会有差异有的模块帧头是 0xFF 0x04有的可能包含更多信息。我在代码里加了一个帧头检测和校验逻辑就是为了识别这类差异。如果你手里的模块输出格式不同建议用串口助手先抓一下原始数据再对应调整解析代码。2.3 接线表与常见注意事项以下是 ESP32-S3 与 JW01 的接线表我用的是 ESP32-S3-DevKitC 开发板板载 USB 转串口芯片和 RGB 灯。JW01 引脚ESP32-S3 引脚说明VCC5V 引脚必须用 5V不要接 3.3VGNDGND共地关键千万别漏TXGPIO18开发板串口 RX 引脚RXGPIO17一般悬空也可接开发板 TX接线时有几个点要特别注意。第一VCC 优先接开发板的 5V 输出引脚而不是从 USB 的 VBUS 上飞线这样供电回路更短压降更小。第二GND 一定要接上如果没有共地串口信号的电平参考点不一致收到的数据会乱成一团。第三确认模块 TX 的引脚别接错接反了会一直收不到数据。3. 供电踩坑实录3.1 坑1贪方便把 JW01 接到了 3.3V第一次接线时我顺手就把 JW01 的 VCC 接到了开发板的 3.3V 引脚上。我当时想的是模块标称电压范围如果包含 3.3V应该没问题。结果上电后串口什么数据都没有等了好几分钟还是一潭死水。用万用表测了一下模块 VCC 引脚的电压3.3V 本身是正常的但我又测了传感器 TX 引脚的静态电平发现波形非常怪异不是干净的高/低电平跳变。后来查了模块资料才明白JW01 内部虽然有一定稳压处理但传感器核心的加热和采样电路需要 5V 才能稳定工作。3.3V 供电会让模块处于一种“低压欠驱动”的状态传感器本身无法正常工作。这个坑的本质是看到了模块标称电压范围里的“3.3~5V”就掉以轻心没仔细看工作电流和工作条件。模块标称的是“可工作电压范围”不等于“推荐电压”。稳妥起见直接上 5V 供电。3.2 坑2USB 供电一开 WiFi 就重启修正供电到 5V 之后串口数据终于正常了。但是过了一小会儿我刚把 WiFi 功能加进代码开发板就开始反复重启启动日志里能看到“Brownout detector was triggered”的提示。这个问题本质是电流余量不足。ESP32-S3 在 WiFi 开启瞬间的峰值电流可以到 300mA 左右再加上 JW01 的 40~60mA 工作电流稳压器输出电流已经非常接近上限。电脑 USB 口一般只能提供 500mA 电流如果开发板上的 LDO 稳压器余量不够稍微有点波动电压就会被拉低触发掉电保护。我的解决方法有两个方向。第一把 JW01 的 VCC 改接到开发板的 5V 引脚这样传感器直接吃 USB 的 5V不经过板上 3.3V LDO减轻 LDO 负担。第二调试期间尽量用带独立供电的 USB Hub或者直接换一个输出不低于 1A 的适配器供电。实测下来改用独立适配器之后WiFi 开启时系统重启的问题彻底消失了。3.3 坑3外部电源单独为传感器供电忘记共地第三个坑是项目后期发生的。我想让 JW01 和 ESP32-S3 分开供电避免相互干扰于是用了一个独立的 5V 电源给 JW01 供电。结果一上电串口数据乱码得非常彻底偶尔能碰到几个正确的帧头但数据一律不对。查了半天最后才意识到问题出在共地上。两个电源各自独立输出虽然电压都是 5V但它们的 GND 参考点之间没有导线相连。JW01 的 TX 引脚输出的信号电平相对它自己的 GND而 ESP32-S3 的 RX 引脚接收信号时参考的是开发板自己的 GND两边参考点不一致信号自然对不上。解决方式很简单把两个电源的 GND 引脚用导线连起来让整个系统有一个统一的参考地。这也是嵌入式系统中“共地是通信前提”这条铁律的最典型体现。3.4 推荐的供电方案与功耗估算经历了上面几个坑之后我把供电方案总结成了几个可以照抄的选项供电方案优点缺点适用场景开发板 USB 直供 5V JW01 接 5V 引脚最简单免额外硬件受 USB 口电流限制WiFi 峰值有风险快速原型、短时间调试5V/2A 适配器供电开发板电流余量充足稳定需要额外电源长时间运行、正式部署18650 锂电池 升压模块便携、可移动需要额外的升压/充电板移动监测、离线使用直流-DC 降压模块从前级取电效率高、纹波小电路复杂、成本略高多设备协同供电关于功耗估算我可以给出一个简单的计算过程。ESP32-S3 开启 WiFi 时平均电流大约 200mA峰值 300mAJW01 稳定工作电流约 40mA启动瞬间不算高。所以整个系统的最低供电电流建议不低于 500mA预留余量后推荐 1A 以上。如果使用 18650 电池按 2600mAh 容量算系统平均电流 250mA理想状态下可以撑 10 小时左右考虑升压效率和电池衰减实际约 8 小时用于短时间移动监测够用。4. 完整 Arduino 代码实现4.1 开发环境准备与开发板配置代码使用 Arduino IDE 编写项目开始前需要先在开发板管理器里安装 ESP32 支持包。在 Arduino IDE 中打开“文件 — 首选项 — 附加开发板管理器网址”填入官方支持的 JSON 地址然后在“工具 — 开发板 — 开发板管理器”中搜索 “esp32” 并安装最新版本。安装完成后在“工具 — 开发板”里选择 ESP32S3 Dev Module确认 USB CDC On Boot 选项设置为 Enabled否则串口监视器可能看不到打印信息。烧录时选择对应的 COM 口ESP32-S3 一般需要按住 BOOT 键再点上传也可以根据开发板自动进入下载模式。很多人反馈 Arduino IDE 2.x 没有代码补全其实在偏好设置里把语言服务打开或者直接换用 VS Code PlatformIO体验会好很多。这个项目代码量不大IDE 自带编辑器足够用。4.2 程序实现思路程序整体分为三块初始化、串口数据解析、周期日志输出。初始化部分配置调试串口、传感器串口、板载 LED 引脚。数据解析部分从 Serial2 读取字节流寻找帧头收满 9 字节后做校验校验通过则提取 CO₂ 浓度并更新数据状态。周期性输出部分每 1 秒通过调试串口打印当前浓度、有效帧计数、无效帧计数和数据时间间隔。帧丢失处理也很关键。如果超过 5 秒没有收到任何有效帧就说明通讯或供电异常通过日志输出提醒排查。4.3 完整代码清单下面是完整的 Arduino 代码针对 ESP32-S3 编写其他 ESP32 型号也能直接用只要改一下串口引脚定义即可。#include Arduino.h // 通信引脚定义 #define SENSOR_RX_PIN 18 // 接 JW01 TX #define SENSOR_TX_PIN 17 // 接 JW01 RX可悬空 #define LED_PIN 48 // 开发板板载 RGB 灯引脚 // JW01 数据帧长度 #define FRAME_LEN 9 // 传感器串口缓冲 uint8_t frame[FRAME_LEN]; uint8_t frameIndex 0; // 数据状态 float co2ppm 0.0f; uint32_t goodFrames 0; uint32_t badFrames 0; unsigned long lastValidFrameMs 0; unsigned long lastLogMs 0; /** * 校验一帧数据 * 规则前 8 字节累加和的低 8 位 第 9 字节 */ bool parseFrame(uint8_t *buf) { if (buf[0] ! 0xFF) { return false; } uint8_t sum 0; for (int i 0; i 8; i) { sum buf[i]; } return (sum buf[8]); } void setup() { // 调试串口 Serial.begin(115200); delay(300); Serial.println(); Serial.println(ESP32-S3 JW01 CO2 Monitor); // 传感器串口9600 8N1 Serial2.begin(9600, SERIAL_8N1, SENSOR_RX_PIN, SENSOR_TX_PIN); // 板载 LED 初始化 pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, HIGH); Serial.println(System ready. Waiting for sensor data...); } void loop() { // 从串口读取一字节按帧结构拼装 while (Serial2.available()) { uint8_t b Serial2.read(); // 等待帧头 if (frameIndex 0 b ! 0xFF) { continue; } frame[frameIndex] b; // 收满一帧 if (frameIndex FRAME_LEN) { if (parseFrame(frame)) { // 解析 CO2 浓度 uint16_t raw (uint16_t)frame[3] * 256 frame[4]; co2ppm (float)raw; goodFrames; lastValidFrameMs millis(); // LED 闪烁一下表示数据有效 digitalWrite(LED_PIN, LOW); delay(20); digitalWrite(LED_PIN, HIGH); } else { badFrames; } frameIndex 0; } } // 每秒打印一次状态 if (millis() - lastLogMs 1000) { lastLogMs millis(); unsigned long age millis() - lastValidFrameMs; Serial.printf([%lu ms] CO2: %.0f ppm | good%u bad%u | data age: %lu ms\n, millis(), co2ppm, goodFrames, badFrames, age); // 超过 5 秒没收到有效帧说明可能供电或接线有问题 if (age 5000) { Serial.println(WARN: No valid frame received for 5s, check power/connection!); } } }4.4 关键代码说明这里解释几个容易被忽略的细节。帧头检测这段我用了“只在第一个字节等待 0xFF”的方式这样做的好处是即使数据流中出现半个字节的错位也能快速重新同步不会被垃圾数据卡住整个解析流程。校验失败的时候我没有直接放弃整帧而是把 badFrames 计数器加一。这样做的好处是可以通过 good/bad 比例判断链路质量。如果 bad 比例持续偏高大概率是供电不稳、电平不匹配或者接线接触不良。打印数据时我特意加上了“data age”这个参数表示当前显示的浓度值已经多少毫秒没有更新了。这比单纯打印浓度值更实用因为你看到 CO₂500ppm 时无法判断是真实数据还是传感器掉线后保留的旧数据。有了 data age问题一目了然。5. 常见问题排查与避坑总结5.1 典型现象与排查方法串口监视器乱码先检查波特率是否设置正确JW01 固定 9600而调试串口是 115200两个串口别搞混。确认信号线是否接反TX 接 RX 这个基本规则也经常有人突出错。排除以上问题后重点检查供电和共地这是最容易出故障的点。数据一直显示 0 或者满量程如果解析出来一直为 0大概率是传感器启动异常可以检查供电电压是否达到 5V 并保持稳定。如果一直显示接近量程上限先判断是否处在预热阶段JW01 上电后需要一定时间预热数据会从高位慢慢回落。如果没有预热问题再看周围环境是否存在干扰源比如靠近空调出风口或者有人密集的空间。数据跳变剧烈一帧 800ppm下一帧突然变成 4000ppm然后又跳回 900ppm。这种情况先排除接线接触不良虚接最典型的表现就是数据无规律跳变。然后是供电纹波给开发板供电的 USB 口如果本身不稳定很容易导致传感器采样异常。最后再考虑传感器本身老化可以换个模块交叉验证。5.2 快速排查速查表现象首先检查其次检查最终手段完全无数据接线方向、帧头VCC 电压是否 5V用串口助手抓原始波形偶发有效、偶发乱码共地线是否可靠信号线是否过长/缠绕缩短信号线或加分压/整形数据恒为 0预热时间是否足够模块是否损坏换模块测试数据跳变剧烈接线端子是否牢固供电波纹是否过大换高余量电源测试WiFi 开启后重启电源电流余量传感器是否接 3.3V接 5V/换大功率适配器长时间运行后漂移预热不够或老化环境温湿度变化在新鲜空气中重校基准5.3 提前躲开的坑电平适配与长期稳定性关于电平适配多说一句。JW01 的 TX 引脚输出电平在不同供电条件下可能不同。我测过手里这块模块在 5V 供电下TX 输出高电平约 3.3V可以直接接 ESP32-S3 的 GPIO。但有些批次的模块输出高电平可能接近 5V而 ESP32-S3 的 GPIO 虽然有一定耐压能力安全起见最好串一个 1kΩ 电阻做保护或者用两个电阻分压到 3.3V。如果你的模块文档没有明确标注输出电平这个保护措施值得加上。长期运行时JW01 因为内部有加热/采样元件外壳会有一定温度这是正常现象。但要注意给传感器留出通风空间别把它密封在完全不透气的壳子里否则测出来的 CO₂ 数值会一直偏高失去参考意义。另外传感器长时间运行后可能存在基线漂移。我的做法是定期在室外新鲜空气环境下通电半小时左右让传感器自动校准到 400ppm 附近的基线值。虽然不是严格意义上的手动校准但对家用场景来说已经能维持不错的参考价值。这个项目做到现在已经可以稳定读取 CO₂ 浓度并上传到本地服务了。回过头看最花时间的其实就是供电那一关接线和代码反而不是难点。如果你也准备做类似项目建议第一步就把供电方案定下来别在 3.3V 和 5V 之间犹豫也别忘了给系统预留足够的电流余量。后续我打算给这套系统加一个 OLED 显示和联动新风的自动化逻辑等调试完善了再来更新一篇实践记录。
返回列表