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);
運行後可以收到主題消息: