RT-Thread開發之路(4)— MQTT通信

RT-Thread開發之路(5)— MQTT通信

基於:使用at_device軟件包通過ESP8266連接到網絡

一、添加pahomqtt軟件包

打開【RT-Thread Settings】,搜索pahomqtt,然後點擊添加
在這裏插入圖片描述
然後保存使之生效。

二、編寫代碼,連接到服務器

首先,包含要用到的頭文件:

#include "paho_mqtt.h"

宏定義一些連接mqtt服務器需要的參數:

#define MQTT_Uri    "tcp://39.96.35.207:1883"   // MQTT服務器的地址和端口號
#define ClientId    "BearPi8266"                // ClientId需要唯一
#define UserName    "BearPi"                    // 用戶名
#define PassWord    "123456"                    // 用戶名對應的密碼

接下來定義一個mqtt客戶端結構體變量

/* 定義一個MQTT客戶端結構體 */
static MQTTClient client;

接下來對MQTT進行配置

    /* 對MQTT客戶端結構體變量進行配置 */
    client.isconnected = 0;
    client.uri = MQTT_Uri;

    /* 配置MQTT的連接參數 */
    MQTTPacket_connectData condata = MQTTPacket_connectData_initializer;
    memcpy(&client.condata, &condata, sizeof(condata));
    client.condata.clientID.cstring = ClientId;
    client.condata.keepAliveInterval = 30;
    client.condata.cleansession = 1;
    client.condata.username.cstring = (char*)USERNAME;
    client.condata.password.cstring = (char*)PASSWORD;

然後爲MQTT的消息緩存申請內存

    /* 爲mqtt申請內存 */
    client.buf_size = client.readbuf_size = 1024;
    client.buf = rt_calloc(1, client.buf_size);
    client.readbuf = rt_calloc(1, client.readbuf_size);
    if (!(client.buf && client.readbuf))
    {
        rt_kprintf("no memory for MQTT client buffer!\r\n");
        return -1;
    }

接下來我們設置下回調函數,以及訂閱一個主題,其中設置的默認的回調函數,是在如果有訂閱的 Topic 沒有設置回調函數時,則使用該默認回調函數

    /* 設置回調函數  */
    client.connect_callback = mqtt_connect_callback;
    client.online_callback = mqtt_online_callback;
    client.offline_callback = mqtt_offline_callback;

    /* 訂閱一個主題,並設置其回調函數 */
    client.messageHandlers[0].topicFilter = rt_strdup("BearPi_Sub");
    client.messageHandlers[0].callback = mqtt_sub_callback;
    client.messageHandlers[0].qos = QOS1;

    /* 設置默認的回調函數 */
    client.defaultMessageHandler = mqtt_sub_default_callback;

然後我們實現各個回調函數,並在上線後發佈一個主題消息

/* 收到訂閱的"Bear_Pi"主題的消息時的回調函數*/
static void mqtt_sub_callback(MQTTClient *c, MessageData *msg_data)
{
    *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0';
    rt_kprintf("Receive topic: %.*s, message data:\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);
    rt_kprintf("%.*s\r\n", msg_data->message->payloadlen, (char *)msg_data->message->payload);
}

/* 默認的訂閱回調函數,如果有訂閱的 Topic 沒有設置回調函數,則使用該默認回調函數 */
static void mqtt_sub_default_callback(MQTTClient *c, MessageData *msg_data)
{
    *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0';
    rt_kprintf("Receive topic: %.*s, message data:\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);
    rt_kprintf("%.*s\r\n", msg_data->message->payloadlen, (char *)msg_data->message->payload);
}

/* 連接成功回調函數 */
static void mqtt_connect_callback(MQTTClient *c)
{
    rt_kprintf("mqtt connect success! \r\n");
}

/* 上線回調函數 */
static void mqtt_online_callback(MQTTClient *c)
{
    rt_kprintf("mqtt online \r\n");
    paho_mqtt_publish(&client, QOS1, "BearPi_Pub", "Hello, I am BearPi with RT_Thread.");
}

都設置好後,就可以啓動MQTT連接了

    /* 啓動 mqtt client */
    paho_mqtt_start(&client);

編譯,下載,運行,打開EMQ的控制檯,可以看到,已經連接上了
在這裏插入圖片描述
另外我們也可以看到,已經收到了其發佈的主題消息
在這裏插入圖片描述
然後我們嘗試對其訂閱的主題發佈一個消息,可以看到,也成功接收到了
在這裏插入圖片描述

三、優化代碼

我們修改其程序,將其放入一個線程內,並將該線程的創建放入應用初始化中,並在這個線程中每隔一秒發佈一個主題消息。代碼如下:

#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>
#include <sys/socket.h>
#include "netdb.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "paho_mqtt.h"

#define MQTT_Uri    "tcp://39.96.35.207:1883"   // MQTT服務器的地址和端口號
#define ClientId    "BearPi8266"                // ClientId需要唯一
#define UserName    "BearPi"                    // 用戶名
#define PassWord    "123456"                    // 用戶名對應的密碼

/* 定義一個MQTT線程句柄結構體指針 */
static rt_thread_t app_mqtt_thread = RT_NULL;

/* 定義一個MQTT客戶端結構體 */
static MQTTClient client;

/* 收到訂閱的"Bear_Pi"主題的消息時的回調函數*/
static void mqtt_sub_callback(MQTTClient *c, MessageData *msg_data)
{
    *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0';
    rt_kprintf("Receive topic: %.*s, message data:\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);
    rt_kprintf("%.*s\r\n", msg_data->message->payloadlen, (char *)msg_data->message->payload);
}

/* 默認的訂閱回調函數,如果有訂閱的 Topic 沒有設置回調函數,則使用該默認回調函數 */
static void mqtt_sub_default_callback(MQTTClient *c, MessageData *msg_data)
{
    *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0';
    rt_kprintf("Receive topic: %.*s, message data:\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);
    rt_kprintf("%.*s\r\n", msg_data->message->payloadlen, (char *)msg_data->message->payload);
}

/* 連接成功回調函數 */
static void mqtt_connect_callback(MQTTClient *c)
{
    rt_kprintf("mqtt connect success! \r\n");
}

/* 上線回調函數 */
static void mqtt_online_callback(MQTTClient *c)
{
    rt_kprintf("mqtt online \r\n");
    paho_mqtt_publish(&client, QOS1, "BearPi_Pub", "Hello, I am BearPi with RT_Thread.");
}

/* 下線回調函數 */
static void mqtt_offline_callback(MQTTClient *c)
{
    rt_kprintf("mqtt offline \r\n");
}

static void app_mqtt_thread_entry(void *parameter)
{
    int count = 1;
    char msg_buf[128];
    /* 對MQTT客戶端結構體變量進行配置 */
    client.isconnected = 0;
    client.uri = MQTT_Uri;

    /* 配置MQTT的連接參數 */
    MQTTPacket_connectData condata = MQTTPacket_connectData_initializer;
    memcpy(&client.condata, &condata, sizeof(condata));
    client.condata.clientID.cstring = ClientId;
    client.condata.keepAliveInterval = 30;
    client.condata.cleansession = 1;
    client.condata.username.cstring = UserName;
    client.condata.password.cstring = PassWord;

    /* 爲mqtt申請內存 */
    client.buf_size = client.readbuf_size = 1024;
    client.buf = rt_calloc(1, client.buf_size);
    client.readbuf = rt_calloc(1, client.readbuf_size);
    if (!(client.buf && client.readbuf))
    {
        rt_kprintf("no memory for MQTT client buffer!\r\n");
        return ;
    }

    /* 設置回調函數  */
    client.connect_callback = mqtt_connect_callback;
    client.online_callback = mqtt_online_callback;
    client.offline_callback = mqtt_offline_callback;

    /* 訂閱一個主題,並設置其回調函數 */
    client.messageHandlers[0].topicFilter = rt_strdup("BearPi_Sub");
    client.messageHandlers[0].callback = mqtt_sub_callback;
    client.messageHandlers[0].qos = QOS1;

    /* 設置默認的回調函數 */
    client.defaultMessageHandler = mqtt_sub_default_callback;

    /* 啓動 mqtt client */
    paho_mqtt_start(&client);

    while(1)
    {
        rt_sprintf(msg_buf, "publish %d times.", count++);
        paho_mqtt_publish(&client, QOS1, "BearPi_Pub", msg_buf);
        rt_thread_mdelay(1000);
    }
}

static int app_mqtt_init(void)
{
    rt_err_t rt_err;
    /* 創建MQTT線程*/
    app_mqtt_thread = rt_thread_create("app_mqtt thread",
            app_mqtt_thread_entry, RT_NULL, 2048, 6, 10);
    /* 如果獲得線程控制塊,啓動這個線程 */
    if (app_mqtt_thread != RT_NULL)
        rt_err = rt_thread_startup(app_mqtt_thread);
    else
        rt_kprintf("app_mqtt_thread create failure !!! \n");

    /* 判斷線程是否啓動成功 */
    if( rt_err == RT_EOK)
        rt_kprintf("app_mqtt_thread startup ok. \n");
    else
        rt_kprintf("app_mqtt_thread startup err. \n");

    return rt_err;
}

INIT_APP_EXPORT(app_mqtt_init);

運行後可以收到主題消息:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章