上一篇博客讲了ESP8266-01S的MQTT固件包烧录过程,配置了阿里云物联网平台,并且利用MQTT.fx验证和阿里云通信的过程,链接如下:
https://blog.csdn.net/weixin_41011452/article/details/140645130
1 MQTT协议
MQTT(Message Queuing Telemetry Transport, 消息队列遥测传输协议)是IBM在1999年发布的,基于TCP/IP提供网络连接,同时也有基于UDP的版本,称为MQTT-SN。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛,使其在物联网、小型设备、移动应用等方面有较广泛的应用。如果想了解大概原理,百度一大堆。我只讲解这个协议怎么用。
MQTT协议是基于TCP/IP的,也就是网络分层里的应用层,它不能独立存在,必须附着于网络上,不像NB-IOT和4G可以自己创建网络,所以啊,MQTT就必须依赖于ESP8266-01S,通过wifi把数据收发。
MQTT协议一共有两个版本,MQTT 3.1.1版本已经足够满足大多数物联网应用的需求,并且由于其广泛的兼容性和稳定性而被广泛使用。而MQTT 5.0版本则提供了更多的高级特性和改进,适用于需要更高性能和更复杂功能的场景。我们使用的是3.1.1版本。
MQTT 3.1.1协议英文原版的下载地址是:
https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/
英文不好的建议看中文,中文的协议的下载地址是:
英文原版协议一共是80页的内容,不用从头看到尾,浪费时间还容易看了后面忘了前面,我读研的时候就这么干过。一般我们做嵌入式开发的,其实用到啥看啥就行了,用到的内容你去看协议里对应的部分就行,用不到的地方就别看了。
STM32通过ESP8266-01S连接阿里云其实用到MQTT的控制报文并不多,只有CONNECT、CONNACK、SUBSCRIBE、SUBACK、PINGRESP、PUBLISH、PINGREQ这七个控制报文。这七个报文在MQTT 3.1.1的控制协议说明书里有
1.1 CONNECT报文及其代码(其他报文和代码放在文末的视频链接讲解)
MQTT协议的每一个报文都有固定的报头,就跟你看新闻热搜一样,热搜列出50条,那么每一条都要有标题,你只会根据标题是否感兴趣决定读不读这条热搜,MQTT也是一样,必须要有个固定的报头。
固定报头都是两个字节的,第一个字节就是根据报文控制类型不同定义的,就是上面那个截图,比如CONNECT的第一个字节就是十进制1,换算成十六进制就是0x10;CONNACK的第一个字节就是十进制2,换算成十六进制就是0x2。
第二个字节就是这条报文的剩余长度,从第三个字节才是可变报头或者有效载荷,报文并不都是有可变报头的,这个要看MQTT协议的每一个报文的具体说明,如果有可变报头那么第三个字节开始就是可变报头,如果没有可变报头,那么第三个字节就是有效载荷,载荷说白了就是你要传输的数据。
上面说了一堆我们要抽丝剥茧,刚刚也说了MQTT的每一个报文都有固定的两个字节报头,可变报头不一定每个报文都有,根据上面截图的CONNECT报文,可以看出它是有可变报头的,并且是有10个字节的可变报头,其中这10个字节的可变报头又分为:6个字节的协议名+1个字节的协议级别+1个字节的连接标志+2个字节的保持连接,所以CONNECT报文一共是12个字节,我们开始分析。
第一个字节(byte1):固定报头,固定为0001 0000,换算成十六进制就是0x10
第二个字节(byte2):固定报头,剩余长度,等于10个字节的可变报头加有效载荷
第三个字节(byte3):可变报头里的协议名的第一个字节,是协议名的长度高8位,固定为0000 0000,换算成十六进制就是0x00
第四个字节(byte4):可变报头里的协议名的第二个字节,是协议名的长度低8位,固定为0000 0100,换算成十六进制就是0x04
第五个字节(byte5):可变报头里的协议名的第三个字节,是协议名MQTT里的M,固定为0100 1101,换算成十六进制就是0x4D,其实就是字母M的ASCII码,请看下图
第六个字节(byte6):可变报头里的协议名的第四个字节,是协议名MQTT里的Q,固定为0101 0001,换算成十六进制就是0x51,其实就是字母Q的ASCII码
第七个字节(byte7):可变报头里的协议名的第五个字节,是协议名MQTT里的T,固定为0101 0100,换算成十六进制就是0x54,其实就是字母T的ASCII码
第八个字节(byte8):可变报头里的协议名的第六个字节,是协议名MQTT里的T,固定为0101 0100,换算成十六进制就是0x54,其实就是字母T的ASCII码
第九个字节(byte9):可变报头里的协议级别,如果是MQTT 3.1.1版本的协议,固定就是0x04,请看下图
第十个字节(byte10):可变报头里的连接标志,连接标志根据设置不同可以有不同的组合,我们的组合是使能用户名和密码校验,不使用遗嘱,不保留会话
第十一个字节(byte11):可变报头里的保持连接的高八位,我们的代码里设置的是0x00
第十二个字节(byte12):可变报头里的保持连接的低八位,我们的代码里设置的是0x64,换算成十进制就是100,也就是保持连接100秒
在STM32中的代码如下所示,除此之外还有有效载荷,有效载荷就放在代码讲解放在文末的视频链接去讲解。
*----------------------------------------------------------*/
/*函数名:连接服务器报文 */
/*参 数:无 */
/*返回值:无 */
/*----------------------------------------------------------*/
void MQTT_ConectPack(void)
{
int temp,Remaining_len;
Fixed_len = 1; //连接报文中,固定报头长度暂时先=1
Variable_len = 10; //连接报文中,可变报头长度=10
Payload_len = 2 + ClientID_len + 2 + Username_len + 2 + Passward_len; //连接报文中,负载长度
Remaining_len = Variable_len + Payload_len; //剩余长度=可变报头长度+负载长度
temp_buff[0]=0x10; //固定报头第1个字节 :固定0x10
do { //循环处理固定报头中的剩余长度字节,字节量根据剩余字节的真实长度变化
temp = Remaining_len%128; //剩余长度取余128
Remaining_len = Remaining_len/128; //剩余长度取整128
if(Remaining_len>0)
temp |= 0x80; //按协议要求位7置位
temp_buff[Fixed_len] = temp; //剩余长度字节记录一个数据
Fixed_len++; //固定报头总长度+1
} while(Remaining_len>0); //如果Remaining_len>0的话,再次进入循环
temp_buff[Fixed_len+0]=0x00; //可变报头第1个字节 :固定0x00
temp_buff[Fixed_len+1]=0x04; //可变报头第2个字节 :固定0x04
temp_buff[Fixed_len+2]=0x4D; //可变报头第3个字节 :固定0x4D
temp_buff[Fixed_len+3]=0x51; //可变报头第4个字节 :固定0x51
temp_buff[Fixed_len+4]=0x54; //可变报头第5个字节 :固定0x54
temp_buff[Fixed_len+5]=0x54; //可变报头第6个字节 :固定0x54
temp_buff[Fixed_len+6]=0x04; //可变报头第7个字节 :固定0x04
temp_buff[Fixed_len+7]=0xC2; //可变报头第8个字节 :使能用户名和密码校验,不使用遗嘱,不保留会话
temp_buff[Fixed_len+8]=0x00; //可变报头第9个字节 :保活时间高字节 0x00
temp_buff[Fixed_len+9]=0x64; //可变报头第10个字节:保活时间高字节 0x64 100s
/* CLIENT_ID */
temp_buff[Fixed_len+10] = ClientID_len/256; //客户端ID长度高字节
temp_buff[Fixed_len+11] = ClientID_len%256; //客户端ID长度低字节
memcpy(&temp_buff[Fixed_len+12],ClientID,ClientID_len); //复制过来客户端ID字串
/* 用户名 */
temp_buff[Fixed_len+12+ClientID_len] = Username_len/256; //用户名长度高字节
temp_buff[Fixed_len+13+ClientID_len] = Username_len%256; //用户名长度低字节
memcpy(&temp_buff[Fixed_len+14+ClientID_len],Username,Username_len); //复制过来用户名字串
/* 密码 */
temp_buff[Fixed_len+14+ClientID_len+Username_len] = Passward_len/256; //密码长度高字节
temp_buff[Fixed_len+15+ClientID_len+Username_len] = Passward_len%256; //密码长度低字节
memcpy(&temp_buff[Fixed_len+16+ClientID_len+Username_len],Passward,Passward_len); //复制过来密码字串
TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len); //加入发送数据缓冲区
}
2 代码讲解
链接地址如下:STM32C8T6+DHT11+ESP8266-01S温湿度上传阿里云物联网平台代码讲解(一)_哔哩哔哩_bilibili
3 实现效果
因为我们的代码用串口1作为输出打印调试信息,所以把STM32的串口1连接到CH340上,然后插入电脑,打开串口助手,程序烧录成功上电后就可以接收数据了。可以看到连接路由器和连接服务器的记录,都是通过串口1打印出来的。
连接成功后去 阿里云物联网平台查看数据是否上传成功,因为我用杜邦线接的线,STM32C8T6单片机模块5V的供电就一个,所以就没接DHT11,所以上传上来的数据始终是0,但是数据传输是没问题的,说明整个通信过程没问题。
4 资料获取
我这个资料也是移植别人的,然后自己改的,网上好多的程序都是坑,移植完了如果自己不改根本没法用,所以需要辨别。想获取资料可以加我个人微信,有偿使用,白嫖的就算了。