目录

引言

安装OpenHarmony的MQTT库

华为云平台的操作

建立设备

建立物模型

连接华为云平台

发布LED灯状态

代码重构

测试结果

接收平台发送的属性修改命令

设备侧API

Topic

下行请求参数说明

上行响应参数说明

程序修改

应用侧API

测试设备属性设置功能

结语


本文首发于电子发烧友论坛:https://bbs.elecfans.com/jishu_2475289_1_1.html

引言

前段时间进行了BQ3568HM开发板的测评,设计了智能家居中控屏并进行了LED灯的控制操作。今天为其加入物联网平台的功能,使其真正成为一个智能家居产品。

安装OpenHarmony的MQTT库

官方有个MQTT软件包:ohpm/mqtt,它的最新版本针对的是API12,而BQ3568HM开发板是基于OpenHarmony 4.1,可以使用老一点的版本,使用如下命令:

ohpm install @ohos/mqtt@2.0.13

上面的操作虽然可以导入老版本的MQTT包,但是当三方包发布新版本后,点击同步工程,会出现默认更新安装的三方包版本情况。为了避免这种情况,手工修改oh-package.json5,将其中@ohos/mqtt一行版本号前面的“^"符号删除掉,这样保证安装固定版本的三方包。

{
  "name": "myapplication",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "@ohos/mqtt": "2.0.13"
  },
  "devDependencies": {
    "@ohos/hypium": "1.0.6"
  },
  "dynamicDependencies": {}
}

华为云平台的操作

建立设备

我需要先在平台创建产品和设备,有关产品和设备的创建,可以参考我以前的博文:【HZHY-AI300G智能盒试用连载体验】在华为IoTDA平台上建立设备_hzhy-ai300g 多少钱-CSDN博客

建立物模型

在线开发产品模型前需要创建产品。创建产品需要输入产品名称、协议类型、数据格式、所属行业和设备类型等信息,产品模型会使用这些信息作为设备能力字段取值。物联网平台提供了标准模型和厂商模型,这些模型涉及多个领域,模型中提供了已经编辑好的产品模型文件,您可以根据自己的需要对产品模型中的字段进行修改和增删;如果选择自定义产品模型,则需要完整定义产品模型。

操作步骤

  1. 访问设备接入服务,单击“管理控制台”进入“设备接入”控制台。选择您的实例,单击实例卡片进入。
  2. 单击左侧导航栏的“产品”,在产品列表中,找到对应的产品,单击产品进入产品详情页。
  3. 在产品详情基本信息页面,单击“自定义模型”,添加服务。
  4. 输入“服务ID”、“服务类型”和“服务描述”,然后单击“确定”。

    • “服务ID”:采用首字母大写的命名方式。比如:WaterMeter、StreetLight。
    • “服务类型”:建议和服务ID保持一致。
    • “服务描述”:比如路灯上报的环境光强度和路灯开关状态的属性。

    添加服务后,在“添加服务”区域,对属性和命令进行定义。每个服务下,可以包含属性和命令,也可以只包含其中之一,请根据此类设备的实际情况进行配置。

  5. 单击步骤4新增的服务ID,在展开的页面单击“新增属性”,在弹出窗口中配置属性的各项参数,然后单击“确定”。

    参数

    说明

    属性名称

    建议采用驼峰形式,如batteryLevel、internalTemperature。

    数据类型

    • int:当上报的数据为整数时,可配置为此类型。
    • long: 当上报的数据为长整型时,可配置为此类型。
    • decimal:当上报的数据为小数时,可配置为此类型。配置“经纬度”属性时,数据类型建议使用“decimal”。
    • string:当上报的数据为字符串、枚举值时,可以配置为此类型。如果为枚举值,值之间需要用英文逗号(“,”)分隔。
    • dateTime:当上报的数据为日期时,可以配置为此类型。

      此类型属性上报格式推荐样例:2020-09-01T18:50:20Z或者2020-09-01T18:50:20.200Z

    • jsonObject:当上报的数据为JSON结构体时,可以配置为此类型。
    • enum: 当上报的数据为枚举值时,可配置为此类型。

      搭配参数enumList格式填写,比如状态属性的enumList填写为OPEN,CLOSE,那么属性上报格式样例为"OPEN"或者"CLOSE"

    • boolean: 当上报的数据为布尔值时,可配置为此类型。

      此类型属性上报推荐格式样例:true/false 或者 0/1

    • stringList: 当上报的数据为字符串数组时,可配置为此类型。

    此类型属性上报推荐格式样例:["str1","str2","str3"]

    访问权限

    • 可读:通过接口可以查询该属性。
    • 可写:通过接口可以修改该属性值。

    取值范围

    请根据此类设备的实际情况进行配置。

    步长

    单位

我建立了一个smarthome的物模型,包括温度、湿度和LED状态3个属性,其中LED状态属性是平台可以修改的,另外两个都是设备上报的。

连接华为云平台

先建立IoTDA.ets,内容如下:

import { MqttAsync, MqttClient, MqttPublishOptions, MqttResponse, MqttSubscribeOptions } from '@ohos/mqtt'

export class MQTT{
  private serverUrl: string = 'tcp://bde4cbe7aa.st1.iotda-device.cn-north-4.myhuaweicloud.com:1883'
  private clientId: string = 'xxxxx_test_0_0_2025020502'
  private userName: string = 'xxxxx_test'
  private password: string = 'xxxxxxxxxxxxxx'
  public mqttAsyncClient: MqttClient = MqttAsync.createMqtt({
    url: this.serverUrl,
    clientId: this.clientId,
    persistenceType: 1,
  })

  private topic: string = "light002"

  connectMqtt() {
    this.mqttAsyncClient.connect({
      userName: this.userName,
      password: this.password,
    }).then(() => {
      this.mqttAsyncClient.isConnected()
        .then((data: boolean) => {
          console.log("连接状态: " + data)
        });
    }).catch(() => {
      console.warn('连接失败')
    })
  }

  subscribeMqtt(topic: string) {
    let subscribeOption: MqttSubscribeOptions = {
      topic: topic,//主题名称
      qos: 0            //消息的服务质量设置
    }
    this.mqttAsyncClient.subscribe(subscribeOption)
      .then((data: MqttResponse) => {
        console.log("订阅成功 " + JSON.stringify(data));
      }).catch((err: MqttResponse) => {
      console.log("订阅失败" + JSON.stringify(err));
    })
  }

  messageArrived() {
    this.mqttAsyncClient.messageArrived((err, data) => {
      if (err) {
        console.error("接收消息时发生错误:", err);
      } else {
        console.log("接收到的消息:",JSON.stringify(data));
      }
    });
  }

  publish(topic:string, message:string) {
    let publishOption: MqttPublishOptions = {
      topic: topic,               //主题名称
      qos: 0,                     //消息的服务质量设置
      payload: message,//发布的消息
    };

    this.mqttAsyncClient.publish(publishOption)
      .then((data: MqttResponse) => {
        console.log("推送成功" + JSON.stringify(data));
      })
      .catch((err: Error) => {
        console.log("推送失败" + JSON.stringify(err));
      });
  }

  disconnect() {
    this.mqttAsyncClient.disconnect()
      .then((data: MqttResponse) => {
        console.log("断开成功:" + JSON.stringify(data));
      })
      .catch((err: MqttResponse) => {
        console.log("断开失败:" + JSON.stringify(err));
      })
  }
}
export const mqtt = new MQTT();

在index.ets开头加上下面的语句:

import {mqtt} from './IoTDA'

 在SmartHomeControlPanel部分加上:

  deviceId: string = "XXXX_test";

  aboutToAppear() {
    // 创建MQTT客户端
    mqtt.connectMqtt()
  }

这样就可以在程序一启动时先连接上华为云的服务器。 

发布LED灯状态

代码重构

我们将原来的LED灯控制逻辑进行一下重构,添加如下函数:

  // 控制LED灯的状态并发布消息
  private setLedStatusAndPublish() {
    if (this.lightStatus) {
      console.info("this toggle is On.");
      let res: string = testNapi.gpio_on(this.ledPath);
      console.info(res);
    } else {
      console.info("this.toggle is Off.");
      let res: string = testNapi.gpio_off(this.ledPath);
      console.info(res);
    }
    let ledstatus: string = this.lightStatus ? 'ON' : 'OFF';
    const topic = `$oc/devices/${this.deviceId}/sys/properties/report`;
    const message = `{"services": [{"serviceId": "smarthome", "properties": {"LED状态": "${ledstatus}"}}]}`;
    mqtt.publish(topic, message);
  }

然后将灯光控制按钮修改为:

      // 灯光控制按钮
      Button({
        type: ButtonType.Capsule,
        stateEffect: true,
      }) {
        Row() {
          Image(this.lightStatus ? $r('app.media.light_off_icon') : $r('app.media.light_on_icon'))
            .width(48)
            .height(48)
            .margin({ right: 10 })
          Text(this.lightStatus ? '关闭灯光' : '打开灯光')
            .fontSize(20)
            .fontColor('#FFFFFF')
        }
      }
      .width('80%')
      .height(80)
      .margin({ top: 20 })
      .onClick(() => {
        this.lightStatus = !this.lightStatus;
        this.setLedStatusAndPublish();
      })

 这样我们每按下灯光控制按钮,就会自动向云平台发送一下最新的状态。

测试结果

我们可以测试一下,看到云平台显示的状态信息如下:

接收平台发送的属性修改命令

设备侧API

当平台向设备设置属性信息时,首先设备必须在线。

设备侧会自动收到特定Topic的查询请求(无需订阅),设备收到属性查询请求后,需要将设备的属性数据返回给平台,如果设备没回响应平台会认为属性查询请求执行超时。

有关文档可以参考:平台设置设备属性_设备接入 IoTDA_华为云

Topic

下行: $oc/devices/{device_id}/sys/properties/set/request_id={request_id}

上行: $oc/devices/{device_id}/sys/properties/set/response/request_id={request_id}

下行请求参数说明

字段名

必选/可选

类型

参数描述

object_device_id

可选

String

参数解释:

  • 平台下发时,若为直连设备,不携带该参数。
  • 平台下发时,若为网关子设备,该参数为Topic中设备的子设备ID。

services

必选

List<ServiceProperty>

设备服务数据列表。

ServiceProperty结构定义:

字段名

必选/可选

类型

参数描述

service_id

必选

String

参数解释:

设备的服务ID,由创建的产品模型确定。

properties

必选

Object

参数解释:

设备服务的属性列表,具体字段在产品模型里定义,可以设置多个字段。

上行响应参数说明

字段名

必选/可选

类型

参数描述

result_code

可选

Integer

参数解释:

命令的执行结果,0表示成功,其他表示失败。不带默认认为成功。

result_desc

可选

String

参数解释:

属性设置的响应描述。

程序修改

我们需要在aboutToAppear中加上接收云端消息的函数,同时在其中进行JSON解析。ArkTS的JSON解析有点特殊,参见我写的博文。下面就是修改好的代码:

// 定义设备属性类
class DeviceProperties {
  温度: number;
  湿度: number;
  LED状态: string;

  constructor(温度: number, 湿度: number, LED状态: string) {
    this.温度 = 温度;
    this.湿度 = 湿度;
    this.LED状态 = LED状态;
  }
}

// 定义服务类
class Service {
  serviceId: string;
  properties: DeviceProperties;

  constructor(serviceId: string, properties: DeviceProperties) {
    this.serviceId = serviceId;
    this.properties = properties;
  }
}

// 定义整个数据结构类
class ServiceData {
  services: Service[];

  constructor(services: Service[]) {
    this.services = services;
  }
}

@Entry
@Component
struct SmartHomeControlPanel {
……

  aboutToAppear() {
    // 创建MQTT客户端
    mqtt.connectMqtt()
    mqtt.mqttAsyncClient.messageArrived((err, data) => {
      if (err) {
        console.error("接收消息时发生错误:", err);
      } else {
        console.log("接收到的消息:",JSON.stringify(data));

        // 处理接收到的消息
        if(data.topic.indexOf('sys/properties/set')) {
          let upTopic = this.getUpstreamTopic(data.topic)
          let resultString = '{"result_code": 0, "result_desc": "success" }'
          mqtt.publish(upTopic, resultString);
        }

        // 解析 JSON 字符串
        const parsedData = JSON.parse(data.payload) as ServiceData;

        const ledStatus = parsedData.services[0].properties.LED状态;

        // 打印结果
        console.log(`Service ID: ${parsedData.services[0].serviceId}`);
        console.log(`LED状态: ${parsedData.services[0].properties.LED状态}`);
        if(ledStatus === 'ON'){
          this.lightStatus = true;
        }else {
          this.lightStatus = false;
        }
        this.setLedStatusAndPublish();
      }
    });
  }

应用侧API

华为的物联网平台提供了应用侧API,实现设备数据采集、命令下发、设备管理等业务场景。

有关应用侧API参见:应用侧API参考_设备接入 IoTDA_华为云

为了简化应用侧程序的开发,华为提供了各种语言的SDK:SDK概述_设备接入 IoTDA_华为云。不过,今天不介绍SDK,只介绍如何用API Explorer进行API测试和学习。

华为云API Explorer为开发者提供一站式API 解决方案 统一平台,集成华为 云服务 所有开放API,支持全量快速检索、可视化调试、帮助文档、代码示例等能力,帮助开发者快速查找、学习API和使用API开发代码。API Explorer致力于帮助您更快地查找华为云Open API,您可以使用API Explorer来检索华为云开放的API并查看相应的文档,同时应用于API调试、 故障排查 等场景。

API Explorer的网站:https://console.huaweicloud.com/apiexplorer/#/openapi/overview

测试设备属性设置功能

我在API Explorer中对test设备的“LED状态”属性进行了修改。

程序可以正常收到下发的指令,并根据指令进行开灯和关灯操作。

结语

至此,我们完成了智能中控屏接入物联网的操作。我们的测试仍然将继续。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐