简介:面向物联网与嵌入式方向学习者的一份室内环境检测系统人机交互代码资源,基于Qt与Zigbee协作构建,聚焦上位机界面与业务逻辑实现。资源完整覆盖五个关键模块:通过串口实现上位机与Zigbee下位机双向通信,接收并显示温度、湿度、甲烷含量等实时数据;建立数据库用于历史数据存储与查询;设计超标警报系统;同时内置用户注册与登录的安全访问机制,适用于课程设计、毕设选题或实际项目二次开发。压缩包共92个文件,以CPP/H源文件、UI界面文件、PNG/JPG图片资源为主,还包含qrc资源文件、MP3提示音、DB数据库以及可直接运行的EXE程序,整体约49.14MB,目录结构按功能模块区分,便于检索与阅读。目前已有2800人学习下载。完整工程源码与界面设计可直接用于学习参考,帮助读者快速掌握Qt串口通信、数据库操作、多界面交互等综合开发技巧,在原有框架上扩展传感器类型或优化交互逻辑。 作为一个经常折腾嵌入式小项目的人,我拿到过不少“某某系统.zip”的工程包,大部分下载下来要么缺库,要么硬件型号对不上,能直接跑通的没几个。但“室内环境检测系统”这类项目,几乎是个人开发者入门物联网和传感器应用的最佳练手项目,它把采集、显示、上报、报警这条完整链路都串起来了,学到的经验可以直接复用到智能家居、农业大棚、机房监控等场景。
这套系统说白了,就是通过单片机配合温湿度、PM2.5、二氧化碳等传感器,把室内空气数据实时读出来,并在本地屏幕和云端平台同步展示,超过阈值还能主动报警。这篇文章我会从功能拆解、硬件选型、实操接线、代码配置到真实踩坑,完整还原一个可复现的搭建过程,适合刚学完单片机基础、想接触完整项目实战的开发者,也适合想低成本给自己做一套空气质量监测设备的朋友参考。
1. 从“一个zip包”到完整项目:先搞清楚它到底是什么
刚解压一个工程包时,最容易犯的错就是着急打开IDE编译,结果报一堆错还不知道从哪下手。我做这类项目的第一步,永远是先看目录结构和说明文档,把项目的整体框架在脑子里过一遍。
1.1 功能定位拆解:这不止是“测温度湿度”那么简单
室内环境检测系统的核心价值,是解决“看不见的空气质量问题”。我们待在室内的时间很长,但二氧化碳浓度过高导致的犯困、装修后甲醛挥发期的担忧、雾霾天不开窗时PM2.5的积累,这些都很难凭感觉判断。市面上几千块的空气检测仪,本质上也是同样的传感器方案,自己动手做一套,成本能控制在两三百元,还能按自己的需求定制显示界面和报警策略。
以我搭建的这套系统为例,它至少包含以下核心功能:
- 实时采集室内温湿度、PM2.5浓度、二氧化碳浓度;
- 在0.96寸OLED屏上轮播显示各项数据;
- 通过Wi-Fi将数据定时上报到云平台,手机端可随时查看;
- 当任一指标超过设定阈值时,本地蜂鸣器报警并同步推送告警消息;
- 预留了数据记录功能,方便后续做趋势分析。
这套功能链路非常典型:“传感器采集 -> 主控处理 -> 本地展示 -> 网络传输 -> 平台应用”,几乎是物联网项目的最小完整闭环。
1.2 谁会需要它,能用在什么场景
如果你只是好奇玩玩,可以做一版精简的,只保留OLED显示和报警。如果你有更实际的需求,这套系统也能直接延展:
- 家里有老人小孩,想实时了解卧室、儿童房的空气状况;
- 刚装修完,需要持续监测甲醛相关的TVOC变化(配合对应传感器);
- 做手工或实验室工作,需要监控温湿度环境;
- 想深入学习物联网通信(MQTT协议、HTTP上报、JSON数据封装)的开发者。
我在实际使用中,把一台改版后的设备放在书房,一台放在客厅,手机端随时能看到二氧化碳浓度变化。开窗通风后数值迅速下降,那种“眼见为实”的感觉,会让你对空气质量的判断从猜测变成数据支撑。
2. 系统整体设计与硬件选型:为什么是这套组合
项目能不能稳定跑起来,七成取决于硬件选型和整体设计。很多新手在选传感器时只图便宜,结果数据飘得没法看,回头还以为是代码问题。这一节我会把选型逻辑讲清楚,希望你少走弯路。
2.1 传感器选型:每一类指标背后的技术考量
我最终选的组合是SHT30温湿度传感器、SDS011激光PM2.5传感器、MH-Z19B红外二氧化碳传感器。理由如下:
温湿度:SHT30是I2C接口的数字传感器,精度在±2%RH和±0.3℃左右,比DHT11稳定太多,而且不需要自己写时序,直接调库就能读。如果你手里有DHT11也可以先用,但要做好数据跳动偏大的心理准备。
PM2.5:SDS011用的是激光散射原理,内置风扇主动吸入空气,通过UART串口输出数据,同时给出PM2.5和PM10两个值。它是我用过的这个价位里稳定性和寿命最均衡的方案。需要注意的是,它工作时风扇会转,有轻微噪音,放在卧室床头不太合适。
二氧化碳:MH-Z19B是非色散红外(NDIR)传感器,利用CO2分子对特定波长红外光的吸收特性来测浓度。它有两个输出接口:UART和PWM。我优先用UART,因为可以直接读到数值,不需要自己转换占空比。
这套组合几乎覆盖了室内空气质量最关心的几项指标,而且三者接口刚好互补:I2C、UART、UART,不会互相占用。
2.2 主控方案对比:ESP32、STM32、Arduino哪个更合适
主控芯片(MCU)的选择直接决定开发效率。我对比过三种常见方案:
| 主控方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| Arduino Uno/Nano | 上手简单,示例多 | 无Wi-Fi,需外接模块,内存小 | 纯本地学习、最早期的原型验证 |
| STM32F103 | 性能强,价格低 | 开发门槛高,调试复杂,Wi-Fi方案麻烦 | 有基础、追求工业级稳定性的场景 |
| ESP32 | 自带Wi-Fi和蓝牙,双核160MHz,内存充足 | 功耗相对高,引脚3.3V逻辑需注意 | 物联网项目首选,本系统最终选择 |
我最终选择ESP32,核心原因就一个:自带Wi-Fi,意味着我可以省去外接ESP8266模块或串口转Wi-Fi模块的繁琐接线。这可是实打实省掉了不少调试时间。而且ESP32的ADC、I2C、UART外设资源丰富,后续想加传感器也用不完。
2.3 数据链路设计:从传感器到手机屏幕的完整路径
整个系统数据流向可以拆成五层:
- 感知层:三类传感器按各自周期采集数据;
- 处理层:ESP32主控通过I2C/UART读取原始数据,转换为物理量(温度、湿度、ug/m3、ppm);
- 本地展示层:OLED屏翻页显示,蜂鸣器根据阈值报警;
- 传输层:ESP32连接家中Wi-Fi,通过MQTT协议将JSON数据包上报至公共MQTT Broker(如EMQX、阿里云IoT平台或自建Mosquitto);
- 应用层:手机安装MQTT调试助手或订阅对应主题,实时查看数据。
这里要特别强调MQTT协议的优势:它是物联网场景下最常用的发布/订阅协议,比HTTP轮询更省流量,而且服务器可以同时给多个客户端推送,非常适合多设备监控。
3. 核心实操:接线、烧录、代码配置全流程
接下来是动手环节。我会按“拿到zip包后从零跑通”的顺序,把每一个关键步骤和需要注意的坑都写清楚。
3.1 拿到工程包后的第一步:整理目录、确认依赖库
解压后,zw_project文件夹里大概率包含以下几个部分(不同作者命名略有差异):
- src/main.cpp或arduino.ino:主程序源码;
- lib/:可能包含传感器驱动库,也可能需要自行安装;
- platformio.ini或库管理文件:用来区分PlatformIO还是Arduino IDE工程;
- docs/或README.md:说明文档,务必先看!
如果你用的是Arduino IDE,需要在“库管理器”中手动安装以下依赖库:
Adafruit SSD1306(OLED驱动) Adafruit GFX(OLED绘图基础库) SHT3X(SHT30温湿度读取) SDS011(PM2.5读取) MH-Z19(CO2读取) PubSubClient(MQTT客户端) ArduinoJson(JSON数据封装与解析)有一个经验教训:版本不匹配是编译失败的头号原因。比如Adafruit SSD1306库在2.5.x版本后,构造函数命名有改动,旧代码用Adafruit_SSD1306 display(128, 64, &Wire, -1);没问题,新版本可能报错。遇到编译失败先看是不是库版本不对,不要急着改代码。
3.2 硬件接线:一张表理清引脚对应关系
我用的ESP32开发板是经典的NodeMCU-32S(38Pin),接线前先确认你的板子引脚图,避免把3.3V设备接到5V上烧掉传感器。
| 模块 | ESP32引脚 | 说明 |
|---|---|---|
| SHT30 VIN | 3.3V | 供电 |
| SHT30 GND | GND | 共地 |
| SHT30 SDA | GPIO21 | I2C数据线 |
| SHT30 SCL | GPIO22 | I2C时钟线 |
| SDS011 TX | GPIO16 | 传感器串口发送接主控接收 |
| SDS011 RX | GPIO17 | 主控发送接传感器接收 |
| MH-Z19B TX | GPIO18 | 同上 |
| MH-Z19B RX | GPIO19 | 同上 |
| OLED SDA | GPIO21 | 与SHT30共用的I2C总线 |
| OLED SCL | GPIO22 | 同上 |
| 蜂鸣器正极 | GPIO25 | 接一个限流电阻再连引脚 |
| 蜂鸣器负极 | GND |
这里有两个坑提醒一下:
SDS011和MH-Z19B虽然都是UART,但ESP32有多个硬件串口,必须用Serial2和Serial3来分别连接,初始化代码里要指定Serial2.begin(9600, SERIAL_8N1, RX1_PIN, TX1_PIN)这样的形式,不能都挤在默认的Serial0上,否则会冲突;
蜂鸣器如果是无源蜂鸣器,需要通过PWM控制频率,不能直接digitalWrite高电平,否则只会产生咔哒声而不是报警音。
3.3 代码核心逻辑:三个关键函数看懂整个程序
主程序的核心结构可以归纳为setup()中初始化+三个循环函数:
void setup() { Wire.begin(); // I2C总线初始化 Serial.begin(115200); // 调试串口 Serial2.begin(9600, SERIAL_8N1, 16, 17); // SDS011串口 Serial3.begin(9600, SERIAL_8N1, 18, 19); // MH-Z19B串口 sensor_sht30.begin(); sensor_sds011.begin(); sensor_mhz19.begin(); display.init(); mqttClient.setServer(MQTT_HOST, MQTT_PORT); mqttClient.setCallback(mqttCallback); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); } void loop() { readSensors(); // 读取三个传感器数据 displayInfo(); // OLED轮播显示 uploadData(); // MQTT上报 checkThreshold(); // 阈值判断与报警 delay(2000); // 2秒采样周期,兼顾实时性与稳定性 }**readSensors()**这个函数有个细节必须注意:SDS011和MH-Z19B上电后都需要预热,尤其是MH-Z19B,刚上电的前3秒可能输出无效数据。我在代码里加了一个启动保护,上电后5秒内不采样,等数据稳定再取值,实测效果好很多。
**uploadData()**上报时,我用的JSON格式如下:
{ "device_id": "living_room_1", "temperature": 26.3, "humidity": 58.2, "pm25": 32, "co2": 780, "timestamp": 1739876543 }MQTT主题建议用分层结构,比如home/livingroom/air,方便后续做多房间管理。上报间隔我设置的是30秒一次,因为每2秒上报一次既费流量,又容易把公共Broker搞挂。本地显示可以每2秒刷新,云端上报30秒一次,这二者不冲突。
3.4 上位机与云平台配置:手机端查看数据
如果你不想自建服务器,最简单的做法是注册一个免费的公共MQTT Broker,比如EMQX Serverless或者阿里云IoT平台。我以EMQX为例说明配置:
- 注册并创建Serverless部署,拿到连接地址、端口(默认8883或8083);
- 创建认证用户名和密码;
- 在代码里填入对应的MQTT_HOST、MQTT_PORT、MQTT_USER、MQTT_PASS;
- 手机安装“MQTT Tool”之类的调试App,填入同样的Broker地址、用户名密码,订阅home/+/air主题,即可看到数据。
如果你后续想玩出更多花样,可以把数据转发到Home Assistant或Node-RED,配合自动化规则,比如“CO2超过1000ppm自动开启新风系统”,这些逻辑都在订阅端实现,不用改动设备端代码。
4. 实战中的常见问题与排查技巧实录
这部分是我最想分享的内容。项目跑不通、数据不对,90%的情况都能从下面几个方向找到原因。我直接整理成问题速查表,配合排查思路,能帮你省下大量时间。
4.1 高频问题速查表
| 问题现象 | 可能原因 | 排查步骤与解决办法 |
|---|---|---|
| OLED屏亮但无字 | I2C地址不对 | 用I2C扫描程序获取实际地址,SHT30常见地址为0x44,OLED为0x3C,在代码中修正 |
| PM2.5始终为0 | SDS011串口接线错 | 确认RX/TX是否交叉连接,用串口调试工具直接读SDS011原始数据,排除代码问题 |
| CO2数值恒为400左右 | MH-Z19B通信失败或自动校准误判 | 检查UART接线;在通风良好的地方执行人工校准,按住传感器上的按钮10秒或发校准指令 |
| 温湿度跳动剧烈 | 传感器靠近发热元件或风口 | 把传感器远离ESP32芯片和电源模块,加一个透气外壳,传感器周围不要有密封空间 |
| 手机收不到数据 | MQTT连接失败 | 先看串口日志中的连接状态,确认IP和端口能通;公共Broker要求TLS加密时,代码要开启WiFiClientSecure |
| 系统反复重启 | 供电不足 | 不要用电脑USB口供电,改用5V/2A手机充电头;SDS011瞬间启动电流较大,建议单独供电 |
4.2 排查思路:按链路从底层到顶层推进
我的排查习惯是“先硬件后软件,先本地后云端”。比如手机收不到数据,我不会一上来就检查MQTT代码,而是用串口看设备端的打印日志:
- 看复位原因:ESP32重启时会输出重启原因,供电不足或看门狗超时能直接从日志里分辨;
- 看Wi-Fi连接状态:有没有拿到IP,连的是不是5G频段(ESP32不支持5G Wi-Fi,只能连2.4G);
- 看MQTT状态:连接日志里有failed、timeout等关键词,再判断是地址填错、端口不通还是认证失败;
- 看发布结果:订阅端如果没收到数据,看设备端发布时返回的RC码,RC=0表示成功,非0则是协议或权限问题。
这条链路排查法可以复用到几乎任何物联网项目,值得养成习惯。
4.3 独家避坑技巧:几个不试不知道的细节
第一个坑:OLED和SHT30共用I2C总线时,片选地址冲突。SSD1306默认地址0x3C,SHT30默认0x44,理论上不冲突,但有些OLED模块的地址引脚被拉高后变成0x3D,如果两个传感器地址重复,I2C设备会互相干扰。遇到这种情况,用跳线把OLED的地址引脚重新设置即可。
第二个坑:SDS011的风扇转速与测量时间。它的数据更新周期是1秒一次,但刚上电的前30秒风扇正在启动,数据处于波动期。我一般取5个采样值的平均值,过滤掉最大最小值,再做数据上报,这样曲线更平滑,也不容易被瞬时波动误导。
第三个坑:MH-Z19B的零点漂移。它在长期通电后可能产生微小漂移,尤其频繁开关机时更明显。建议在空气质量很好的天气(比如雨后),把设备拿到室外通风处开机10分钟,然后执行一次零点校准。方法很简单,发送0xFF 0x01 0x87 0x00 0x00 0x00 0x00 0x00 0xF2这组指令即可。
第四个坑:编译时引脚定义与开发板型号不匹配。很多ESP32开发板虽然外观一样,但板载LED引脚、Flash大小有差异。我的程序里如果用了LED_BUILTIN,而你的板子这个引脚没接LED,程序可能正常运行但没有指示灯反馈,容易误判“死机”。建议在代码里用自定义引脚宏代替LED_BUILTIN,或者参考开发板的原理图。
5. 空间进一步扩展:从一个检测器到一套环境控制系统
基础版跑通之后,我很快就不满足于“只看数据”了。于是我在代码和硬件上都做了扩展,让这套系统的应用价值提升了一个档次。
5.1 功能扩展方向与实现方案
最直接也最有用的扩展是联动控制。我给系统增加了一个继电器模块和一个5V风扇,当CO2超过900ppm时,自动启动风扇排风;当温度超过28℃时,自动打开风扇降温。代码改动很小,在checkThreshold()里加两行digitalWrite控制即可。
另一个实用的扩展是数据本地存储与离线日志。我在ESP32上挂了一张MicroSD卡模块,每小时记录一次数据到CSV文件,这样即使断网或云平台到期,历史数据依然完整。对这个项目来说,数据连续性远比实时显示重要,因为空气质量趋势才能反映真实的居住环境变化。
再有一个是多设备组网。我又做了两块从机板,一块放卧室、一块放厨房,从机通过Wi-Fi把数据上报到同一个MQTT主题,主机板订阅所有主题并在OLED上轮播显示。这时MQTT的主题分层设计优势就体现出来了,不同房间的Topic直接区分开,不需要改主机代码。
5.2 我的实测感受与最终建议
这套系统从裸板到现在稳定运行,我踩过不少坑,也积累了比任何教程都更深刻的理解。我最大的体会是:做这类嵌入式项目,把基础模块逐一验证通过再组合,比一次性焊好全部模块再联调顺利太多。先单独测SHT30,确保OLED能显示温度;再单独测SDS011,串口能读到PM2.5;最后才把三者数据合并到一个JSON里上报MQTT。每加一个模块就验证一次,问题定位会快很多。
另外,如果你打算长期运行,外壳设计千万不要忽略。我的第一版设备裸板放桌上,一周后传感器进灰,PM2.5读数明显偏小。后来用3D打印了一个开孔外壳,侧面开了进气口和出气口,让SDS011的主动风扇能形成稳定气流,数据才恢复可信。
最后分享一个小技巧:给ESP32的固件程序加上OTA(空中升级)功能。因为设备日常放在不便于插USB的地方,每次改阈值都要重新插线非常痛苦。配好OTA后,程序直接用Wi-Fi上传更新,后续调整报警阈值、修改上报间隔就跟手机App更新一样方便,这算得上是提升体验最明显的一个改动。
本文还有配套的精品资源,点击获取