目录
引言
安装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博客
建立物模型
在线开发产品模型前需要创建产品。创建产品需要输入产品名称、协议类型、数据格式、所属行业和设备类型等信息,产品模型会使用这些信息作为设备能力字段取值。物联网平台提供了标准模型和厂商模型,这些模型涉及多个领域,模型中提供了已经编辑好的产品模型文件,您可以根据自己的需要对产品模型中的字段进行修改和增删;如果选择自定义产品模型,则需要完整定义产品模型。
操作步骤
- 访问设备接入服务,单击“管理控制台”进入“设备接入”控制台。选择您的实例,单击实例卡片进入。
- 单击左侧导航栏的“产品”,在产品列表中,找到对应的产品,单击产品进入产品详情页。
- 在产品详情基本信息页面,单击“自定义模型”,添加服务。
- 输入“服务ID”、“服务类型”和“服务描述”,然后单击“确定”。
- “服务ID”:采用首字母大写的命名方式。比如:WaterMeter、StreetLight。
- “服务类型”:建议和服务ID保持一致。
- “服务描述”:比如路灯上报的环境光强度和路灯开关状态的属性。
添加服务后,在“添加服务”区域,对属性和命令进行定义。每个服务下,可以包含属性和命令,也可以只包含其中之一,请根据此类设备的实际情况进行配置。
- 单击步骤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 | 参数解释:
|
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状态”属性进行了修改。
程序可以正常收到下发的指令,并根据指令进行开灯和关灯操作。
结语
至此,我们完成了智能中控屏接入物联网的操作。我们的测试仍然将继续。