杀鸡焉用牛刀 发表于 2024-7-24 07:49:43

小项目:使用MQTT上传温湿度到Onenet服务器

前言

我们之前分别编写了 DHT11、ESP8266 和 MQTT 的代码,如今我们将它们仨整合在一起,来做一个温湿度检测小项目。这个项目可以及时地将 DHT11 传感器获取到的温湿度数据上传到 OneNET 平台。通过登录 OneNET,我们随时随地可以查看温湿度数据。
这种环境监测项目的应用场景有很多,其中特殊实用于温室环境监测,园丁可以随时随地了解温室中空气情况,以确保温室环境得当娇贵的花草树木生长。
1. 源码下载及前置阅读

本文首发 良许嵌入式网 :https://www.lxlinux.net/e/ ,接待关注!
本文所涉及的源码及安装包如下(由于平台限定,请点击以下链接阅读原文下载):
https://www.lxlinux.net/e/stm32/little-project-dht11-esp8266-onenet.html
如果你是嵌入式开发小白,那么建议你先读读下面几篇文章。


[*]STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html
[*]一文教你使用MDK开发工具:https://www.lxlinux.net/e/stm32/mdk-development-tool-tutorial.html
[*]零基础快速上手STM32开发(手把手保姆级教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html
前期教程,没看过的小搭档可以先看下。


[*]手把手教你玩转ESP8266(原理+驱动):https://www.lxlinux.net/e/stm32/esp8266-tutorial.html
[*]手把手教你玩转DHT11(原理+驱动):https://www.lxlinux.net/e/stm32/dht11-tutorial.html
[*]万字猛文:MQTT原理及案例:https://www.lxlinux.net/e/stm32/mqtt-turorial.html
2. 整体系统设计

使用 STM32F103C8T6 作主控芯片,共同 DHT11 温湿度传感器,及时监测周围环境的温湿度变化。通过 ESP8266 模块以 MQTT 协议将获取到的温湿度数据通过无线网络毗连上传至 OneNET 平台,以便用户可以随时随地通过手机或电脑查看数据。
https://img-blog.csdnimg.cn/5e9e32f44f8a4380891f216a9b48e4ed.png
3. 硬件选型

本教程使用的硬件如下:


[*]单片机:STM32F103C8T6
这款单片机具有 64K flash,20K RAM,4 个定时器,3 个串口,网络上资料好几吨,非常得当初学者入门,强烈推荐。
https://img-blog.csdnimg.cn/9e58110560ed402cac00b28256c9518e.png


[*]WiFi模块:ESP-01S(ESP8266)
ESP8266 可以利用串口与单片机进行通讯,从而编程实现控制。
https://img-blog.csdnimg.cn/e11be815d2f44b77a1ea79e4385ea1d6.png


[*]温湿度传感器:DHT11
DHT11 有 3 脚和 4 脚两款,在使用上没有差别,接线都一样,重要接三根,四脚的款式有一脚悬空。四脚款接杜邦线会有点不稳,得当插面包板或开发板上。
DHT11 工作参数:

[*]湿度丈量范围:20~90%RH
[*]湿度丈量精度:±5%RH
[*]温度丈量范围:0~50℃
[*]温度丈量精度:±2℃
[*]工作电压:DC 3.3V/5V
https://img-home.csdnimg.cn/images/20230724024159.png?be=1&origin_url=https://lxlinux.superbed.verylink.top/item/655b12b2c458853aef4b8a5b.jpg

[*]串口:USB 转 TTL
这种设备重要作用是用来调试或下载程序,本文用于串口输出作调试。代价也很便宜,普遍 5~8 元。
https://img-blog.csdnimg.cn/78eab6d9a901433c80805ffdfbae46ec.png


[*]烧录器:ST-LINK V2
ST-Link 是一种用于 STM32 微控制器的调试和编程工具,它可以通过 SWD 或 JTAG 接口与开发板进行通讯。本文用做烧录。一般也很便宜,七八元左右。
https://img-home.csdnimg.cn/images/20230724024159.png?be=1&origin_url=https://pic.imgdb.cn/item/65222a41c458853aefb2ce8a.jpg 4. OneNET 物联网平台

MQTT 服务端可以是云平台,OneNET、阿里云、华为云、腾讯云等;也可以本身搭建服务端,用 EMQ 或 Mosquitto 。
本次我们使用 OneNET,接下来我们来配置一下 OneNET。
点开 OneNET 官方网址:中移坤灵 - 中国移动物联网开放平台 (10086.cn)
有账号的登录,没账号的注册一下。
https://img-blog.csdnimg.cn/21cd58a1b8fe4c79b19853a5ffbd3eaf.png
登录好后点击「开发者中心」。
https://img-blog.csdnimg.cn/63a7516f86864e3ca51fc5a1241815f8.png
4.1 创建产品

接下来我们先创建一个产品,之后再创建详细的设备。
https://img-blog.csdnimg.cn/e55f9e3c365640ce85bdd20d52675dc1.png
可按照下图参数创建产品。
https://img-blog.csdnimg.cn/a67827b8d9c14c20a02099feccd7948c.png
4.2 创建物模型

通过物模型我们可以界说设备的属性、服务和变乱功能。我们必要创建几个物模型,用于上传数据和变乱告警。
https://img-blog.csdnimg.cn/8fe70b5b44624edf88651650ae7264fc.png
创建两个物模型:


[*]当前湿度,用于存储及时湿度数据。
[*]当前温度,用于存储及时温度数据。
本教程只用到「当前湿度」和「当前温度」,剩下的物模型是下篇教程使用的。
https://img-blog.csdnimg.cn/5ca9cf4311d2475f8bdb59670d06bb8f.png
4.3 创建设备

接下来就开始创建产品下的详细设备。
https://img-blog.csdnimg.cn/3d18417c6b534cedac1c82ca5ccdad78.png
4.4 天生MQTT三元组

「MQTT 三元组」是 MQTT 协议中至关重要的,就像去考试的时候,肯定要带上准考据、身份证才气进考场,要有「MQTT 三元组」才气毗连 MQTT 服务端。
https://img-blog.csdnimg.cn/ed7aad1c275a4e95b9073ed91e287349.png
https://img-blog.csdnimg.cn/ac1d778cc08d41bf862020e322464e20.png
得到初步「MQTT 三元组」:


[*]设备 ID:temp01
[*]产品 ID:P2k4KV0low
[*]设备密钥:REhWUEhWbDlIOTdRUFEzU1dGQXk4TlZKZ25oQ0N4S3M=
设备密钥必要经过加密,加密必要用 OneNET 官方的 token 天生工具。
官网下载地址:OneNET - 中国移动物联网开放平台 (10086.cn)
也可以拿文章开头提供的,也是官网下载的。
https://img-blog.csdnimg.cn/e071c6e12dde429fb939777d31d881dd.png
下载好 token 天生工具,打开界面如下,我来告诉大家每个空填啥。
https://img-blog.csdnimg.cn/6efd20ec50e14027874a3f4b72af64dd.png
各个参数先容如下表:
名称范例参数阐明参数示例resstring访问资源 resource 格式为:products/{产品id}/devices/{设备名字}products/P2k4KV0low/devices/temp01etint访问逾期时间,单位秒,unix 时间。当一次访问参数中的 et 时间小于当前时间时,平台会认为访问参数逾期从而拒绝该访问2017881776 表示:北京时间 2033-12-11 10:42:56keystringMQTT 三元组的设备密钥REhWUEhWbDlIOTdRUFEzU1dGQXk4TlZKZ25oQ0N4S3M=methodstring加密方式,支持 hmacmd5、hmacsha1、hmacsha256md5(代表使用hmacmd5算法)
sha1(代表使用hmacsha1算法)
sha256(代表使用hmacsha256 算法)versionstring参数组版本号,日期格式,目前仅支持"2018-10-31"2018-10-31 et 的时间戳可以用这个在线工具转换,网页地址:时间戳(Unix timestamp)转换工具 - 在线工具 (tool.lu)
https://img-blog.csdnimg.cn/013f4e69e6fd4b8eaaaa4cdaccc7a004.png
根据先容,填好各个参数的空,我们选择 sha1 的加密方式,大家可以选择本身喜欢的。填好如下操作:
https://img-blog.csdnimg.cn/d694c459385c4eb1b289197e2fb944a9.png
得到终极「MQTT 三元组」:


[*]设备 ID:temp01
[*]产品 ID:P2k4KV0low
[*]token:version=2018-10-31&res=products%2FP2k4KV0low%2Fdevices%2Ftemp01&et=2017881776&method=sha1&sign=M3jVJvfeFLnggMrUPhYm5uRirXs%3D
4.5 主题订阅格式

4.5.1 OneNET地址

OneNET 服务器地址是 mqtts.heclouds.com : 1883,地址是从 OneNET 文档中心得到的。
https://img-blog.csdnimg.cn/ceef2c14418a4e9c8711e0fd65d6306a.png
4.5.2 订阅主题

选择设置直连设备属性:$sys/P2k4KV0low/{device-name}/thing/property/set
{device-name} 是设备ID,比如我们的就是 temp01。
https://img-blog.csdnimg.cn/135a4b5ec2d446059e3ec26a80418453.png
4.5.3 上报主题

选择直连设备上报属性:$sys/P2k4KV0low/{device-name}/thing/property/post
{device-name} 是设备ID,比如我们的就是 temp01。
https://img-blog.csdnimg.cn/1d30ce6d977b4841898c084eb262c38f.png
5. STM32 设备端开发

5.1 硬件接线

接线可参照下表:
ESP8266DHT11STM32USB 转 TTL3V33.3TXA3RXA2GNDGVCC3.3DATAA5GNDGA10TXA9RXGGND 烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章 STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html。
ST-Link V2STM32SWCLKSWCLKSWDIOSWDIOGNDGND3.3V3V3 5.2 设备实物图

接好如下图。
https://img-blog.csdnimg.cn/d8b5988313234f6fb95039572dde4175.png
5.3 DHT11温湿度传感器代码

详细代码解析可以看手把手教你玩转DHT11(原理+驱动)。
5.3.1 读取1字节数据

将 DHT11 发来的二进制数据存储到 ReadData 变量中,读取一位后,左移一位,循环8次,终极得到 1 byte 数据。
那么如何判断我们读到的数据是 0 照旧 1 呢?
通过 3.2.3 的分析可以知道,0 和 1 的时序只是高电平持续时间差别,以是我们只必要在 DHT11 拉低电平之后延时 40~60 微秒(代码中使用 50 微秒),再读取电平状态就可以了,如果是高电平则为 1,低电平则为 0 。
uint8_t DHT_Read_Byte(void)//从DHT11读取一位(8字节)信号
{
    uint8_t i;
    uint8_t ReadData = 0;    //ReadData用于存放8bit数据,即8个单次读取的1bit数据的组合
    uint8_t temp;            //临时存放信号电平(0或1)

    for(i=0;i<8;i++){
      while(!HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));
      Delay_us(50);
      if(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN) == 1){
            temp = 1;
            while(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));
      }else{
            temp = 0;
      }
      ReadData = ReadData << 1;
      ReadData |= temp;
    }
    return ReadData;
} 5.3.2 一次数据读取及显示

根据 DHT11 的时序,我们就可以使用代码实现 DHT11 一次读取数据过程。
注意:DHT11 读取数据间隔至少为 2 秒,否则读取到的数据大概不稳定,以是在末了可以延时 2 秒。
void DHT_Read()
{
    uint8_t i;

    DHT11_Start();
    DHT_GPIO_INPUT();

    for(i= 0;i < 5;i++){
      Data = DHT_Read_Byte();
    }
    if((Data+Data+Data+Data)==Data)
    {
      printf("湿度: %d.%dRH ,", Data, Data);
      printf("温度: %d.%d℃\r\n", Data, Data);
    }else{
      printf("ERROR DATA\r\n");
    }
    HAL_Delay(2000);
} 5.4 ESP8266模块代码

详细代码解析可以看手把手教你玩转ESP8266(原理+驱动)。
5.4.1 ESP8266 初始化

按照项目需求编写 ESP8266 初始化代码,我们必要将 ESP8266 设置工作模式为STA、单路毗连模式、毗连 WIFI 等操作。
uint8_t esp8266_init(uint32_t baudrate)
{
    char ip_buf;

    esp8266_uart_init(baudrate);                /* ESP8266 UART初始化 */

    /* 让WIFI退出透传模式 */
    printf("\r\n    退出透传模式\r\n");
    esp8266_exit_unvarnished();

    printf("    1.测试ESP8266是否存在(AT)\r\n");
    while(esp8266_at_test())
      delay_ms(500);

    printf("    2.重启ESP8266(AT+RST)\r\n");
    while(esp8266_sw_reset())
      delay_ms(500);
    while(esp8266_disconnect_tcp_server())
      delay_ms(500);

    printf("    3.设置工作模式为STA(AT+CWMODE=1)\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE))
      delay_ms(500);

    printf("    4.设置单路连接模式(AT+CIPMUX)\r\n");//设置单路连接模式,透传只能使用此模式
    while(esp8266_single_connection())
      delay_ms(500);

    printf("    5.连接WiFi,SSID:%s,PWD:%s\r\n", WIFI_SSID, WIFI_PWD);      //连接WIFI
    while(esp8266_join_ap(WIFI_SSID, WIFI_PWD))
      delay_ms(1000);

    printf("    6.获取IP地址(AT+CIFSR):");
    while(esp8266_get_ip(ip_buf))
      delay_ms(500);

    printf("%s\r\n\r\n", ip_buf);

    printf("ESP8266初始化完成\r\n");
    return ESP8266_EOK;
} 初始化结束后毗连 OneNET 服务器并进入透传模式。
void esp8266_connect_server(char *server_ip, char *server_port)
{
    esp8266_disconnect_tcp_server();
    printf("    7.连接云服务器(AT+CIPSTART),server_ip:%s,server_port:%s\r\n", server_ip, server_port);
    while(esp8266_connect_tcp_server(server_ip, server_port))
      delay_ms(500);

    printf("    8.进入透传模式(AT+CIPMODE)\r\n");
    while(esp8266_enter_unvarnished())
      delay_ms(500);

    printf("已连接上云服务器并进入透传模式。\r\n");
} 5.5 MQTT代码

5.5.1 CONNECT 报文

CONNECT 报文的编写及发送代码,报文编写就按照我们7.2节的理论编写即可,报文内容:10+剩余长度+00 04 4D 51 54 54 04 C2+保持毗连时间+L+设备 ID+L+产品 ID+L+token。
uint8_t mqtt_connect(char *ClientID,char *Username,char *Password)
{
    uint8_t i,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 = 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 = encodedByte;
    }while ( DataLen > 0 );

    //可变报头
    //协议名
    mqtt_txbuf = 0;            // Protocol Name Length MSB   
    mqtt_txbuf = 4;         // Protocol Name Length LSB   
    mqtt_txbuf = 'M';            // ASCII Code for M   
    mqtt_txbuf = 'Q';            // ASCII Code for Q   
    mqtt_txbuf = 'T';            // ASCII Code for T   
    mqtt_txbuf = 'T';            // ASCII Code for T   
    //协议级别
    mqtt_txbuf = 4;                // MQTT Protocol version = 4   
    //连接标志
    mqtt_txbuf = 0xc2;            // conn flags
    mqtt_txbuf = 0;                // Keep-alive Time Length MSB   
    mqtt_txbuf = 100;            // Keep-alive Time Length LSB100S心跳包

    mqtt_txbuf = BYTE1(ClientIDLen);// Client ID length MSB   
    mqtt_txbuf = BYTE0(ClientIDLen);// Client ID length LSB      
    memcpy(&mqtt_txbuf,ClientID,ClientIDLen);
    mqtt_txlen += ClientIDLen;

    if(UsernameLen > 0)
    {   
      mqtt_txbuf = BYTE1(UsernameLen);      //username length MSB   
      mqtt_txbuf = BYTE0(UsernameLen);      //username length LSB   
      memcpy(&mqtt_txbuf,Username,UsernameLen);
      mqtt_txlen += UsernameLen;
    }

    if(PasswordLen > 0)
    {   
      mqtt_txbuf = BYTE1(PasswordLen);      //password length MSB   
      mqtt_txbuf = BYTE0(PasswordLen);      //password length LSB
      memcpy(&mqtt_txbuf,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++)
      {
            delay_ms(50);
            if (esp8266_wait_receive() == ESP8266_EOK)
                esp8266_copy_rxdata((char *)mqtt_rxbuf);

            //CONNECT
            if(mqtt_rxbuf==parket_connetAck && mqtt_rxbuf==parket_connetAck && mqtt_rxbuf==parket_connetAck) //连接成功               
            {
                return 0;//连接成功
            }
      }
    }
    return 1;
}
5.5.2 PUBLISH 报文

PUBLISH 报文的编写及发送代码,报文编写就按照我们7.2节的理论编写即可,报文内容:30 +剩余长度+L+主题+数据(JSON)。
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 = 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 = encodedByte;
    }while ( DataLen > 0 );   

    mqtt_txbuf = BYTE1(topicLength);//主题长度MSB
    mqtt_txbuf = BYTE0(topicLength);//主题长度LSB
    memcpy(&mqtt_txbuf,topic,topicLength);//拷贝主题
    mqtt_txlen += topicLength;

    //报文标识符
    if(qos)
    {
      mqtt_txbuf = BYTE1(id);
      mqtt_txbuf = BYTE0(id);
      id++;
    }
    memcpy(&mqtt_txbuf,message,messageLength);
    mqtt_txlen += messageLength;

//    int i = 0;
//    for(i=0;i<mqtt_txlen;i++)
//      printf("%02X ", mqtt_txbuf);
//    printf("\r\n");
    mqtt_send_data(mqtt_txbuf,mqtt_txlen);
    return mqtt_txlen;
} 5.6 主函数逻辑代码

主函数代码如下:
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "esp8266.h"
#include "onenet.h"
#include "dht11.h"


extern char dht11_data;

int main(void)
{
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟,72M */
    delay_init(72);                           /* 初始化延时函数 */
    usart_init(115200);                         /* 波特率设为115200 */

    printf("初始化ESP8266...\r\n");
    esp8266_init(115200);

    printf("初始化MQTT...\r\n");
    mqtt_init();

    printf("MQTT连接...\r\n");
    mqtt_connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord);

    while(1)
    {
      uint8_t data_send_buff;
      memset(data_send_buff, 0, sizeof(data_send_buff));
      dht11_read();
      sprintf((char *)data_send_buff,"{\"id\":\"1386772172\",\"version\":\"1.0\",\"params\":{\"temperature\":{\"value\":%d.%d},\"humidity\":{\"value\":%d.%d}}}"
            ,dht11_data, dht11_data, dht11_data, dht11_data);
      mqtt_publish_data(POST_TOPIC, (char *)data_send_buff, 0);
      HAL_Delay(3000);      //3s发送一次

      printf("\r\n~~~~~~~~发送心跳包~~~~~~~~\r\n");
      mqtt_send_heart();
      printf("~~~~~~~~心跳包发送结束~~~~~~~~\r\n");
    }
}
5.7 运行过程

烧录好后,串口效果如下:
https://img-blog.csdnimg.cn/4315c0c131b94bf099d39ea6f5df54a9.png
OneNET 平台效果如下:
https://img-blog.csdnimg.cn/eb48b8f60f514603a8961e5c78f32ac3.png
https://img-blog.csdnimg.cn/87628007516d402bbb88c21347b28582.png
总结

把前面学的知识整合成一个小项目后是不是成绩感爆棚了,接下来会不断继续优化这个小项目,让它更完整,更人性。感谢各位看官,love and peace!
别的,想进大厂的同学,肯定要好好学算法,这是口试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。
https://img-blog.csdnimg.cn/83e330ffe12c4268bb3be140e859a55e.png
刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!
有劳绩?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:


[*]程序员必备编程资料大全
[*]程序员必备软件资源
接待关注我的博客:良许嵌入式教程网,满满都是干货!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 小项目:使用MQTT上传温湿度到Onenet服务器