【BQ3568HM开发板】智能家居中控屏连接华为云IoTDA物联网平台

目录

引言

安装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,内容如下:

TypeScript 复制代码
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开头加上下面的语句:

TypeScript 复制代码
import {mqtt} from './IoTDA'

在SmartHomeControlPanel部分加上:

TypeScript 复制代码
  deviceId: string = "XXXX_test";

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

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

发布LED灯状态

代码重构

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

TypeScript 复制代码
  // 控制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);
  }

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

TypeScript 复制代码
      // 灯光控制按钮
      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解析有点特殊,参见我写的博文。下面就是修改好的代码:

TypeScript 复制代码
// 定义设备属性类
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状态"属性进行了修改。

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

结语

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

相关推荐
BY组态18 小时前
【技术分析】Ricon组态系统的模块化架构设计
物联网·iot·web组态·组态
BY组态19 小时前
【教程】如何使用Ricon组态系统快速构建监控画面
物联网·iot·web组态·组态
BY组态1 天前
【对比分析】Ricon组态系统 vs 传统组态软件
运维·物联网·web组态·组态
猿小猴子1 天前
主流 AI IDE 之一的 华为云码道「CodeArts」 介绍
ide·人工智能·ai·华为云
zhaoshuzhaoshu2 天前
BLE(蓝牙低功耗)连接过程详解
物联网·蓝牙·无线
搜佛说2 天前
下一代跨语言原生操作系统商业计划书
物联网·软件工程
BY组态2 天前
Ricon组态系统在实际项目中的应用案例分享
物联网·web组态·组态
Zevalin爱灰灰2 天前
零基础入门学用物联网(ESP8266) 第一部分 基础知识篇(五)
单片机·物联网·嵌入式·esp8266
Web3_Daisy2 天前
Token 分红机制详解:实现逻辑、激励结构与风险分析
大数据·人工智能·物联网·web3·区块链
BY组态2 天前
从零开始:Ricon组态系统快速入门指南
运维·物联网·web组态·组态