2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云

STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云

时间:2024-05-03 10:07:28

相关推荐

STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云

STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云

目录

STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云 一、硬件及软件准备1、硬件:STM32单片机、ESP8266(ESP-12F)、DS18B202、软件:Keil5、安信可串口调试助手、wireshark网络抓包工具、paho MQTT协议库二、 实现效果1、 温度上传2、手机APP实时查看温度数据三、 MQTT协议四、 paho MQTT协议库移植1、下载paho库2、移植文件3、添加进工程4、修改transport.c和transport.h5、编译查看工程是否存在错误五、 阿里云物联网产品及设备创建六、ESP8266配置1、测试ESP8266模块2、配置WIFI工作模式3、重启WIFI模块4、关闭回显5、连接WIFI6、设为透传模式7、建立TCP连接七、核心代码1、 aliyun_mqtt.c2、aliyun_mqtt.h3、main.c八、设备上线九、手机APP获取温度数据1、数据流转2、数据流转实现十、代码调试注意事项及方法1、设备保活2、关于串口助手无法打印接收数据中含有"0x00"数据的解决办法3、通过wireshark网络抓包工具调试代码十一、说明

一、硬件及软件准备

完整工程源码下载地址:/download/qq_44062900/18392320

1、硬件:STM32单片机、ESP8266(ESP-12F)、DS18B20

注:ESP8266刷的固件是标准的AT指令固件,当然也可以刷MQTT透传固件,MQTT透传固件不需要我们实现MQTT协议,因为固件已经内置了MQTT协议,操作起来更加简单方便,但我们这里使用的是标准的AT指令固件,需要自己实现MQTT协议或移植MQTT协议库。有人可能会问为什么不直接刷MQTT固件实现连接阿里云呢?这里我们主要还是学习MQTT协议和MQTT协议的移植,这样的话如果设备不支持MQTT协议,但只要设备支持TCP协议就可以实现MQTT通信。

2、软件:Keil5、安信可串口调试助手、wireshark网络抓包工具、paho MQTT协议库

安信可串口调试助手下载:https://docs.ai-/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B72

wireshark下载地址:/download.html

paho MQTT协议库下载:/eclipse/paho.mqtt.embedded-c

二、 实现效果

1、 温度上传

2、手机APP实时查看温度数据

三、 MQTT协议

有关MQTT协议请查看官方中文PDF文档:/mqtt/protocol/MQTT-3.1.1-CN.pdf

四、 paho MQTT协议库移植

1、下载paho库

paho MQTT协议库下载:/eclipse/paho.mqtt.embedded-c

下载完成后解压如下:

2、移植文件

将目录“paho.mqtt.embedded-c-1.1.0\MQTTPacket\src”下的所有.c和.h移植到自己的工程文件夹中(可以只移植部分.c,因为库里面是包含服务器和客户端的实现代码,而我们只需要用到客户端部分的代码即可)

然后再移植"paho.mqtt.embedded-c-1.1.0\MQTTPacket\samples"目录下的"transport.c"和"transport.h"文件,这个两个文件是用于传输报文的,需要根据自己的硬件做相应的修改

3、添加进工程

注意还需添加相应的头文件路径

4、修改transport.c和transport.h

transport.c

#include "transport.h"#include "usart.h"#include "string.h"#include "led.h"#include "delay.h"void USART3_Send_Buff(unsigned char* buf, int buflen){while(buflen--)//字符串是否发送完成{USART3->DR = *buf;//发送一个字符while(!(USART3->SR & (1<<7)));//等待发送完成buf++;}}int transport_sendPacketBuffer( unsigned char* buf, int buflen){USART3_RX_FLAG=0;USART3_RX_CNT=0;memset((char *)USART3_RX_BUFF,0,USART_RX_MAXLEN);//清空接收缓冲区USART3_Send_Buff(buf,buflen);//发送数据return buflen;}int transport_getdata(unsigned char* buf, int count){u16 rx_cnt=0;static u16 read_count=0;while(!USART3_RX_FLAG && rx_cnt<=100)//等待接收响应数据报{Delay_Ms(10);rx_cnt++;}if(rx_cnt>100) return 1;//超时,未接收到数据rx_cnt=0;memcpy(buf,USART3_RX_BUFF+read_count,count);read_count+=count;//USART3_BUFF数据偏移if(read_count>=USART3_RX_CNT)//已经读完USART3_BUFF数据{USART3_RX_FLAG=0;USART3_RX_CNT=0;read_count=0;}return count;}int transport_getdatanb(void *sck, unsigned char* buf, int count){return 0;}int transport_open(char* addr, int port){return 0;}int transport_close(int sock){return 0;}

transport.h

#ifndef _TRANSPORT_H_#define_TRANSPORT_H_#include "stm32f10x.h"int transport_sendPacketBuffer(unsigned char* buf, int buflen);int transport_getdata(unsigned char* buf, int count);int transport_getdatanb(void *sck, unsigned char* buf, int count);int transport_open(char* host, int port);int transport_close(int sock);#endif

5、编译查看工程是否存在错误

五、 阿里云物联网产品及设备创建

登录和创建物联网平台请查看官方文档,下面直接从控制台着手,控制台地址:/login/login.htm?oauth_callback=https%3A%2F%2Fiot.%2F%3Fspm%linkwan.J_8058803260.641.21022d3aDlclYO&lang=en

登录后选择“公共示例”

点击“创建产品”

输入产品信息,点击“确认”即可创建产品

点击"前往定义物模型"

点击“编辑草稿”为设备添加物模型

点击“发布上线”

创建设备,该设备为ESP8266

选择产品,输入设备名

点击“一键复制设备证书”以保存设备信息

后面激活设备会使用到这三个信息

因为我们需要手机获取温度信息,所以我们使用同样的方法创建另一个设备,然后保存设备信息。具体实现手机APP如何获取温度信息见后文。

六、ESP8266配置

由于MQTT协议是基于TCP传输层协议之上的应用层协议,所以在使用MQTT进行通信之前首先要和服务器建立TCP连接,这样我们才能通过MQTT协议连接到阿里云物联网平台。

配置总流程:测试ESP8266>>>配置WIFI工作模式为Station(终端)或AP+Station(接入点+终端)>>>重启模块>>>关闭回显>>>连接WIFI>>>设为透传模式>>>建立TCP连接>>>开启透传>>>使用MQTT协议进行通信

注:ESP-12F的固件是标准的AT指令,在给ESP8266发送指令时,要在指令最后加上"\r\n"结束符

1、测试ESP8266模块

指令:“AT\r\n”

测试成功则返回"OK"

2、配置WIFI工作模式

指令:“AT+CWMODE=1\r\n”

配置成功则返回"OK"

3、重启WIFI模块

指令:“AT+RST\r\n”

发送指令后需等待几秒钟,等ESP8266复位完成

4、关闭回显

指令:“ATE0\r\n”

配置成功返回"OK"

5、连接WIFI

指令:“AT+CWJAP=“ssid”,“pass”\r\n”

其中ssid为WIFI名称,pass为WIFI密码

配置成功返回"OK"

6、设为透传模式

指令:“AT+CIPMODE=1\r\n”

配置成功返回"OK"

7、建立TCP连接

指令:“AT+CIPSTART=“TCP”,“ip”,port\r\n”

其中ip为服务器ip,这里为a1LuysWJE8a.iot-as--

port为固定的1883

格式参考

配置成功返回"CONNECT OK "

注:当连接成功后必须10S中之内通过MQTT协议登录到阿里云物联网平台,否则服务器会主动断开。

6、开启透传

指令:“AT+CIPSEND\r\n”

当模块返回">“时,此时进入透传模式,发送的所有数据都被视为是数据,当单独发送一包数据中含有”+++"时,模块会退出透传模式

通过以上步骤后,此时可以通过MQTT协议和服务器进行通信

七、核心代码

1、 aliyun_mqtt.c

#include "aliyun_mqtt.h"#include "MQTTPacket.h"#include "transport.h"#include "string.h"#include "usart.h"#include "delay.h"#include "led.h"u8 mqtt_buf[MQTT_MAX_BUFF];u16 rx_cnt=0;u8 MQTT_Connect(char *client_id,char *user_name,char *password,u16 keep_alive){MQTTPacket_connectData data = MQTTPacket_connectData_initializer;data.clientID.cstring =client_id;//客户端IDdata.keepAliveInterval = keep_alive;//保活时间data.cleansession = 1;//清除会话data.username.cstring = user_name;//用户名data.password.cstring = password;//密码u16 send_len=MQTTSerialize_connect(mqtt_buf,MQTT_MAX_BUFF,&data);//序列化连接数据报transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送连接数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);if (MQTTPacket_read(mqtt_buf, MQTT_MAX_BUFF, transport_getdata) == CONNACK){unsigned char sessionPresent, connack_rc;if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, mqtt_buf, MQTT_MAX_BUFF) != 1 || connack_rc != 0)//反序列化CONNACK报文{return 1;//接收报文类型正确,但连接失败}}else return 1;//接收报文类型错误return 0;}void MQTT_Disconnect(void){u16 send_len=MQTTSerialize_disconnect(mqtt_buf, MQTT_MAX_BUFF);transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送断开连接数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);send_len=MQTTSerialize_disconnect(mqtt_buf, MQTT_MAX_BUFF);transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送断开连接数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);}u8 MQTT_Subscribe_Topic(char *sub_topic,int req_qos,int msgid){MQTTString topicString = MQTTString_initializer;topicString.cstring = sub_topic;u16 send_len = MQTTSerialize_subscribe(mqtt_buf, MQTT_MAX_BUFF, 0, msgid, 1, &topicString, &req_qos);//序列化订阅数据报transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送订阅数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);if(MQTTPacket_read(mqtt_buf,MQTT_MAX_BUFF, transport_getdata) == SUBACK)//判断是否是SUBACK报文{unsigned short submsgid;int subcount;int granted_qos;u8 rc=MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, mqtt_buf, MQTT_MAX_BUFF);//反序列化SUBACK报文if(!rc || submsgid!=msgid || granted_qos==0x80)return 1;接收报文类型正确,但订阅失败}else return 1;//接收报文类型错误return 0;}u8 MQTT_Unsubscribe_Topic(char *unsub_topic,int msgid){MQTTString topicString = MQTTString_initializer;topicString.cstring = unsub_topic;u16 send_len = MQTTSerialize_unsubscribe(mqtt_buf,MQTT_MAX_BUFF, 0, msgid,1, &topicString);//序列化取消订阅数据报transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送取消订阅数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);if(MQTTPacket_read(mqtt_buf,MQTT_MAX_BUFF, transport_getdata) == UNSUBACK)//判断是否是UNSUBACK报文{unsigned short unsubmsgid;u8 rc=MQTTDeserialize_unsuback(&unsubmsgid,mqtt_buf,MQTT_MAX_BUFF);//反序列化UNSUBACK报文if(!rc || unsubmsgid!=msgid )return 1;接收报文类型正确,但取消订阅失败}else return 1;//接收报文类型错误return 0;}u8 MQTT_Publish(char *pub_topic, unsigned char* payload){MQTTString topicString = MQTTString_initializer;topicString.cstring = pub_topic;u16 send_len = MQTTSerialize_publish(mqtt_buf,MQTT_MAX_BUFF,0,0,0,0,topicString, payload, strlen((const char*)payload));//序列化发布数据报transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送发布数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);return 0;}u8 MQTT_Heart_Send(void){u16 send_len=MQTTSerialize_pingreq(mqtt_buf,MQTT_MAX_BUFF);transport_sendPacketBuffer(mqtt_buf,send_len);//向服务器发送PING数据报memset(mqtt_buf,0,MQTT_MAX_BUFF);if(MQTTPacket_read(mqtt_buf,MQTT_MAX_BUFF, transport_getdata) != PINGRESP)//判断是否是PINGRESP报文{return 1;//接收报文类型错误}return 0;}

2、aliyun_mqtt.h

#ifndef _ALIYUN_MQTT_H_#define_ALIYUN_MQTT_H_#include "stm32f10x.h"#define Aliyun_Client_ID "ESP8266|securemode=3,signmethod=hmacsha1|"#define Aliyun_UserName"ESP8266&a1LuysWJE8a"#define Aliyun_password"853545d20890aa6a55a80a7cccb3d2d41c46dcb9"#define Aliyun_Pub_Topic "/sys/a1LuysWJE8a/ESP8266/thing/event/property/post"#define Aliyun_Sub_Topic "/sys/a1LuysWJE8a/ESP8266/thing/service/property/set"#define Aliyun_Server_IP "a1LuysWJE8a.iot-as--"#define Aliyun_Server_Port 1883#define MQTT_MAX_BUFF 512enum REQ_QoS{REQ_QoS_0=0,REQ_QoS_1,REQ_QoS_2};u8 MQTT_Connect(char *client_id,char *user_name,char *password,u16 keep_alive);void MQTT_Disconnect(void);u8 MQTT_Subscribe_Topic(char *topic,int req_qos,int msgid);u8 MQTT_Unsubscribe_Topic(char *topic,int msgid); u8 MQTT_Publish(char *pub_topic, unsigned char* payload);u8 MQTT_Heart_Send(void);#endif

3、main.c

#include "stm32f10x.h"#include "delay.h"#include "led.h"#include "beep.h"#include "key.h"#include "usart.h"#include <string.h>#include "timer.h"#include "esp8266.h"#include <stdlib.h>#include "oled.h"#include "rtc.h"#include "ds18b20.h"#include "aliyun_mqtt.h"#include <stdio.h>#include <string.h>#include <stdlib.h>#include "usart.h"char Pub_buff[512];u16 cnt=0;RTC_Time SetTime;int main(void){u8 key=0;Delay_Ms(1000);BEEP_Init();//蜂鸣器初始化LED_Init();//LED初始化KEY_Init();//按键初始化USARTx_Init(USART1,72,115200);//串口1初始化USARTx_Init(USART3,36,115200);//串口3初始化TIMERx_Init(TIM2,72,2000*10);//设置分频系数为72,定时10ms.分频系数7200-->100us,72-->1usTIMERx_Init(TIM4,72,2000*10);//设置分频系数为72,定时10ms.分频系数7200-->100us,72-->1usDS18B20_Init();ESP_UnvarnishedMode_Init("test123","12344321",Aliyun_Server_IP,Aliyun_Server_Port);if(MQTT_Connect(Aliyun_Client_ID,Aliyun_UserName,Aliyun_password,300))printf("connect failure\n");else printf("connect success\n");while(1){Delay_Ms(10);cnt++;if(cnt==300){snprintf(Pub_buff,sizeof(Pub_buff),"{\"method\":\"thing.event.property.post\",\"id\":\"0000000002\",\"params\":{\"CurrentTemperature\":%0.2f},\"version\":\"1.0.0\"}",DS18B20_Read_Temp()*0.0625);MQTT_Publish(Aliyun_Pub_Topic,(unsigned char*)Pub_buff);cnt=0;}key=KEY_GetVal();if(key==2){if(MQTT_Subscribe_Topic(Aliyun_Sub_Topic,REQ_QoS_0,1)){printf("subscribe failure\n");}else printf("subscribe success\n");}else if(key==3){if(MQTT_Unsubscribe_Topic(Aliyun_Sub_Topic,1)){printf("unsubscribe failure\n");}else printf("unsubscribe success\n");}else if(key==4){snprintf(Pub_buff,sizeof(Pub_buff),"{\"method\":\"thing.event.property.post\",\"id\":\"0000000002\",\"params\":{\"CurrentTemperature\":%0.2f},\"version\":\"1.0.0\"}",DS18B20_Read_Temp()*0.0625);if(MQTT_Publish(Aliyun_Pub_Topic,(unsigned char*)Pub_buff)){printf("publish failure\n");}else printf("publish success\n");}else if(key==5){if(MQTT_Heart_Send()){printf("ping failure\n");}else printf("ping success\n");}}}

注意:需要在aliyun_mqtt.h中替换Aliyun_Client_ID、Aliyun_UserName、Aliyun_password三个宏以及订阅和发布的Topic

替换aliyun_mqtt.h中的Aliyun_Client_ID宏的值,格式如下 替换aliyun_mqtt.h中的Aliyun_UserName宏的值,格式如下 替换aliyun_mqtt.h中的Aliyun_password宏的值,格式如下

clientId{DeviceName}deviceName{DeviceName}productKey{ProductKey}

其中{DeviceName}、{ProductKey}为创建设备时生成的三元组信息

{DeviceName}为设备名,{ProductKey}为产品秘钥

最终的Aliyun_password的值还需将上述文本进行一次hmacsha1加密

在线加密网站:/

加密的密钥为设备秘钥: {DeviceSecret}

加密后的密码如下

复制加密后的密码到工程

在这里也可以使用官方的生成工具:阿里云MQTT Password生成工具,实测有效。

订阅和发布的Topic查找

替换aliyun_mqtt.h中对应的Topic

八、设备上线

烧录代码后可以在控制台中看到设备已经上线

点击“查看”,再点击“物模型数据”即可看到ESP8266上传的温度数据

九、手机APP获取温度数据

手机APP获取温度数据的方式是通过物联网平台中的数据流转功能,手机APP中也是采用MQTT协议连接到阿里云物联网平台的

1、数据流转

数据流转是指当前有两个设备,需要实现一个设备(手机)和另一个设备(ESP8266)之间的数据共享。则手机需要订阅“/sys/a1LuysWJE8a/Android /thing/service/property/set”这个主题,而ESP8266将温度数据发布到“/sys/a1LuysWJE8a/ESP8266/thing/event/property/post”这个主题下,然后通过数据流转功能转将ESP8266发布到主题的数据流转到手机订阅的Topic下,这样就可以实现手机获取ESP8266上传的温度数据。

2、数据流转实现

首先打开物联网控制台,操作如下

填写需要流转的数据,这里进行一下过滤,这里只要是物模型上报的数据都会被流转,而我们只需要温度物模型数据。或者不进行过滤,在手机端进行数据解析时抛弃不需要的数据即可。

添加要流转的操作

设置如下

点击确定后返回主界面

启动数据流转规则

启动成功后如下

手机客户端接收流转数据

十、代码调试注意事项及方法

1、设备保活

在MQTT协议中,在连接MQTT服务器时会设置一个保活时间,如果在这个保活时间内客户端没有给MQTT服务器发送数据,则MQTT服务器会主动断开连接,所以在给定的保活时间内必须发送至少一条消息到MQTT服务器,以保证持续连接。阿里云官方给出的文档中保活时间的取值范围为30~1200s,如果保活时间不在这个范围内,则MQTT服务器会拒绝连接,这个非常重要,保活时间必须在这个范围内。

2、关于串口助手无法打印接收数据中含有"0x00"数据的解决办法

在串口接收MQTT服务器返回的响应报文中会含有"0x00"的数据,而由于‘\0’对应的ASCII值为0x00,故不能使用字符串辅助函数(如printf()、strlen())对接收的数据进行处理,因为这些函数都是检测到’\0’时会结束操作,所以会造成’0x00’后面的数据都无法打印出来

建议采用wireshark抓包查看原始数据进行代码调试

如下图所示:

例如:服务器返回SUBACK报文时,通过wireshark抓包工具查看到MQTT服务器返回的SUBACK报文数据是"0x90 0x03 0x00 0x01 0x01"

而通过printf(“%s”)方法打印mqtt_buff里面数据时不能完全打印出来

如果需要打印出“0x00”及之后的数据,则可以使用以下方法打印

for(u8 i=0;i<rec_len;i++){printf("%c",mqtt_buff[i]);}

此时可以打印出服务器返回的完整数据报文

3、通过wireshark网络抓包工具调试代码

通过网络抓包工具可以很清楚的看到客户端和服务器通过MQTT通信的数据报,比串口调试助手更加可靠直观,也便于学习MQTT协议。

这里我是用笔记本开热点,然后ESP8266连接这个热点,这样我们就可以抓取数据报了

使用方法:

双击活动的网卡

在过滤规则栏中输入"mqtt",即可过滤出使用MQTT协议通信的数据报

这里可以看到客户端向MQTT服务器发送的连接报文

服务器返回的CONNACK报文

十一、说明

说明:所发布工程只完成了基本的MQTT通信部分,可能会存在一些bug,如有发现可以评论或私信告诉我,其他应用功能还需要自己根据实际需要进行更改。

如教程中存在错误,欢迎在评论区指正!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。