
最近把家里阳台的温湿度监控改成了 WiFi 无线版核心用的就是 STM32 ESP8266 DHT11 OLED 这套经典组合。整体做下来最大的感受是这套方案成本不高、原理清晰、每个模块单独拎出来都有很多可以深入的点特别适合作为物联网入门到进阶的过渡项目。这篇文章就把整个制作过程完整拆开从硬件选型、接线、驱动代码到 ESP8266 网络上传再到日常调试中踩过的坑全部记录下来希望能给正在做类似项目的朋友省点时间。这套东西能做什么传感器负责每分钟采集一次温湿度OLED 屏幕上实时刷新显示ESP8266 通过 WiFi 把数据上报到局域网服务端或者云平台这样即使人不在现场也能在手机或电脑上看到数据变化。它的定位很明确是一个可以稳定跑通的无线传感器节点方案而不是玩具级 demo。适合作的人包括刚学完 STM32 基础外设想找一个完整项目的同学也包括想给家里做环境监控的嵌入式爱好者。1. 整体方案与硬件选型思路1.1 为什么是 STM32 ESP8266 DHT11 OLED很多类似项目会用 ESP8266 直接连 DHT11 和 OLED毕竟 ESP8266 本身也有 GPIO逻辑上完全可行。但我这次刻意保留 STM32 作为主控原因有三一是 STM32 的生态更偏“单片机工程师日常”定时器、中断、I2C、串口等外设配合 HAL 库非常成熟适合把这个项目当成外设综合练习二是 ESP8266 只干通信这一件事模块本身的无线协议栈处理已经比较复杂如果再把传感器时序和显示刷新都压给它调试时很难分清问题是出在数据采集还是网络传输上三是这套架构天然可以扩展到多传感器后续换 SHT30、BMP280或者加继电器控制设备主控逻辑完全不用动。DHT11 的精度确实一般温度误差 ±2°C湿度误差 ±5%RH但胜在便宜、协议简单用来学习单总线时序非常合适。如果做实际产品我建议换成 DHT22 / SHT30但教程逻辑完全一致驱动函数只有时序参数略有不同。OLED 选 0.96 寸 I2C 接口的 SSD1306128x64 分辨率只需要两根数据线就能驱动省 GPIO 也省事。1.2 硬件清单与功能拆解主控STM32F103C8T6 最小系统板蓝色 pill 板价格低资料多HAL 库支持完善。WiFi 模块ESP8266-01S 或 ESP-12F 转接板我用的是 ESP-01S性价比高但只有 GPIO0/GPIO2 引出做纯透传足够。传感器DHT11 蓝色三针模块板载上拉电阻直接插杜邦线就能用。显示0.96 寸 SSD1306 OLEDI2C 接口四针VCC/GND/SCL/SDA。电源USB-TTL 模块的 5V 供电或者 AMS1117-3.3 稳压模块。STM32 板载 LDO 可以输出 3.3V但 ESP8266 峰值电流可能到 200-300mA最好独立供电避免 OLED 显示闪烁。调试工具USB-TTLCH340用于给 ESP8266 烧 AT 固件和看日志。整个系统的数据流是DHT11 通过单总线传给 STM32STM32 处理后一方面送 OLED 刷新另一方面通过 UART 把数据帧发给 ESP8266ESP8266 以 AT 指令模式连上 WiFi用 TCP 或 MQTT 上报到远端。2. 硬件接线与模块调试前的准备2.1 各模块接线表接线是整个项目最容易翻车的第一关我整理了一张自己的接线表主机是 STM32F103C8T6串口调试信息走 USART1ESP8266 走 USART2OLED 走硬件 I2C1。STM32 引脚连接目标说明3.3VOLED VCC、ESP8266 VCC通过外部稳压逻辑电源GNDOLED GND、ESP8266 GND、DHT11 GND共地必须接PB7OLED SCLI2C1_SCL硬件 I2CPB6OLED SDAI2C1_SDA硬件 I2CPB8DHT11 DATA单总线双向引脚PA2ESP8266 RX通过分压USART2_TXPA3ESP8266 TXUSART2_RXPA9 / PA10USB-TTL 的 RX/TX调试日志 USART1注意 PB8 作为 DHT11 数据线建议加一个 4.7k 上拉电阻到 3.3V。虽然很多模块板载上拉但如果买的是单独的传感器而不是蓝色小板不加的话时序高电平会偏弱偶尔读到 0。2.2 电平匹配与供电注意事项ESP8266 是 3.3V 逻辑但 UART 引脚并不都是 5V 耐受所以 STM32 的 3.3V 供电系统下其实可以直接互连。不过如果用的是 5V 供电的 STM32 板子比如某些开发板通过 VIN 供电后逻辑还是 3.3V发现通信不稳定时要检查一下 TX 电平。最稳妥的方式是 STM32 TX 到 ESP8266 RX 之间串一个 1k 电阻分压或者用两个电阻组成 1.8k/3.3k 分压。供电上我的经验是ESP8266 与 STM32 分成两路供电。USB-TTL 输出 5V 进 STM32 的 5V 引脚同时 5V 给 AMS1117-3.3 输入端然后 AMS1117 输出单独给 ESP8266 供电。不要直接从 STM32 的 3.3V 引脚给 ESP8266 供电因为板载 LDO 在 WiFi 发射瞬间压降明显会出现 OLED 亮度突变甚至 MCU 复位。2.3 ESP8266 AT 固件检查与网络配置拿到 ESP8266 第一步不是忙着接线而是先用 USB-TTL 单独测试模块能否响应 AT 指令。把 ESP-01S 的 VCC、GND、TX、RX、GPIO0 接好GPIO0 接高电平进入运行模式然后在串口工具里发送AT返回OK说明固件正常。接下来配置 WiFi 参数。我在调试阶段最常用这几条指令ATCWMODE1 ATCWJAP你的SSID,你的密码 ATCIPSTARTTCP,192.168.8.100,8080 ATCIPMODE1 ATCIPSENDATCWMODE1 是 Station 模式只连接路由不创建热点。ATCWJAP 连上 WiFi 后用 ATCIFSR 查询模块拿到的 IP确认已经拿到地址。ATCIPSTART 建立 TCP 连接ATCIPMODE1 进入透传模式之后单片机发给串口的数据都会直接通过 TCP 发出去。退出透传的方法是发送注意前后不要跟回车否则会被当成数据发出去。如果模块一直返回 ERROR我建议先用 ATCWMODE1 和 ATRST 复位然后再试。老固件可能不支持 ATCWMODE3 和透传同时使用所以配置顺序很有讲究。3. 关键驱动代码与协议实现3.1 DHT11 单总线时序与 HAL 库实现DHT11 只有一根数据线所有通信都在这一根线上完成协议本身不复杂但对时序敏感。裸机编程里最直接的做法是用延时函数精确控制高低电平宽度。在 STM32 HAL 库环境下我用的是 GPIOD 读引脚 Delay_us 微秒延时实现的。DHT11 的通信过程分成两步主机发起始信号传感器响应后连续输出 40 bit 数据。起始信号是主机先把数据线拉低至少 18ms然后释放并拉高 20-40us之后传感器会拉低 80us 作为响应再把总线拉高 80us然后开始输出数据位。每一位数据都是一个低电平 高电平的组合数据 0 的高电平持续时间约 26-28us数据 1 的高电平持续时间约 70us。读位的代码可以写成“等待低电平结束然后统计高电平持续时间根据时间判断 0/1”。uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { while (HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) GPIO_PIN_RESET); delay_us(40); if (HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) GPIO_PIN_SET) { data | (1 (7 - i)); while (HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) GPIO_PIN_SET); } } return data; }这段代码的思路是先等待低电平结束也就是数据位开始后的低电平部分。接着延时 40us如果此时引脚还是高电平说明是数据 1高电平持续长否则是数据 0。这个技巧在逻辑分析仪上验证过只要主频 72MHz 下延时函数足够准就能稳定读取。读完整帧后5 个字节分别是湿度整数、湿度小数、温度整数、温度小数、校验和。校验和等于前四个字节之和的低 8 位如果校验失败我直接丢弃这帧数据不更新 OLED 和网络值。DHT11 手册要求两次读取间隔大于 1 秒推荐 2 秒一次我最终改成每 5 秒采集一次避免温度上升时传感器自身发热影响读数。3.2 OLED 显示驱动与自定义汉字显示OLED 部分我直接用 STM32 硬件 I2C1 驱动 SSD1306。初始化流程主要是发送 SSD1306 的配置序列关闭显示、设置显示时钟分频、设置复用比、设置显示偏移、开启显示等。这里不贴完整初始化数组网上 HAL 库 SSD1306 的初始化代码非常多核心是确认 I2C 地址是 0x787 位地址 0x3C。很多人一开始 OLED 点不亮最大的坑是 I2C 引脚复用配置不对。开启硬件 I2C 后需要把 GPIOB 的 AF 配置为 I2C1 的复用功能并且把时钟使能。在 CubeMX 里直接选 I2C1然后在 GPIO 设置里选开漏输出、上拉 4.7k这样最省事。显示内容上我除了显示温度湿度数值还想显示“温度”“湿度”汉字。SSD1306 不带字库需要自己取模。我的办法是取模软件设置字符大小为 16x16点阵格式选列行式生成数组后定义一个小字库。比如const uint8_t tian_[] {0x04,0x04,0xFC,0x04,0x00,0x00,...};然后写一个OLED_ShowChinese(x, y, index)函数按 16 像素宽度水平扫描写入显存最后统一调用OLED_Refresh()刷新到屏幕。刷新频率不要太高否则 I2C 会吃掉不少 CPU 时间DHT11 时序容易受影响。我实测 5Hz 刷新完全流畅肉眼看不见闪烁。OLED 驱动有一个细节写入显存后必须调用刷新函数否则屏幕是空的。如果在刷新之前去操作传感器显示会出现撕裂感最好的做法是定时器中断里做“采集 刷新”主循环只处理网络上报任务分块。3.3 STM32 与 ESP8266 的串口通信与 AT 指令控制STM32 和 ESP8266 之间走的是 USART2波特率我固定 115200数据格式 8N1。STM32 作为主机通过 HAL_UART_Transmit 发送 AT 指令通过 HAL_UART_Receive_IT 接收 ESP8266 的回应。这里最需要处理的是“何时发指令、何时等回应”。我的做法是封装一个简单的 AT 命令函数uint8_t ESP8266_SendCmd(char *cmd, char *ack, uint16_t timeout) { HAL_UART_Transmit(huart2, (uint8_t *)cmd, strlen(cmd), 100); return ESP8266_WaitAck(ack, timeout); }ESP8266_WaitAck会循环接收串口数据把收到的内容暂存在缓冲区里然后用strstr查找期望的关键字比如OK或SEND OK。如果超时还没匹配到就返回失败。这个函数是整个通信稳定性的关键应答超时控制的粒度尽量短我一般用 10ms 轮询一次。模块上电后我不会立刻发连接指令而是先循环发送 AT 等待模块就绪同时打印日志。实际测试中 ESP-01S 从上电到 AT 可响应至少要 500ms-1s如果上电后立刻发协议指令大概率会丢第一条。网络部分我做了两种模式TCP 透传和 MQTT 模式。TCP 透传适合自建局域网服务端数据格式简单。MQTT 模式是让 ESP8266 先连上 TCP 到 MQTT Broker然后按照 MQTT 协议包格式用 AT 指令的透传模式发送 PUBLISH 报文。如果只是本地监控TCP 透传就够了如果想要接入云平台建议直接使用 ESP8266 上的 MQTT 固件或者换 ESP32单芯片搞定传感器网络省去 STM32 也能跑但在这个项目里我更享受把每个环节拆开调通的过程。4. 数据上传方案与远程监控扩展4.1 局域网 TCP 服务端接收数据最简单的一种方案是 STM32 把 JSON 格式的温湿度数据通过 ESP8266 透传发给局域网内的服务端。我写了 Python 脚本在电脑上监听 8080 端口手机或另一台电脑就能看到数据。STM32 端串口发送的数据格式我定为{temp: 25.6, humi: 60.2, id: 1}ESP8266 在透传模式下收到串口字节流就会原样发到 TCP 服务端。要注意的是透传模式下模块不会自动加回车换行所以 STM32 发送时要在末尾加上\r\n作为帧结束符方便服务端按行解析。服务端 Python 的简化代码import socket s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((0.0.0.0, 8080)) s.listen(5) while True: conn, addr s.accept() data conn.recv(128) if data: print(data.decode().strip())实测效果模块稳定运行 7 天偶尔会丢一两个包但重连机制做好之后基本不影响使用。4.2 基于 MQTT 的云平台接入思路如果人不在同一个局域网靠 TCP 透传就搞不定了这时候需要走 MQTT。MQTT 是很适合低带宽设备的协议一是消息体积小二是有 QoS 机制三是连接保持长连接比 HTTP 轮询省电省流量。接入公共 MQTT Broker 的思路是ESP8266 AT 指令建立一个 TCP 连接然后按照 MQTT CONNECT 报文格式发送连接请求再发送 PUBLISH 报文。但直接用裸 AT 指令拼 MQTT 包非常痛苦协议帧、剩余长度编码、报文标识符都要自己算。如果只是学习我建议先用 AT 指令连上本地部署的 Mosquitto然后用 Wireshark 抓包对比协议格式。如果想把功能跑起来更高效的办法是换一个刷了 MQTT 固件的 ESP8266或者直接用 ESP32。以 STM32 ESP8266 这套架构来说我自己最终是把 ESP8266 刷成了 NodeMCU 固件然后 STM32 通过串口向 ESP8266 发简单的 TCP 命令后续再做协议封装就容易多了。4.3 数据解析与状态判断逻辑数据往上送之前最好先在 MCU 里做一次合理性判断。DHT11 偶尔会因为上电瞬间或干扰读到一个特别离谱的数比如湿度 248%温度 -15°C。我加了一个简单的滤波if (temp 50 || temp -10 || humi 99 || humi 0) { // 丢弃该帧不更新显示和上报 return; }另外我做了简单的滑动平均取最近 5 次有效读数的平均值再上报。这样显示界面上的数字不会忽上忽下地跳人看起来舒服一点。状态判断方面我在主机端定义了一个枚举DATA_OK、SENSOR_ERR、WIFI_DISCONN。OLED 第三行会实时显示当前状态。如果 WiFi 断开屏幕会有一个小圆点闪烁提示而传感器错误时温度湿度区域直接显示--.-。这个细节在调试时非常有用因为系统跑在无人环境下没有状态提示隔天回来看数据全是 0根本不知道是传感器挂了还是 WiFi 断了。5. 常见问题与排查技巧实录5.1 DHT11 读不到数据或读到固定值这是新手最容易卡住的地方。现象有三个数据一直为 0、数据偶尔变一次然后不变、OLED 上温度湿度永远是 0.0。排查顺序我固定这样走先看模块供电是不是 3.3-5V 稳定DHT11 对电压很敏感然后量 DATA 引脚上拉电压正常空闲状态是 3.3V 高电平接着用示波器或者老式逻辑分析仪看起始信号确认主机有没有发出 18ms 低电平最后检查两次读取时间间隔如果循环里连续快速读DHT11 会不响应。我踩过一次很隐蔽的坑初始化 DHT11 前忘记把 PB8 设成开漏输出。DHT11 数据线需要开漏输出这样外部上拉才能把电平拉高而我一开始用推挽输出结果时序一直不对读出来的数据七零八落。改成开漏之后一次通过。5.2 OLED 不显示或花屏OLED 不显示首先查 I2C 地址SSD1306 通常为 0x3C写操作地址 0x78。如果扫描不到设备检查 SCL/SDA 是否接反然后看有没有把 GPIO 配成开漏输出并加速上拉。花屏多数情况是刷新时序问题。我遇到过在 OLED 刷新过程中 DHT11 的延时被打断导致屏幕显示残缺。解决办法是刷新操作放主循环DHT11 读取时屏蔽掉相同优先级的调度或者用 DMA 传输 I2C 数据。SSD1306 还有一个常见要求初始化序列不能省略很多人直接从网上拷一段不完整的初始化代码导致显示不正常。5.3 ESP8266 连接不上 WiFi 或丢包这里我建议按下面顺序自查现象可能原因解决办法AT 有响应但连不上 AP路由器频段是 5G不支持切成 2.4G 频段上电后 AT 卡死供电不足独立 3.3V 电源供电连上 AP 但 TCP 连接失败服务端 IP/端口错误同一局域网用 CIFSR 查一下 IP透传模式收不到数据没有先发 ATCIPSEND重新设置透传并确认收到OK串口打印乱码波特率不匹配确认两边都是 115200丢包方面我建议不要直接用 72MHz 主频下普通延时函数来发送大缓冲区数据。USART 发送是阻塞的如果一次发 512 字节MCU 在发送期间无法处理中断ESP8266 内部缓冲区如果满了就会丢。做法是拆分数据包每包不超过 128 字节或者在发送之间加 10ms 间隔。5.4 STM32 频繁复位或程序跑飞STM32F103C8T6 如果电源质量差很容易在上电和 WiFi 通信瞬间复位。排查方法首先是独立供电然后检查复位引脚是否外部干扰最后看看门狗配置。如果开了 IWDG但主循环处理网络超时超过喂狗时间就会不断复位。我做网络重连时会把喂狗放到独立定时器里保证即使串口等待应答也不会触发看门狗复位。还有一点是中断优先级。DHT11 的延时循环如果被高优先级串口中断打断时序会崩。我的做法是 DHT11 读取函数运行期间关闭所有中断读取完成后恢复。虽然这会短暂打断其他事务但也就 1ms 左右影响很小。6. 经验总结与可扩展的方向这套系统从原型到稳定运行我大概花了两天时间。如果只看最终的代码量其实不大核心驱动加逻辑不到 500 行但调试过程接触到的知识点密度很高。回过头来想几个点对后续做复杂项目特别有参考价值硬件上电源分区处理软件上任务调度和状态机设计协议上 AT 指令异步应答处理这些都是物联网节点开发的基本功。如果还想继续扩展有几个方向我亲测可以玩把 DHT11 换成 SHT30 走 I2C精度提升明显增加一个继电器模块根据温度和湿度自动控制加湿器或排风扇OLED 显示界面可以增加趋势曲线最近 24 小时的小时柱状图云端存储用 MQTT 接入开源面板手机上直接看折线图。另外如果对低功耗有需求可以让 STM32 进入 STOP 模式ESP8266 用 ATGSLP 睡眠定时唤醒采集一次数据然后继续睡这样用 18650 电池也能撑很久。最后分享一个我在实际测试中总结的小技巧不要一开始就追求 PCB 一体化先把模块用杜邦线搭起来验证每个环节的信号。很多所谓“玄学问题”最后查出来都是杜邦线接触不良或者供电引脚氧化。等整套逻辑稳定了再画一块小板把 DHT11、OLED 和 ESP8266 焊上去你会有种豁然开朗的感觉。做嵌入式项目稳定压倒一切功能再花哨跑一会儿就挂也没意义。这套 WiFi 温湿度监控方案算是把稳定性和学习性平衡得特别好的一套组合了。