0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

通過(guò)paho-mqtt軟件包入門rt-thread的sal

冬至子 ? 來(lái)源:happycode999 ? 作者:happycode999 ? 2023-08-09 15:37 ? 次閱讀

一、paho-mqtt軟件包程序流程

1.1 paho_mqtt_start
在rt_wlan_register_event_handler函數(shù)注冊(cè)好RT_WLAN_EVT_READY的回調(diào)函數(shù)paho_mqtt_start,當(dāng)wifi準(zhǔn)備好后調(diào)用mq_start啟動(dòng)mqtt。在mq_start中,初始化MQTTClient結(jié)構(gòu)體,設(shè)置mqtt連接的參數(shù):mqtt的uri、mqtt的用戶名(username)和密碼(password)、mqtt發(fā)布和訂閱的主題Topic、消息質(zhì)量等級(jí)QoS,最后調(diào)用paho_mqtt_start創(chuàng)建處理mqtt的線程paho_mqtt_thread。

static void mq_start(void)
{
/* init condata param by using MQTTPacket_connectData_initializer /
MQTTPacket_connectData condata = MQTTPacket_connectData_initializer;
static char cid[20] = { 0 };
static int is_started = 0;
if (is_started)
{
return;
}
/
config MQTT context param /
{
client.isconnected = 0;
client.uri = MQTT_URI;
/
generate the random client ID /
rt_snprintf(cid, sizeof(cid), "rtthread%d", rt_tick_get());
/
config connect param /
memcpy(&client.condata, &condata, sizeof(condata));
client.condata.clientID.cstring = cid;
client.condata.keepAliveInterval = 60;
client.condata.cleansession = 1;
client.condata.username.cstring = MQTT_USERNAME;
client.condata.password.cstring = MQTT_PASSWORD;
/
config MQTT will param. /
client.condata.willFlag = 1;
client.condata.will.qos = 1;
client.condata.will.retained = 0;
client.condata.will.topicName.cstring = MQTT_PUBTOPIC;
client.condata.will.message.cstring = MQTT_WILLMSG;
/
malloc buffer. /
client.buf_size = client.readbuf_size = 1024;
client.buf = malloc(client.buf_size);
client.readbuf = malloc(client.readbuf_size);
if (!(client.buf && client.readbuf))
{
LOG_E("no memory for MQTT client buffer!");
goto _exit;
}
/
set event callback function /
client.connect_callback = mqtt_connect_callback;
client.online_callback = mqtt_online_callback;
client.offline_callback = mqtt_offline_callback;
/
set subscribe table and event callback /
client.messageHandlers[0].topicFilter = MQTT_SUBTOPIC;
client.messageHandlers[0].callback = mqtt_sub_callback;
client.messageHandlers[0].qos = QOS1;
/
set default subscribe event callback /
client.defaultMessageHandler = mqtt_sub_default_callback;
}
/
run mqtt client /
paho_mqtt_start(&client);
is_started = 1;
_exit:
return;
}
rt_wlan_register_event_handler(RT_WLAN_EVT_READY, (void (
)(int, struct rt_wlan_buff *, void *))mq_start, RT_NULL);

1.2 paho_mqtt_thread
在paho_mqtt_thread中調(diào)用paho-mqtt提供的接口rt-thread的sal的接口完成與mqtt服務(wù)器的交互,包括以下幾個(gè)方面:與服務(wù)器的連接、訂閱主題、向服務(wù)器發(fā)送心跳包、處理服務(wù)器發(fā)送下來(lái)的消息(CONNACK、PUBACK、SUBACK、PUBLISH、PUBREC、PUBCOMP、PINGRESP)、回環(huán)服務(wù)器通過(guò)topic發(fā)送下來(lái)的消息。

static void paho_mqtt_thread(void *param)
{
MQTTClient *c = (MQTTClient )param;
int i, rc, len;
int rc_t = 0;
c->pub_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (c->pub_sock == -1)
{
debug_printf("create pub_sock error!n");
goto _mqtt_exit;
}
/
bind publish socket. */
{
struct sockaddr_in pub_server_addr;
c->pub_port = pub_port;
pub_port ++;
pub_server_addr.sin_family = AF_INET;
pub_server_addr.sin_port = htons((c->pub_port));
pub_server_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(pub_server_addr.sin_zero), 0, sizeof(pub_server_addr.sin_zero));
rc = bind(c->pub_sock, (struct sockaddr *)&pub_server_addr, sizeof(struct sockaddr));
if (rc == -1)
{
debug_printf("pub_sock bind error!n");
goto _mqtt_exit;
}
}
_mqtt_start:
if (c->connect_callback)
{
c->connect_callback(c);
}
rc = net_connect(c);
if (rc != 0)
{
goto _mqtt_restart;
}
rc = MQTTConnect(c);
if (rc != 0)
{
goto _mqtt_restart;
}
for (i = 0; i < MAX_MESSAGE_HANDLERS; i++)
{
const char topic = c->messageHandlers[i].topicFilter;
if(topic == RT_NULL)
continue;
rc = MQTTSubscribe(c, topic, QOS2);
debug_printf("Subscribe #%d %s %s!n", i, topic, (rc < 0) ? ("fail") : ("OK"));
if (rc != 0)
{
goto _mqtt_disconnect;
}
}
if (c->online_callback)
{
c->online_callback(c);
}
c->tick_ping = rt_tick_get();
while (1)
{
int res;
rt_tick_t tick_now;
fd_set readset;
struct timeval timeout;
tick_now = rt_tick_get();
if (((tick_now - c->tick_ping) / RT_TICK_PER_SECOND) > (c->keepAliveInterval - 5))
{
timeout.tv_sec = 1;
//debug_printf("tick close to ping.n");
}
else
{
timeout.tv_sec = c->keepAliveInterval - 10 - (tick_now - c->tick_ping) / RT_TICK_PER_SECOND;
//debug_printf("timeount for ping: %dn", timeout.tv_sec);
}
timeout.tv_usec = 0;
FD_ZERO(&readset);
FD_SET(c->sock, &readset);
FD_SET(c->pub_sock, &readset);
/
int select(maxfdp1, readset, writeset, exceptset, timeout); /
res = select(((c->pub_sock > c->sock) ? c->pub_sock : c->sock) + 1,
&readset, RT_NULL, RT_NULL, &timeout);
if (res == 0)
{
len = MQTTSerialize_pingreq(c->buf, c->buf_size);
rc = sendPacket(c, len);
if (rc != 0)
{
debug_printf("[%d] send ping rc: %d n", rt_tick_get(), rc);
goto _mqtt_disconnect;
}
/
wait Ping Response. /
timeout.tv_sec = 5;
timeout.tv_usec = 0;
FD_ZERO(&readset);
FD_SET(c->sock, &readset);
res = select(c->sock + 1, &readset, RT_NULL, RT_NULL, &timeout);
if (res <= 0)
{
debug_printf("[%d] wait Ping Response res: %dn", rt_tick_get(), res);
goto _mqtt_disconnect;
}
} /
res == 0: timeount for ping. */
if (res < 0)
{
debug_printf("select res: %dn", res);
goto _mqtt_disconnect;
}
if (FD_ISSET(c->sock, &readset))
{
//debug_printf("sock FD_ISSETn");
rc_t = MQTT_cycle(c);
//debug_printf("sock FD_ISSET rc_t : %dn", rc_t);
if (rc_t < 0) goto _mqtt_disconnect;
continue;
}
if (FD_ISSET(c->pub_sock, &readset))
{
struct sockaddr_in pub_client_addr;
uint32_t addr_len = sizeof(struct sockaddr);
MQTTMessage *message;
MQTTString topic = MQTTString_initializer;
//debug_printf("pub_sock FD_ISSETn");
len = recvfrom(c->pub_sock, c->readbuf, c->readbuf_size, MSG_DONTWAIT,
(struct sockaddr *)&pub_client_addr, &addr_len);
if (pub_client_addr.sin_addr.s_addr != *((uint32_t )(&netif_default->ip_addr)))
{
#if 1
char client_ip_str[16]; /
###.###.###.### */
strcpy(client_ip_str,
inet_ntoa(*((struct in_addr *) & (pub_client_addr.sin_addr))));
debug_printf("pub_sock recvfrom len: %s, skip!n", client_ip_str);
#endif
continue;
}
if (len < sizeof(MQTTMessage))
{
c->readbuf[len] = '?';
debug_printf("pub_sock recv %d byte: %sn", len, c->readbuf);
if (strcmp((const char *)c->readbuf, "DISCONNECT") == 0)
{
debug_printf("DISCONNECTn");
goto _mqtt_disconnect_exit;
}
continue;
}
message = (MQTTMessage *)c->readbuf;
message->payload = c->readbuf + sizeof(MQTTMessage);
topic.cstring = (char *)c->readbuf + sizeof(MQTTMessage) + message->payloadlen;
//debug_printf("pub_sock topic:%s, payloadlen:%dn", topic.cstring, message->payloadlen);
len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id,
topic, (unsigned char *)message->payload, message->payloadlen);
if (len <= 0)
{
debug_printf("MQTTSerialize_publish len: %dn", len);
goto _mqtt_disconnect;
}
if ((rc = sendPacket(c, len)) != PAHO_SUCCESS) // send the subscribe packet
{
debug_printf("MQTTSerialize_publish sendPacket rc: %dn", rc);
goto _mqtt_disconnect;
}
} /* pbulish sock handler. */
} /* while (1) */
_mqtt_disconnect:
MQTTDisconnect(c);
_mqtt_restart:
if (c->offline_callback)
{
c->offline_callback(c);
}
net_disconnect(c);
rt_thread_delay(RT_TICK_PER_SECOND * 5);
debug_printf("restart!n");
goto _mqtt_start;
_mqtt_disconnect_exit:
MQTTDisconnect(c);
net_disconnect(c);
_mqtt_exit:
debug_printf("thread exitn");
return;
}

二、與mqtt broker的交互
paho-mqtt軟件包提供了兩種發(fā)布消息到mqtt broker的方式:udp和管道。在MQTTClient結(jié)構(gòu)體中有三個(gè)成員與通信有關(guān):sock、pub_sock、pub_pipe,其中sock是與mqtt broker通信的套接字,pub_sock和pub_pipe是兩種不同的發(fā)布方式:pub_sock是通過(guò)udp的方式發(fā)布消息;pub_pipe是通過(guò)管道,最終由sock發(fā)布消息。如下面的代碼所示,使用哪種方式可以通過(guò)宏來(lái)配置。下面展開(kāi)描述這兩種方式如何與mqtt broker交互的。

/* publish interface */

#if defined(RT_USING_POSIX) && (defined(RT_USING_DFS_NET) || defined(SAL_USING_POSIX))
int pub_pipe[2];
#else
int pub_sock;
int pub_port;
#endif

2.1 管道(pipe)方式
在paho_mqtt_pipe.c中的paho_mqtt_thread,下面的代碼完成了發(fā)布消息、接收訂閱消息、處理心跳包的工作。下面以三個(gè)點(diǎn)細(xì)說(shuō)。

當(dāng)需要發(fā)布消息時(shí),應(yīng)用層需要調(diào)用MQTTPublish,這個(gè)函數(shù)會(huì)調(diào)用write向管道的寫端pub_pipe[1]寫入待發(fā)送的數(shù)據(jù)。而管道的讀端pub_pipe[0]在select中被監(jiān)聽(tīng),當(dāng)MQTTPublish被調(diào)用時(shí),select可以往下執(zhí)行,首先調(diào)用read從管道中讀取數(shù)據(jù),接著調(diào)用MQTTSerialize_publish將數(shù)據(jù)封包,最后調(diào)用sendPacket將數(shù)據(jù)發(fā)送出去。

當(dāng)接收到訂閱的消息時(shí),select會(huì)往下執(zhí)行,接著調(diào)用MQTT_cycle讀取并解析出數(shù)據(jù)。
select的超時(shí)時(shí)間是50s,如果50s沒(méi)有消息處理,則向broker發(fā)送心跳包。

FD_ZERO(&readset);
FD_SET(c->sock, &readset);
FD_SET(c->pub_pipe[0], &readset);
/* int select(maxfdp1, readset, writeset, exceptset, timeout); /
res = select(((c->pub_pipe[0] > c->sock) ? c->pub_pipe[0] : c->sock) + 1,
&readset, RT_NULL, RT_NULL, &timeout);
if (res == 0)
{
len = MQTTSerialize_pingreq(c->buf, c->buf_size);
rc = sendPacket(c, len);
if (rc != 0)
{
LOG_E("[%d] send ping rc: %d ", rt_tick_get(), rc);
goto _mqtt_disconnect;
}
/
wait Ping Response. /
timeout.tv_sec = 5;
timeout.tv_usec = 0;
FD_ZERO(&readset);
FD_SET(c->sock, &readset);
res = select(c->sock + 1, &readset, RT_NULL, RT_NULL, &timeout);
if (res <= 0)
{
LOG_E("[%d] wait Ping Response res: %d", rt_tick_get(), res);
goto _mqtt_disconnect;
}
} /
res == 0: timeount for ping. */
if (res < 0)
{
LOG_E("select res: %d", res);
goto _mqtt_disconnect;
}
if (FD_ISSET(c->sock, &readset))
{
//LOG_D("sock FD_ISSET");
rc_t = MQTT_cycle(c);
//LOG_D("sock FD_ISSET rc_t : %d", rc_t);
if (rc_t < 0) goto _mqtt_disconnect;
continue;
}
if (FD_ISSET(c->pub_pipe[0], &readset))
{
MQTTMessage *message;
MQTTString topic = MQTTString_initializer;
//LOG_D("pub_sock FD_ISSET");
len = read(c->pub_pipe[0], c->readbuf, c->readbuf_size);
if (len < sizeof(MQTTMessage))
{
c->readbuf[len] = '?';
LOG_D("pub_sock recv %d byte: %s", len, c->readbuf);
if (strcmp((const char *)c->readbuf, "DISCONNECT") == 0)
{
LOG_D("DISCONNECT");
goto _mqtt_disconnect_exit;
}
continue;
}
message = (MQTTMessage *)c->readbuf;
message->payload = c->readbuf + sizeof(MQTTMessage);
topic.cstring = (char *)c->readbuf + sizeof(MQTTMessage) + message->payloadlen;
//LOG_D("pub_sock topic:%s, payloadlen:%d", topic.cstring, message->payloadlen);
len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id,
topic, (unsigned char *)message->payload, message->payloadlen);
if (len <= 0)
{
LOG_D("MQTTSerialize_publish len: %d", len);
goto _mqtt_disconnect;
}
if ((rc = sendPacket(c, len)) != PAHO_SUCCESS) // send the subscribe packet
{
LOG_D("MQTTSerialize_publish sendPacket rc: %d", rc);
goto _mqtt_disconnect;
}
}

2.2 udp方式
udp方式中,處理流程與管道方式基本相似。下面說(shuō)明一下這種方式兩個(gè)套接字的工作流程。
MQTTClient結(jié)構(gòu)體中有兩個(gè)socket,一個(gè)是基于tcp的負(fù)責(zé)控制與服務(wù)器連接的sock,另一個(gè)是基于udp協(xié)議的負(fù)責(zé)消息發(fā)布的pub_sock。

2.2.1 sock
連接:在net_connect調(diào)用socket、connet函數(shù)建立與服務(wù)器的tcp連接。
處理:sock接收到服務(wù)器的數(shù)據(jù)后,在MQTT_cycle中處理來(lái)自服務(wù)器的CONNACK、PUBACK、SUBACK、PUBLISH、PUBREC、PUBCOMP、PINGRESP消息。
斷開(kāi)連接:在net_disconnect函數(shù)中調(diào)用closesocket關(guān)閉與服務(wù)器的tcp連接。

2.2.2 pub_sock
連接:分為pub_sock的綁定和mqtt連接的建立
1、調(diào)用socket創(chuàng)建pub_sock,之后調(diào)用bind綁定pub_sock到udp端口
2、在MQTTConnect函數(shù)中,通過(guò)sock發(fā)送connect消息給服務(wù)器,建立mqtt連接。
處理:先recvfrom將接受的數(shù)據(jù)拷貝到MQTTClient的readbuf,再將數(shù)據(jù)回環(huán)發(fā)布到服務(wù)器。
斷開(kāi)連接:通過(guò)sock向服務(wù)器發(fā)送DISCONNECT消息,斷開(kāi)mqtt連接。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    18927

    瀏覽量

    227231
  • WLAN技術(shù)
    +關(guān)注

    關(guān)注

    0

    文章

    23

    瀏覽量

    9249
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1239

    瀏覽量

    39434
  • MQTT
    +關(guān)注

    關(guān)注

    5

    文章

    629

    瀏覽量

    22286
  • MQTT協(xié)議
    +關(guān)注

    關(guān)注

    0

    文章

    93

    瀏覽量

    5308
  • TCP通信
    +關(guān)注

    關(guān)注

    0

    文章

    146

    瀏覽量

    4184
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    RT-Thread記錄(十八、SHT21與24C02軟件包

    本文學(xué)習(xí)測(cè)試一下幾款典型設(shè)備的 RT-Thread I2C軟件包
    的頭像 發(fā)表于 07-08 11:07 ?3067次閱讀
    <b class='flag-5'>RT-Thread</b>記錄(十八、SHT21與24C02<b class='flag-5'>軟件包</b>)

    如何利用RT-Thread開(kāi)發(fā)的PahoMQTT軟件包MQTT服務(wù)器進(jìn)行通信?

    本教程就是介紹如何利用 RT-Thread 開(kāi)發(fā)的 Paho MQTT 軟件包MQTT 服務(wù)器進(jìn)行通信的。
    發(fā)表于 03-30 08:09

    介紹RT-Thread軟件包

    學(xué)習(xí)要點(diǎn)介紹 RT-Thread軟件包;簡(jiǎn)介 nRF24L01 軟件包的使用,講解如何使用此軟件包將數(shù)據(jù)正確發(fā)送和接收;學(xué)習(xí)線程間的通信,IPC 的使用,即獲取溫度的線程 A 與無(wú)
    發(fā)表于 07-27 06:07

    WIZnet軟件包對(duì)接RT-Thread SAL套接字抽象層實(shí)現(xiàn)對(duì)BSD Socket APIs的支持

    RT-Thread SAL 套接字抽象層,實(shí)現(xiàn)對(duì)標(biāo)準(zhǔn) BSD Socket APIs 的支持,完美的兼容多種軟件包和網(wǎng)絡(luò)功能實(shí)現(xiàn),提高 WIZnet 設(shè)備兼容性。1.1 目錄結(jié)構(gòu)WIZnet
    發(fā)表于 05-17 17:00

    使用menuconfig配置基于RT-Thread的NimBLE軟件包

    最近在學(xué)習(xí) RT-Thread 中的 NimBLE 軟件包,使用 menuconfig 配置選中 NimBLE 軟件包,設(shè)置各種選項(xiàng)后,成功通過(guò)編譯并且運(yùn)行起來(lái)。不過(guò)這僅僅只是按照文檔
    發(fā)表于 06-27 11:18

    UIoT RT-Thread軟件包介紹

    UIoT RT-Thread 軟件包實(shí)現(xiàn)了 IoT 設(shè)備與 UCloud UIoT Core 物聯(lián)網(wǎng)通信云平臺(tái)連接,包含設(shè)備注冊(cè)、MQTT、設(shè)備影子、物模型、OTA、文件上傳等功能,開(kāi)發(fā)者進(jìn)行靈活裁剪。
    發(fā)表于 09-26 07:22

    RT-Thread 軟件包介紹

    。軟件包的使用RT-Thread 為開(kāi)發(fā)者提供的全面的使用教程,詳情如下:入門文檔教程:位于 RT-Thread 官網(wǎng),文檔->開(kāi)發(fā)指南->env 工具用戶手冊(cè)
    發(fā)表于 05-21 19:38 ?5444次閱讀

    RT-Thread軟件包定義和使用

    RT-Thread軟件包是運(yùn)行于RT-Thread物聯(lián)網(wǎng)操作系統(tǒng)平臺(tái)上,面向不同應(yīng)用領(lǐng)域的通用軟件組件 。RT-Thread 同時(shí)提供了開(kāi)放
    的頭像 發(fā)表于 05-21 11:29 ?1w次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>軟件包</b>定義和使用

    RT-Thread社區(qū)上有哪些優(yōu)秀的軟件包?

    SX12XX 軟件包是基于 semtech 官網(wǎng) Firmware Drivers V2.1.0代碼庫(kù)的移植實(shí)現(xiàn),目前只支持 SX1278 LoRa設(shè)備。該軟件包在原代碼庫(kù)功能的基礎(chǔ)上,對(duì)接 RT-Thread SPI 設(shè)備驅(qū)動(dòng)
    的頭像 發(fā)表于 04-03 15:35 ?7514次閱讀

    2022 RT-Thread全球技術(shù)大會(huì):RT-Thread軟件包

    RT-Thread在2022年上半年新增幾十個(gè)軟件包:rtt-rust、LuatOS、gui-guider-demo。
    的頭像 發(fā)表于 05-27 14:12 ?968次閱讀
    2022 <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):<b class='flag-5'>RT-Thread</b><b class='flag-5'>軟件包</b>

    RT-Thread全球技術(shù)大會(huì):恩智浦新增100+軟件包

    RT-Thread全球技術(shù)大會(huì):恩智浦新增100+軟件包,支持了很多有用的功能。 ? ? ? ? ? ? 審核編輯:彭靜
    的頭像 發(fā)表于 05-27 14:26 ?868次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術(shù)大會(huì):恩智浦新增100+<b class='flag-5'>軟件包</b>

    RT-Thread在線軟件包改為本地軟件包的方法

    RT-Thread軟件包,使用時(shí)需要手動(dòng)通過(guò) ENV 工具 更新到 本地的 packages 目錄,并且 packages 目錄默認(rèn)不參與 Git 工程管理,軟件包多了,偶爾需要更
    的頭像 發(fā)表于 08-11 15:02 ?1029次閱讀
    <b class='flag-5'>RT-Thread</b>在線<b class='flag-5'>軟件包</b>改為本地<b class='flag-5'>軟件包</b>的方法

    RT-Thread使用cjson軟件包發(fā)送64位長(zhǎng)整型數(shù)據(jù)

    開(kāi)發(fā)環(huán)境:野火的stm32f407,rt-thread studio版本是版本: 2.2.6,stm32f4的資源為0.2.2,rt-thread版本為4.1.1,cjson軟件包使
    的頭像 發(fā)表于 10-11 15:09 ?713次閱讀
    <b class='flag-5'>RT-Thread</b>使用cjson<b class='flag-5'>軟件包</b>發(fā)送64位長(zhǎng)整型數(shù)據(jù)

    RT-Thread中Agile Modbus軟件包的使用方法

    開(kāi)發(fā)環(huán)境:野火的stm32f407,rt-thread studio版本是版本: 2.2.6,stm32f4的資源為0.2.2,Agile Modbus軟件包版本為v1.1.2。工程使用上一篇
    的頭像 發(fā)表于 10-11 15:37 ?1870次閱讀
    <b class='flag-5'>RT-Thread</b>中Agile Modbus<b class='flag-5'>軟件包</b>的使用方法

    RT-Thread中mymqtt軟件包的使用方法

    在上一篇文章 RT-Thread中Lan8720和lwip協(xié)議棧的使用的工程基礎(chǔ)上添加mymqtt軟件包。 使能mqtt example和mqtt test,保存,等待下載更新
    的頭像 發(fā)表于 10-13 10:44 ?808次閱讀
    <b class='flag-5'>RT-Thread</b>中mymqtt<b class='flag-5'>軟件包</b>的使用方法