小車控制由簡(jiǎn)入繁之MQTT控制
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1. 簡(jiǎn)介
- #開發(fā)板漂流計(jì)劃#小車控制由簡(jiǎn)入繁之按鍵控制
- #開發(fā)板漂流計(jì)劃#小車控制由簡(jiǎn)入繁之UDP控制
本文在前面兩篇帖子的基礎(chǔ)上,在Openharmony3.0上實(shí)現(xiàn)小車上電后自動(dòng)連接到指定WIFI,并與指定的MQTT Broker建立鏈接、訂閱小車控制“carControl”主題。電腦Windows系統(tǒng)下使用paho軟件連接到相同的MQTT Broker,通過發(fā)送主題為“carControl”的消息來控制小車狀態(tài)。
閱讀本文前,推薦閱讀連志安老師的以下文章:
- MQTT 協(xié)議開發(fā)入門 - MQTT 簡(jiǎn)述、設(shè)計(jì)規(guī)范、主要特性、協(xié)議原理、協(xié)議數(shù)據(jù)包結(jié)構(gòu)
- 如何在鴻蒙系統(tǒng)中移植 Paho-MQTT 實(shí)現(xiàn)MQTT協(xié)議- 基于openharmony1.0移植Paho-MQTT教程
- 在鴻蒙系統(tǒng)上使用MQTT編程-對(duì)應(yīng)連老師Gitee倉庫 harmony_mqtt的使用介紹,本文基于這個(gè)版本實(shí)現(xiàn)MQTT控制小車
本文主要分享MQTT Broker的搭建、在Openharmony3.0上如何使用harmony_mqtt、并實(shí)現(xiàn)MQTT控制小車的Demo。
2. 在Ubuntu20.04虛擬機(jī)安裝mosquitto搭建Broker
安裝前請(qǐng)確保你的Ubuntu能正常上網(wǎng)并且局網(wǎng)內(nèi)可以訪問到。
2.1 Ubuntu下mosquitto 安裝
安裝指令如下:
- sudo apt-get install mosquitto
- sudo apt-get install mosquitto-clients
- sudo apt-get install mosquitto-dev
2.2 安裝完成后可以用以下指令查看狀態(tài)
- service mosquitto status
正常狀態(tài)顯示如下

這樣子測(cè)試用的Broker就搭建好了,Broker的IP地址就是你Ubuntu局域網(wǎng)中的IP,端口默認(rèn)1883,如果搭建有問題歡迎留言交流。
3.在Openharmony3.0上如何使用harmony_mqtt
3.1 讓harmony_mqtt成功編譯
3.1.1 將harmony_mqtt 下載到Openharmony3.0源碼third_party目錄下并重命名為pahomqtt
可以用git clone 下載后重命名也可以下載壓縮包后解壓。如下圖Z:\ohos300_iot\是我Openharmony3.0源碼的根目錄

3.1.2 將pahomqtt添加到hispark_pegasus編譯
修改device\hisilicon\hispark_pegasus\sdk_liteos\BUILD.gn在lite_component(“sdk”)中添加"//third_party/pahomqtt:pahomqtt_static"
(注意:此處修改的BUILD.gn 和Openharmony1.0 版本有區(qū)別,1.0 路徑是vendor\hisi\hi3861\hi3861\BUILD.gn)
- lite_component("sdk") {
- features = []
- deps = [
- "//build/lite/config/component/cJSON:cjson_static",
- "//device/hisilicon/hispark_pegasus/hi3861_adapter/kal",
- "//third_party/pahomqtt:pahomqtt_static",
- ]
- }
修改完成后重新執(zhí)行編譯后從編譯日志中可以用mqtt看下log,修改成功會(huì)有以下信息。

但是編譯到最后會(huì)有錯(cuò)誤提示如下,接下來就是修這個(gè)編譯錯(cuò)誤了。

3.1.3 修改編譯錯(cuò)誤
錯(cuò)誤日志截取如下
- [OHOS ERROR] riscv32-unknown-elf-ld: ohos/libs/libpahomqtt_static.a(libpahomqtt_static.MQTTLiteOS.o): in function `MutexInit':
- [OHOS ERROR] MQTTLiteOS.c:(.text.MutexInit+0x0): multiple definition of `MutexInit'; ohos/libs/libdiscovery.a(libdiscovery.os_adapter.o):os_adapter.c:(.text.MutexInit+0x0): first defined here
- [OHOS ERROR] riscv32-unknown-elf-ld: ohos/libs/libpahomqtt_static.a(libpahomqtt_static.MQTTLiteOS.o): in function `MutexLock':
- [OHOS ERROR] MQTTLiteOS.c:(.text.MutexLock+0x0): multiple definition of `MutexLock'; ohos/libs/libdiscovery.a(libdiscovery.os_adapter.o):os_adapter.c:(.text.MutexLock+0x0): first defined here
- [OHOS ERROR] riscv32-unknown-elf-ld: ohos/libs/libpahomqtt_static.a(libpahomqtt_static.MQTTLiteOS.o): in function `MutexUnlock':
- [OHOS ERROR] MQTTLiteOS.c:(.text.MutexUnlock+0x0): multiple definition of `MutexUnlock'; ohos/libs/libdiscovery.a(libdiscovery.os_adapter.o):os_adapter.c:(.text.MutexUnlock+0x0): first defined here
- [OHOS ERROR] scons: *** [output/bin/Hi3861_wifiiot_app.out] Error 1
- [OHOS ERROR] BUILD FAILED!!!!
- [OHOS ERROR] Failed building output/bin/Hi3861_wifiiot_app.out: Error 1
- [OHOS ERROR] you can check build log in /home/soon/ohos300_iot/out/hispark_pegasus/wifiiot_hispark_pegasus/build.log
- [OHOS ERROR] command: "/home/soon/ohostool_101/ninja/ninja -w dupbuild=warn -C /home/soon/ohos300_iot/out/hispark_pegasus/wifiiot_hispark_pegasus" failed
- [OHOS ERROR] return code: 1
- [OHOS ERROR] execution path: /home/soon/ohos300_iot
- soon@soon-u20:~/ohos300_iot $
我的修改方式是將
third_party\pahomqtt\MQTTClient-C\src\MQTTClient.c
third_party\pahomqtt\MQTTClient-C\src\liteOS\MQTTLiteOS.c
third_party\pahomqtt\MQTTClient-C\src\liteOS\MQTTLiteOS.h
這三個(gè)檔案中的所有的
- MutexInit(Mutex*);
- MutexLock(Mutex*);
- MutexUnlock(Mutex*);
對(duì)應(yīng)替換為
- MqttMutexInit(Mutex*);
- MqttMutexLock(Mutex*);
- MqttMutexUnlock(Mutex*);
這樣子就能正常編譯通過了
3.1.4 其他修改和修改后的代碼
third_party\pahomqtt\BUILD.gn中以下三個(gè)有沒有并不會(huì)影響編譯/使用,所以我把他拿掉了。
- "MQTTClient-C\src\liteOS",
- "//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include",
- "//kernel/liteos_m/components/cmsis/2.0",
修改后的代碼可以直接從附件下載或者從碼云 https://gitee.com/soonliao/pahomqtt 下載。
3.2 MQTT控制小車代碼的實(shí)現(xiàn)
3.2.1 applications\sample\wifi-iot\app\car_mqtt\BUILD.gn 的修改
在應(yīng)用的BUILD.gn添加以下三行內(nèi)容才能調(diào)用到pahomqtt的相關(guān)函數(shù)
- static_library("car_mqtt") {
- sources = [
- ......
- ]
- include_dirs = [
- ......
- "//third_party/pahomqtt:pahomqtt_static",
- "//third_party/pahomqtt/MQTTPacket/src",
- "//third_party/pahomqtt/MQTTClient-C/src",
- ]
- }
3.2.2 MQTT 初始化主題訂閱
主要在以下函數(shù)實(shí)現(xiàn),詳細(xì)的說明可以參考在鴻蒙系統(tǒng)上使用MQTT編程
- #include "MQTTClient.h"
- #define MQTT_BROKER "192.168.123.230"//改成你的MQTT Broker 地址
- #define MQTT_PORT 1883
- MQTTMessage ackmsg;
- int needAck = 0;
- struct opts_struct
- {
- char* clientid;
- int nodelimiter;
- char* delimiter;
- enum QoS qos;
- char* username;
- char* password;
- char* host;
- int port;
- int showtopics;
- }
- opts =
- {
- (char*)"stdout-subscriber", 0, (char*)"\n", QOS2, NULL, NULL, (char*)MQTT_BROKER, MQTT_PORT, 1
- };
- unsigned char buf[100];
- unsigned char readbuf[100];
- int car_mqtt(void)
- {
- int rc = 0;
- MQTTMessage pubmsg;
- //小車控制主題carControl
- char* topic = "carControl";
- if (strchr(topic, '#') || strchr(topic, '+'))
- opts.showtopics = 1;
- if (opts.showtopics)
- printf("topic is %s\n", topic);
- Network n;
- MQTTClient c;
- //網(wǎng)絡(luò)初始化
- NetworkInit(&n);
- NetworkConnect(&n, opts.host, opts.port);
- //設(shè)置MQTT緩存和啟動(dòng)MQTT線程
- MQTTClientInit(&c, &n, 1000, buf, 100, readbuf, 100);
- MQTTStartTask(&c);
- //設(shè)置MQTT相關(guān)參數(shù)
- MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
- data.willFlag = 0;
- data.MQTTVersion = 3;
- data.clientID.cstring = opts.clientid;
- data.username.cstring = opts.username;
- data.password.cstring = opts.password;
- data.keepAliveInterval = 10;
- data.cleansession = 1;
- printf("Connecting to %s %d\n", opts.host, opts.port);
- //連接到MQTT服務(wù)器
- rc = MQTTConnect(&c, &data);
- printf("Connected %d\n", rc);
- //訂閱主題和接收消息
- printf("Subscribing to %s\n", topic);
- rc = MQTTSubscribe(&c, topic, opts.qos, messageArrived);
- printf("Subscribed %d\n", rc);
- //狀態(tài)變化后發(fā)布主題carStatus,消息內(nèi)容ackmsg.payload
- memset(&ackmsg, '\0', sizeof(ackmsg));
- ackmsg.payload = (void*)"ACK";
- ackmsg.payloadlen = strlen((char*)ackmsg.payload);
- ackmsg.qos = QOS0;
- ackmsg.retained = 0;
- ackmsg.dup = 0;
- while (1)
- {
- if(needAck == 1)//收到消息后,發(fā)送主題carStatus并攜帶狀態(tài)消息
- {
- needAck = 0;
- printf("Publish carStatus ackmsg %d %s \n", (int)ackmsg.payloadlen, (char*)ackmsg.payload);
- MQTTPublish(&c, "carStatus", &ackmsg);
- }
- sleep(1);
- }
- printf("Stopping\n");
- MQTTDisconnect(&c);
- NetworkDisconnect(&n);
- return 0;
- }
3.2.1 小車控制代碼
通過MQTTSubscribe(&c, topic, opts.qos, messageArrived);注冊(cè)messageArrived回調(diào)實(shí)現(xiàn),代碼如下
- void messageArrived(MessageData* md)
- {
- MQTTMessage* message = md->message;
- memset(&ackmsg, '\0', sizeof(ackmsg));
- ackmsg.qos = QOS0;
- ackmsg.retained = 0;
- ackmsg.dup = 0;
- if (opts.showtopics)
- printf("%.*s\t", md->topicName->lenstring.len, md->topicName->lenstring.data);
- if (opts.nodelimiter)
- printf("%.*s\n", (int)message->payloadlen, (char*)message->payload);
- else
- printf("%.*s%s\n", (int)message->payloadlen, (char*)message->payload, opts.delimiter);
- if(strncmp("forward", message->payload, 7)== 0) {
- needAck = 1;
- car_go_forward();
- ackmsg.payload = (void*)"forward";
- }
- if(strncmp("back", message->payload, 4) == 0) {
- needAck = 1;
- car_go_back();
- ackmsg.payload = (void*)"back";
- }
- if(strncmp("left", message->payload, 4) == 0) {
- needAck = 1;
- car_turn_left();
- ackmsg.payload = (void*)"left";
- }
- if(strncmp("right", message->payload, 5) == 0) {
- needAck = 1;
- car_turn_right();
- ackmsg.payload = (void*)"right";
- }
- if(strncmp("stop", message->payload, 4) == 0) {
- needAck = 1;
- car_stop();
- ackmsg.payload = (void*)"stop";
- }
- if(needAck == 1) {
- ackmsg.payloadlen = strlen((char*)ackmsg.payload);
- }
- }
4.編譯及測(cè)試
4.1 小車控制代碼的編譯
完整的代碼請(qǐng)見附件或者Gitee https://gitee.com/soonliao/car_mqtt,使用方式如下
4.1.1 將car_mqtt解壓或者git clone 到applications\sample\wifi-iot\app\路徑下

4.1.2 修改applications\sample\wifi-iot\app\BUILD.gn
- import("//build/lite/config/component/lite_component.gni")
- lite_component("app") {
- features = [
- "car_mqtt",
- ]
- }
4.1.3 修改WIFI SSID、密碼及MQTT Broker IP
修改applications\sample\wifi-iot\app\car_mqtt\src\car_main.c
- #define WIFI_SSID "SSIDABCD"//WIFI賬號(hào)
- #define WIFI_PW "MIMA1234"//WIFI密碼
- #define MQTT_BROKER "192.168.123.230"//改成你的MQTT Broker 地址
- #define MQTT_PORT 1883//改成你的MQTT Broker 端口,默認(rèn)1883
4.1.4 開啟PWM 功能,開啟方式如下
電機(jī)驅(qū)動(dòng)會(huì)用到PWM 模塊所以要開啟
device/hisilicon/hispark_pegasus/sdk_liteos/build/config/usr_config.mk
- CONFIG_PWM_SUPPORT is not set
改為
- CONFIG_PWM_SUPPORT=y
加上第3部分中的pahomqtt相關(guān)修改,整個(gè)Demo就可以編譯通了。
4.2 小車控制代碼的測(cè)試
4.2.1 從串口log查看
成功連接WIFI,且連上MQTT Broker訂閱主題的log如下

4.2.2 電腦Windows系統(tǒng)下paho軟件發(fā)送測(cè)試指令
測(cè)試軟件在附件,或者參考連老師的說明下載,控制指令支持前進(jìn)“forward”、后退“back”、右轉(zhuǎn)“right”、左轉(zhuǎn)“left”、停止“stop”,在小車成功執(zhí)行指令后會(huì)返回一條carStatus的主題并攜帶狀態(tài)消息。

接收到指令后串口會(huì)有以下打印

文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載
https://harmonyos.51cto.com/resource/1319
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)