STM32+LWIP协议栈实现MQTT协议并挂载到EMQ_X_CLOUD平台
引言
在前几篇文章中,我主要用LWIP协议栈实现了httpd服务器,及其一些应用。本篇我们来实现另外一种在物联网中应用非常广泛的MQTT协议。MQTT协议的定义我就不讲了,主要记住MQTT协议是一种订阅/发布式的协议。
云平台选择
要想将我们的设备使用MQTT协议挂载到云平台上,第一步我们需要选择合适的云平台,阿里云,腾讯云都可以,在这里我选择EMQ X Cloud云平台,这个云平台是我在知乎上看到的,现在新人免费试用14天。 进入控制面板后我们可以看到具体操作界面,进入监控选项,我们可以在这里看到我们的客户端连接。 这个平台的具体使用方法,我现在发现的主要有2个方式可以进行客户端连接。
1、在客户端连接指引中,下载MQTT X工具
在这个软件中,可以连接到我们的MQTT服务器。
2、在EMQ X Cloud云平台中,进行在线调试
这个是网页版的调试。
这两种方式都可以当做客户端连接。可以先用来测试云平台设置的对不对,能不能进行正常的订阅/发布。
代码编写
LWIP协议栈中带的有MQTT协议栈。我们直接用就可以,具体代码在mqtt.c文件中,这是一套较为完整的MQTT库,我们只需要编写mqtt客户端的C文件。 LWIP官方给了一个简单的样例,我们按照官方教程写一个mqtt客户端文件,命名为mqtt_client.c。 我写的带freeRTOS操作系统,不带操作系统的可以看这位大佬的文章STM32H743ZI+LWIP+MQTT(无OS)
#include <stdio.h>
#include <string.h>
#include "mqtt_client.h"
#include "mqtt.h"
#include "cmsis_os.h"
#define ipaddr_mqtt "120.79.215.239"
#define mqtt_usrname "897923957"
#define mqtt_password "XXXXX"
osThreadId_t mqttpub_TaskHandle;
const osThreadAttr_t defaultTask_attribute = {
.name = "defaultTask",
.priority = (osPriority_t) osPriorityNormal,
.stack_size = 128 * 4
};
void example_do_connect(mqtt_client_t *client);
static void mqtt_pub_request_cb(void *arg, err_t result)
{
if(result != ERR_OK) {
printf("Publish result: %d\n", result);
}
}
void example_publish(mqtt_client_t *client, void *arg)
{
const char *pub_payload= "PubSubHubLubJub";
err_t err;
u8_t qos = 2;
u8_t retain = 0;
err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
if(err != ERR_OK) {
printf("Publish err: %d\n", err);
}
}
static int inpub_id;
static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
{
printf("Incoming publish at topic %s with total length %u\n", topic, (unsigned int)tot_len);
if(strcmp(topic, "subtopic") == 0) {
inpub_id = 0;
} else if(topic[0] == 'A') {
inpub_id = 1;
} else {
inpub_id = 2;
}
}
static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
printf("Incoming publish payload with length %d, flags %u\n", len, (unsigned int)flags);
if(flags & MQTT_DATA_FLAG_LAST) {
if(inpub_id == 0) {
printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
} else if(inpub_id == 1) {
} else {
printf("mqtt_incoming_data_cb: Ignoring payload...\n");
}
} else {
}
}
static void mqtt_sub_request_cb(void *arg, err_t result)
{
printf("Subscribe result: %d\n", result);
}
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
err_t err;
if(status == MQTT_CONNECT_ACCEPTED) {
printf("mqtt_connection_cb: Successfully connected\n");
mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
err = mqtt_subscribe(client, "subtopic", 1, mqtt_sub_request_cb, arg);
if(err != ERR_OK) {
printf("mqtt_subscribe return: %d\n", err);
}
} else {
printf("mqtt_connection_cb: Disconnected, reason: %d\n", status);
example_do_connect(client);
}
}
void example_do_connect(mqtt_client_t *client)
{
struct mqtt_connect_client_info_t ci;
ip_addr_t ip_mqtt;
err_t err;
memset(&ci, 0, sizeof(ci));
ci.client_id = "lwip_test";
ci.client_user = mqtt_usrname;
ci.client_pass = mqtt_password;
IP4_ADDR(&ip_mqtt, 120, 79, 215, 239);
err = mqtt_client_connect(client, &ip_mqtt, MQTT_PORT, mqtt_connection_cb, 0, &ci);
printf("mqtt_connect return %d\n", err);
if(err != ERR_OK) {
printf("mqtt_connect return %d\n", err);
}
}
void mqtt_init(void)
{
mqtt_client_t *client = mqtt_client_new();
if(client != NULL) {
example_do_connect(client);
}
}
这只是一个简单的例子,用来测试连接服务器。在初始化的时候调用mqtt_init()函数就可以了。
实验结果
调用初始化函数后,我们可以看到连接已经建立,能够在云平台上看到,lwip_test就是我们的STM32单片机。 也能够正常接收到其他客户端发布的消息。 注意一下客户端发布需要在同样的主题下发布,在这里我用的都是subtopic这个主题。 到此为止,这个实验已经做完。
踩坑记录
在这个实验中踩坑主要有2个。
1、Assertion “sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty” failed at line 216 in src/core/timers.c错误
参考这位大佬的文章 解决方法 把CUBEMX这个选项改大一点,或者直接在代码中搜索这个宏的定义,自己改。默认好像是3,我改到了13.
2、程序没理由的死机
发现还是在上一篇文章中的问题一样,lwip栈设置的太小,在lwipopts.h中改大。
总结
这几天把MQTT协议做了一遍,做的比较简单,日后用的的话再深入研究,这最多算个demo,用来测试。下一个任务把OTA升级做一下,STM32有线网络这一块基本就结束了,有帮助的话多多点赞。
|