鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联 原创

九九花开
发布于 2025-5-19 09:57
浏览
0收藏

一、原理图

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

本篇不涉及硬件相关的功能开发,硬件设备使用MQTT客户端模拟,如果有硬件相关经验的可以直接使用真实硬件代替MQTT客户端。

1、华为云物联网服务器

华为云物联网平台是硬件设备端跟移动APP端数据的中转平台,硬件设备端定时上报采集都的数据到云平台,移动APP端可以从云平台定时的拉取硬件设备端上报的数据并且显示给管理员,移动APP端可以发送指令到云平台,硬件设备端通过”订阅“云平台指定的服务,从而获得移动APP端发送的指令,从而对设备端进行相应的操作。

2、硬件设备端

(1)订阅

硬件设备端可以”订阅“云平台设备的服务,从而获取到移动APP端通过云平台下发给硬件设备端的指令。

(2)发布

硬件设备端可以定时的通过自身的4G或者WIFI模块向云平台上报自己采集的数据。

3、移动APP端

(1)数据展示

移动APP端通过丰富直观的页面效果将传感器采集到的数据展示给管理员。

(2)拉取影子数据

移动APP端可以通过平台提供的接口,通过HTTP请求获取硬件设备端上报的数据。

(3)发送指令

移动APP端可以通过平台提供的接口,通过HTTP请求向硬件设备端发送操作指令。

二、华为IOT云平台

1、注册登录

需要先注册并且登录完成实名认证(https://www.huaweicloud.com/)

2、购买免费版的I0TDA

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

3、创建产品

相当于java的类概念,在物联网平台中,某一类具有相同能力或特征的设备的合集被称为一款产品,比如智慧农业中所有的温度传感器,或者湿度传感器等。

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

4、给产品创建模型

所谓的模型就是该产品需要上传那些数据,这些数据在云平台对应的存储变量,比如温度计需要上传温度。

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

4.1、创建服务ID

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

4.2、添加属性

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

5、创建设备

相当于java的对象概念。

5.1、设备注册

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

5.2、保存设备id以及密码(重要)

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

三、模拟硬件设备客户端

我们目前不考虑硬件设备端的开发过程,我们使用模拟客户端实现硬件设备的模拟。(所有的硬件设备都会遵循相同的数据交互规范,即MQTT协议)

1、模拟客户端下载

一位大佬自研开发的客户端,大家多多支持。

链接:https://pan.baidu.com/s/1xpROACEco3CJj3Vlo3sObQ 提取码:qgm2
鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2、获得云平台IP以及登录端口

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2.1、云平台IP

复制第三步的域名,打开cmd,输入命令:ping 域名

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2.2、端口号

MQTT默认端口号1883

3、换取硬件设备登录云平台的三元组

所谓的三原则就是客户端id、用户名、以及密码,其中需要两个重要的数据就是5.2小节保存的文件里面的device_id跟secret,通过以下链接换取:

https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

4、把三元组信息填入模拟客户端

设备上线成功

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

四、硬件设备端连接云平台

1、订阅

1.1、订阅地址

设备端订阅云平台数据,当移动APP端发送指令,设备端可以获得该指令数据。

固定的订阅地址格式:$oc/devices/{device_Id}/sys/commands/#

在该案例中device_Id替换为668758a55830dc113ecaf2f0_temperature_sen##_###,完整的订阅路径为:

$oc/devices/668758a55830dc113ecaf2f0_temperature_sen##_###/sys/commands/#

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2、上报数据

2.1、上报地址

设备端可以上报自己采集到的数据,上报的数据格式是JSON格式。

固定的上报地址格式:$oc/devices/{device_Id}/sys/properties/report

在该案例中device_Id替换为668758a55830dc113ecaf2f0_temperature_sen###_###,完整的上报路径为:

$oc/devices/668758a55830dc113ecaf2f0_temperature_sen###_###/sys/properties/report

2.2、上报数据格式

{
	"services":[
		{
			"service_id":"all_temperature_sensor",
			"properties":{
				"temperature":11
			}
		}
	]
}
{"services":[{"service_id":"all_temperature_sensor","properties":{"temperature":11}}]}

其中的service_id为:

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

properties为模型里面的数据:

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2.3、模拟客户端

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

云平台查看

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

五、移动APP端连接云平台

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

移动APP端基本有两个操作,一个是获取平台存储的设备端上传的数据,二是通过云平台给设备端发送指定。

1、创建IAM账号

我们每次从云平台上获取设备端上传的数据都需要身份认证,认证的令牌就是token,我们需要调用指定的接口,上传我们个人信息获取token,在线调试以及官方文档:https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneCreateAgencyToken。

1.1、创建IAM账号(要求必须新建一个用户)

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

赋予管理员权限(实际生产根据自身情况赋予权限)

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

在该过程中包含获取token三个非常重要的数据:

IAMDomain:IAM用户所属账号名,就是主账号名称
IAMUser:IAM用户名,就是刚才创建的用户的用户名
IAMPassword:IAM用户密码,就是刚才创建的用户的密码

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

另外一个参数是项目名称:根据自己创建IoTDA实例的地区取对应的值

cn-north-1:项目名称,取值就是cn-north-4

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

然后到统一身份认证里面的项目分类中查看

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

1.2、请求地址

POST https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens?nocatalog=true

1.3、请求参数格式

请求的header参数:
{
	"Content-Type": "application/json"
}
请求的body参数:
{
    "auth": {
        "identity": {
            "methods": [
                "password"                         //固定写法
            ],
            "password": {
                "user": {
                    "domain": {
                        "name": "IAMDomain"        //IAM用户所属账号名
                    },
                    "name": "IAMUser",             //IAM用户名
                    "password": "IAMPassword"      //IAM用户密码
                }
            }
        },
        "scope": {
            "project": {
                "name": "cn-north-4"               //项目名称
            }
        }
    }
}

2、应用内获取token

2.1、安装axios模块

本次项目的HTTP请求使用了axios模块,所以需要先给项目安装axios模块

打开编辑器终端输入命令回车即可:

ohpm install @ohos/axios

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2.2、构建请求体数据模型

因为harmony OS NEXT严格要求数据类型,所以需要创建用于构建请求body参数的数据模型对象

代码简单并且没有技术含量这里省略代码

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

2.3、发送请求获取token

因为token的有效期是24小时,所以我们没有必须每次跟云平台交货都请求token,所以在移动App端把第一次获取到的token存储到首选项中,然后通过定时器每隔24小时以后再次获取一次即可。获取token是全局的方式,所以可以把获取token相关的操作放到项目启动的时候经行,对应到程序中就是写在EntryAbility的onWindowStageCreate方法里面即可

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { IAMTokenPreferencesDataUtil } from '../utils/IAMTokenPreferencesDataUtil';
import { AuthInfo } from '../IAMAuth/AuthInfo';
import { Auth } from '../IAMAuth/Auth';
import { Identity } from '../IAMAuth/Identity';
import { Password } from '../IAMAuth/Password';
import { User } from '../IAMAuth/User';
import { Domain } from '../IAMAuth/Domain';
import { Scope } from '../IAMAuth/Scope';
import axios, { AxiosError, AxiosResponse } from '@ohos/axios';
import { Project } from '../IAMAuth/Project';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }
  getIAMToken = ():void => {
    /**
     * 封装请求body参数
     */
    const authInfo: AuthInfo  = new AuthInfo();
    const auth: Auth = new Auth();

    const identity: Identity = new Identity();
    identity.methods = ['password'];

    const password: Password = new Password();

    const user: User = new User();
    user.name = 'IAM用户名';
    user.password = 'IAM用户密码';
    const domain: Domain = new Domain();
    domain.name = 'IAM用户所属账号名';
    user.domain = domain;
    password.user = user;
    identity.password = password;

    const  scope: Scope = new Scope();
    const project: Project = new Project();
    project.name = 'cn-north-4';
    scope.project = project;

    auth.identity = identity;
    auth.scope = scope;
    authInfo.auth = auth;

    /**
     * 通过axios发送请求
     */
    const url: string = 'https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens?nocatalog=true';
    const axiosInstance = axios.create({
      headers:{
        "Content-Type": "application/json"
      }
    });
    axiosInstance.post(url, authInfo).then((response: AxiosResponse) => {
      /**
       * 相应头里面的x-subject-token就是我们要的token
       */
      const token: string = response.headers['x-subject-token'];
      console.log('testTag','getIAMToken获得的token是:', token);
      /**
       * 将token存储到首选项中
       */
      IAMTokenPreferencesDataUtil.init().setPreferencesData('token', token);
    }).catch((error: AxiosError) => {
      console.log('testTag',JSON.stringify(error));
    })
  }


  onDestroy(): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
	/**
		定时器默认不会先执行先执行一次,必须要到24小时以后才会执行,所以自己手动先调用下
	*/
    this.getIAMToken();
    setInterval(this.getIAMToken.bind(this), 24 * 60 * 59 * 10000);

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

2.4、保存token到首选项

在上面的代码中涉及到的通过首选项保存用户数据的操作,一下是工具类

import { preferences } from '@kit.ArkData';

export class IAMTokenPreferencesDataUtil{
  private static initObject: IAMTokenPreferencesDataUtil = new IAMTokenPreferencesDataUtil();
  private constructor() {
  }
  static init(): IAMTokenPreferencesDataUtil{
    return IAMTokenPreferencesDataUtil.initObject;
  }

  private getPreferencesObject(): preferences.Preferences {
    const preferencesObject = preferences.getPreferencesSync(getContext(), {name: 'iamToken'});
    return preferencesObject;
  }

  getPreferencesData(name: string) {
    const obj: preferences.Preferences = this.getPreferencesObject();
    const token = obj.getSync(name, '') as string;
    return token;
  }
  setPreferencesData(name: string, value: string) {
    const obj: preferences.Preferences = this.getPreferencesObject();
    obj.put(name, value)
  }
}

3、获取云平台设备上传的数据

3.1、请求地址

先通过测试地址显示的配置参数,从而获取真实的请求地址,测试地址如下:

https://console.huaweicloud.com/apiexplorer/#/openapi/IoTDA/doc?api=ShowDeviceShadow

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

3.2、请求参数格式

只需要带两个headers参数:
{
    "Content-Type": "application/json",
    "X-Auth-Token": token
}

3.3、获取云平台数据

我们可以每隔5秒获取一下数据,保证移动APP端和设备端数据同步

import { SensorInfo } from '../../models/SensorInfo';
import { WeatherDetailsCard } from './WeatherDetailsCard'
import { IAMTokenPreferencesDataUtil } from '../../utils/IAMTokenPreferencesDataUtil';
import axios, { AxiosError, AxiosResponse } from '@ohos/axios';
import { hilog } from '@kit.PerformanceAnalysisKit';

@Component
export struct WeatherDetailsCardList {

  @State sensorInfo: SensorInfo = new SensorInfo();

  aboutToAppear(): void {
    this.getSensorAll();
    setInterval(this.getSensorAll.bind(this), 3000);
  }
  getSensorAll = (): void => {
    const token = IAMTokenPreferencesDataUtil.init().getPreferencesData('token');
      //这里记住该成自己的地址
    const url = 'https://#######:443/v5/iot/c7214059c7394275a056d234b44af71c/devices/6684b5b95830dc113eca9620_temperature_sensor_1/shadow';
    const axiosInstance = axios.create({
      headers:{
        "Content-Type": "application/json",
        "X-Auth-Token": token
      }
    });

    axiosInstance.get(url).then((response: AxiosResponse) => {
      const shadow: string = JSON.stringify(response.data['shadow']);
      const shadowObject = JSON.parse(shadow)[0].reported.properties as SensorInfo;
      // 逐个属性进行赋值
      this.sensorInfo.outdoor_temperature = shadowObject.outdoor_temperature;
      this.sensorInfo.longhua_1_green_house_temperature = shadowObject.longhua_1_green_house_temperature;
      this.sensorInfo.longhua_1_green_house_humidity = shadowObject.longhua_1_green_house_humidity;
      this.sensorInfo.longhua_1_green_house_blower = shadowObject.longhua_1_green_house_blower;
      console.log('获取的影子数据是: : ', JSON.stringify(shadow))
      hilog.info(2311, 'testTag', '获取的影子数据是: ', JSON.stringify(this.sensorInfo))
    }).catch((error: AxiosError) => {
      console.log('testTag', JSON.stringify(error));
    })
  }
  build() {
    Row({space: 20}) {
      WeatherDetailsCard({
        title: '室外温度',
        value: this.sensorInfo.outdoor_temperature + '°C',
        icon: $r('app.media.wendu'),
        errorValue: '',
        errorFlagIcon: $r('app.media.shangjiantou'),
        flag: false
      });
      WeatherDetailsCard({
        title: '室内温度',
        value: this.sensorInfo.longhua_1_green_house_temperature + '°C',
        icon: $r('app.media.wendu'),
        errorValue: '10°C',
        errorFlagIcon: $r('app.media.shangjiantou'),
        flag: true
      });
    }
  }
}
注意:
1、setInterval(this.getSensorAll.bind(this), 3000);定时器的这个语法会导致getSensorAll方法内丢失this,我们需要进行两步操作,第一步在定义getSensorAll方法的时候一箭头函数的方式定义: getSensorAll = (): void => {},第二步在定时器的第一个参数上绑定this:this.getSensorAll.bind(this)
2、请求响应回来的数据在获取的时候比较麻烦(相应JSON嵌套的有点多),所以建议一个一个获取。

3.4、数据显示

正确的绑定到页面组件即可

4、通过云平台给设备下发指令

因为刚才我们创建的温度计产品不需要下发指令,我们可以创建另外的产品,比如补光灯、鼓风机等,原理基本相同,这里使用鼓风机为例(产品以及设备自己创建)

4.1、请求地址

同样的先通过测试地址测试获取真实的请求地址,测试地址如下:

https://console.huaweicloud.com/apiexplorer/#/openapi/IoTDA/doc?api=UpdateProperties

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

测试成功获得真实请求地址:

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

注意: 因为是模拟设备,所以模拟设备收到指定以后无法自动回应,这样会导致当前请求超时,但是没关系,模拟设备已经接收到的指令。

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

4.2、请求参数格式

{
  "services": {
    "longhua_1_green_house_blower": 0
  }
}

鸿蒙版《智慧农业APP》通过华为云IoT平台实现软件硬件互联-鸿蒙开发者社区

4.3、移动APP下发指令

4.1、4.2已经实现了在云平台通过HTTPS请求下发指令给设备了,把这个操作通过APP里面的一个按钮点击发送对应的HTTPS请求即可。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
收藏
回复
举报
回复
    相关推荐