近期在做STM32之MQTT客户端连接到服务器。野火家的教程是移植别人家的MQTT客户端代码,实际LwIP源码中就包含这个,但是这个在网上很少能找到,所以我就先抛砖引玉了,有问题可以联系我哦。
MQTT介绍这里就不在说了,主要说实现方法。
这个前提是你的准备一个LwIP协议栈TCP可以正确运行起来的工程。以下说明中LwIP版本均为2.1.2,其他版本没有查看和测试,环境为keil5.34 AC6 主控为STM32F407VE。
这里先贴一个连接效果图
这里再贴一个订阅的效果图
这里再贴一个发布的效果图
观察源码会发现,LwIP源码目录里是有关于MQTT客户端相关的API,但是几乎没有相关例程。源码位置如下图所示
相关头文件的位置如下图所示
首先要在你的工程里包含这个源文件,如下图所示
然后添加头文件路径,正常情况是不需要关注的(仅供参考),如下图所示
对应的是源文件的头文件路径是这个(这里只是参考)
接下来就是实际程序编写了
#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核心的风格不一样,所以一开始会绝的比较生涩,弄完了之后其实也不难。