基于STM32实现MQTT

发布者:VS821001最新更新时间:2019-09-06 来源: eefocus关键字:STM32  MQTT  物理网络层 手机看文章 扫描二维码
随时随地手机看文章

1、MQTT协议

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

   协议详情: MQTT Version 3.1.1(英文版)

                    (中文版)

2、STM32实现的自我理解

   既然是一个协议,所以有一个基本的物理网络层就可以实现

   将就手上的一个ESP8266无线wifi串口模块,一个STM32的板子。也就可以完成MQTT的测试了。

   一般来说在协议中扮演的是一个客户端。主要的工作就是连接服务器、订阅消息、发送消息几个功能。最开始我在网上找了MQTT的客户端代码包,打开一看就蒙了。太多、太杂了,完全大于了我们的需求。所以我自己通过抓包对着协议写了一个数据包生成代码。

3、测试前准备

   MQTT服务器:我用的是Apollo Console(账号默认为:admin 密码默认:password 后面连接时要用)

   MQTT客户端: 通信猫。

   具体怎么用大家百度。。。

4、代码实现

4.1、固定报头 Fixed header

类型:

标志位:

根据这个可以写一个生成固定头的函数

 
  1. #define MQTT_TypeCONNECT 1//请求连接

  2. #define MQTT_TypeCONNACK 2//请求应答

  3. #define MQTT_TypePUBLISH 3//发布消息

  4. #define MQTT_TypePUBACK 4//发布应答

  5. #define MQTT_TypePUBREC 5//发布已接收,保证传递1

  6. #define MQTT_TypePUBREL 6//发布释放,保证传递2

  7. #define MQTT_TypePUBCOMP 7//发布完成,保证传递3

  8. #define MQTT_TypeSUBSCRIBE         8//订阅请求

  9. #define MQTT_TypeSUBACK 9//订阅应答

  10. #define MQTT_TypeUNSUBSCRIBE                 10//取消订阅

  11. #define MQTT_TypeUNSUBACK 11//取消订阅应答

  12. #define MQTT_TypePINGREQ 12//ping请求

  13. #define MQTT_TypePINGRESP 13//ping响应

  14. #define MQTT_TypeDISCONNECT                 14//断开连接

  15. unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain)

  16. {

  17. unsigned char dat = 0;

  18. dat = (MesType & 0x0f) << 4;

  19. dat |= (DupFlag & 0x01) << 3;

  20. dat |= (QosLevel & 0x03) << 1;

  21. dat |= (Retain & 0x01);

  22. return dat;

  23. }

 

4.2、连接

连接时抓的包的:

根据协议分析

 

固定报头 Fixed header

也就是数据包前两个10 21

后面是可变头:协议名、协议级别、连接标志、清除会话、遗嘱标志、遗嘱、遗嘱保留、用户名标志、密码标志、保持连接。

然后是有效载荷:客户端标识符、遗嘱主题、遗嘱消息、用户名、密码。

 
  1. #define MQTT_StaCleanSession 1 //清理会话

  2. #define MQTT_StaWillFlag 0 //遗嘱标志

  3. #define MQTT_StaWillQoS 0 //遗嘱QoS连接标志的第4和第3位。

  4. #define MQTT_StaWillRetain 0 //遗嘱保留

  5. #define MQTT_StaUserNameFlag 1 //用户名标志 User Name Flag

  6. #define MQTT_StaPasswordFlag 1 //密码标志 Password Flag

  7. #define MQTT_KeepAlive         60       //心跳包秒数

  8. #define MQTT_ClientIdentifier         "test"                          //客户端标识符 Client Identifier

  9. #define MQTT_WillTopic "" //遗嘱主题 Will Topic

  10. #define MQTT_WillMessage "" //遗嘱消息 Will Message

  11. #define MQTT_UserName "admin" //用户名 User Name

  12. #define MQTT_Password "password" //密码 Password

  13. void GetDataConnet(unsigned char *buff)//获取连接的数据包正确连接返回20 02 00 00

  14. {

  15. unsigned int i,len,lennum = 0;

  16. unsigned char *msg;

  17. buff[0] = GetDataFixedHead(MQTT_TypeCONNECT,0,0,0);

  18. buff[2] = 0x00;

  19. buff[3] = 0x04;

  20. buff[4] = 'M';

  21. buff[5] = 'Q';

  22. buff[6] = 'T';

  23. buff[7] = 'T';

  24. buff[8] = 0x04;//协议级别 Protocol Level

  25. buff[9] = 0 | (MQTT_StaCleanSession << 1) | (MQTT_StaWillFlag << 1)

  26.     | (MQTT_StaWillQoS << 3) | (MQTT_StaWillRetain << 5)

  27.     | (MQTT_StaPasswordFlag << 6) |(MQTT_StaUserNameFlag << 7);//连接标志

  28. buff[10] = MQTT_KeepAlive >> 8;

  29. buff[11] = MQTT_KeepAlive;

  30. len = strlen(MQTT_ClientIdentifier);

  31. buff[12] = len >> 8;

  32. buff[13] = len;

  33. msg = MQTT_ClientIdentifier;

  34. for(i = 0;i

  35. {

  36. buff[14+i] = msg[i];

  37. }

  38. lennum += len;

  39. if(MQTT_StaWillFlag)

  40. {

  41. len = strlen(MQTT_WillTopic);

  42. buff[13 + lennum + 1] = len >> 8;

  43. buff[13 + lennum + 2] = len;

  44. lennum += 2;

  45. msg = MQTT_WillTopic;

  46. for(i = 0;i

  47. {

  48. buff[14+lennum+i] = msg[i];

  49. }

  50. lennum += len;

  51. len = strlen(MQTT_WillMessage);

  52. buff[12] = len >> 8;

  53. buff[13] = len;

  54. lennum += 2;

  55. msg = MQTT_WillMessage;

  56. for(i = 0;i

  57. {

  58. buff[14+lennum+i] = msg[i];

  59. }

  60. lennum += len;

  61. }

  62. if(MQTT_StaUserNameFlag)

  63. {

  64. len = strlen(MQTT_UserName);

  65. buff[13 + lennum + 1] = len >> 8;

  66. buff[13 + lennum + 2] = len;

  67. lennum += 2;

  68. msg = MQTT_UserName;

  69. for(i = 0;i

  70. {

  71. buff[14+lennum+i] = msg[i];

  72. }

  73. lennum += len;

  74.  

  75. }

  76. if(MQTT_StaPasswordFlag)

  77. {

  78. len = strlen(MQTT_Password);

  79. buff[13 + lennum + 1] = len >> 8;

  80. buff[13 + lennum + 2] = len;

  81. lennum += 2;

  82. msg = MQTT_Password;

  83. for(i = 0;i

  84. {

  85. buff[14+lennum+i] = msg[i];

  86. }

  87. lennum += len;

  88. }

  89. buff[1] = 13 + lennum - 1;//最后计算长度

  90. }

4.3、订阅主题

订阅a/b时抓的包:

 
  1. /**********************

  2. 订阅主题的数据包

  3. Num:主题序号 (每次连接从1开始)

  4. RequestedQoS:服务质量要求0,1或2

  5. **********************/

  6. void GetDataSUBSCRIBE(unsigned char *buff,const char *dat,unsigned int Num,unsigned char RequestedQoS)

  7. {

  8. unsigned int i,len = 0,lennum = 0;

  9. buff[0] = 0x82;

  10. len = strlen(dat);

  11. buff[2] = Num>>8;

  12. buff[3] = Num;

  13. buff[4] = len>>8;

  14. buff[5] = len;

  15. for(i = 0;i

  16. {

  17. buff[6+i] = dat[i];

  18. }

  19. lennum = len;

  20. buff[6 + lennum ] = RequestedQoS;

  21. buff[1] = lennum + 5;

  22. }

4.4、发布信息

固定报头:

 

 

 

发送a/c主题信息为123时抓的包:

 

 

 
  1. /**************************************

  2. buff:数据包数组

  3. dup :重发标志

  4. qos :服务质量等级

  5. retain:保留标志

  6. topic:主题如“a/c”

  7. msg:消息

  8. ************************************/

  9. void GetDataPUBLISH(unsigned char *buff,unsigned char dup, unsigned char qos,unsigned char retain,const char *topic ,const char *msg)

  10. {   

  11.         unsigned int i,len=0,lennum=0;

  12. buff[0] = GetDataFixedHead(MQTT_TypePUBLISH,dup,qos,retain);

  13. len = strlen(topic);

  14. buff[2] = len>>8;

  15. buff[3] = len;

  16. for(i = 0;i

  17. {

  18. buff[4+i] = topic[i];

  19. }

  20. lennum = len;

  21. len = strlen(msg);

  22. for(i = 0;i

  23. {

  24. buff[4+i+lennum] = msg[i];

  25. }

  26. lennum += len;

  27. buff[1] = lennum + 2;

  28. }

4.5、心跳请求

客户端发送PINGREQ报文给服务端的。

用于:

    在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。

    请求服务端发送 响应确认它还活着。

    使用网络以确认网络连接没有断开。

心跳请求抓的包:

可以看到就是发了一个C0 00

而服务器返回一个    d0 00

5、总结

    文中就讲了几个非常重要的请求格式。esp8266连接tcp服务器后就可以发送生成的数据包,就可以实现MQTT的功能。

    代码我已经测试过完全可用。

下面给完整的代码:

mqtt.h

 
  1. #ifndef __MQTT_H

  2. #define __MQTT_H

  3. #include "sys.h"

  4. #include

  5. #define MQTT_TypeCONNECT 1//请求连接

  6. #define MQTT_TypeCONNACK 2//请求应答

  7. #define MQTT_TypePUBLISH 3//发布消息

  8. #define MQTT_TypePUBACK 4//发布应答

  9. #define MQTT_TypePUBREC 5//发布已接收,保证传递1

  10. #define MQTT_TypePUBREL 6//发布释放,保证传递2

  11. #define MQTT_TypePUBCOMP 7//发布完成,保证传递3

  12. #define MQTT_TypeSUBSCRIBE 8//订阅请求

  13. #define MQTT_TypeSUBACK 9//订阅应答

  14. #define MQTT_TypeUNSUBSCRIBE 10//取消订阅

  15. #define MQTT_TypeUNSUBACK 11//取消订阅应答

  16. #define MQTT_TypePINGREQ 12//ping请求

  17. #define MQTT_TypePINGRESP 13//ping响应

  18. #define MQTT_TypeDISCONNECT 14//断开连接

  19.  

  20. #define MQTT_StaCleanSession 1 //清理会话

  21. #define MQTT_StaWillFlag 0 //遗嘱标志

  22. #define MQTT_StaWillQoS 0 //遗嘱QoS连接标志的第4和第3位。

  23. #define MQTT_StaWillRetain 0 //遗嘱保留

  24. #define MQTT_StaUserNameFlag 1 //用户名标志 User Name Flag

  25. #define MQTT_StaPasswordFlag 1 //密码标志 Password Flag

  26. #define MQTT_KeepAlive 60

  27. #define MQTT_ClientIdentifier "Tan1" //客户端标识符 Client Identifier

  28. #define MQTT_WillTopic "" //遗嘱主题 Will Topic

  29. #define MQTT_WillMessage "" //遗嘱消息 Will Message

  30. #define MQTT_UserName "admin" //用户名 User Name

  31. #define MQTT_Password "password" //密码 Password

  32.  

  33. unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain);

  34. void GetDataPUBLISH(unsigned char *buff,unsigned char dup, unsigned char qos,unsigned char retain,const char *topic ,const char *msg);//获取发布消息的数据包

  35. void GetDataSUBSCRIBE(unsigned char *buff,const char *dat,unsigned int Num,unsigned char RequestedQoS);//订阅主题的数据包 Num:主题序号 RequestedQoS:服务质量要求0,1或2

  36. void GetDataDisConnet(unsigned char *buff);//获取断开连接的数据包

  37. void GetDataConnet(unsigned char *buff);//获取连接的数据包正确连接返回20 02 00 00

  38. void GetDataPINGREQ(unsigned char *buff);//心跳请求的数据包成功返回d0 00

  39. #endif

mqtt.c

 
  1. #include "mqtt.h"

  2.  

  3. unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain)

  4. {

  5. unsigned char dat = 0;

  6. dat = (MesType & 0x0f) << 4;

  7. dat |= (DupFlag & 0x01) << 3;

  8. dat |= (QosLevel & 0x03) << 1;

  9. dat |= (Retain & 0x01);

  10. return dat;

  11. }

  12. void GetDataConnet(unsigned char *buff)//获取连接的数据包正确连接返回20 02 00 00

  13. {

  14. unsigned int i,len,lennum = 0;

  15. unsigned char *msg;

  16. buff[0] = GetDataFixedHead(MQTT_TypeCONNECT,0,0,0);

  17. buff[2] = 0x00;

  18. buff[3] = 0x04;

  19. buff[4] = 'M';

  20. buff[5] = 'Q';

  21. buff[6] = 'T';

  22. buff[7] = 'T';

  23. buff[8] = 0x04;//协议级别 Protocol Level

  24. buff[9] = 0 | (MQTT_StaCleanSession << 1) | (MQTT_StaWillFlag << 1)

  25. | (MQTT_StaWillQoS << 3) | (MQTT_StaWillRetain << 5)

  26. | (MQTT_StaPasswordFlag << 6) |(MQTT_StaUserNameFlag << 7);//连接标志

  27. buff[10] = MQTT_KeepAlive >> 8;

  28. buff[11] = MQTT_KeepAlive;

  29. len = strlen(MQTT_ClientIdentifier);

  30. buff[12] = len >> 8;

  31. buff[13] = len;

  32. msg = MQTT_ClientIdentifier;

  33. for(i = 0;i

  34. {

  35. buff[14+i] = msg[i];

  36. }

  37. lennum += len;

  38. if(MQTT_StaWillFlag)

  39. {

  40. len = strlen(MQTT_WillTopic);

  41. buff[13 + lennum + 1] = len >> 8;

  42. buff[13 + lennum + 2] = len;

  43. lennum += 2;

  44. msg = MQTT_WillTopic;

  45. for(i = 0;i

  46. {

  47. buff[14+lennum+i] = msg[i];

  48. }

  49. lennum += len;

  50. len = strlen(MQTT_WillMessage);

  51. buff[12] = len >> 8;

  52. buff[13] = len;

  53. lennum += 2;

  54. msg = MQTT_WillMessage;

  55. for(i = 0;i

  56. {

  57. buff[14+lennum+i] = msg[i];

  58. }

  59. lennum += len;

  60. }

  61. if(MQTT_StaUserNameFlag)

  62. {

  63. len = strlen(MQTT_UserName);

  64. buff[13 + lennum + 1] = len >> 8;

  65. buff[13 + lennum + 2] = len;

  66. lennum += 2;

  67. msg = MQTT_UserName;

  68. for(i = 0;i

  69. {

  70. buff[14+lennum+i] = msg[i];

  71. }

  72. lennum += len;

  73.  

  74. }

  75. if(MQTT_StaPasswordFlag)

  76. {

  77. len = strlen(MQTT_Password);

  78. buff[13 + lennum + 1] = len >> 8;

  79. buff[13 + lennum + 2] = len;

  80. lennum += 2;

  81. msg = MQTT_Password;

  82. for(i = 0;i

  83. {

  84. buff[14+lennum+i] = msg[i];

  85. }

  86. lennum += len;

  87. }

  88. buff[1] = 13 + lennum - 1;

  89. }

  90. void GetDataDisConnet(unsigned char *buff)//获取断开连接的数据包

  91. {

  92. buff[0] = 0xe0;

  93. buff[1] = 0;

  94. }

  95. void GetDataPINGREQ(unsigned char *buff)//心跳请求的数据包成功返回d0 00

[1] [2]
关键字:STM32  MQTT  物理网络层 引用地址:基于STM32实现MQTT

上一篇:STM32实战1:按键点亮LED小灯 hh
下一篇:STM32 Cube点亮第一个LED

推荐阅读最新更新时间:2024-11-13 11:11

谈一下STM32的启动流程
STM32三种启动模式 下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存,这就是所谓的启动过程。 STM32上电或者复位后,代码区始终从0x00000000开始,其实就是将存储空间的地址映射到0x00000000中。三种启动模式如下: 从主闪存存储器启动,将主Flash地址0x08000000映射到0x00000000,这样代码启动之后就相当于从0x08000000开始。主闪存存储器是STM32内置的Flash,作为芯片内置的Flash,是正常的工作模式。一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。 从系统存储器启动。首先控制BOOT0、BOOT1管脚
[单片机]
谈一下<font color='red'>STM32</font>的启动流程
stm32专题一:GPIO输出—点亮LED
新建led文件夹和bsp_led.c和bsp_led.h的板级支持包。 在bsp_led.c中,函数LED_GPIO_Config(void)提供了gpio的初始化过程: 定义GPIO初始化结构体; 开启RCC时钟,这一步非常重要,要找到对应的总线和外设时钟; 给初始化结构体成员赋值,由于和硬件相关,都在bsp_led.h中定义成宏,提高可移植性。 bsp_led.c // bsp: board support package 板级支持包 #include bsp_led.h void LED_GPIO_Config(void) { // 定义GPIO初始化结构体 GPIO_InitTypeDef G
[单片机]
<font color='red'>stm32</font>专题一:GPIO输出—点亮LED
STM32基于标准外设库的外部中断配置
本文介绍了STM32基于标准外设库的外部中断配置,以及基于参考手册如何更加寄存器配置外部中断 2 STM32的外部中断 下图来自《STM32参考手册》,从整个架构图可以知道,外部中断的功能可以配置六个寄存器; 中断屏蔽寄存器(EXTI_IMR) 事件屏蔽寄存器(EXTI_EMR) 上升沿触发选择寄存器(EXTI_RTSR) 下降沿触发选择寄存器(EXTI_FTSR) 软件中断事件寄存器(EXTI_SWIER) 挂起寄存器(EXTI_PR) EXTI支持配置20个中断和事件屏蔽位; GPIO端口以下图的方式连接到16个外部中断/事件线上;EXTI_Line0 — EXTI_Line15; EXTI_Line16 连接到PVD
[单片机]
<font color='red'>STM32</font>基于标准外设库的外部中断配置
STM32之蓝牙透传模块(迅通科技PTR5518)
一、简介 本文介绍如何使用STM32控制蓝牙透传模块,蓝牙透传模块使用迅通科技的PTR5518。 二、实验平台 库版本:STM32F10x_StdPeriph_Lib_V3.5.0 编译软件:MDK4.53 硬件平台:STM32开发板(主芯片stm32f103c8t6) 仿真器:JLINK 手机型号: 小米4S 安卓版本:安卓5.1 安卓app:BLE Tool 三、版权声明 四、实验前提 1、在进行本文步骤前,请先阅读以下博文: 1)《STM32之蓝牙透传模块(昇润科技)》:http://blog.csdn.net/feilusia/article/details/52838571
[单片机]
<font color='red'>STM32</font>之蓝牙透传模块(迅通科技PTR5518)
工程师应该掌握的STM32单片机关键基础精华
  从51开始,单片机玩了很长时间了,有51,PIC,AVR等等,早就想跟潮流玩玩ARM,但一直没有开始,原因-----不知道玩了ARM可以做什么(对我自己而言)。如果为学习而学习,肯定学不好。然后cortex-m3出来了,据说,这东西可以替代单片机,于是马上开始关注。也在第一时间开始学习,可惜一开始就有点站错了队,选错了型(仍是对我自己而言)。我希望这种芯片应该是满大街都是,随便哪里都可以买得到,但我选的第一种显然做不到。为此,大概浪费了一年多时间吧,现在,回到对我来说是正确的道路上来啦,边学边写点东西。      这里写的是我的学习的过程,显然,很多时候会是不全面的,不系统的,感悟式的,甚至有时会是错误的,有些做法会是不专
[单片机]
工程师应该掌握的<font color='red'>STM32</font>单片机关键基础精华
STM32单片机的特点和功能是什么
STM32单片机是一款基于ARM Cortex-M内核的32位闪存微控制器,由STMicroelectronics公司(意法半导体)生产。STM32单片机具有高性能、低功耗、丰富的外设和易于开发的特点,广泛应用于工业控制、消费电子、通信设备等领域。 一、STM32单片机的特点 高性能:STM32单片机采用了ARM Cortex-M内核,最高运行频率可达72MHz,具有单周期乘法和硬件除法等高级功能,能够满足各种高性能应用的需求。 低功耗:STM32单片机具有多种低功耗模式,如睡眠模式、停止模式和待机模式等,能够在低功耗应用中实现长时间的工作。 丰富的外设:STM32单片机集成了丰富的外设,如GPIO、UART、SPI、I2C、
[单片机]
手机红外遥控器发送红外信号到STM32【HAL库】
现在大部分手机上都有红外遥控功能,而且好像大部分信号STM32都是可以收到的。现在就使用STM32发送红外信号,由STM32上的红外接收器接收后执行不同的操作。以下是注意事项: 在标准库中有 EXTI_ClearITPendingBit(EXTI_Line15); 清除中断位,使用HAL库不用使用清除中断位 在HAL库中NVIC的设置在文件stm32f1xx_hal_cortex.c中 HAL_Delay并不能实现us的延时效果,要自己写 void delay_us(uint32_t i) { uint32_t temp; SysTick- LOA
[单片机]
基于STM32的带触摸屏的无线解说器
本实例是以STM32F103系列单片机作为核心处理器,利用VS1003芯片进行音频解码的一种无线解说器。通过对触摸显示屏的操作,实现手持部分和终端部分二者的无线通讯。系统采用大容量的SD卡作为存储部分,通过SPI将VS1003B与SD卡的数据与STM32进行交互通信。本解说器在播放时没有出现理论上的断续情况,音质较好,占用的软硬件资源也较少,为后续的扩展留下了很大空间。 无线讲解器通常用于工厂、博物馆、景区等室外空旷场所供参观介绍用,通过事先在场所安放无线发射模块,并控制发射模块的工作范围。听众到达景点后,手上的讲解器将自动接收各个地点的无线编码信号,经解码后即可将存储在SD卡中的语音播放,以便清晰地全程收听全部介绍内容。 1
[电源管理]
基于<font color='red'>STM32</font>的带触摸屏的无线解说器
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved