STM32+ESP8266移植paho MQTT协议连接阿里云物联网平台

Posted Fog.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32+ESP8266移植paho MQTT协议连接阿里云物联网平台相关的知识,希望对你有一定的参考价值。

STM32+ESP8266移植paho MQTT协议库连接阿里云物联网平台

一、硬件及软件准备

1、硬件:STM32F103C8T6、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-thinker.com/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B72
wireshark下载地址:https://www.wireshark.org/download.html
paho MQTT协议库下载:https://github.com/eclipse/paho.mqtt.embedded-c

完整工程源码下载地址:https://download.csdn.net/download/qq_44062900/18392320

二、 实现效果

1、 温度上传

在这里插入图片描述

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

在这里插入图片描述

三、 MQTT协议

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

四、 paho MQTT协议库移植

1、下载paho库

paho MQTT协议库下载:https://github.com/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、编译查看工程是否存在错误

在这里插入图片描述

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

登录和创建物联网平台请查看官方文档,下面直接从控制台着手,控制台地址:https://account.aliyun.com/login/login.htm?oauth_callback=https%3A%2F%2Fiot.console.aliyun.com%2F%3Fspm%3D5176.cnlinkwan.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-mqtt.cn-shanghai.aliyuncs.com
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;//客户端ID
	data.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-mqtt.cn-shanghai.aliyuncs.com"
#define Aliyun_Server_Port 1883

#define MQTT_MAX_BUFF 512

enum 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-->1us
	TIMERx_Init(TIM4,72,2000*10);//设置分频系数为72,定时10ms.分频系数7200-->100us,72-->1us
	DS18B20_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(key3-STM32+ESP8266连接onenet上传数据+远程控制(MQTT)

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

13-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案微信小程序篇(网页版MQTT)

14-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案升级篇(AT指令版升级+用户程序加入MQTT支持)

stm32f373利用esp8266连接onenet平台