【STM32】采集温湿度数据上传至OneNET平台项目

目录

一、项目需求

使用 ESP8266 连接 OneNET 云平台,并通过 MQTT 协议上传 DHT11 获取的温湿度值。

二、硬件清单

  • ESP8266
  • DHT11
  • 杜邦线
  • STM32
  • ST-Link
  • USB转TTL

三、硬件接线

四、项目框图

五、完整代码

main.c

c 复制代码
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dht11.h"
#include "string.h"
#include "esp8266.h"
#include "onenet.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //串口1初始化
    uart1_init(115200);
    //ESP8266初始化
    esp8266_init(115200);
    //串口1打印测试
    printf("串口1打印测试:hello world\r\n");
    //MQTT初始化
    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)
    {
        //清空dht11数据值缓存区
        memset(dht11_data, 0, 4);
        //读取dht11数据值
        dht11_read(dht11_data);
        //拼装温湿度JSON字符串
        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");
    }
}

led.c

c 复制代码
#include "led.h"
#include "sys.h"

//初始化GBIO口函数
void led_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;               //LED1,LED2对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    //关闭LED
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
    
}

//点亮LED1的函数
void led1_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);   //拉低LED1引脚,点亮LED1
}


//熄灭LED1的函数

void led1_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);     //拉高LED1引脚,熄灭LED1
}

//翻转LED1的函数
void led1_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);                   //翻转LED1引脚电平
}

//点亮LED2的函数
void led2_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);   //拉低LED2引脚,点亮LED2
}


//熄灭LED2的函数

void led2_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);     //拉高LED2引脚,熄灭LED2
}

//翻转LED2的函数
void led2_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);                   //翻转LED2引脚电平
}

led.h

c 复制代码
#ifndef __LED_H__
#define __LED_H__

//初始化GBIO口函数
void led_init(void);
//点亮LED1的函数
void led1_on(void);
//熄灭LED1的函数
void led1_off(void);
//翻转LED1的函数
void led1_toggle(void);
//点亮LED2的函数
void led2_on(void);
//熄灭LED2的函数
void led2_off(void);
//翻转LED2的函数
void led2_toggle(void);

#endif 

dht11.c

c 复制代码
#include "dht11.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"

//定义温湿度存放变量
char dht11_data[5] = {0};    

//设置DHT11GPIO输入
void dht11_gpio_input(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能DHT11端口时钟
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;                //DHT11对应引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;         //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

//设置DHT11GPIO输出
void dht11_gpio_output(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能DHT11端口时钟
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;                //DHT11对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;         //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

//DHT11启动
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拉低电平
}

//DHT11GPIO读一个字节
uint8_t dht11_read_byte(void)
{
    uint8_t temp = 0;
    uint8_t i = 0;
    uint8_t read_data = 0;
    
    //循环读取8位
    for(i = 0; i < 8; i++){
        //读取位时序
        while(!DHT11_DQ_IN);   //等待DHT11拉高电平
        delay_us(50);
        if(DHT11_DQ_IN == 1){
            temp = 1;
            while(DHT11_DQ_IN);     //等待DHT11拉低电平
        }else{
            temp = 0;
        }
        //循环放入读取缓存
        read_data = read_data << 1;
        read_data |= temp;
    }
    
    return read_data;
}

//DHT11读数据
void dht11_read(uint8_t *result)
{
    uint8_t i = 0;
    
    //启动DHT11
    dht11_start();
    //设置GPIO输入模式
    dht11_gpio_input();
    
    //循环读取5次字节
    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("\r\n");
        printf("湿度:%d.%dRH,",dht11_data[0],dht11_data[1]);
        printf("温度:%d.%d℃",dht11_data[2],dht11_data[3]);
        printf("\r\n");
    }
    
    delay_ms(2000);
}

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)                     

//DHT11读数据
void dht11_read(uint8_t *result);
                              
#endif

esp8266.c

c 复制代码
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

//定义发送缓存区
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE] = {0};
//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    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);
}

//esp8266中断服务函数
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;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

////esp8266发送
//void esp8266_send_data(char *fmt, ...)
//{
//    // 定义可变参数列表变量
//    va_list ap;
//    // 定义长度变量,用于存储字符串长度
//    uint16_t len;
//    // 初始化可变参数列表,ap指向fmt后的第一个参数
//    va_start(ap, fmt);
//    // 使用vsprintf将格式化后的字符串输出到发送缓冲区
//    // vsprintf会自动处理可变参数,根据fmt格式字符串将参数格式化后存入缓冲区
//    vsprintf((char* )esp8266_tx_buf, fmt, ap);
//    // 计算格式化后字符串的长度(不包括字符串结束符'\0')
//    len = strlen((const char *)esp8266_tx_buf);
//    // 通过UART发送数据
//    HAL_UART_Transmit(&esp8266_handle, esp8266_tx_buf, len, 100);
//}

//esp8266发送
void esp8266_send_data(char *data, uint16_t len)
{
    esp8266_rx_clear();
    HAL_UART_Transmit(&esp8266_handle, (unsigned char*)data, len, 100);
}

//拷贝RXDATA内容
uint16_t esp8266_copy_rxdata(char *data)
{
    memcpy(data, esp8266_rx_buf, esp8266_cnt_Pre);
    return esp8266_cnt_Pre;
}

//esp8266接收
void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        esp8266_rx_clear();
    }
}

//esp8266发送函数
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((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_unvarnishied(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;
    }   
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    //串口初始化
    esp8266_uart_init(baudrate);
    
    //配置wifi模块
    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(2000);
    }
    printf("5. 连接云服务器,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_unvarnishied()){
        delay_ms(500);
    }
    printf("ESP8266已经连接服务器并进入透传模式\r\n");
    printf("ESP8266初始化完成\r\n");
}

////测试函数
//void esp8266_test(void)
//{
//    esp8266_send_data("This is from esp8266\r\n",20);
//    esp8266_receive_data();
//}

esp8266.h

c 复制代码
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.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

//定义AT指令填充字段
#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                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

#define TCP_SERVER_IP               "mqtts.heclouds.com"
#define TCP_SERVER_PORT             "1883"

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
void esp8266_receive_data(void);
//测试函数
void esp8266_test(void);
//esp8266发送
void esp8266_send_data(char *data, uint16_t len);
//拷贝RXDATA内容
uint16_t esp8266_copy_rxdata(char *data);
//等待接收函数
uint8_t esp8266_wait_receive(void);

#endif

onenet.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);
    strcpy(MQTT_PassWord,"version=2018-10-31&res=products%2Fi2RQ1t6XsF%2Fdevices%2Fdht11_01&et=2082789772&method=sha1&sign=dkwxvxjiukh6p%2FhQSW0wPfZCNDQ%3D");
}

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);
}

onenet.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 "i2RQ1t6XsF"
#define DEVICE_NAME "dht11_01"
#define DEVICE_SECRET "dkwxvxjiukh6p%2FhQSW0wPfZCNDQ%3D"

//订阅与发布的主题
#define RELY_PUBLISH_TOPIC  "$sys/i2RQ1t6XsF/dht11_01/thing/property/set_reply"  //属性设置应答订阅主题,onenet studio定义好的
#define SET_TOPIC  "$sys/i2RQ1t6XsF/dht11_01/thing/property/set"
#define POST_TOPIC "$sys/i2RQ1t6XsF/dht11_01/thing/property/post"
//事件上报主题
#define EVENT_PUBLISH_TOPIC   "$sys/i2RQ1t6XsF/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
相关推荐
FL16238631292 小时前
flash_attn windows whl下载安装教程
windows·stm32·单片机
d111111111d2 小时前
配置STM32F411CEU6的系统时钟-避免芯片内核锁死
笔记·stm32·单片机·嵌入式硬件·学习
小李做物联网3 小时前
【单片机毕设】77.2基于单片机stm32智能大棚环境监控-语音
stm32·单片机·嵌入式硬件·物联网
易水寒陈3 小时前
使用Event Recoder 调试
单片机·嵌入式硬件
d111111111d3 小时前
STM32内核锁死补救方法-STM32F411CEU6
笔记·stm32·单片机·嵌入式硬件·学习
rosemary5123 小时前
MSPM0G3507 GPIO配置 - TI Drivers
单片机·嵌入式硬件·mspm0g3507
剑之所向3 小时前
c# modbus怎么遍历从站
单片机
wdfk_prog3 小时前
PotPlayer采集卡选麦克风会导致黑屏及音频修复方案
stm32·单片机·音视频
ACP广源盛139246256734 小时前
GSV2016@ACP#2016产品规格参数详解及产品应用场景分享
单片机·嵌入式硬件·计算机外设·音视频