飞道的博客

智慧农业: STM32F103ZE+ESP8266+腾讯云物联网平台+微信小程序设计

330人阅读  评论(0)

一、环境介绍

单片机采用:STM32F103ZET6

上网方式:采用ESP8266,也可以使用其他设备代替,只要支持TCP协议即可。比如:GSM模块、有线网卡等。

与物联网云平台的通信协议:  标准MQTT协议3.1.1(TCP)

开发软件:keil5

物联网平台: 腾讯IOT物联网物联网平台。腾讯的物联网平台比起其他厂家的物联网平台更加有优势,腾讯物联网平台可以将数据推到微信小程序上,用户可以直接使用小程序绑定设备,完成与设备之间交互,现在用户基本都会使用微信,所以使用起来非常方便。

本项目完整代码下载地址: https://download.csdn.net/download/xiaolong1126626497/18973282

 

 

 

二、智慧农业介绍

智慧农业就是将物联网技术运用到传统农业中去,运用传感器和软件通过移动平台或者电脑平台对农业生产进行控制,使传统农业更具有“智慧”。除 了精准感知、控制与决策管理外,从广泛意义上讲,智慧农业还包括农业电子商务、食品溯源防伪、农业休闲旅游、农业信息服务等方面的内容。
所谓“智慧农业”就是充分应用现代信息技术成果,集成应用计算机与网络技术、物联网技术、音视频技术、3S技术、无线通信技术及专家智慧与知识,实现农业可视化远程诊断、远程控制、灾变预警等智能管理。

智慧农业是农业生产的高级阶段,是集新兴的互联网、移动互联网、云计算和物联网技术为一体,依托部署在农业生产现场的各种传感节点(环境温湿度、土壤水分、二氧化碳、图像等)和无线通信网络实现农业生产环境的智能感知、智能预警、智能决策、智能分析、专家在线指导,为农业生产提供精准化种植、可视化管理、智能化决策。
“智慧农业”是云计算、传感网、3S等多种信息技术在农业中综合、全面的应用,实现更完备的信息化基础支撑、更透彻的农业信息感知、更集中的数据资源、更广泛的互联互通、更深入的智能控制、更贴心的公众服务。“智慧农业”与现代生物技术、种植技术等科学技术融合于一体,对建设世界水平农业具有重要意义。

   本项目采用STM32F103ZET6 + ESP8266 设计一个智慧农业管理系统, 能够获取空气中的温湿度数据,光照度数据等,根据种植区的空气温湿度数据,判断是否进行灌溉;可以通过ESP8266 + MQTT 协议将采集的温湿度、光照度上传至腾讯云物联网平台,并推送到微信小程序上实时查看;可以在小程序上直接控制电机抽水灌溉。

硬件详情介绍:

主控MCU:  STM32F103ZET6

环境光传感器: BH1750

温湿度传感器: SHT30

本地OLED显示屏:   中景园电子0.96寸 SPI接口 OLED显示屏

电机:  微型直流电机

三、创建腾讯云物联网平台设备并配置微信小程序

如果之前从来没有使用过腾讯云物联网平台,创建的详细步骤请看这里:  https://blog.csdn.net/xiaolong1126626497/article/details/116902653

下面就是登录腾讯云物联网平台,创建一个智慧农业的设备的关键步骤,有些细节步骤没写,细节请看上面链接这篇文章。

官网地址: https://console.cloud.tencent.com/iotexplorer

 

四、生成腾讯物联网平台的设备登录信息

使用MQTT协议登录需要一些参数信息,需要使用官网提供的方式生成。

Python示例代码:


  
  1. #!/usr/bin/python
  2. # -*- coding: UTF -8 -*-
  3. import base64
  4. import hashlib
  5. import hmac
  6. import random
  7. import string
  8. import time
  9. import sys
  10. # 生成指定长度的随机字符串
  11. def RandomConnid(length):
  12. return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))
  13. # 生成接入物联网通信平台需要的各参数
  14. def IotHmac(productID, devicename, devicePsk):
  15. # 1. 生成 connid 为一个随机字符串,方便后台定位问题
  16. connid = RandomConnid( 5)
  17. # 2. 生成过期时间,表示签名的过期时间,从纪元 19701100: 00: 00 UTC 时间至今秒数的 UTF8 字符串
  18. expiry = int(time.time()) + 30* 24* 60 * 60
  19. # 3. 生成 MQTT 的 clientid 部分, 格式为 ${productid}${devicename}
  20. clientid = "{}{}".format(productID, devicename)
  21. # 4. 生成 MQTT 的 username 部分, 格式为 ${clientid};${sdkappid};${connid};${expiry}
  22. username = "{};12010126;{};{}".format(clientid, connid, expiry)
  23. # 5. 对 username 进行签名,生成token
  24. secret_key = devicePsk.encode( 'utf-8') # convert to bytes
  25. data_to_sign = username.encode( 'utf-8') # convert to bytes
  26. secret_key = base64.b64decode(secret_key) # this is still bytes
  27. token = hmac. new(secret_key, data_to_sign, digestmod=hashlib.sha256).hexdigest()
  28. # 6. 根据物联网通信平台规则生成 password 字段
  29. password = "{};{}".format(token, "hmacsha256")
  30. return {
  31. "clientid" : clientid,
  32. "username" : username,
  33. "password" : password
  34. }
  35. if __name__ == '__main__':
  36. # 参数分别填入: 产品ID,设备名称,设备密匙
  37. print(IotHmac( "6142CX41XE", "SmartAgriculture", "20Y/aAcmj+y6SDDh+ANR9g=="))

输出的登录参数,用于MQTT协议填参数: 


  
  1. { 'clientid': '6142CX41XESmartAgriculture', 'username': '6142CX41XESmartAgriculture;12010126;HUA2G;1624271589',
  2. 'password': 'a8aadebe9721f70e6f9e14fe56ff1d2b5cac9625fa1f96af2f0e0098597fe78b;hmacsha256'}

五、使用MQTT软件测试

MQTT客户端软件下载地址: https://download.csdn.net/download/xiaolong1126626497/18784012

该软件采用QT开发,源码地址: https://blog.csdn.net/xiaolong1126626497/article/details/116779490

设备主题发布与定义的格式:

设备消息数据上传格式:

{"method":"report","clientToken":"123","params":{"light":78.4,"temperature":21.4,"humidity":60.8,"motor":1}}

登录成功之后,就可以看到设备在线:

打开微信小程序可以查看到设备上传的数据: 

六、编写STM32设备端代码

6.1 main.c代码


  
  1. #include "stm32f10x.h"
  2. #include "led.h"
  3. #include "delay.h"
  4. #include "key.h"
  5. #include "usart.h"
  6. #include <string.h>
  7. #include "timer.h"
  8. #include "esp8266.h"
  9. #include "mqtt.h"
  10. #include "oled.h"
  11. #include "fontdata.h"
  12. #include "bh1750.h"
  13. #include "iic.h"
  14. #include "sht3x.h"
  15. /*
  16. 硬件连接方式:
  17. ESP8266串口WIFI模块与STM32的串口3相连接。
  18. PB10--RXD 模块接收脚
  19. PB11--TXD 模块发送脚
  20. GND---GND 地
  21. VCC---VCC 电源(3.3V~5.0V)
  22. OLED接线:
  23. D0----SCK-----PB14
  24. D1----MOSI----PB13
  25. RES—复位(低电平有效)—PB12
  26. DC---数据和命令控制管脚—PB1
  27. CS---片选引脚-----PA7
  28. 微型直流电机: PB8
  29. 紫光灯: PB9
  30. LED硬件连接: PB5 PE5
  31. KEY硬件连接:PE3 PE4
  32. */
  33. #define ESP8266_WIFI_AP_SSID "CMCC-Cqvn" //将要连接的路由器名称 --不要出现中文、空格等特殊字符
  34. #define ESP8266_AP_PASSWORD "99pu58cb" //将要连接的路由器密码
  35. //腾讯物联网服务器的设备信息
  36. #define MQTT_ClientID "6142CX41XESmartAgriculture"
  37. #define MQTT_UserName "6142CX41XESmartAgriculture;12010126;HUA2G;1624271589"
  38. #define MQTT_PassWord "a8aadebe9721f70e6f9e14fe56ff1d2b5cac9625fa1f96af2f0e0098597fe78b;hmacsha256"
  39. //订阅与发布的主题
  40. #define SET_TOPIC "$thing/down/property/6142CX41XE/SmartAgriculture" //订阅
  41. #define POST_TOPIC "$thing/up/property/6142CX41XE/SmartAgriculture" //发布
  42. char mqtt_message[ 200]; //上报数据缓存区
  43. char OLED_ShowBuff[ 100];
  44. u8 ESP8266_Stat= 0;
  45. /*
  46. 函数功能: 温湿度\光强度显示
  47. */
  48. void ShowTemperatureAndHumidity(float temp,float humi,float light)
  49. {
  50. sprintf(OLED_ShowBuff, "T: %.2f",temp);
  51. OLED_ShowString( 40, 16* 0, 16,OLED_ShowBuff);
  52. sprintf(OLED_ShowBuff, "H: %.2f%%",humi);
  53. OLED_ShowString( 40, 16* 1, 16,OLED_ShowBuff);
  54. sprintf(OLED_ShowBuff, "L: %.2f%%",light);
  55. OLED_ShowString( 40, 16* 2, 16,OLED_ShowBuff);
  56. }
  57. /*
  58. 函数功能: ESP8266显示页面
  59. */
  60. void ESP8266_ShowPageTable(void)
  61. {
  62. if(ESP8266_Stat)OLED_ShowString( 0, 16* 0, 16, "WIFI STAT:ERROR");
  63. else OLED_ShowString( 0, 16* 0, 16, "WIFI STAT:OK");
  64. //显示字符串
  65. sprintf(( char*)OLED_ShowBuff, "%s",ESP8266_WIFI_AP_SSID);
  66. OLED_ShowString( 0, 16* 1, 16,OLED_ShowBuff);
  67. sprintf(( char*)OLED_ShowBuff, "%s",ESP8266_AP_PASSWORD);
  68. OLED_ShowString( 0, 16* 2, 16,OLED_ShowBuff);
  69. }
  70. int main()
  71. {
  72. u32 time_cnt= 0;
  73. u32 i;
  74. u8 key;
  75. u8 page= 0;
  76. float temp= 0;
  77. float humi= 0;
  78. float light= 0;
  79. u8 motor_state= 0;
  80. float Humidity;
  81. float Temperature;
  82. delay_ms( 1000);
  83. delay_ms( 1000);
  84. LED_Init();
  85. KEY_Init();
  86. IIC_Init();
  87. //OLED初始化
  88. OLED_Init( 0xc8, 0xa1); //OLED显示屏初始化--正常显示;
  89. //清屏
  90. OLED_Clear( 0);
  91. USART1_Init( 115200);
  92. TIMER1_Init( 72, 20000); //超时时间20ms
  93. USART3_Init( 115200); //串口-WIFI
  94. TIMER3_Init( 72, 20000); //超时时间20ms
  95. Init_SHT30();
  96. USART1_Printf( "正在初始化WIFI请稍等.\n");
  97. if(ESP8266_Init())
  98. {
  99. ESP8266_Stat= 1;
  100. USART1_Printf( "ESP8266硬件检测错误.\n");
  101. }
  102. else
  103. {
  104. //非加密端口
  105. USART1_Printf( "WIFI:%d\n",ESP8266_STA_TCP_Client_Mode(ESP8266_WIFI_AP_SSID,ESP8266_AP_PASSWORD, "106.55.124.154", 1883, 1));
  106. }
  107. //2. MQTT协议初始化
  108. MQTT_Init();
  109. //3. 连接腾讯云IOT服务器
  110. while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
  111. {
  112. USART1_Printf( "服务器连接失败,正在重试...\n");
  113. delay_ms( 500);
  114. }
  115. USART1_Printf( "服务器连接成功.\n");
  116. //3. 订阅主题
  117. if(MQTT_SubscribeTopic(SET_TOPIC, 0, 1))
  118. {
  119. USART1_Printf( "主题订阅失败.\n");
  120. }
  121. else
  122. {
  123. USART1_Printf( "主题订阅成功.\n");
  124. }
  125. while( 1)
  126. {
  127. //按键可以测试开锁和关锁
  128. key=KEY_Scan( 0);
  129. if(key== 1)
  130. {
  131. //清屏
  132. OLED_Clear( 0);
  133. //翻页
  134. if(page>= 1)
  135. {
  136. page= 0;
  137. }
  138. else
  139. {
  140. page++;
  141. }
  142. LED1=!LED1; //LEd状态灯
  143. }
  144. else if(key== 2)
  145. {
  146. LED1=!LED1; //LEd状态灯
  147. time_cnt= 0;
  148. //电机状态改变
  149. MOTOR_DEV=!MOTOR_DEV;
  150. //电机状态
  151. motor_state=MOTOR_DEV;
  152. //补光灯
  153. LIGHT_DEV=!LIGHT_DEV;
  154. }
  155. //微信小程序开锁方式: 接收WIFI返回的数据
  156. if(USART3_RX_FLAG)
  157. {
  158. USART3_RX_BUFFER[USART3_RX_CNT]= '\0';
  159. //向串口打印微信小程序返回的数据
  160. for(i= 0;i<USART3_RX_CNT;i++)
  161. {
  162. USART1_Printf( "%c",USART3_RX_BUFFER[i]);
  163. }
  164. //如果是下发了属性,判断是开锁还是关锁
  165. if(USART3_RX_CNT> 5)
  166. {
  167. //使用字符串查找函数
  168. if( strstr(( char*)&USART3_RX_BUFFER[ 5], "\"motor\":1"))
  169. {
  170. LED1= 0; //亮灯
  171. MOTOR_DEV= 1; //开电机
  172. motor_state= 1;
  173. }
  174. else if( strstr(( char*)&USART3_RX_BUFFER[ 5], "\"motor\":0"))
  175. {
  176. LED1= 1; //灭灯
  177. MOTOR_DEV= 0; //关电机
  178. motor_state= 0;
  179. }
  180. }
  181. USART3_RX_CNT= 0;
  182. USART3_RX_FLAG= 0;
  183. }
  184. //定时与保持与微信小程序的同步--1秒一次
  185. delay_ms( 10);
  186. time_cnt++;
  187. if(time_cnt== 50)
  188. {
  189. time_cnt= 0;
  190. //状态灯 --表示程序还活着
  191. LED2=!LED2;
  192. //读取光强度
  193. light=Read_BH1750_Data();
  194. //读取温湿度
  195. SHT3x_ReadData(&Humidity,&Temperature);
  196. humi=Humidity;
  197. temp=Temperature;
  198. //上传数据
  199. sprintf(mqtt_message, "{\"method\":\"report\",\"clientToken\":\"123\",\"params\":{\"temperature\":%f,\"humidity\":%f,\"motor\":%d,\"light\":%f}}",
  200. temp,humi,motor_state,light);
  201. MQTT_PublishData(POST_TOPIC,mqtt_message, 0);
  202. //根据湿度自动灌溉
  203. if(humi< 50.0) //小于50自动灌溉
  204. {
  205. motor_state= 1; //电机状态更新
  206. MOTOR_DEV= 1; //开电机
  207. }
  208. }
  209. //OLED显示屏
  210. if(page== 0)
  211. {
  212. ShowTemperatureAndHumidity(temp,humi,light);
  213. }
  214. else if(page== 1)
  215. {
  216. ESP8266_ShowPageTable();
  217. }
  218. }
  219. }

6.2 mqtt.c 代码


  
  1. #include "mqtt.h"
  2. u8 *mqtt_rxbuf;
  3. u8 *mqtt_txbuf;
  4. u16 mqtt_rxlen;
  5. u16 mqtt_txlen;
  6. u8 _mqtt_txbuf[ 256]; //发送数据缓存区
  7. u8 _mqtt_rxbuf[ 256]; //接收数据缓存区
  8. typedef enum
  9. {
  10. //名字 值 报文流动方向 描述
  11. M_RESERVED1 = 0 , // 禁止 保留
  12. M_CONNECT , // 客户端到服务端 客户端请求连接服务端
  13. M_CONNACK , // 服务端到客户端 连接报文确认
  14. M_PUBLISH , // 两个方向都允许 发布消息
  15. M_PUBACK , // 两个方向都允许 QoS 1消息发布收到确认
  16. M_PUBREC , // 两个方向都允许 发布收到(保证交付第一步)
  17. M_PUBREL , // 两个方向都允许 发布释放(保证交付第二步)
  18. M_PUBCOMP , // 两个方向都允许 QoS 2消息发布完成(保证交互第三步)
  19. M_SUBSCRIBE , // 客户端到服务端 客户端订阅请求
  20. M_SUBACK , // 服务端到客户端 订阅请求报文确认
  21. M_UNSUBSCRIBE , // 客户端到服务端 客户端取消订阅请求
  22. M_UNSUBACK , // 服务端到客户端 取消订阅报文确认
  23. M_PINGREQ , // 客户端到服务端 心跳请求
  24. M_PINGRESP , // 服务端到客户端 心跳响应
  25. M_DISCONNECT , // 客户端到服务端 客户端断开连接
  26. M_RESERVED2 , // 禁止 保留
  27. }_typdef_mqtt_message;
  28. //连接成功服务器回应 20 02 00 00
  29. //客户端主动断开连接 e0 00
  30. const u8 parket_connetAck[] = { 0x20, 0x02, 0x00, 0x00};
  31. const u8 parket_disconnet[] = { 0xe0, 0x00};
  32. const u8 parket_heart[] = { 0xc0, 0x00};
  33. const u8 parket_heart_reply[] = { 0xc0, 0x00};
  34. const u8 parket_subAck[] = { 0x90, 0x03};
  35. void MQTT_Init(void)
  36. {
  37. //缓冲区赋值
  38. mqtt_rxbuf = _mqtt_rxbuf;
  39. mqtt_rxlen = sizeof(_mqtt_rxbuf);
  40. mqtt_txbuf = _mqtt_txbuf;
  41. mqtt_txlen = sizeof(_mqtt_txbuf);
  42. memset(mqtt_rxbuf, 0,mqtt_rxlen);
  43. memset(mqtt_txbuf, 0,mqtt_txlen);
  44. //无条件先主动断开
  45. MQTT_Disconnect();
  46. delay_ms( 100);
  47. MQTT_Disconnect();
  48. delay_ms( 100);
  49. }
  50. /*
  51. 函数功能: 登录服务器
  52. 函数返回值: 0表示成功 1表示失败
  53. */
  54. u8 MQTT_Connect(char *ClientID,char *Username,char *Password)
  55. {
  56. u8 i,j;
  57. int ClientIDLen = strlen(ClientID);
  58. int UsernameLen = strlen(Username);
  59. int PasswordLen = strlen(Password);
  60. int DataLen;
  61. mqtt_txlen= 0;
  62. //可变报头+Payload 每个字段包含两个字节的长度标识
  63. DataLen = 10 + (ClientIDLen+ 2) + (UsernameLen+ 2) + (PasswordLen+ 2);
  64. //固定报头
  65. //控制报文类型
  66. mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT
  67. //剩余长度(不包括固定头部)
  68. do
  69. {
  70. u8 encodedByte = DataLen % 128;
  71. DataLen = DataLen / 128;
  72. // if there are more data to encode, set the top bit of this byte
  73. if ( DataLen > 0 )
  74. encodedByte = encodedByte | 128;
  75. mqtt_txbuf[mqtt_txlen++] = encodedByte;
  76. } while ( DataLen > 0 );
  77. //可变报头
  78. //协议名
  79. mqtt_txbuf[mqtt_txlen++] = 0; // Protocol Name Length MSB
  80. mqtt_txbuf[mqtt_txlen++] = 4; // Protocol Name Length LSB
  81. mqtt_txbuf[mqtt_txlen++] = 'M'; // ASCII Code for M
  82. mqtt_txbuf[mqtt_txlen++] = 'Q'; // ASCII Code for Q
  83. mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T
  84. mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T
  85. //协议级别
  86. mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4 对于 3.1.1 版协议,协议级别字段的值是 4(0x04)
  87. //连接标志
  88. mqtt_txbuf[mqtt_txlen++] = 0xc2; // conn flags
  89. mqtt_txbuf[mqtt_txlen++] = 0; // Keep-alive Time Length MSB
  90. mqtt_txbuf[mqtt_txlen++] = 100; // Keep-alive Time Length LSB 100S心跳包 保活时间
  91. mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen); // Client ID length MSB
  92. mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen); // Client ID length LSB
  93. memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen);
  94. mqtt_txlen += ClientIDLen;
  95. if(UsernameLen > 0)
  96. {
  97. mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen); //username length MSB
  98. mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen); //username length LSB
  99. memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen);
  100. mqtt_txlen += UsernameLen;
  101. }
  102. if(PasswordLen > 0)
  103. {
  104. mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen); //password length MSB
  105. mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen); //password length LSB
  106. memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen);
  107. mqtt_txlen += PasswordLen;
  108. }
  109. memset(mqtt_rxbuf, 0,mqtt_rxlen);
  110. MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
  111. for(j= 0;j< 10;j++)
  112. {
  113. delay_ms( 50);
  114. if(USART3_RX_FLAG)
  115. {
  116. memcpy(( char *)mqtt_rxbuf,USART3_RX_BUFFER,USART3_RX_CNT);
  117. //memcpy
  118. for(i= 0;i<USART3_RX_CNT;i++)USART1_Printf( "%#x ",USART3_RX_BUFFER[i]);
  119. USART3_RX_FLAG= 0;
  120. USART3_RX_CNT= 0;
  121. }
  122. //CONNECT
  123. if(mqtt_rxbuf[ 0]==parket_connetAck[ 0] && mqtt_rxbuf[ 1]==parket_connetAck[ 1]) //连接成功
  124. {
  125. return 0; //连接成功
  126. }
  127. }
  128. return 1;
  129. }
  130. /*
  131. 函数功能: MQTT订阅/取消订阅数据打包函数
  132. 函数参数:
  133. topic 主题
  134. qos 消息等级 0:最多分发一次 1: 至少分发一次 2: 仅分发一次
  135. whether 订阅/取消订阅请求包 (1表示订阅,0表示取消订阅)
  136. 返回值: 0表示成功 1表示失败
  137. */
  138. u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether)
  139. {
  140. u8 i,j;
  141. mqtt_txlen= 0;
  142. int topiclen = strlen(topic);
  143. int DataLen = 2 + (topiclen+ 2) + (whether? 1: 0); //可变报头的长度(2字节)加上有效载荷的长度
  144. //固定报头
  145. //控制报文类型
  146. if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息类型和标志订阅
  147. else mqtt_txbuf[mqtt_txlen++] = 0xA2; //取消订阅
  148. //剩余长度
  149. do
  150. {
  151. u8 encodedByte = DataLen % 128;
  152. DataLen = DataLen / 128;
  153. // if there are more data to encode, set the top bit of this byte
  154. if ( DataLen > 0 )
  155. encodedByte = encodedByte | 128;
  156. mqtt_txbuf[mqtt_txlen++] = encodedByte;
  157. } while ( DataLen > 0 );
  158. //可变报头
  159. mqtt_txbuf[mqtt_txlen++] = 0; //消息标识符 MSB
  160. mqtt_txbuf[mqtt_txlen++] = 0x0A; //消息标识符 LSB
  161. //有效载荷
  162. mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen); //主题长度 MSB
  163. mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen); //主题长度 LSB
  164. memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
  165. mqtt_txlen += topiclen;
  166. if(whether)
  167. {
  168. mqtt_txbuf[mqtt_txlen++] = qos; //QoS级别
  169. }
  170. for(i= 0;i< 10;i++)
  171. {
  172. memset(mqtt_rxbuf, 0,mqtt_rxlen);
  173. MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
  174. for(j= 0;j< 10;j++)
  175. {
  176. delay_ms( 50);
  177. if(USART3_RX_FLAG)
  178. {
  179. memcpy(( char *)mqtt_rxbuf,( char*)USART3_RX_BUFFER,USART3_RX_CNT);
  180. USART3_RX_FLAG= 0;
  181. USART3_RX_CNT= 0;
  182. }
  183. if(mqtt_rxbuf[ 0]==parket_subAck[ 0] && mqtt_rxbuf[ 1]==parket_subAck[ 1]) //订阅成功
  184. {
  185. return 0; //订阅成功
  186. }
  187. }
  188. }
  189. return 1; //失败
  190. }
  191. //MQTT发布数据打包函数
  192. //topic 主题
  193. //message 消息
  194. //qos 消息等级
  195. u8 MQTT_PublishData(char *topic, char *message, u8 qos)
  196. {
  197. int topicLength = strlen(topic);
  198. int messageLength = strlen(message);
  199. static u16 id= 0;
  200. int DataLen;
  201. mqtt_txlen= 0;
  202. //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
  203. //QOS为0时没有标识符
  204. //数据长度 主题名 报文标识符 有效载荷
  205. if(qos) DataLen = ( 2+topicLength) + 2 + messageLength;
  206. else DataLen = ( 2+topicLength) + messageLength;
  207. //固定报头
  208. //控制报文类型
  209. mqtt_txbuf[mqtt_txlen++] = 0x30; // MQTT Message Type PUBLISH
  210. //剩余长度
  211. do
  212. {
  213. u8 encodedByte = DataLen % 128;
  214. DataLen = DataLen / 128;
  215. // if there are more data to encode, set the top bit of this byte
  216. if ( DataLen > 0 )
  217. encodedByte = encodedByte | 128;
  218. mqtt_txbuf[mqtt_txlen++] = encodedByte;
  219. } while ( DataLen > 0 );
  220. mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength); //主题长度MSB
  221. mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength); //主题长度LSB
  222. memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength); //拷贝主题
  223. mqtt_txlen += topicLength;
  224. //报文标识符
  225. if(qos)
  226. {
  227. mqtt_txbuf[mqtt_txlen++] = BYTE1(id);
  228. mqtt_txbuf[mqtt_txlen++] = BYTE0(id);
  229. id++;
  230. }
  231. memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength);
  232. mqtt_txlen += messageLength;
  233. MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
  234. return mqtt_txlen;
  235. }
  236. void MQTT_SentHeart(void)
  237. {
  238. MQTT_SendBuf((u8 *)parket_heart, sizeof(parket_heart));
  239. }
  240. void MQTT_Disconnect(void)
  241. {
  242. MQTT_SendBuf((u8 *)parket_disconnet, sizeof(parket_disconnet));
  243. }
  244. void MQTT_SendBuf(u8 *buf,u16 len)
  245. {
  246. USARTx_DataSend(USART3,buf,len);
  247. }

 


转载:https://blog.csdn.net/xiaolong1126626497/article/details/117167261
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场