近期在做STM32之MQTT客户端连接到服务器。野火家的教程是移植别人家的MQTT客户端代码,实际LwIP源码中就包含这个,但是这个在网上很少能找到,所以我就先抛砖引玉了,有问题可以联系我哦。

MQTT介绍这里就不在说了,主要说实现方法。

这个前提是你的准备一个LwIP协议栈TCP可以正确运行起来的工程。以下说明中LwIP版本均为2.1.2,其他版本没有查看和测试,环境为keil5.34 AC6 主控为STM32F407VE。

这里先贴一个连接效果图

STM32向云服务器传输数据 stm32连接服务器_mqtt

这里再贴一个订阅的效果图

STM32向云服务器传输数据 stm32连接服务器_协议栈_02


这里再贴一个发布的效果图

STM32向云服务器传输数据 stm32连接服务器_mqtt_03


观察源码会发现,LwIP源码目录里是有关于MQTT客户端相关的API,但是几乎没有相关例程。源码位置如下图所示

STM32向云服务器传输数据 stm32连接服务器_mqtt_04


相关头文件的位置如下图所示

STM32向云服务器传输数据 stm32连接服务器_协议栈_05


首先要在你的工程里包含这个源文件,如下图所示

STM32向云服务器传输数据 stm32连接服务器_STM32向云服务器传输数据_06


然后添加头文件路径,正常情况是不需要关注的(仅供参考),如下图所示

STM32向云服务器传输数据 stm32连接服务器_STM32向云服务器传输数据_07


对应的是源文件的头文件路径是这个(这里只是参考)

STM32向云服务器传输数据 stm32连接服务器_协议栈_08


接下来就是实际程序编写了

#include "lwip/apps/mqtt.h"
#include "string.h"
#include "SystemLed.h"/*这个是私有的,和本次样例无关*/

void Cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
void cb(void *arg, err_t err);
void mipcb(void *arg, const char *topic, u32_t tot_len);
void midcb(void *arg, const u8_t *data, u16_t len, u8_t flags);

char ClientId[] = "LwIPMqttId-1";
char ClientUser[] = "LwIPMqttUser-1";
char ClientPass[] = "LwIPMqttPass-1";
char topic[] = "LwIP-TopicTest";//发布主题
char payload[] = "This is a test Info!";//发布主题的消息
char SubscribeTopic[] = "Test1";//需要订阅的主题

void MqttTestTask(void *pvParameter)
{
	while(1)
	{
		//创建一个MQTT客户端
		mqtt_client_t* MqttClient = mqtt_client_new();
		//准备MQTT服务器地址
		ip4_addr_t MqttServer;
		IP4_ADDR(&MqttServer,192,168,1,214);
		//新建一个客户端连接到服务器需要的信息块,以及填充相应的信息
		//结构体参数分别是 客户端ID 用户名 用户密码 心跳时间 遗愿主题 遗愿消息 遗愿消息等级 有无遗愿
		struct mqtt_connect_client_info_t Ci = {ClientId,ClientUser,ClientPass,10,NULL,NULL,0,0};
		//连接到服务器方法 参数分别是:客户端 服务器地址 服务器端口 连接状态变化回调 回调的参数 客户端信息
		mqtt_client_connect(MqttClient,&MqttServer,1883,Cb,NULL,&Ci);
		//这里只为测试,因为是RTOS需要调度,要等协议栈连接 上边的连接服务器方法不会阻塞 所以添加延时
		vTaskDelay(1000);
		//注册订阅信息后 收到数据的回调 参数分别是:客户端 订阅主题的回调 数据回调 回调的参数
		mqtt_set_inpub_callback(MqttClient,mipcb,midcb,NULL);
		//订阅主体 参数分别是:客户端 订阅的主题 主题的消息等级 订阅结果回调 回调参数
		mqtt_subscribe(MqttClient,SubscribeTopic,0,cb,"subscribe");
		//查询是否连接到服务器 参数:客户端
		while(mqtt_client_is_connected(MqttClient))
		{
			vTaskDelay(1000);
			//发布消息主体 参数:客户端 发布的主题 主题的消息 消息的长度 消息等级 服务器是否保留 发布结果回调 回调参数
			mqtt_publish(MqttClient,topic,payload,strlen(payload),0,0,cb,"publish");
		}
		//断开客户端到服务器的连接 参数客户端
		mqtt_disconnect(MqttClient);
		//释放创建的客户端所占内存
		mqtt_client_free(MqttClient);
		vTaskDelay(10);
	}
}

//客户端连接到服务状态  当状态发生变化时会进入这个回调
void Cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
	//源码中所有状态的例举
	switch((uint16_t)status)
	{
		case MQTT_CONNECT_ACCEPTED:;
		case MQTT_CONNECT_REFUSED_PROTOCOL_VERSION:;
		case MQTT_CONNECT_REFUSED_IDENTIFIER:;
		case MQTT_CONNECT_REFUSED_SERVER:;
		case MQTT_CONNECT_REFUSED_USERNAME_PASS:;
		case MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_:;
		case MQTT_CONNECT_DISCONNECTED:;
		case MQTT_CONNECT_TIMEOUT:;
	}
}

 //订阅或者发布时的结果回调 arg是注册时给的参数 err是状态 通LwIP的状态
void cb(void *arg, err_t err)
{
	char* p = arg;
	if(strcmp(p,"publish") == 0)
		return;
	else if(strcmp(p,"subscribe") == 0)
		return;
}

//订阅主体后 客户端收到服务端推送的主题回调
void mipcb(void *arg, const char *topic, u32_t tot_len)
{
	Led2Flash();
	return;
}

//订阅主体后 客户端收到服务端推送的主题的消息回调
void midcb(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
	return;
}

代码量也不多,也比较容易实现,但给我的感觉是和LwIP核心的风格不一样,所以一开始会绝的比较生涩,弄完了之后其实也不难。