1. 项目概述
这个项目实现了一个基于STM32F103C8T6单片机与ESP8266 WiFi模块的物联网终端设备,通过ONENET云平台实现远程LED控制和温湿度数据上传功能。作为一名嵌入式开发者,我在实际项目中经常需要搭建这类物联网原型系统,今天就把这个经典方案的完整实现过程分享给大家。
核心功能包括:
- 通过手机APP或网页远程控制STM32开发板上的LED灯
- 定时采集环境温湿度数据并上传至ONENET云平台
- 双向通信:既接收云端指令,又主动上报传感器数据
这个方案特别适合需要快速验证物联网功能的场景,硬件成本不到100元,软件全部采用开源库实现。下面我会从硬件选型到代码实现的每个环节详细解析,包括那些容易踩坑的细节。
2. 硬件准备与连接
2.1 硬件清单
核心组件:
- STM32F103C8T6最小系统板(蓝色PCB版)
- ESP8266-01S WiFi模块(建议选择带金属屏蔽罩的版本)
- DHT11温湿度传感器(或更精确的SHT30)
- LED灯及220Ω限流电阻
- USB转TTL串口模块(用于调试)
选型考量:
- STM32F103C8T6性价比极高,HAL库开发效率高,且引脚数量刚好满足需求
- ESP8266-01S体积小巧,AT指令稳定,比ESP-01多了RST按键和更稳定的电源设计
- DHT11虽然精度一般(湿度±5%,温度±2℃),但胜在价格便宜且驱动简单
提示:ESP8266务必选择3.3V供电版本,5V会直接烧毁模块!
2.2 电路连接
接线示意图:
code复制STM32F103C8T6 ESP8266-01S DHT11
PA9(TX) ---- URXD
PA10(RX) ---- UTXD
3.3V ---- VCC ---- VCC
GND ---- GND ---- GND
DATA ---- PC13
PC15 CH_PD ---- 3.3V
(控制LED) RST ---- 悬空
关键连接细节:
- 串口交叉连接:STM32的TX接ESP8266的RX,RX接TX
- ESP8266的CH_PD必须接高电平才能工作
- DHT11数据线建议加上4.7K上拉电阻
- LED通过PC15控制,串联220Ω电阻保护IO口
常见问题:如果ESP8266无法启动,检查CH_PD引脚是否接好,电源电压是否稳定在3.3V(建议用示波器查看)
3. 软件环境搭建
3.1 开发工具准备
必需软件:
- STM32CubeMX 6.5.0(配置HAL库)
- Keil MDK 5.36(ARMCC编译器)
- 串口调试助手(推荐SecureCRT)
- ONENET Studio账号
环境配置步骤:
- 安装STM32CubeMX时勾选F1系列支持包
- Keil安装后需要注册(社区版有32K代码限制)
- ONENET上创建"多协议接入"产品,记录产品ID和API Key
3.2 CubeMX工程配置
关键配置项:
- 时钟树:外部8MHz晶振,系统时钟设为72MHz
- USART1:波特率115200,8N1,开启全局中断
- GPIO:PC13输入模式(DHT11),PC15推挽输出(LED)
- 定时器TIM2:1ms中断用于系统时基
生成代码前注意:
- 项目名称不要含中文
- 代码生成选项中勾选"生成外设初始化代码"
- 堆栈大小建议设为0x800(默认0x400可能不够)
4. 通信协议实现
4.1 ESP8266 AT指令控制
基础AT指令序列:
c复制// WiFi连接
sendAT("AT+CWMODE=1"); // STA模式
sendAT("AT+CWJAP=\"SSID\",\"PASSWORD\"");
// MQTT配置
sendAT("AT+MQTTUSERCFG=0,1,\"设备名称\",\"产品ID\",\"API_KEY\",0,0,\"\"");
sendAT("AT+MQTTCONN=0,\"183.230.40.96\",6002,1)");
实测技巧:每条AT指令后建议延迟100ms,ESP8266响应需要时间
4.2 ONENET MQTT协议封装
主题定义:
- 数据上报:$sys/{pid}/{devname}/thing/property/post
- 指令接收:$sys/{pid}/{devname}/thing/property/set
数据格式示例:
json复制// 温湿度上报
{
"id": "123",
"version": "1.0",
"params": {
"temp": {"value": 25.3},
"humi": {"value": 56.2}
}
}
// LED控制指令
{
"params": {
"led": {"value": 1}
}
}
4.3 DHT11驱动实现
数据采集时序:
c复制void DHT11_Read(uint8_t *temp, uint8_t *humi) {
// 主机拉低18ms后释放
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(18);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
// 等待从机响应
while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET);
while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET);
while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET);
// 读取40bit数据
for(int i=0; i<5; i++) {
for(int j=0; j<8; j++) {
while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_RESET);
uint32_t t = HAL_GetTick();
while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET);
data[i] <<= 1;
if(HAL_GetTick()-t > 40) data[i] |= 1;
}
}
}
避坑指南:DHT11时序要求严格,建议关闭中断期间读取,否则容易校验失败
5. 主程序逻辑设计
5.1 状态机架构
c复制typedef enum {
SYS_INIT,
WIFI_CONNECT,
MQTT_CONNECT,
DATA_REPORT,
CMD_WAIT
} SystemState;
void main() {
SystemState state = SYS_INIT;
while(1) {
switch(state) {
case SYS_INIT:
if(HAL_Init() == HAL_OK) state = WIFI_CONNECT;
break;
case WIFI_CONNECT:
if(connectWiFi()) state = MQTT_CONNECT;
break;
// ...其他状态转换
}
processMQTTMessages(); // 处理云端指令
}
}
5.2 数据上报策略
采用"定时上报+变化触发"双机制:
- 每5分钟固定上报一次数据
- 当温度变化±1℃或湿度变化±5%时立即上报
- 每次上报包含3次重试机制
实现代码片段:
c复制void checkSensorUpdate() {
static uint8_t last_temp = 0, last_humi = 0;
uint8_t temp, humi;
DHT11_Read(&temp, &humi);
if(abs(temp-last_temp)>=1 || abs(humi-last_humi)>=5) {
reportData(temp, humi);
last_temp = temp;
last_humi = humi;
}
}
6. 云端配置详解
6.1 ONENET产品创建
关键步骤:
- 进入开发者中心 → 多协议接入 → MQTT
- 创建新产品时选择"自定义品类"
- 添加物模型:
- 温度属性(float, 单位℃)
- 湿度属性(float, 单位%)
- LED控制(bool, 可写)
6.2 设备鉴权设置
安全建议:
- 使用"一机一密"模式
- API Key建议包含大小写字母和数字
- 开启设备权限管理:
- 订阅:thing/property/set
- 发布:thing/property/post
6.3 数据可视化配置
实用功能:
- 创建数据流图表展示温湿度曲线
- 设置阈值告警(如温度>30℃触发通知)
- 添加开关控件绑定LED状态
7. 常见问题排查
7.1 ESP8266连接异常
现象:AT指令无响应
排查步骤:
- 检查TX/RX接线是否交叉
- 测量3.3V电源实际电压(需>3.2V)
- 尝试降低波特率到9600测试
- 按住RST键重新上电
7.2 数据上报失败
错误代码分析:
- 401:鉴权失败 → 检查产品ID/设备名称/API Key
- 500:服务器错误 → 等待一段时间后重试
- 503:服务不可用 → 检查MQTT连接状态
7.3 指令响应延迟
优化方案:
- 增加看门狗定时器防卡死
- MQTT心跳间隔设为120秒
- 使用QoS1保证指令必达
8. 性能优化技巧
8.1 低功耗设计
- 修改ESP8266睡眠模式:
c复制sendAT("AT+SLEEP=2"); // 轻度睡眠
- STM32进入STOP模式,通过串口中断唤醒
- 传感器采样间隔调整为10分钟(静态环境)
8.2 通信可靠性提升
- 增加ACK确认机制:
c复制if(publishSuccess) {
sendAT("AT+MQTTPUB=0,\"$sys/.../ack\",\"OK\",1,0");
}
- 实现离线缓存(Flash存储最近3次数据)
- 使用腾讯云DNS(119.29.29.29)解析ONENET地址
8.3 固件远程升级
- 通过ONENET下发包含固件URL的指令
- ESP8266使用HTTP分段下载
- STM32 IAP编程实现:
c复制void JumpToBootloader() {
void (*SysMemBootJump)(void);
volatile uint32_t addr = 0x1FFFF000;
HAL_RCC_DeInit();
HAL_DeInit();
SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr + 4)));
__set_MSP(*(uint32_t *)addr);
SysMemBootJump();
}
这个项目最让我惊喜的是STM32+ESP8266的组合在稳定性上的表现,连续运行30天没有出现异常重启。实际部署时建议给ESP8266加上PCB天线而不是陶瓷天线,在信号弱的场景下差异非常明显。如果要做商业化产品,可以考虑换成ESP32-C3,原生支持蓝牙+WiFi且价格相当。