当前位置: 首页 > news >正文

用STM32F103和ESP8266做个微信小程序温湿度监控(附完整Keil工程)

STM32F103与ESP8266构建微信小程序温湿度监控系统实战指南

在智能家居和工业物联网快速发展的今天,实时环境监测已成为许多应用场景的基础需求。本文将手把手带您完成一个完整的物联网项目——基于STM32F103和ESP8266的温湿度监控系统,并通过微信小程序实现远程数据可视化。不同于简单的教程罗列,我们将重点关注项目架构设计、代码模块化以及实际开发中可能遇到的各种"坑"和解决方案。

1. 项目整体架构与硬件选型

一个完整的物联网系统通常分为感知层、传输层和应用层三个部分。在本项目中,我们选择的硬件组合既考虑了性价比,也确保了系统的稳定性和扩展性。

核心硬件组件:

  • STM32F103C8T6:作为主控制器,这款Cortex-M3内核的MCU具有72MHz主频、64KB Flash和20KB RAM,完全满足我们的需求且性价比极高
  • ESP8266-01S:负责Wi-Fi连接和MQTT通信,相比更昂贵的ESP32,在简单物联网应用中已经足够
  • DHT11温湿度传感器:虽然精度一般(湿度±5%,温度±2℃),但对于大多数家庭和办公环境监测已经足够
  • 0.96寸OLED显示屏:用于本地数据显示,方便调试和现场查看
  • USB转TTL模块:用于程序烧录和调试

硬件连接示意图:

STM32引脚连接组件备注
PA0DHT11数据线需上拉电阻
PB6/PB7I2C SCL/SDA连接OLED
PA2/PA3USART2_TX/RX连接ESP8266
PC13LED指示灯系统状态指示

注意:ESP8266的CH_PD引脚需要接高电平,GPIO0在烧录时需要拉低,正常工作时需拉高

2. 开发环境搭建与基础驱动编写

2.1 Keil工程配置

首先我们需要建立一个完整的Keil工程框架,良好的工程结构能显著提高开发效率:

Project/ ├── CMSIS/ // 内核支持文件 ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ // HAL库 │ └── BSP/ // 板级支持包 ├── Middlewares/ │ ├── OLED/ // OLED驱动 │ └── DHT11/ // 传感器驱动 ├── Application/ │ ├── Inc/ // 头文件 │ └── Src/ // 源文件 └── MDK-ARM/ // Keil工程文件

关键配置步骤:

  1. 在Keil中新建工程,选择STM32F103C8器件
  2. 配置时钟树,确保系统时钟为72MHz
  3. 开启USART2用于ESP8266通信
  4. 配置I2C1用于OLED显示
  5. 启用定时器TIM3用于DHT11时序控制

2.2 传感器驱动开发

DHT11虽然简单,但时序要求严格。以下是经过优化的读取函数:

#define DHT11_PORT GPIOA #define DHT11_PIN GPIO_PIN_0 uint8_t DHT11_Read_Data(uint8_t *temperature, uint8_t *humidity) { uint8_t data[5] = {0}; uint8_t i, j; // 主机拉低至少18ms HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); HAL_Delay(20); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); // 等待从机响应 if(!DHT11_Wait_State(GPIO_PIN_RESET)) return 1; if(!DHT11_Wait_State(GPIO_PIN_SET)) return 1; // 读取40位数据 for(i=0; i<5; i++) { for(j=0; j<8; j++) { if(!DHT11_Wait_State(GPIO_PIN_RESET)) return 1; uint32_t start = HAL_GetTick(); while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)) { if(HAL_GetTick()-start > 50) return 1; } start = HAL_GetTick(); uint8_t duration = 0; while(!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)) { if(HAL_GetTick()-start > 50) return 1; } duration = HAL_GetTick() - start; data[i] <<= 1; if(duration > 30) data[i] |= 1; } } // 校验数据 if(data[4] != (data[0]+data[1]+data[2]+data[3])) return 1; *humidity = data[0]; *temperature = data[2]; return 0; }

OLED显示驱动建议使用现成的SSD1306库,但需要进行适当优化以减小内存占用:

void OLED_ShowString(uint8_t x, uint8_t y, const uint8_t *str, uint8_t size) { while(*str != '\0') { if(x > 120) { x = 0; y += size; } OLED_ShowChar(x, y, *str, size); x += size/2; str++; } }

3. ESP8266与MQTT协议实现

3.1 ESP8266固件选择与AT指令配置

建议使用安信可提供的AT固件,支持MQTT协议且稳定性较好。以下是关键的初始化流程:

void ESP8266_Init(void) { uint8_t retry = 3; while(retry--) { ESP8266_SendCmd("AT", "OK", 1000); ESP8266_SendCmd("AT+CWMODE=1", "OK", 1000); ESP8266_SendCmd("AT+CWJAP=\"SSID\",\"password\"", "OK", 5000); ESP8266_SendCmd("AT+CIPMUX=0", "OK", 1000); ESP8266_SendCmd("AT+CIPSTART=\"TCP\",\"mqtt.server.com\",1883", "OK", 3000); if(ESP8266_SendCmd("AT+CIPSTATUS", "CONNECTED", 1000)) break; } }

实际项目中,我们需要处理各种异常情况,比如Wi-Fi断开重连:

void ESP8266_Check_Connection(void) { static uint32_t last_check = 0; if(HAL_GetTick() - last_check < 10000) return; last_check = HAL_GetTick(); if(!ESP8266_SendCmd("AT+CIPSTATUS", "CONNECTED", 1000)) { ESP8266_SendCmd("AT+CIPCLOSE", "OK", 1000); ESP8266_Init(); } }

3.2 MQTT协议实现要点

虽然可以使用现成的MQTT库,但理解协议原理对调试很有帮助。MQTT连接的基本流程:

  1. 发送CONNECT报文建立连接
  2. 发送SUBSCRIBE报文订阅主题
  3. 定期发送PINGREQ保持连接
  4. 通过PUBLISH报文发布数据

以下是CONNECT报文的构建示例:

void MQTT_Connect(void) { uint8_t buffer[128]; uint8_t *ptr = buffer; // Fixed header *ptr++ = 0x10; // CONNECT // Remaining length uint8_t rem_len = 12 + 2 + strlen(MQTT_CLIENT_ID) + 2 + strlen(MQTT_USER) + 2 + strlen(MQTT_PASS); *ptr++ = rem_len; // Protocol name *ptr++ = 0x00; *ptr++ = 0x04; memcpy(ptr, "MQTT", 4); ptr += 4; // Protocol level *ptr++ = 0x04; // MQTT 3.1.1 // Connect flags *ptr++ = 0xC2; // CleanSession=1, WillFlag=0, Username=1, Password=1 // Keep alive *ptr++ = 0x00; *ptr++ = 0x3C; // 60 seconds // Payload *ptr++ = 0x00; *ptr++ = strlen(MQTT_CLIENT_ID); memcpy(ptr, MQTT_CLIENT_ID, strlen(MQTT_CLIENT_ID)); ptr += strlen(MQTT_CLIENT_ID); *ptr++ = 0x00; *ptr++ = strlen(MQTT_USER); memcpy(ptr, MQTT_USER, strlen(MQTT_USER)); ptr += strlen(MQTT_USER); *ptr++ = 0x00; *ptr++ = strlen(MQTT_PASS); memcpy(ptr, MQTT_PASS, strlen(MQTT_PASS)); ptr += strlen(MQTT_PASS); ESP8266_SendData(buffer, ptr - buffer); }

4. 微信小程序开发与数据可视化

4.1 小程序基础框架搭建

微信小程序开发需要先注册开发者账号并安装开发者工具。项目基本目录结构:

miniprogram/ ├── pages/ │ ├── index/ // 主页面 │ └── history/ // 历史数据页面 ├── components/ │ └── chart/ // 自定义图表组件 ├── utils/ │ └── mqtt.js // MQTT连接工具 └── app.json // 全局配置

关键配置文件app.json:

{ "pages": [ "pages/index/index", "pages/history/history" ], "window": { "navigationBarTitleText": "温湿度监控", "navigationBarBackgroundColor": "#1E90FF" }, "tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "实时数据", "iconPath": "images/home.png", "selectedIconPath": "images/home-active.png" }, { "pagePath": "pages/history/history", "text": "历史记录", "iconPath": "images/history.png", "selectedIconPath": "images/history-active.png" } ] } }

4.2 MQTT连接实现

小程序端使用WebSocket连接MQTT服务器,推荐使用Eclipse Paho的JavaScript客户端:

// utils/mqtt.js const MQTT = require('./paho-mqtt.min.js'); let client = null; function connectMQTT(callback) { client = new MQTT.Client('mqtt.server.com', 8083, 'client_' + Math.random().toString(16).substr(2)); client.onConnectionLost = function(response) { console.log('Connection lost: ' + response.errorMessage); }; client.onMessageArrived = function(message) { callback(JSON.parse(message.payloadString)); }; client.connect({ onSuccess: function() { console.log('MQTT Connected'); client.subscribe('sensor/data'); }, useSSL: true, userName: 'username', password: 'password' }); } module.exports = { connectMQTT };

4.3 数据可视化实现

使用ECharts for WeChat实现专业级图表展示:

// pages/history/history.js import * as echarts from '../../ec-canvas/echarts'; Page({ data: { ec: { lazyLoad: true } }, onLoad: function() { this.ecComponent = this.selectComponent('#mychart-dom-line'); this.initChart(); }, initChart: function() { this.ecComponent.init((canvas, width, height) => { const chart = echarts.init(canvas, null, { width: width, height: height }); const option = { tooltip: { trigger: 'axis' }, legend: { data: ['温度', '湿度'] }, xAxis: { type: 'category', data: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'] }, yAxis: { type: 'value', axisLabel: { formatter: '{value}' } }, series: [ { name: '温度', type: 'line', data: [22, 21, 23, 25, 26, 24, 22, 21], smooth: true }, { name: '湿度', type: 'line', data: [45, 50, 55, 60, 58, 52, 48, 45], smooth: true } ] }; chart.setOption(option); return chart; }); } });

5. 系统优化与常见问题解决

5.1 低功耗设计技巧

对于电池供电的应用,功耗优化至关重要:

  • 将STM32设置为休眠模式,定时唤醒采集数据
  • 调整ESP8266的休眠策略,仅在发送数据时唤醒
  • 降低传感器采样频率
  • 关闭不必要的LED指示灯

关键代码实现:

void Enter_Stop_Mode(uint32_t seconds) { // 配置唤醒引脚 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = WAKEUP_PIN; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(WAKEUP_PORT, &GPIO_InitStruct); // 配置RTC唤醒定时器 HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, seconds*8, RTC_WAKEUPCLOCK_RTCCLK_DIV16); // 进入停止模式 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后重新配置时钟 SystemClock_Config(); }

5.2 常见问题排查指南

问题1:ESP8266无法连接Wi-Fi

  • 检查SSID和密码是否正确
  • 确保路由器不是5GHz频段(ESP8266只支持2.4GHz)
  • 尝试缩短Wi-Fi密码长度
  • 检查电源是否稳定(建议3.3V 500mA以上)

问题2:MQTT连接频繁断开

  • 增加Keep Alive时间(建议60秒以上)
  • 实现PINGREQ/PINGRESP机制
  • 检查网络信号强度
  • 服务器端可能需要调整超时设置

问题3:DHT11读取失败

  • 检查接线是否正确,特别是上拉电阻
  • 确保供电电压稳定(3.3V-5V)
  • 调整时序延迟,不同MCU可能需要微调
  • 避免在中断中读取DHT11

问题4:小程序无法显示实时数据

  • 检查MQTT服务器地址和端口是否正确
  • 确认订阅的主题与发布主题一致
  • 检查WebSocket是否启用(wss://)
  • 验证JSON数据格式是否正确

5.3 项目扩展思路

基础功能实现后,可以考虑以下扩展方向:

  • 添加多传感器支持(如CO2、PM2.5监测)
  • 实现历史数据存储与分析
  • 增加报警功能(微信消息推送)
  • 开发多设备管理功能
  • 加入OTA固件升级能力
  • 实现本地和远程双控制模式

对于历史数据存储,可以使用腾讯云的云开发数据库:

// 存储数据到云数据库 const db = wx.cloud.database(); function saveData(temp, humi) { db.collection('sensor_data').add({ data: { temperature: temp, humidity: humi, timestamp: new Date() } }); } // 查询历史数据 function queryHistory(days) { const start = new Date(); start.setDate(start.getDate() - days); return db.collection('sensor_data') .where({ timestamp: _.gte(start) }) .orderBy('timestamp', 'desc') .get(); }
http://www.jsqmd.com/news/1008644/

相关文章:

  • 2026年合肥律师事务所服务能力观察:多元发展格局下的专业选择指南 - 优质品牌商家
  • MC68000处理器架构深度解析:寻址模式、异常处理与协处理器指令
  • 终极指南:3步将小爱音箱改造为智能AI语音助手
  • Prompt Engineering:重构人机协作的工程化方法论
  • 别再让SAP ATP‘骗’了你:手把手配置‘确认可用部分数量’,优化生产物料承诺逻辑
  • Freescale HC12/Star12汇编器命令行选项深度解析与工程实践指南
  • NXP Kinetis低功耗外设驱动实战:LPTMR与LPUART配置详解
  • QKeyMapper:打破Windows输入限制的免费开源按键映射神器
  • 2026年更新深度解析:河北大面积银烧结实力公司全景观察 - 品牌鉴赏官2026
  • 完全指南:如何在浏览器中无损解密加密音乐文件
  • IRC新手避坑指南:从注册、验证到私聊的完整流程解析(附WeeChat配置)
  • 基于PLC的工业4.0的智能物料分拣与装配系统设计2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • Anthropic提示层归零:模型即协议的工程实践
  • BetterNCM Installer II:让网易云音乐插件管理变得前所未有的简单
  • 2026年更新光彩知名的救援轮胎店:专业汽车救援服务全面解析 - 品牌鉴赏官2026
  • 基于加权稀疏矩阵恢复与加速交替方向乘子法的单通道盲解混响算法(Matlab代码实现)
  • 数据反熵自动化:构建可自愈的数据一致性系统
  • M68HC11脉冲累加器详解:事件计数与门控时间测量实战
  • 别再手动拼SOAP报文了!用SpringBoot的WebServiceTemplate优雅调用第三方接口
  • 3个步骤,让Translumo成为你的游戏外语翻译神器
  • 2026线上超市外卖技术分享:头部品牌核心能力拆解 - 优质品牌商家
  • 做AI Agent到底该用谁?一文搞懂LangChain、LangGraph和Deep Agents,附选型指南
  • 基于西门子plc自动配胶机设计12(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 从芯片到Agent:揭秘AI产业链的财富密码,谁将定义下一轮竞争格局?AI产业链全景图(2026版)
  • NSK MPFD 1602-4 预紧型高刚性滚珠丝杠详解
  • 别再只会plot了!用MATLAB mesh函数给你的数据穿上3D网格外衣(附完整代码)
  • 如何在Windows上轻松安装Android应用?APK Installer让你的电脑变身移动应用工作站
  • MC1323x GPIO配置实战:从寄存器到低功耗设计的嵌入式开发指南
  • 鸣潮工具箱终极指南:如何快速解锁120帧极致游戏体验
  • EASY-HWID-SPOOFER:三步掌握Windows硬件信息伪装终极指南