十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

WiFi版温湿度报警器设计:从单片机采集到远程报警全流程

WiFi版温湿度报警器设计:从单片机采集到远程报警全流程 简介一套完整的基于单片机温湿度报警器WiFi版设计方案面向智能家居、课程设计及物联网入门学习者旨在解决环境温湿度采集、超限报警与手机远程监控的结合问题。资源共99个文件约40.9MB包含Keil工程源程序.c/.uvproj/.hex、原理图与PCB工程.pcbdoc/.pcb/.SCH、Word/PDF说明文档、手机APP安装包及E4A源码、ESP8266配置资料和参考论文并配有实物图、元件清单、操作视频指引便于对照搭建与调试。设计覆盖DHT11/DHT22传感器数据读取、ESP8266 WiFi模块通信、本地蜂鸣器报警与APP远程推送等关键环节也涉及单片机C语言编程、硬件电路连接、无线通信协议和手机端功能实现能够帮助初学者完整走通“原理图→PCB→实物→APP联动”的开发流程整体资料组织清晰、可移植性强。已有1223人学习下载适合课程设计、毕业设计改版以及希望快速入门的物联网开发者在校生和工程师均可参考。1. 温湿度报警器加上 WiFi先拆清“报警”归谁管把一个本来用数码管显示温湿度的单片机小项目改成 WiFi 版最容易踩的坑不是传感器读不准而是把“采集”“联网”“报警”三件事全塞进一颗 51 内核里最后中断打架、内存爆掉、网络一卡整个系统复位。做这类毕设或课程设计最常见的可靠分工是单片机只管采集和本地判断WiFi 模块负责传输报警动作由“本地声光 远程消息”双通道触发。这篇文章按“端侧采集 → WiFi 透传 → 远程报警 → 数据验证”这条线展开硬件选型不影响你手上已有的板子固件逻辑也尽量跟具体芯片解耦。适合正在做单片机课设、毕设或者想把老式温湿度报警器改造成可远程查看的工程师。先记住一个结论WiFi 版的本质不是“把线去掉”而是引入一个双向通道——下行收阈值配置上行发实时数据报警器只是这个通道上的一个节点。2. 整体方案WiFi 版温湿度报警器的三种组网形态2.1 端侧采集、WiFi 传输、远程报警的三层结构WiFi 版温湿度报警器在架构上比普通版多出了“网络层”。传统方案里传感器数据从单片机读出来后直接驱动蜂鸣器和显示屏闭环在本地完成。加了 WiFi 之后数据路径变成DHT22 或 SHT30 采集 → 单片机做阈值判断 → 本地声光报警的同时把数据打包成 JSON 或自定义帧 → 通过 UART 发给 WiFi 模块 → 模块以 TCP 或 MQTT 方式推送到服务器或手机 App。--------------------- UART --------------------- | 单片机 (STC/STM32) | --------------- | WiFi 模块 (ESP8266) | | - 传感器采集 | | - TCP/MQTT 协议栈 | | - 本地阈值判断 | | - 断线重连 | | - 蜂鸣器/LED 驱动 | -------------------- --------------------- | | WiFi ----------v---------- | 远程端 (服务器/App) | ---------------------这个结构里单片机不直接参与网络协议解析只把“当前温度、湿度、报警状态”通过串口丢给 WiFi 模块。好处是代码逻辑清晰51 单片机那点内存也够用坏处是串口通信协议得自己定模块固件版本不一样AT 指令行为也有差异。我一般会把串口波特率固定在 115200帧格式用“帧头 长度 数据 校验”避免 WiFi 模块缓冲区溢出导致粘包。2.2 两种组网方式直连 TCP 与 MQTT 中转常见做法是二选一直连 TCP 或者走 MQTT。直连 TCP 最简单——WiFi 模块作为客户端主动连上服务器某个端口单片机把数据帧发过去服务器端用 Socket 接收。这种方式延迟低但服务器 IP 一变就得改固件重烧公网部署还要考虑 IP 限制。MQTT 则是引入一个 Broker设备只管发布主题手机订阅同一主题就能收数据阈值报警也可以做成一条独立主题。报警消息走 MQTT 的好处是手机端不用一直保持 TCP 长连接Broker 会帮你暂存离线消息。代价是 WiFi 模块固件得支持 MQTT或者你在单片机侧用一个轻量协议栈自己拼 MQTT 报文。对毕设来说前者更省事对想练底层的人来说后者价值更大。2.3 状态机设计把“采集、联网、报警”拆成三个状态WiFi 版最容易翻车的点是网络阻塞导致采集周期漂移。比如 WiFi 模块重连要 3 秒这 3 秒里传感器数据没读报警判断就漏了。我的做法是用一个简单的状态机enum { ST_IDLE, // 等待采集周期 ST_READ, // 读取传感器 ST_SEND, // 串口发送给 WiFi 模块 ST_ALARM, // 本地报警 ST_WAIT_ACK // 等待模块回复带超时 };主循环里非阻塞轮询每个状态只做一件事串口发送完成后立刻回到 ST_IDLE不等模块回复。WiFi 模块自己的重连逻辑放在模块侧不影响单片机采集周期。这样即使网络完全断开本地声光报警依然正常工作——这是 WiFi 版报警器的底线远程通道可以断本地报警不能断。3. 硬件选型与电路设计MCU、传感器、WiFi 模块的搭配3.1 单片机选型51 够用但要注意 UART 数量和外中断做 WiFi 版不一定要上 STM32。如果传感器是 DHT22用普通 I/O 口模拟时序读取WiFi 模块走 UART蜂鸣器占一个定时器做 PWM——这些需求一颗 STC15 系列或者普通 51 都能扛住。但有一个硬性约束WiFi 模块的 UART 不能和下载口共用。很多开发板用 CH340 转串口下载程序同时这个串口又接到 ESP8266 上下载时模块会把 TX 拉高干扰烧录。我的建议是板子上留两个串口或者下载时把 WiFi 模块的 VCC 断开。STM32 的选择理由主要是内存和 DMA。ESP8266 的 AT 固件返回数据可能一次来几十个字节51 的串口中断收完再处理容易丢STM32 可以用 DMA空闲中断一帧一帧收。如果手上只有 51也不是不行把串口缓冲区开大比如 64 字节收到帧尾再统一解析。3.2 传感器对比DHT22 与 SHT30 的精度和时序差异这里给一张选型表直接照抄就行参数DHT22 (AM2302)SHT30温度精度±0.5°C±0.3°C湿度精度±2% RH±2% RH接口单总线时序敏感I2C采样周期≥2 秒可配置最快 0.5ms价格低中DHT22 的问题是它用的是单总线协议对时序要求苛刻中断一多就读失败。SHT30 走 I2C靠地址寻址对时序宽容得多。做 WiFi 版我倾向 SHT30本身数据手册里就带 CRC 校验读回来的数据可信度高不用自己在应用层再包一层校验。但有个坑SHT30 的 I2C 地址是 0x44如果板子上还有其他 I2C 设备要把地址错开。3.3 WiFi 模块接入电路电平转换和供电别省ESP8266 的逻辑电平是 3.3V如果单片机是 5V 的 STC 系列串口 TX 直接连过去会烧模块。常见做法是加一个 1kΩ 2kΩ 电阻分压把 5V 降到 3.3V 左右或者直接用 3.3V 供电的单片机。模块的 VCC 要单独用 AMS1117-3.3 供电不要和单片机共用同一个 LDO——ESP8266 发射瞬间电流能到 300mA共用会把单片机电压拉低导致复位。STC 5V TX --[1kΩ]---- ESP8266 RX | [2kΩ] | GND这个分压电路是最省事的成本不到一毛钱。如果你手上有 2N7002 电平转换芯片效果更稳但这个分压方案在 115200 波特率下实测没问题。注意 WiFi 模块的 EN使能脚要接 3.3V不能悬空否则模块不启动。4. 固件实现从串口驱动到报警触发的完整流程4.1 Modbus 风格的帧格式设计网上搜“modbus单片机帧接收数据程序”能搜到一堆实现核心就是“帧头 地址 长度 数据 CRC”。这里给一个简化版适合单片机资源有限的场景// 帧格式: 0xAA 0x55 LEN DATA0 DATA1 ... CHECKSUM // LEN 表示 DATA 段的字节数 // CHECKSUM 所有字节(从 0xAA 到最后一个DATA)之和取低8位 uint8_t frame_buf[32]; uint8_t frame_len 0; uint8_t calc_checksum(const uint8_t* buf, uint8_t len) { uint8_t sum 0; for (uint8_t i 0; i len; i) sum buf[i]; return sum; } void send_frame(uint8_t* data, uint8_t len) { uint8_t buf[40]; buf[0] 0xAA; buf[1] 0x55; buf[2] len; memcpy(buf[3], data, len); buf[3 len] calc_checksum(buf, 3 len); UART_Send(buf, 4 len); }模电抄板过来最容易犯的错是“帧头校验和”的范围没算对发送端和接收端算的字节数不一致导致第一帧之后全部判废。这里的约定是CHECKSUM 从帧头 0xAA 开始算一直到最后一个 DATA 字节不包括 CHECKSUM 本身。接收端收到后先找 0xAA 0x55 连续两个字节再读 LEN按 LEN 收完数据最后算一次校验和。4.2 DHT22 读取时序时序敏感怎么处理DHT22 的读取时序里最麻烦的是判断每个 bit 的高电平持续时间——26-28us 是 070us 左右是 1。51 单片机在 12MHz 晶振下做一个 NOP 大约是 1us所以可以用延时函数卡时间。这里给一个简化但可靠的读取流程uint8_t dht22_read(float* temp, float* humi) { uint8_t data[5] {0}; // 主机拉低至少 1ms 触发 DHT_PIN_OUT; DHT_LOW; delay_ms(1); DHT_HIGH; delay_us(30); DHT_PIN_IN; // 等待应答 if (DHT_READ) return 1; // 设备没应答 while (!DHT_READ); // 等待拉低结束 while (DHT_READ); // 等待 80us 高电平 // 读取 40 bit: 湿度高8位, 湿度低8位, 温度高8位, 温度低8位, 校验和 for (int i 0; i 40; i) { while (!DHT_READ); // 等低电平结束 delay_us(40); // 40us 后采样 data[i / 8] 1; if (DHT_READ) data[i / 8] | 1; while (DHT_READ); // 等当前 bit 结束 } // 校验: data[0..3] 之和等于 data[4] if ((uint8_t)(data[0] data[1] data[2] data[3]) ! data[4]) return 2; *humi ((data[0] 8) | data[1]) / 10.0f; *temp ((data[2] 8) | data[3]) / 10.0f; return 0; }注意delay_us(40)的位置必须在低电平结束后的高电平中间采样如果函数调用开销太大导致超过 50us读到的全是 1。我一般会把这段代码的编译优化等级开到最高或者在 STC 上用_nop_()手动凑时间。另一个坑是 DHT22 两次读取之间必须间隔 2 秒以上否则传感器不响应。4.3 ESP8266 初始化与数据上报的 AT 指令序列如果模块刷的是官方 AT 固件初始化流程固化如下// 串口发送 AT 指令等待 OK 回复 void wifi_init(void) { // 1. 测试模块是否响应 uart_send_string(AT\r\n); wait_ok(1000); // 2. 设置为 Station 模式 uart_send_string(ATCWMODE1\r\n); wait_ok(1000); // 3. 连接路由器 uart_send_string(ATCWJAP\MyWiFi\,\password123\\r\n); wait_ok(5000); // 连接路由器可能要 3-5 秒 // 4. 开启 TCP 透传连到服务器 uart_send_string(ATCIPSTART\TCP\,\192.168.1.100\,8080\r\n); wait_ok(3000); // 5. 开启透传模式 uart_send_string(ATCIPMODE1\r\n); wait_ok(500); uart_send_string(ATCIPSEND\r\n); wait_ok(500); }参数说明ATCWMODE1是 Station 模式如果你希望设备还能被手机直连配网要用ATCWMODE3APStation 共存。ATCWJAP里的 SSID 和密码是明文如果怕代码泄露可以在编译时把字符串拆开放到外部配置区。透传模式开启后单片机往串口写什么模块就原样发给服务器直到收到才退出透传。4.4 报警触发逻辑本地和远程双通道void check_alarm(float temp, float humi) { static uint8_t local_alarm_state 0; static uint8_t remote_alarm_sent 0; uint8_t temp_alarm (temp TEMP_HIGH_LIMIT) || (temp TEMP_LOW_LIMIT); uint8_t humi_alarm (humi HUMI_HIGH_LIMIT) || (humi HUMI_LOW_LIMIT); uint8_t any_alarm temp_alarm || humi_alarm; if (any_alarm !local_alarm_state) { // 刚进入报警状态: 本地响 远程推一次 buzzer_on(); char msg[48]; snprintf(msg, sizeof(msg), {\t\:%.1f,\h\:%.1f,\alarm\:1}, temp, humi); send_frame((uint8_t*)msg, strlen(msg)); local_alarm_state 1; remote_alarm_sent 1; } else if (!any_alarm local_alarm_state) { // 恢复正常: 关蜂鸣器, 远程发恢复消息 buzzer_off(); send_frame((uint8_t*){\alarm\:0}, 11); local_alarm_state 0; remote_alarm_sent 0; } }这段逻辑里最关键的细节是“刚进入报警状态才推送一次”不是每秒钟都刷消息。否则 WiFI 断线重连后积压的报警消息会把服务器打爆。阈值量TEMP_HIGH_LIMIT这些可以用宏定义写死也可以留一个串口指令在运行时修改后者就是“远程设阈值”的功能扩展点了。5. 数据格式与远程监控JSON 结构化与断线补偿5.1 上报数据格式JSON 虽然浪费字节但调试效率高单片机上报的数据到底用自定义帧还是 JSON这个看应用端。如果服务器端是 Node-RED 或 App Inventor 这类工具直接收 JSON 最省事——它们内置 JSON 解析你自定义帧还得写解析代码。数据量方面一条完整的 JSON 大约 60-80 字节115200 波特率下耗时不到 10ms对 2 秒的采集周期来说可以忽略。{id:dev01,t:26.5,h:58.2,alarm:0,ts:1710000000}字段说明id是设备标识多设备部署时用来区分数据来源t温度单位摄氏度h相对湿度百分比alarm当前报警状态0 正常/1 报警ts是 Unix 时间戳如果单片机没有 RTC 芯片这个字段可以留空由服务器端补。5.2 断线重连与数据补偿机制WiFi 版必坑场景半夜断网早上恢复这一晚上的数据全丢了。常见的可靠做法是“环形缓冲 一次性补传”#define HISTORY_NUM 30 // 保留最近 30 条历史数据 typedef struct { float temp; float humi; uint8_t alarm; } history_record_t; history_record_t history[HISTORY_NUM]; uint8_t history_idx 0; void record_history(float temp, float humi, uint8_t alarm) { history[history_idx % HISTORY_NUM] (history_record_t){temp, humi, alarm}; history_idx; } // 网络恢复后补传所有未上报的数据 void resend_pending(void) { uint8_t count history_idx HISTORY_NUM ? history_idx : HISTORY_NUM; for (uint8_t i 0; i count; i) { uint8_t idx (history_idx - count i) % HISTORY_NUM; char msg[64]; snprintf(msg, sizeof(msg), {\t\:%.1f,\h\:%.1f,\alarm\:%d}, history[idx].temp, history[idx].humi, history[idx].alarm); send_frame((uint8_t*)msg, strlen(msg)); delay_ms(50); // 留出发送间隔避免服务器压力 } }这里用环形缓冲区存最近 30 条记录掉电不保存只防瞬时断网。如果要求掉电不丢得外挂 Flash 或 EEPROM。补传的时间点放在 WiFi 模块重连成功之后用ATCIPSTATUS查询连接状态返回CIPSTATUS:0,3对 TCP 而言 3 表示已连接再触发。5.3 Web 配网让用户不用串口写 SSID很多毕设卡在“家里路由器密码是啥”这个问题上——不可能让每个用户都打开串口调试助手输 AT 指令。常见的体验做法是 Web 配网模块上电先以 AP 模式开一个热点用户手机连上后访问 192.168.4.1在网页表单里填自家 WiFi 的 SSID 和密码模块用这个信息去连路由器。ATCWMODE3 // AP Station 共存 ATCWSAPSmartDevice,12345678,5,3 // 配置 AP 热点 ATCIPMUX1 // 开启多连接 ATCIPSERVER1,80 // 开启 TCP 服务器配网页不用做得很花哨一个 HTML 表单交给 ESP8266 的 WEB 接口处理就够了。单片机这边要处理的逻辑是接收 HTTP POST 请求从 body 里解析出 SSID 和密码存到 Flash然后重启模块让配置生效。注意网页请求里 SSID 如果有空格或特殊字符需要用 URL 编码转义否则解析会出错。6. 上位机验证与进阶写一个最小可用的远程监控页面6.1 一个纯 HTML JavaScript 的实时数据面板远程端不用一上来就搞 App写一个浏览器打开的页面就够验证整条链路。用 Node.js 起一个 WebSocket 服务只做消息转发单片机通过 TCP 把 JSON 发到服务端服务端原样推给浏览器。// 服务端: Node.js ws 模块 const WebSocket require(ws); const net require(net); const wss new WebSocket.Server({ port: 8081 }); const tcpServer net.createServer((socket) { socket.on(data, (data) { // 把单片机上报的数据广播给所有 WebSocket 客户端 wss.clients.forEach((client) { if (client.readyState WebSocket.OPEN) { client.send(data.toString()); } }); }); }); tcpServer.listen(8080, 0.0.0.0);前端页面最核心的两个函数如下一个建 WebSocket 连接一个更新 DOMconst ws new WebSocket(ws://your-server-ip:8081); ws.onmessage (event) { const obj JSON.parse(event.data); document.getElementById(temp).innerText obj.t °C; document.getElementById(humi).innerText obj.h % RH; // 报警时页面变红 if (obj.alarm 1) { document.body.style.backgroundColor #ffcccc; document.getElementById(alarm-text).innerText ⚠ 超限报警; } else { document.body.style.backgroundColor #ffffff; document.getElementById(alarm-text).innerText 正常; } };6.2 三张表验收能响、能传、能恢复测试项操作通过标准本地报警用手捂住传感器升温蜂鸣器在阈值 ±0.5°C 内响应远程报警观察浏览器页面页面背景变红时间延迟 2 秒断线恢复拔掉路由器电源等 30 秒再插回WiFi 模块自动重连补发历史数据6.3 传感器校准技巧DHT22 这类传感器出厂前做过校准但长时间使用后会漂移。最简单的校准方法是做“两点校准”把传感器放进冰水混合物里记低温偏差再放进 50°C 热水里记高温偏差然后在固件里用一次线性修正float cali_temp(float raw) { // offset 和 scale 从外部串口命令写入 Flash return (raw - offset) * scale; }到这一步整个系统的闭环已经完整传感器数据 → 单片机本地判断 → WiFi 上报 → 浏览器可视化 → 阈值远程触发报警。剩下的空间——比如用巴法云之类的免费云平台替代自建服务器、加一个 OLED 屏显示实时数据、或者把报警推送到钉钉/微信机器人——都可以在这个骨架上继续加核心的串口帧协议和状态机不用动。本文还有配套的精品资源点击获取
返回列表