一、项目介绍
久闻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); //5s
goto 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); //10s
if (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_cflags
include_dirs = board_include_dirs
include_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); //10s
if (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:01
in 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:38
heap space [0x229344, 0x247c00), size 125116
cpu clock 160000000 Hz
HF clock 40000000 Hz
sdk option:
XIP : enable
INT LF OSC : enable
SIP flash : enable
mac address:
efuse : 80:74:84:05:b9:0e
in 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
可以看出已经达到想要的效果。
感谢阅读!感兴趣可以交流,谢谢。~~~~