一、项目介绍
久闻openharmony大名,一直没有机会接触,感谢极术社区和全志社区的这次活动,让我能够了解并上手这个系统。
openhamony 1.1的内核是基于liteos内核系统进行构建的,liteos作为物联网系统,结合xr806小型开发板,特别适合用于构建iot项目,同时openharmony拥有丰富的中间件,使得开发变得容易,因此考虑结合mqtt与cjson这两个iot开发过程中必不可少的两项技术来进行花式点灯。下图是xr806的配置图。
二、环境准备
1、mqtt系统搭建
MQTT协议作为一种轻量、简单、开放和易于实现的协议,由服务端与客户端构成,客户端负责发布与订阅消息,服务端则负责完成客户端的管理与设备间的信息交互。本项目中就是通过云服务器来实现pc与开发板间的交互。
1.1、客户端(开发板)
本项目使用所提供的开发包中的mqtt例程进行改写的,该例程调用Eclipse Paho MQTT开发库进行编写,使用MQTTClient-C库,将开发板作为客户端。可以在该目录下找到例程。
harmony\device\xradio\xr806\xr\_skylark\project\example\mqtt
1.2、客户端(PC)
本项目中使用MQTT.fx软件来作为PC的客户端,MQTT.fx是一款也是基于Eclipse Paho库的软件,通过配置后与服务端进行交互。主界面如下图所示,通过添加服务器的地址来与服务器进行交互。
.png")
1.3、服务端
本项目中服务端使用阿里云服务器进行mqtt服务端搭建,有云服务器的小伙伴可以参考这篇文章。
https://blog.csdn.net/qq\_45168614/article/details/107183583
没有云服务可以使用阿里云或者腾讯云的提供的物联网服务,也提供了mqtt服务器的功能,并且功能更加强大。
mqtt服务端的界面如下图所示。
.png")
三、代码编写
main.c代码如下:
/** Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:* 1. Redistributions of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the* distribution.* 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of* its contributors may be used to endorse or promote products derived* from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/#include <stdio.h>
#include <stdlib.h>
#include "ohos_init.h"
#include "kernel/os/os.h"
#include <string.h>
#include "wifi_device.h"
#include "wifi_hotspot.h"
#include "cjson/cJSON.h"
#include "common/framework/platform_init.h"
#include "net/wlan/wlan.h"
#include "common/framework/net_ctrl.h"
#include "net/mqtt/MQTTClient-C/MQTTClient.h"
#include "iot_gpio.h" //(8)#define MQTT_DEMO_THREAD_STACK_SIZE (8 * 1024) /* ssl need more stack */
static OS_Thread_t mqtt_demo_thread;
static OS_Thread_t blink_thread;static MQTTPacket_connectData mqtt_demo_connectData = MQTTPacket_connectData_initializer;
static Client mqtt_demo_client;
static Network mqtt_demo_network;#define WIFI_DEVICE_CONNECT_AP_SSID "xtg2018" //填写wifi名称
#define WIFI_DEVICE_CONNECT_AP_PSK "xtg2018" //填写wifi密码#define MQTT_DEMO_CLIENT_ID "IOT control"
#define MQTT_DEMO_HOST_NAME "xxx.xxx.xxx.xxx" //填写mqtt服务器地址
#define MQTT_DEMO_PORT "1883"
#define MQTT_DEMO_USERNAME "easitmickly" //填写mqtt用户名
#define MQTT_DEMO_PASSWORD "" //填写mqtt密码
#define MQTT_DEMO_TOPIC "/vo84Hm3xbUj/xr806_senor_set0/data"
#define MQTT_DEMO_TOPIC2 "/vo84Hm3xbUj/xr806_senor_set1/data"
#define GPIO_ID_PA21 21#define MQTT_DEMO_BUF_SIZE (2*1024)#define MQTT_DEMO_MSG_TEXT "mqtt demo test"
static OS_Thread_t g_main_thread;void wifi_scan_connect(){if(WIFI_SUCCESS != EnableWifi()){printf("Error:wifi enable fail!\n");return;}if(WIFI_SUCCESS != Scan()){printf("Error:wifi scan fail!\n");}printf("WIFI SCAN STARTED!\n");OS_Sleep(3);WifiScanInfo scan_result[30];unsigned int scan_num = 30;if(WIFI_SUCCESS != GetScanInfoList(scan_result, &scan_num)){printf("Error: get scan result fail!\n");return;}printf("Scan successful,you've got:\n");for(int i = 0;i < scan_num;i++){printf("SSID: %s ",scan_result[i].ssid);printf("RSSI: %d",scan_result[i].rssi);}printf("Scan End \n");const char ssid_want_connect[] = WIFI_DEVICE_CONNECT_AP_SSID;const char psk[] = WIFI_DEVICE_CONNECT_AP_PSK;printf("Connecting...");if(WIFI_STA_ACTIVE == IsWifiActive()){printf("Wifi is active.\n");}OS_Sleep(1);WifiDeviceConfig config = {0};int netId = 0;int i;for(i = 0;i < scan_num;i++){if(0 == strcmp(scan_result[i].ssid, ssid_want_connect)){memcpy(config.ssid, scan_result[i].ssid, WIFI_MAX_SSID_LEN);memcpy(config.bssid, scan_result[i].bssid, WIFI_MAC_LEN);strcpy(config.preSharedKey, psk);config.securityType = scan_result[i].securityType;config.wapiPskType = WIFI_PSK_TYPE_ASCII;config.freq = scan_result[i].frequency;break;}}if(i >= scan_num){printf("Error: No SSID SET FOUND!\n");return;}printf("Found Correct SSID in Scan List!\n");if(WIFI_SUCCESS != AddDeviceConfig(&config, &netId)){printf("Error: Add Device config failed!\n");return;}printf("Add Device Config successful!\n");if(WIFI_SUCCESS != ConnectTo(netId)){printf("Error: Connect to Wifi FAILED!\n");return;}printf("Connect to %s successful!\n",config.ssid);OS_Sleep(3);
}static int mqtt_demo_init(void)
{char *send_buf;char *recv_buf;/* init client id */mqtt_demo_connectData.clientID.cstring = MQTT_DEMO_CLIENT_ID;/* init keep alive interval */mqtt_demo_connectData.keepAliveInterval = 30; // 30s/* enable session reuse */mqtt_demo_connectData.cleansession = 0;/* set mqtt version */mqtt_demo_connectData.MQTTVersion = 4; //Version of MQTT 3.1.1/* send/recv buffer must free when mqtt deinit */send_buf = malloc(MQTT_DEMO_BUF_SIZE);if (send_buf == NULL) {printf("no memory\n");return -1;}recv_buf = malloc(MQTT_DEMO_BUF_SIZE);if (recv_buf == NULL) {free(send_buf);printf("no memory\n");return -1;}/* init network */NewNetwork(&mqtt_demo_network);/* init mqtt client object */MQTTClient(&mqtt_demo_client, &mqtt_demo_network, 6000,(unsigned char *)send_buf, MQTT_DEMO_BUF_SIZE,(unsigned char *)recv_buf, MQTT_DEMO_BUF_SIZE);/*** set will function, when this client disconnect,* server will sent the message to every client in MQTT_DEMO_TOPIC*/mqtt_demo_connectData.willFlag = 1;mqtt_demo_connectData.will.topicName.cstring = MQTT_DEMO_TOPIC;mqtt_demo_connectData.will.message.cstring = "I am disconnected";mqtt_demo_connectData.will.retained = 0;mqtt_demo_connectData.will.qos = 0;/* set username and password */mqtt_demo_connectData.username.cstring = MQTT_DEMO_USERNAME;mqtt_demo_connectData.password.cstring = MQTT_DEMO_PASSWORD;return 0;
}static int mqtt_demo_connect(char *host_name, char *host_port)
{int ret = -1;/* need connect the server in tcp level first, if use ssl, use TLSConnectNetwork() */ret = ConnectNetwork(&mqtt_demo_network, host_name, atoi(host_port));if (ret != 0) {printf("mqtt connect faild, ret:%d, host:%s, port:%s\n", ret, host_name, host_port);goto exit;}/* if tcp level connected, then connect mqtt level */ret = MQTTConnect(&mqtt_demo_client, &mqtt_demo_connectData);if (ret != 0) {printf("mqtt connect faild, ret:%d\n", ret);/* disconnect the tcp level */mqtt_demo_network.disconnect(&mqtt_demo_network);goto exit;}printf("mqtt connected\n");exit:return ret;
}static void mqtt_demo_msg_cb(MessageData *data)
{printf("get a message, topic: %.*s, msg: %.*s\n", data->topicName->lenstring.len,data->topicName->lenstring.data, data->message->payloadlen,(char *)data->message->payload);parse_json((char *)data->message->payload);
}void Led_blink(void *arg)
{int *pinter = (int *)arg;int inter = *pinter;printf("transport inter is %d\n", *pinter);while(1){IoTGpioSetOutputVal(GPIO_ID_PA21, 0);OS_MSleep(1000 * inter);IoTGpioSetOutputVal(GPIO_ID_PA21, 1);OS_MSleep(1000 * inter);}
}int parse_json(char *s)
{cJSON *root = cJSON_Parse(s);if(!root) {printf("get root faild !\n");return -1;}cJSON *js_list = cJSON_GetObjectItem(root, "list");if(!js_list) {printf("no list!\n");return -1;}printf("list type is %d\n",js_list->type);cJSON *pin = cJSON_GetObjectItem(js_list, "pin style");if(!pin) {printf("No pin style!\n");return -1;}printf("pin style is %s\n",pin->valuestring);cJSON *inter = cJSON_GetObjectItem(js_list, "inter");if(!inter) {printf("no inter!\n");return -1;}printf("inter is %d\n",inter->valueint);if(pin){OS_ThreadDelete(&blink_thread);if (0 == strcmp(pin->valuestring, "open")){IoTGpioSetOutputVal(GPIO_ID_PA21, 1);}else if(0 == strcmp(pin->valuestring, "close")){IoTGpioSetOutputVal(GPIO_ID_PA21, 0);}else if(0 == strcmp(pin->valuestring, "blink")){if(inter){if(OS_ThreadCreate(&blink_thread, "BlinkThread", Led_blink, &inter->valueint, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK){printf("[ERR] Create BlinkThread Failed\n");}}}else{printf("unknown pin style\n");}}if(root)cJSON_Delete(root);return 0;
}static int mqtt_demo_subscribe(char *topic)
{int ret = -1;if (mqtt_demo_client.isconnected) {/* set the message callback */ret = MQTTSubscribe(&mqtt_demo_client, topic, 0, mqtt_demo_msg_cb);if (ret != 0)printf("mqtt subscribe faild ret:%d\n", ret);}return ret;
}static int mqtt_demo_unsubscribe(char *topic)
{int ret = -1;if (mqtt_demo_client.isconnected) {ret = MQTTUnsubscribe(&mqtt_demo_client, topic);if (ret != 0)printf("mqtt unsubscribe faild, ret:%d\n", ret);}return ret;
}static int mqtt_demo_publish(char *topic, char *msg)
{int ret = -1;MQTTMessage message;memset(&message, 0, sizeof(message));message.qos = 0;message.retained = 0; /* disable retain the message in server */message.payload = msg;message.payloadlen = strlen(msg);ret = MQTTPublish(&mqtt_demo_client, topic, &message);if (ret != 0)printf("mqtt publish faild, ret:%d\n", ret);return ret;
}static int mqtt_demo_disconnect(void)
{int ret = -1;if (mqtt_demo_client.isconnected) {/* need disconnect mqtt level first */ret = MQTTDisconnect(&mqtt_demo_client);if (ret != 0)printf("mqtt disconnect fail, ret:%d\n", ret);/* then disconnect tcp level */mqtt_demo_network.disconnect(&mqtt_demo_network);}return ret;
}static void mqtt_demo_deinit(void)
{if (mqtt_demo_client.buf) {free(mqtt_demo_client.buf);mqtt_demo_client.buf = NULL;}if (mqtt_demo_client.readbuf) {free(mqtt_demo_client.readbuf);mqtt_demo_client.readbuf = NULL;}
}static void mqtt_demo_fun(void *arg)
{int ret;int reconnect_times = 0;/* mqtt init */mqtt_demo_init();/* mqtt connect */ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT);if (ret != 0)goto exit;/* subscribe topic */ret = mqtt_demo_subscribe(MQTT_DEMO_TOPIC2);if (ret != 0)goto exit;while (1) {/* publish message to topic */mqtt_demo_publish(MQTT_DEMO_TOPIC, MQTT_DEMO_MSG_TEXT);ret = MQTTYield(&mqtt_demo_client, 300);if (ret != 0) {printf("mqtt yield err, ret:%d\n", ret);
reconnect:printf("mqtt reconnect\n");mqtt_demo_disconnect();ret = mqtt_demo_connect(MQTT_DEMO_HOST_NAME, MQTT_DEMO_PORT);if (ret != 0) {reconnect_times++;if (reconnect_times > 5)goto exit;OS_MSleep(5000); //5sgoto reconnect;}}OS_MSleep(1000); //1s}exit:mqtt_demo_unsubscribe(MQTT_DEMO_TOPIC2);mqtt_demo_disconnect();mqtt_demo_deinit();OS_ThreadDelete(&mqtt_demo_thread);
}static void MainThread(void *arg)
{wifi_scan_connect();
}int main(void)
{IoTGpioInit(GPIO_ID_PA21);IoTGpioSetDir(GPIO_ID_PA21, IOT_GPIO_DIR_OUT);if(OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK){printf("[ERR] Create MainThread Failed\n");}OS_MSleep(10000); //10sif (OS_ThreadCreate(&mqtt_demo_thread,"mqtt_demo_thread",mqtt_demo_fun,(void *)NULL,OS_THREAD_PRIO_APP,MQTT_DEMO_THREAD_STACK_SIZE) != OS_OK) {printf("[ERR] Create MainThread Failed\n");}}SYS_RUN(main);
build文件如下:
import("//device/xradio/xr806/liteos_m/config.gni")static_library("app_mqtt") {configs = []sources = ["main.c",]cflags = board_cflagsinclude_dirs = board_include_dirsinclude_dirs += [".","//kernel/liteos_m/kernel/arch/include","//base/iot_hardware/peripheral/interfaces/kits","//utils/native/lite/include","//foundation/communication/wifi_lite/interfaces/wifiservice","//device/xradio/xr806/xr_skylark/include/cjson","//device/xradio/xr806/xr_skylark/project"]
}
main.c代码使用mqtt例程与wifi例程中提供的函数,并调用cjson库。
if(OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK){printf("[ERR] Create MainThread Failed\n");}OS_MSleep(10000); //10sif (OS_ThreadCreate(&mqtt_demo_thread,"mqtt_demo_thread",mqtt_demo_fun,(void *)NULL,OS_THREAD_PRIO_APP,MQTT_DEMO_THREAD_STACK_SIZE) != OS_OK) {printf("[ERR] Create MainThread Failed\n");}
main函数中首先对led端口进行初始化,然后初始化wifi,延时10s使得wifi连接网络,最后进行mqtt连接。
int parse_json(char *s)
{cJSON *root = cJSON_Parse(s);if(!root) {printf("get root faild !\n");return -1;}cJSON *js_list = cJSON_GetObjectItem(root, "list");if(!js_list) {printf("no list!\n");return -1;}printf("list type is %d\n",js_list->type);cJSON *pin = cJSON_GetObjectItem(js_list, "pin style");if(!pin) {printf("No pin style!\n");return -1;}printf("pin style is %s\n",pin->valuestring);cJSON *inter = cJSON_GetObjectItem(js_list, "inter");if(!inter) {printf("no inter!\n");return -1;}printf("inter is %d\n",inter->valueint);if(pin){if (0 == strcmp(pin->valuestring, "open")){IoTGpioSetOutputVal(GPIO_ID_PA21, 1);}else if(0 == strcmp(pin->valuestring, "close")){IoTGpioSetOutputVal(GPIO_ID_PA21, 0);}else if(0 == strcmp(pin->valuestring, "blink")){if(inter){if(OS_ThreadDelete(&blink_thread)){printf("[ERR] Create OS_ThreadDelete BlinkThread Failed\n");}if(OS_ThreadCreate(&blink_thread, "BlinkThread", Led_blink, &inter->valueint, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK){printf("[ERR] Create BlinkThread Failed\n");}}}else{printf("unknown pin style\n");}}if(root)cJSON_Delete(root);return 0;
}
parse\_json函数对接受到的json消息进行解析,消息体中包含两个参数:点灯类型和跳动间隔。消息体如下:
{“list”:{“pin style”:“blink”,“inter”:3}}
pin style有三个参数:open、close和blink,而inter参数则在blink状态生效。当pin style参数为blink时,将进入Led\_blink线程,循环点灯。
四、效果展示
服务端展示的两个客户端状态如图所示:
.png")
pc端收到的开发板消息如下图:
.png")
开发板显示消息如下:
====================================================================Hello! OpenHarmony!System tag : OpenHarmony 1.1.2_LTS
====================================================================use default flash chip mJedec 0x0
[FD I]: mode: 0x10, freq: 96000000Hz, drv: 0
[FD I]: jedec: 0x0, suspend_support: 1
mode select:e[2022-01-22 13:28:55.546]# RECV ASCII>wlan information ===================================================
firmware:version : R0-XR_C07.08.52.65_02.84 May 27 2021 11:41:33-Y02.84 buffer : 8
driver:version : XR_V02.05
mac address:in use : 0c:6d:88:3d:34:01in use : 0c:6d:88:3d:34:02
====================================================================wlan mode:a
[VFS INF] SPIFFS mount success.platform information ===============================================
XR806 SDK v1.2.0 Jan 19 2022 20:05:38heap space [0x229344, 0x247c00), size 125116cpu clock 160000000 Hz
HF clock 40000000 Hzsdk option:XIP : enableINT LF OSC : enableSIP flash : enablemac address:efuse : 80:74:84:05:b9:0ein use : 0c:6d:88:3d:34:01
====================================================================IoTGpioInit port0, pin21
[net INF] no need to switch wlan mode 0
WIFI SCAN STARTED![2022-01-22 13:28:56.088]# RECV ASCII>
[net INF] msg <wlan scan success>[2022-01-22 13:28:58.579]# RECV ASCII>
Scan successful,you've got:
SSID: xtgy2018 RSSI: 122SSID: LUCIO_Wi-Fi5 RSSI: 42SSID: ChinaNet-KFVB6L RSSI: 38SSID: TP-LINK_shc RSSI: 34SSID: CMCC-V5xN RSSI: 32SSID: ziroom501 RSSI: 26SSID: ChinaNet-UnjV RSSI: 22SSID: ChinaNet-aDhv RSSI: 6SSID: CMCC-FSU6 RSSI: -2SSID: 201 RSSI: -6SSID: RSSI: -16SSID: CMCC-d5Nj RSSI: -20Scan End
Connecting...Wifi is active.[2022-01-22 13:28:59.593]# RECV ASCII>
Found Correct SSID in Scan List!
Add Device Config successful!
[net INF] no need to switch wlan mode 0[2022-01-22 13:29:00.354]# RECV ASCII>
en1: Trying to associate with cc:2d:21:86:4b:01 (SSID='xtg2018' freq=2437 MHz)
Connect to xtgy2018 successful![2022-01-22 13:29:00.512]# RECV ASCII>
en1: Associated with cc:2d:21:86:4b:01
en1: WPA: Key negotiation completed with cc:2d:21:86:4b:01 [PTK=CCMP GTK=TKIP]
en1: CTRL-EVENT-CONNECTED - Connection to cc:2d:21:86:4b:01 completed [id=0 id_str=]
[net INF] msg <wlan connected>
[net INF] netif is link up
[net INF] start DHCP...[2022-01-22 13:29:01.401]# RECV ASCII>
[net INF] netif (IPv4) is up
[net INF] address: 192.168.0.128
[net INF] gateway: 192.168.0.1
[net INF] netmask: 255.255.255.0
[net INF] msg <network IPv6 state>[2022-01-22 13:29:02.398]# RECV ASCII>
[net INF] IPv6 addr state change: 0x0 --> 0x1
[net INF] msg <>[2022-01-22 13:29:04.396]# RECV ASCII>
WAR drop=1117, fctl=0x00d0.[2022-01-22 13:29:05.536]# RECV ASCII>
hiview init success.
console init success[2022-01-22 13:29:05.631]# RECV ASCII>
mqtt connected[2022-01-22 13:29:14.784]# RECV ASCII>
get a message, topic: /vo84Hm3xbUj/xr806_senor_set1/data, msg: {"list":{"pin style":"blink","inter":3}}
list type is 64
pin style is blink
inter is 3
[os E] OS_ThreadDelete():110, handle 0
transport inter is 3
可以从消息中看出已经连接上了wifi与mqtt服务器,并接受到了从pc端发送的消息,视频演示如下:
https://www.bilibili.com/video/BV12q4y1A7zM/?aid=550751435&cid=489852768&page=1
可以看出已经达到想要的效果。
感谢阅读!感兴趣可以交流,谢谢。~~~~