基于STM32F103+ESP8266的OneNet物联网数据上传实战
一、项目概述
本项目使用STM32F103C8T6作为主控,通过DHT11传感器采集温湿度数据,利用ESP8266 WiFi模块连接OneNet物联网平台,实现以下核心功能:
- 周期采集环境温湿度数据(精度:温度±2℃,湿度±5%RH)
- 通过MQTT协议对接中国移动OneNet平台
- 实现设备级心跳包(60秒)和异常重连机制
- 数据可视化展示与历史记录存储
二、硬件准备
组件 | 型号 | 接口方式 |
---|---|---|
主控芯片 | STM32F103C8T6 | - |
WiFi模块 | ESP-01S | UART |
温湿度传感器 | DHT11 | GPIO |
开发板 | Blue Pill | - |
USB-TTL转换器 | CH340G | - |
接线示意图:
text
DHT11_DATA -> PA5
ESP8266_TX -> PA3 (USART2_RX)
ESP8266_RX -> PA2 (USART2_TX)
三、开发环境配置
- 安装 STM32CubeMX 6.9+
- 创建工程配置:
- 系统时钟:72MHz(外部8MHz晶振)
- USART2:115200bps,异步模式
- GPIO:PA1设置为上拉输入
- 生成HAL库基础代码
我并没有使用CubeMx,我使用的HAL代码进行的初始化。大家可以获取我的模板资源。
四、核心代码实现
1. DHT11驱动开发
dht11.h
c
#ifndef __DHT11_H__
#define __DHT11_H__
#include "sys.h"
#define DHT11_PORT GPIOA
#define DHT11_PIN GPIO_PIN_5
#define DHT11_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DHT11_DQ_OUT(x) do{ x ? \
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET);\
}while(0)
#define DHT11_DQ_IN HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)
void dht11_read(uint8_t *result);
#endif
dht11.c
c
#include "dht11.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"
char dht11_data[5] = {0};
void dht11_gpio_input(void)
{
GPIO_InitTypeDef gpio_initstruct;
DHT11_CLK_ENABLE();
gpio_initstruct.Pin = DHT11_PIN;
gpio_initstruct.Mode = GPIO_MODE_INPUT;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}
void dht11_gpio_output(void)
{
GPIO_InitTypeDef gpio_initstruct;
DHT11_CLK_ENABLE();
gpio_initstruct.Pin = DHT11_PIN;
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}
void dht11_start(void)
{
dht11_gpio_output();
DHT11_DQ_OUT(1);
DHT11_DQ_OUT(0);
delay_ms(20);
DHT11_DQ_OUT(1);
dht11_gpio_input();
while(DHT11_DQ_IN); //等待DHT11拉低电平
while(!DHT11_DQ_IN); //等待DHT11拉高电平
while(DHT11_DQ_IN); //等待DHT11拉低电平
}
uint8_t dht11_read_byte(void)
{
uint8_t temp = 0;
uint8_t i = 0;
uint8_t read_data = 0;
for(i = 0; i < 8; i++)
{
while(!DHT11_DQ_IN);
delay_us(50);
if(DHT11_DQ_IN == 1)
{
temp = 1;
while(DHT11_DQ_IN);
}
else
temp = 0;
read_data = read_data << 1;
read_data |= temp;
}
return read_data;
}
void dht11_read(uint8_t *result)
{
uint8_t i = 0;
dht11_start();
dht11_gpio_input();
for(i = 0; i < 5; i++)
dht11_data[i] = dht11_read_byte();
if(dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3] == dht11_data[4])
{
memcpy(result, dht11_data, 4);
printf("湿度:%d.%dRH ,", dht11_data[0], dht11_data[1]);
printf("温度:%d.%d℃\r\n", dht11_data[2], dht11_data[3]);
}
delay_ms(2000);
}
2. ESP8266通信协议栈
esp8266.h
c
#ifndef __ESP8266_H__
#define __ESP8266_H__
#include "sys.h"
#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64
#define ESP8266_EOK 0
#define ESP8266_ERROR 1
#define ESP8266_ETIMEOUT 2
#define ESP8266_EINVAL 3
#define ESP8266_STA_MODE 1
#define ESP8266_AP_MODE 2
#define ESP8266_STA_AP_MODE 3
#define ESP8266_SINGLE_CONNECTION 0
#define ESP8266_MULTI_CONNECTION 1
#define WIFI_SSID "HuaweiAP-1ED0_Guest"
#define WIFI_PWD "gcc11111111"
#define TCP_SERVER_IP "mqtts.heclouds.com"
#define TCP_SERVER_PORT "1883"
void esp8266_init(uint32_t baudrate);
void esp8266_receive_data(void);
void esp8266_send_data(char *data, uint16_t len);
uint16_t esp8266_copy_rxdata(char *data);
uint8_t esp8266_wait_receive(void);
#endif
esp8266.c
c
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE];
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE];
uint16_t esp8266_cnt = 0, esp8266_cntPre = 0;
UART_HandleTypeDef esp8266_handle = {0};
void esp8266_uart_init(uint32_t baudrate)
{
esp8266_handle.Instance = USART2;
esp8266_handle.Init.BaudRate = baudrate;
esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
esp8266_handle.Init.StopBits = UART_STOPBITS_1;
esp8266_handle.Init.Parity = UART_PARITY_NONE;
esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
esp8266_handle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&esp8266_handle);
}
void USART2_IRQHandler(void)
{
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET)
{
if(esp8266_cnt >= sizeof(esp8266_rx_buf))
esp8266_cnt = 0;
HAL_UART_Receive(&esp8266_handle, &receive_data, 1, 1000);
esp8266_rx_buf[esp8266_cnt++] = receive_data;
//uart1_cnt++;
//HAL_UART_Transmit(&uart1_handle, &receive_data, 1, 1000);
}
}
uint8_t esp8266_wait_receive(void)
{
if(esp8266_cnt == 0)
return ESP8266_ERROR;
if(esp8266_cnt == esp8266_cntPre)
{
esp8266_cnt = 0;
return ESP8266_EOK;
}
esp8266_cntPre = esp8266_cnt;
return ESP8266_ERROR;
}
void esp8266_rx_clear(void)
{
memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
esp8266_cnt = 0;
}
void esp8266_receive_data(void)
{
if(esp8266_wait_receive() == ESP8266_EOK)
{
printf("esp8266 recv: %s\r\n", esp8266_rx_buf);
esp8266_rx_clear();
}
}
//void esp8266_send_data(char *fmt, ...)
//{
// va_list ap;
// uint16_t len;
//
// va_start(ap, fmt);
// vsprintf((char *)esp8266_tx_buf, fmt, ap);
// va_end(ap);
//
// len = strlen((const char *)esp8266_tx_buf);
// HAL_UART_Transmit(&esp8266_handle, esp8266_tx_buf, len, 100);
//}
void esp8266_send_data(char *data, uint16_t len)
{
esp8266_rx_clear();
HAL_UART_Transmit(&esp8266_handle, (unsigned char *)data, len, 100);
}
uint16_t esp8266_copy_rxdata(char *data)
{
memcpy(data, esp8266_rx_buf, esp8266_cntPre);
return esp8266_cntPre;
}
uint8_t esp8266_send_command(char *cmd, char *res)
{
uint8_t time_out = 250;
esp8266_rx_clear();
HAL_UART_Transmit(&esp8266_handle, (uint8_t *)cmd, strlen(cmd), 100);
while(time_out--)
{
if(esp8266_wait_receive() == ESP8266_EOK)
{
if(strstr((const char*)esp8266_rx_buf, res) != NULL)
return ESP8266_EOK;
}
delay_ms(10);
}
return ESP8266_ERROR;
}
uint8_t esp8266_at_test(void)
{
return esp8266_send_command("AT\r\n", "OK");
}
uint8_t esp8266_set_mode(uint8_t mode)
{
switch(mode)
{
case ESP8266_STA_MODE:
return esp8266_send_command("AT+CWMODE=1\r\n", "OK");
case ESP8266_AP_MODE:
return esp8266_send_command("AT+CWMODE=2\r\n", "OK");
case ESP8266_STA_AP_MODE:
return esp8266_send_command("AT+CWMODE=3\r\n", "OK");
default:
return ESP8266_EINVAL;
}
}
uint8_t esp8266_join_ap(char *ssid, char *pwd)
{
char cmd[64];
sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, pwd);
return esp8266_send_command(cmd, "WIFI GOT IP");
}
uint8_t esp8266_connection_mode(uint8_t mode)
{
char cmd[64];
sprintf(cmd, "AT+CIPMUX=%d\r\n", mode);
return esp8266_send_command(cmd, "OK");
}
uint8_t esp8266_connect_tcp_server(char *server_ip, char *server_port)
{
char cmd[64];
sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", server_ip, server_port);
return esp8266_send_command(cmd, "CONNECT");
}
uint8_t esp8266_enter_unvarnished(void)
{
uint8_t ret;
ret = esp8266_send_command("AT+CIPMODE=1\r\n", "OK");
ret += esp8266_send_command("AT+CIPSEND\r\n", ">");
if (ret == ESP8266_EOK)
return ESP8266_EOK;
else
return ESP8266_ERROR;
}
void esp8266_init(uint32_t baudrate)
{
printf("esp8266初始化开始...\r\n");
esp8266_uart_init(baudrate);
//esp8266的其它初始化
printf("1. 测试esp8266是否存在...\r\n");
while(esp8266_at_test())
delay_ms(500);
printf("2. 设置工作模式为STA...\r\n");
while(esp8266_set_mode(ESP8266_STA_MODE))
delay_ms(500);
printf("3. 设置单路链接模式...\r\n");
while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION))
delay_ms(500);
printf("4. 连接wifi,SSID: %s, PWD: %s\r\n", WIFI_SSID, WIFI_PWD);
while(esp8266_join_ap(WIFI_SSID, WIFI_PWD))
delay_ms(1500);
printf("5. 连接TCP服务器,server_ip:%s, server_port:%s\r\n", TCP_SERVER_IP, TCP_SERVER_PORT);
while(esp8266_connect_tcp_server(TCP_SERVER_IP, TCP_SERVER_PORT))
delay_ms(500);
printf("6. 进入到透传模式...\r\n");
while(esp8266_enter_unvarnished())
delay_ms(500);
printf("ESP8266已连接上TCP服务器并进入透传模式\r\n");
printf("ESP8266初始化完成!\r\n");
}
//void esp8266_test(void)
//{
// esp8266_send_data("this is from esp8266\r\n");
// esp8266_receive_data();
//}
3. MQTT协议封装
mqtt.c
c
#include "onenet.h"
#include "esp8266.h"
char MQTT_ClientID[100]; //MQTT_客户端ID
char MQTT_UserName[100]; //MQTT_用户名
char MQTT_PassWord[200]; //MQTT_密码
uint8_t *mqtt_rxbuf;
uint8_t *mqtt_txbuf;
uint16_t mqtt_rxlen;
uint16_t mqtt_txlen;
uint8_t _mqtt_txbuf[512];//发送数据缓存区
uint8_t _mqtt_rxbuf[512];//接收数据缓存区
typedef enum
{
//名字 值 报文流动方向 描述
M_RESERVED1 =0 , // 禁止 保留
M_CONNECT , // 客户端到服务端 客户端请求连接服务端
M_CONNACK , // 服务端到客户端 连接报文确认
M_PUBLISH , // 两个方向都允许 发布消息
M_PUBACK , // 两个方向都允许 QoS 1消息发布收到确认
M_PUBREC , // 两个方向都允许 发布收到(保证交付第一步)
M_PUBREL , // 两个方向都允许 发布释放(保证交付第二步)
M_PUBCOMP , // 两个方向都允许 QoS 2消息发布完成(保证交互第三步)
M_SUBSCRIBE , // 客户端到服务端 客户端订阅请求
M_SUBACK , // 服务端到客户端 订阅请求报文确认
M_UNSUBSCRIBE , // 客户端到服务端 客户端取消订阅请求
M_UNSUBACK , // 服务端到客户端 取消订阅报文确认
M_PINGREQ , // 客户端到服务端 心跳请求
M_PINGRESP , // 服务端到客户端 心跳响应
M_DISCONNECT , // 客户端到服务端 客户端断开连接
M_RESERVED2 , // 禁止 保留
}_typdef_mqtt_message;
//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 e0 00
const uint8_t parket_connetAck[] = {0x20,0x02,0x00,0x00};
const uint8_t parket_disconnet[] = {0xe0,0x00};
const uint8_t parket_heart[] = {0xc0,0x00};
const uint8_t parket_heart_reply[] = {0xc0,0x00};
const uint8_t parket_subAck[] = {0x90,0x03};
/*
函数功能: 初始化阿里云物联网服务器的登录参数
*/
//密码
//加密之前的数据格式: clientId*deviceName*productKey#
// *替换为DeviceName #替换为ProductKey 加密密钥是DeviceSecret 加密方式是HmacSHA1
//PassWord明文= clientIdiot_devicedeviceNameiot_deviceproductKeya1VMIfYeEEE
//hmacsha1加密网站:http://encode.chahuo.com/
//加密的密钥:DeviceSecret
void mqtt_login_init(char *ProductKey,char *DeviceName,char *DeviceSecret)
{
// sprintf(MQTT_ClientID,"%s.%s|securemode=2,signmethod=hmacsha256,timestamp=1695871022945|",ProductKey,DeviceName);
// sprintf(MQTT_UserName,"%s&%s",DeviceName,ProductKey);
// sprintf(MQTT_PassWord,"%s","a8921500839307ec3fedbbcd8c0cbc19f133f68c831dcad41fe13d92dc90b89d");
sprintf(MQTT_ClientID,"%s", DeviceName);
sprintf(MQTT_UserName,"%s", ProductKey);
sprintf(MQTT_PassWord,"version=2018-10-31&res=products%%2F%s%%2Fdevices%%2F%s&et=2017881776&method=sha1&sign=%s",ProductKey,DeviceName,DEVICE_SECRET);
}
void mqtt_init(void)
{
mqtt_login_init(PRODUCT_KEY,DEVICE_NAME,DEVICE_SECRET);
//缓冲区赋值
mqtt_rxbuf = _mqtt_rxbuf;
mqtt_rxlen = sizeof(_mqtt_rxbuf);
mqtt_txbuf = _mqtt_txbuf;
mqtt_txlen = sizeof(_mqtt_txbuf);
memset(mqtt_rxbuf,0,mqtt_rxlen);
memset(mqtt_txbuf,0,mqtt_txlen);
//无条件先主动断开
mqtt_disconnect();
delay_ms(100);
mqtt_disconnect();
delay_ms(100);
}
/*
函数功能: 登录服务器
函数返回值: 0表示成功 1表示失败
*/
uint8_t mqtt_connect(char *ClientID,char *Username,char *Password)
{
// uint8_t i;
uint8_t j;
int ClientIDLen = strlen(ClientID);
int UsernameLen = strlen(Username);
int PasswordLen = strlen(Password);
int DataLen;
mqtt_txlen=0;
//可变报头+Payload 每个字段包含两个字节的长度标识
DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
//固定报头
//控制报文类型
mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT
//剩余长度(不包括固定头部)
do
{
uint8_t encodedByte = DataLen % 128;
DataLen = DataLen / 128;
// if there are more data to encode, set the top bit of this byte
if ( DataLen > 0 )
encodedByte = encodedByte | 128;
mqtt_txbuf[mqtt_txlen++] = encodedByte;
}while ( DataLen > 0 );
//可变报头
//协议名
mqtt_txbuf[mqtt_txlen++] = 0; // Protocol Name Length MSB
mqtt_txbuf[mqtt_txlen++] = 4; // Protocol Name Length LSB
mqtt_txbuf[mqtt_txlen++] = 'M'; // ASCII Code for M
mqtt_txbuf[mqtt_txlen++] = 'Q'; // ASCII Code for Q
mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T
mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T
//协议级别
mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4
//连接标志
mqtt_txbuf[mqtt_txlen++] = 0xc2; // conn flags
mqtt_txbuf[mqtt_txlen++] = 0; // Keep-alive Time Length MSB
mqtt_txbuf[mqtt_txlen++] = 100; // Keep-alive Time Length LSB 100S心跳包
mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB
memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen);
mqtt_txlen += ClientIDLen;
if(UsernameLen > 0)
{
mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen); //username length MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen); //username length LSB
memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen);
mqtt_txlen += UsernameLen;
}
if(PasswordLen > 0)
{
mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen); //password length MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen); //password length LSB
memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen);
mqtt_txlen += PasswordLen;
}
// for(i=0;i<10;i++)
// {
memset(mqtt_rxbuf,0,mqtt_rxlen);
mqtt_send_data(mqtt_txbuf,mqtt_txlen);
// for(j=0;j<10;j++)
// printf("%c",mqtt_txbuf[j]);
for(j=0;j<10;j++)
{
delay_ms(50);
if (esp8266_wait_receive() == ESP8266_EOK)
esp8266_copy_rxdata((char *)mqtt_rxbuf);
//CONNECT
if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1] && mqtt_rxbuf[2]==parket_connetAck[2]) //连接成功
{
return 0;//连接成功
}
}
// }
return 1;
}
/*
函数功能: MQTT订阅/取消订阅数据打包函数
函数参数:
topic 主题
qos 消息等级 0:最多分发一次 1: 至少分发一次 2: 仅分发一次
whether 订阅/取消订阅请求包 (1表示订阅,0表示取消订阅)
返回值: 0表示成功 1表示失败
*/
uint8_t mqtt_subscribe_topic(char *topic,uint8_t qos,uint8_t whether)
{
// uint8_t i;
uint8_t j;
mqtt_txlen=0;
int topiclen = strlen(topic);
int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
//固定报头
//控制报文类型
if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息类型和标志订阅
else mqtt_txbuf[mqtt_txlen++] = 0xA2; //取消订阅
//剩余长度
do
{
uint8_t encodedByte = DataLen % 128;
DataLen = DataLen / 128;
// if there are more data to encode, set the top bit of this byte
if ( DataLen > 0 )
encodedByte = encodedByte | 128;
mqtt_txbuf[mqtt_txlen++] = encodedByte;
}while ( DataLen > 0 );
//可变报头
mqtt_txbuf[mqtt_txlen++] = 0; //消息标识符 MSB
mqtt_txbuf[mqtt_txlen++] = 0x01; //消息标识符 LSB
//有效载荷
mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主题长度 MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主题长度 LSB
memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
mqtt_txlen += topiclen;
if(whether)
{
mqtt_txbuf[mqtt_txlen++] = qos;//QoS级别
}
// for(i=0;i<10;i++)
// {
memset(mqtt_rxbuf,0,mqtt_rxlen);
mqtt_send_data(mqtt_txbuf,mqtt_txlen);
for(j=0;j<10;j++)
{
delay_ms(50);
if (esp8266_wait_receive() == ESP8266_EOK)
esp8266_copy_rxdata((char *)mqtt_rxbuf);
if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //订阅成功
{
return 0;//订阅成功
}
}
// }
return 1; //失败
}
//MQTT发布数据打包函数
//topic 主题
//message 消息
//qos 消息等级
uint8_t mqtt_publish_data(char *topic, char *message, uint8_t qos)
{
int topicLength = strlen(topic);
int messageLength = strlen(message);
static uint16_t id=0;
int DataLen;
mqtt_txlen=0;
//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
//QOS为0时没有标识符
//数据长度 主题名 报文标识符 有效载荷
if(qos) DataLen = (2+topicLength) + 2 + messageLength;
else DataLen = (2+topicLength) + messageLength;
//固定报头
//控制报文类型
mqtt_txbuf[mqtt_txlen++] = 0x30; // MQTT Message Type PUBLISH
//剩余长度
do
{
uint8_t encodedByte = DataLen % 128;
DataLen = DataLen / 128;
// if there are more data to encode, set the top bit of this byte
if ( DataLen > 0 )
encodedByte = encodedByte | 128;
mqtt_txbuf[mqtt_txlen++] = encodedByte;
}while ( DataLen > 0 );
mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主题长度MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主题长度LSB
memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷贝主题
mqtt_txlen += topicLength;
//报文标识符
if(qos)
{
mqtt_txbuf[mqtt_txlen++] = BYTE1(id);
mqtt_txbuf[mqtt_txlen++] = BYTE0(id);
id++;
}
memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength);
mqtt_txlen += messageLength;
// int i = 0;
// for(i=0;i<mqtt_txlen;i++)
// printf("%02X ", mqtt_txbuf[i]);
// printf("\r\n");
mqtt_send_data(mqtt_txbuf,mqtt_txlen);
return mqtt_txlen;
}
uint8_t mqtt_receive_handle(uint8_t *data_received, Mqtt_RxData_Type *rx_data)
{
uint8_t *p;
uint8_t encodeByte = 0;
uint32_t multiplier = 1, Remaining_len = 0;
uint8_t QS_level = 0;
p = data_received;
memset(rx_data, 0, sizeof(Mqtt_RxData_Type));
//解析接收数据
if((*p != 0x30)&&(*p != 0x32)&&(*p != 0x34)) //不是发布报文头
return 1;
if(*p != 0x30) QS_level = 1; //标记qs等级不为0
p++;
//提取剩余数据长度
do{
encodeByte = *p++;
Remaining_len += (encodeByte & 0x7F) * multiplier;
multiplier *= 128;
if(multiplier > 128*128*128) //超出剩余长度最大4个字节的要求,错误
return 2;
}while((encodeByte & 0x80) != 0);
//提取主题数据长度
rx_data->topic_len = *p++;
rx_data->topic_len = rx_data->topic_len * 256 + *p++;
//提取主题
memcpy(rx_data->topic,p,rx_data->topic_len);
p += rx_data->topic_len;
if(QS_level != 0) //跳过报文标识符
p += 2;
//提取payload
rx_data->payload_len = Remaining_len - rx_data->topic_len - 2;
memcpy(rx_data->payload, p, rx_data->payload_len);
// printf("topic: %s\r\n", rx_data->topic);
// printf("topic_len: %d\r\n", rx_data->topic_len);
// printf("payload: %s\r\n", rx_data->payload);
// printf("payload_len: %d\r\n", rx_data->payload_len);
return 0;
}
void mqtt_send_response(uint8_t *id)
{
char buf[128] = {0};
sprintf(buf,"{\"id\":\"%s\",\"code\":200,\"msg\":\"success\"}",id);
mqtt_publish_data(RELY_PUBLISH_TOPIC,(char *)buf,0);
printf("\r\n发布数据:\r\n");
printf((const char *)buf); //发布的数据打印出来
printf("\r\n");
}
void mqtt_send_heart(void)
{
mqtt_send_data((uint8_t *)parket_heart,sizeof(parket_heart));
}
void mqtt_disconnect(void)
{
mqtt_send_data((uint8_t *)parket_disconnet,sizeof(parket_disconnet));
}
void mqtt_send_data(uint8_t *buf,uint16_t len)
{
esp8266_send_data((char *)buf, len);
}
mqtt.h
c
#ifndef _ONENET_H_
#define _ONENET_H_
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "delay.h"
#define BYTE0(dwTemp) (*( char *)(&dwTemp))
#define BYTE1(dwTemp) (*((char *)(&dwTemp) + 1))
#define BYTE2(dwTemp) (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp) (*((char *)(&dwTemp) + 3))
extern char MQTT_ClientID[100]; //MQTT_客户端ID
extern char MQTT_UserName[100]; //MQTT_用户名
extern char MQTT_PassWord[200]; //MQTT_密码
typedef struct
{
uint8_t topic[512];
uint16_t topic_len;
uint8_t payload[512];
uint16_t payload_len;
} Mqtt_RxData_Type;
//云服务器的设备证书
#define PRODUCT_KEY "pC0uTV161W"
#define DEVICE_NAME "dht11_01"
#define DEVICE_SECRET "75AKO7FD5KBEuSJ6BTDLPFC227w%3D"
//订阅与发布的主题
#define RELY_PUBLISH_TOPIC "$sys/pC0uTV161W/dht11_01/thing/property/set_reply" //属性设置应答订阅主题,onenet studio定义好的
#define SET_TOPIC "$sys/pC0uTV161W/dht11_01/thing/property/set"
#define POST_TOPIC "$sys/pC0uTV161W/dht11_01/thing/property/post"
//事件上报主题
#define EVENT_PUBLISH_TOPIC "$sys/pC0uTV161W/dht11_01/thing/event/post" //发布主题,onenet studio定义好的
//阿里云用户名初始化
void mqtt_login_init(char *ProductKey,char *DeviceName,char *DeviceSecret);
//MQTT协议相关函数声明
uint8_t mqtt_publish_data(char *topic, char *message, uint8_t qos);
uint8_t mqtt_subscribe_topic(char *topic,uint8_t qos,uint8_t whether);
void mqtt_init(void);
uint8_t mqtt_connect(char *ClientID,char *Username,char *Password);
void mqtt_send_heart(void);
void mqtt_disconnect(void);
void mqtt_send_data(uint8_t *buf,uint16_t len);
void mqtt_send_response(uint8_t *id);
uint8_t mqtt_receive_handle(uint8_t *data_received, Mqtt_RxData_Type *rx_data);
#endif
五、系统主程序
c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dht11.h"
#include "esp8266.h"
#include "onenet.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* 初始化LED灯 */
uart1_init(115200);
esp8266_init(115200);
printf("hello world!\r\n");
printf("MQTT初始化...\r\n");
mqtt_init();
printf("MQTT连接...\r\n");
mqtt_connect(MQTT_ClientID, MQTT_UserName, MQTT_PassWord);
uint8_t data_send[512] = {0};
uint8_t dht11_data[4] = {0};
while(1)
{
memset(dht11_data, 0, 4);
dht11_read(dht11_data);
sprintf((char *)data_send, "{\"id\":\"1386772172\",\"version\":\"1.0\",\"params\":{\"CurrentTemperature\":{\"value\":%d.%d},\"CurrentHumidity\":{\"value\":%d.%d}}}",
dht11_data[2], dht11_data[3], dht11_data[0], dht11_data[1]);
mqtt_publish_data(POST_TOPIC, (char *)data_send, 0);
delay_ms(3000);
printf("\r\n~~~~~~~~~~~~~~~~~发送心跳包~~~~~~~~~~~~~~~~~\r\n");
mqtt_send_heart();
printf("\r\n~~~~~~~~~~~~~~~~~发送心跳包结束~~~~~~~~~~~~~~~~~\r\n");
}
}
六、OneNet平台配置
- 登录OneNet控制台
- 创建新产品 → 选择MQTT协议
- 添加设备并记录以下信息:
- Product ID
- Device ID
- API Key
- 创建数据流模板:
- temperature(单位:℃)
- humidity(单位:%RH)
七、常见问题排查
1. ESP8266无法连接WiFi
- 检查供电是否稳定(建议单独3.3V供电)
- 确认AT指令响应格式
- 使用AT+CWLAP扫描可用网络
2. 数据上传失败
- 检查MQTT连接参数是否正确
- 验证JSON格式有效性
- 查看OneNet设备日志
3.DHT11读取超时
- 检查上拉电阻(4.7KΩ)
- 调整时序延时精度
- 更换传感器测试
八、项目优化方向
- 增加 断线重连机制
- 实现 本地数据缓存
- 添加 低功耗模式
- 支持 OTA固件升级
完整源码获取:[https://gitee.com/bad-lemon/mcu-development-record.git\]
文章包含详细的代码实现和平台对接说明,实际开发时需根据硬件连接情况调整引脚定义。建议配合示波器调试时序问题,使用串口调试助手验证AT指令交互流程。