基于OpenHarmony3.0LTS和HarmonyOS2.0手机的温湿度计案例Demo 原创 精华

Soon_L
发布于 2022-4-5 16:10
浏览
5收藏

【本文正在参与优质创作者激励】
基于OpenHarmony3.0LTS和HarmonyOS2.0手机的温湿度计案例Demo-鸿蒙开发者社区

1、概述

南向:基于OpenHarmony-v3.0-LTS 在润和智能家居套件上实现
1).自动连接到用鸿蒙手机创建的热点,指定热点名称OHOS、加密类型WPA2-Personal、密*码1234567890。
2).进行温湿度燃气检测,在OLED上显示,将数据通过UDP发送到手机端(192.168.43.1)。

北向: 开发鸿蒙手机应用监听UDP数据,并显示温湿度数据

目前不足:
1).Wifi 配网目前是代码里面写死的,碰一碰没有上
2).UDP 接收端IP也是写死的

作为简单Demo分享,如果想要比较完善的案例可以参考数字管家上的案例.

2、南向Openharmony部分

主要代码结构如下图,完整代码见附件thermostat.zip
基于OpenHarmony3.0LTS和HarmonyOS2.0手机的温湿度计案例Demo-鸿蒙开发者社区

2.1 南向设备端温湿度的读取显示

2.1.1 南向设备端开启I2C配置

温湿度传感器AHT20、OLED显示需要用到I2C功能所以需要开启
device\hisilicon\hispark_pegasus\sdk_liteos\build\config\usr_config.mk

CONFIG_I2C_SUPPORT=y

2.1.2 南向设备端AHT20驱动修改

Code base用润和官方aht20.caht20.h Openharmony1.0的驱动修改而来,主要修改如下,修改后的完整驱动见附件:
头文件和define修改如下:

#include "wifiiot_i2c.h"
#include "wifiiot_errno.h"

#define AHT20_I2C_IDX WIFI_IOT_I2C_IDX_0

改为

#include "hi_i2c.h"

#define AHT20_I2C_IDX 0

并添加以下define和struct

/**
* @brief Defines a module-level return value to indicate a successful operation.
*
*/
#define WIFI_IOT_SUCCESS    0
/**
* @brief Defines a module-level return value to indicate an operation failure.
*
*/
#define WIFI_IOT_FAILURE   (-1)


// unsigned int I2cSetBaudrate(WifiIotI2cIdx id, unsigned int baudrate);
typedef struct {
    /** Pointer to the buffer storing data to send */
    unsigned char *sendBuf;
    /** Length of data to send */
    unsigned int  sendLen;
    /** Pointer to the buffer for storing data to receive */
    unsigned char *receiveBuf;
    /** Length of data received */
    unsigned int  receiveLen;
} IotI2cData;

修改AHT20_Read和AHT20_Write如下

static uint32_t AHT20_Read(uint8_t* buffer, uint32_t buffLen)
{
    WifiIotI2cData data = { 0 };
    data.receiveBuf = buffer;
    data.receiveLen = buffLen;
    uint32_t retval = I2cRead(AHT20_I2C_IDX, AHT20_READ_ADDR, &data);
    if (retval != WIFI_IOT_SUCCESS) {
        printf("I2cRead() failed, %0X!\n", retval);
        return retval;
    }
    return WIFI_IOT_SUCCESS;
}

static uint32_t AHT20_Write(uint8_t* buffer, uint32_t buffLen)
{
    WifiIotI2cData data = { 0 };
    data.sendBuf = buffer;
    data.sendLen = buffLen;
    uint32_t retval = I2cWrite(AHT20_I2C_IDX, AHT20_WRITE_ADDR, &data);
    if (retval != WIFI_IOT_SUCCESS) {
        printf("I2cWrite(%02X) failed, %0X!\n", buffer[0], retval);
        return retval;
    }
    return WIFI_IOT_SUCCESS;
}

改为

static uint32_t AHT20_Read(uint8_t* buffer, uint32_t buffLen)
{
    IotI2cData data = { 0 };
    data.receiveBuf = buffer;
    data.receiveLen = buffLen;
    uint32_t retval = IoTI2cRead(AHT20_I2C_IDX, AHT20_READ_ADDR, data.receiveBuf, data.receiveLen);
    if (retval != WIFI_IOT_SUCCESS) {
        printf("I2cRead() failed, %0X!\n", retval);
        return retval;
    }
    return WIFI_IOT_SUCCESS;
}

static uint32_t AHT20_Write(uint8_t* buffer, uint32_t buffLen)
{
    IotI2cData data = { 0 };
    data.sendBuf = buffer;
    data.sendLen = buffLen;
    uint32_t retval = IoTI2cWrite(AHT20_I2C_IDX, AHT20_WRITE_ADDR, data.sendBuf, data.sendLen);
    if (retval != WIFI_IOT_SUCCESS) {
        printf("I2cWrite(%02X) failed, %0X!\n", buffer[0], retval);
        return retval;
    }
    return WIFI_IOT_SUCCESS;
}

2.1.3 南向设备端OLED 显示驱动修改

见附件的oled.c、oled.h和oledfont.h,忘记哪里来的code base了,openharmony3.0与openharmony1.0的版本的差异仍然是I2C的API,主要差异如下
Openharmony1.0上

#include "wifiiot_i2c.h"

u8 g_send_data[2] = { 0 };

u32 my_i2c_write(WifiIotI2cIdx id, u16 device_addr, u32 send_len)
{
    u32 status;
    WifiIotI2cData es8311_i2c_data = { 0 };

    es8311_i2c_data.sendBuf = g_send_data;
    es8311_i2c_data.sendLen = send_len;
    status = I2cWrite(id, device_addr, &es8311_i2c_data);
    if (status != 0) {
        printf("===== Error: I2C write status = 0x%x! =====\r\n", status);
        return status;
    }

    return 0;
}

/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(u8 IIC_Command)
{
    g_send_data[0] = 0x00;
    g_send_data[1] = IIC_Command;

    my_i2c_write(WIFI_IOT_I2C_IDX_0, 0x78, 2);
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(u8 IIC_Data)
{
    g_send_data[0] = 0x40;
    g_send_data[1] = IIC_Data;

    my_i2c_write(WIFI_IOT_I2C_IDX_0, 0x78, 2);
}

Openharmony3.0

#include "iot_i2c.h"

#define OLED_I2C_IDX 0

#define OLED_I2C_ADDR 0x78 // 默认地址为 0x78
#define OLED_I2C_CMD 0x00 // 0000 0000       写命令
#define OLED_I2C_DATA 0x40 // 0100 0000(0x40) 写数据

// unsigned int I2cSetBaudrate(WifiIotI2cIdx id, unsigned int baudrate);
typedef struct {
    /** Pointer to the buffer storing data to send */
    unsigned char *sendBuf;
    /** Length of data to send */
    unsigned int  sendLen;
    /** Pointer to the buffer for storing data to receive */
    unsigned char *receiveBuf;
    /** Length of data received */
    unsigned int  receiveLen;
} IotI2cData;

static uint32_t I2cWiteByte(uint8_t regAddr, uint8_t byte)
{
    unsigned int id = OLED_I2C_IDX;
    uint8_t buffer[] = {regAddr, byte};
    IotI2cData i2cData = {0};

    i2cData.sendBuf = buffer;
    i2cData.sendLen = sizeof(buffer)/sizeof(buffer[0]);

    return IoTI2cWrite(id, OLED_I2C_ADDR, i2cData.sendBuf, i2cData.sendLen);
}

/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(u8 IIC_Command)
{
	I2cWiteByte(OLED_I2C_CMD, IIC_Command);
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(u8 IIC_Data)
{
	I2cWiteByte(OLED_I2C_DATA, IIC_Data);
}

2.2 南向设备端WIFI连接及UDP通讯的建立

可以参考之前的帖子#开发板漂流计划#小车控制由简入繁之UDP控制

2.2.1 南向设备端WIFI 连接的建立

wifi连接的代码仍然使用了润和Gitee中的wifi_connecter.cwifi_connecter.h
在thermostat.c调用主要代码如下,如需更改wifi账号和密*码只需修改WIFI_SSID和WIFI_PWD

#include "wifi_connecter.h"
...
#define WIFI_SSID "OHOS"//手机开启的热点名称
#define WIFI_PWD "1234567890"//手机开启热点的密*码
#define WIFI_HOST_IP "192.168.43.1"//手机开启热点后默认的IP
...
static void Thermostat_DemoTask(void *arg)
{
...
    // setup your AP params
    WifiDeviceConfig apConfig = {0};
    strcpy(apConfig.ssid, WIFI_SSID);
    strcpy(apConfig.preSharedKey, WIFI_PWD);
    apConfig.securityType = WIFI_SEC_TYPE_PSK;
	
	int netId = ConnectToHotspot(&apConfig);//配网开始会阻塞等待直到联网成功
...
}

2.2.2 南向设备端UDP Server 温度检测和数据组合发送

主要代码在applications\sample\wifi-iot\app\thermostat\src\thermostat.c中,相关代码如下,进行温湿度和燃气检测,然后组合成字符串“temp: 24.83, humi: 31.00, gas: 0.54”发送

#include "lwip/sockets.h"

#define WIFI_HOST_IP "192.168.43.1"//手机开启热点后默认的IP


static float ConvertToVoltage(unsigned short data)
{
    return (float)data * 1.8 * 4 / 4096;
}

void UdpServer(unsigned short port)
{
    ssize_t retval = 0;
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // UDP socket
    float humidity = 0.0f;
    float temperature = 0.0f;
    float gasSensorResistance = 0.0f;
    static char line[32] = {0};
    static char sendmessage[128] = "";
    unsigned short data = 0;

    struct sockaddr_in clientAddr = {0};
    clientAddr.sin_family = AF_INET;
    clientAddr.sin_port = htons(port); // 端口号,从主机字节序转为网络字节序
    if (inet_pton(AF_INET, WIFI_HOST_IP, &clientAddr.sin_addr) <= 0) { // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
        printf("inet_pton failed!\r\n");
        goto do_cleanup;
    }
    socklen_t clientAddrLen = sizeof(clientAddr);
    struct sockaddr_in serverAddr = {0};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    if (retval < 0) {
        printf("bind failed, %ld!\r\n", retval);
        goto do_cleanup;
    }
    printf("bind to port %d success!\r\n", port);

    //初始化AHT20
    while (0 != AHT20_Calibrate()) {
        printf("AHT20 sensor init failed!\r\n");
        sleep(1);
        OLED_Clear();
        OLED_ShowString(0,16,"AHT20 Init Fail",16);
        OLED_Refresh();
    }

    while(1)
    {
        // 发送 触发测量 命令,开始测量
        retval = AHT20_StartMeasure();
        if (retval != 0) {
            printf("trigger measure failed!\r\n");
        }
        // 接收测量结果,拼接转换为标准值
        retval = AHT20_GetMeasureResult(&temperature, &humidity);
        if (retval != 0) {
            printf("get humidity data failed!\r\n");
        }

        //读取燃气传感器ADC值
        if (hi_adc_read(HI_ADC_CHANNEL_2, &data, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAIS_DEFAULT, 0)
                == 0) {
            float Vx = ConvertToVoltage(data);

            // Vcc            ADC            GND
            //  |    ______   |     ______   |
            //  +---| MG-2 |---+---| 1kom |---+
            //       ------         ------
            // 查阅原理图,ADC 引脚位于 1K 电阻和燃气传感器之间,燃气传感器另一端接在 5V 电源正极上
            // 串联电路电压和阻止成正比:
            // Vx / 5 == 1kom / (1kom + Rx)
            //   => Rx + 1 == 5/Vx
            //   =>  Rx = 5/Vx - 1
            gasSensorResistance = 5 / Vx - 1;
        }
        //将信息显示到OLED上
        OLED_ShowString(0, 0, "UDP Port:62021", 16);

        snprintf(line, sizeof(line), "temp: %.2f", temperature);
        OLED_ShowString(0, 16, line, 16);

        snprintf(line, sizeof(line), "humi: %.2f", humidity);
        OLED_ShowString(0, 32, line, 16);

        snprintf(line, sizeof(line), "gas: %.2f kom", gasSensorResistance);
        OLED_ShowString(0, 48, line, 16);
        OLED_Refresh();
        sleep(1);
        //组合要发送的字符数据
        snprintf(sendmessage, sizeof(sendmessage), "temp: %.2f, humi: %.2f, gas: %.2f", temperature, humidity, gasSensorResistance);
        //通过UDP发送数据
        retval = sendto(sockfd, sendmessage, strlen(sendmessage), 0, (struct sockaddr *)&clientAddr, sizeof(clientAddr));
    }

do_cleanup:
    printf("do_cleanup...\r\n");
    close(sockfd);
}

static void Thermostat_DemoTask(void *arg)
{
...
    UdpServer(62021);//启动 UDP 服务,端口62021
...
}

3、北向HarmonyOS 2.0部分

代码结构如下
基于OpenHarmony3.0LTS和HarmonyOS2.0手机的温湿度计案例Demo-鸿蒙开发者社区
关键代码见附件src.zip

3.1 北向手机端 UI部分

只是做文本显示布局截图如下
基于OpenHarmony3.0LTS和HarmonyOS2.0手机的温湿度计案例Demo-鸿蒙开发者社区
DemoApp\entry\src\main\js\default\pages\index\index.hml 内容如下

<div class="container">
    <text class="text">
        {{ $t('strings.temp') }} : {{ temp }} ℃
    </text>
    <text class="text">
        {{ $t('strings.humi') }} :{{ humi }} %RH
    </text>
    <text class="text">
        {{ $t('strings.gas') }} : {{ gas }}
    </text>
</div>

3.2 北向手机端添加网络权限

需要在DemoApp\entry\src\main\config.json中增加网络权限ohos.permission.INTERNET这样才能监听UDP数据

  "module": {
  
    ...
	
    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
  
    ...
  }

3.3 北向手机端UDP 消息的监听

完整代码在\DemoApp\entry\src\main\java\com\soon\demoapp\UpdateDataServiceAbility.java下,关键代码截取如下

import java.net.DatagramPacket;
import java.net.DatagramSocket;


    @Override
    protected void onStart(Intent intent) {
        HiLog.info(LABEL, "DemoApp onStart");

        UpdateDataServiceAbility.GetStatusInfo getstatusinfo = new UpdateDataServiceAbility.GetStatusInfo();
        Thread t = new Thread(getstatusinfo, "getstatusinfoThread");
        t.start();
        HiLog.info(LABEL, "线程开始执行--> " + t.isAlive());//判断是否启动
        super.onStart(intent);
    }


    class GetStatusInfo implements Runnable {
        @Override
        public void run() {
            try {
                //监听端口设为 62021
                server_sock = new DatagramSocket(62021);
                recv_buffer = new byte[1024];//接收缓冲区,byte型
                pac = new DatagramPacket(recv_buffer, recv_buffer.length);//构造一个packet
                recv_string = "";
                HiLog.info(LABEL, "开始等待消息 ");
                while (true)//循环接受数据
                {
                    server_sock.receive(pac);//阻塞式接收数据
                    //将byte[]转化成string
                    recv_string = new String(recv_buffer, 0, pac.getLength());
                    HiLog.info(LABEL, "接受到UDP数据:" + recv_string + "长度:" + recv_string.length());
                    if(recv_string.length() >= 35) {
                        /*
                        System.out.println("Soon 0~4:" + recv_string.substring(0, 4));
                        System.out.println("Soon 6~11:" + recv_string.substring(6, 11));

                        System.out.println("Soon 13~17:" + recv_string.substring(13, 17));
                        System.out.println("Soon 19~24:" + recv_string.substring(19, 24));

                        System.out.println("Soon 26~29:" + recv_string.substring(26, 29));
                        System.out.println("Soon 31~35:" + recv_string.substring(31, 35));
                        */
                        if (recv_string.substring(0, 4).equals("temp")
                            &&recv_string.substring(13,17).equals("humi")
                            &&recv_string.substring(26,29).equals("gas")) {
                            tempValue = recv_string.substring(6, 11);
                            humiValue = recv_string.substring(19, 24);
                            gasValue = recv_string.substring(31, 35);
                            HiLog.info(LABEL, "tempValue:" + tempValue);
                            HiLog.info(LABEL, "humiValue:" + humiValue);
                            HiLog.info(LABEL, "gasValue:" + gasValue);
                            try {
                                Intent intent = new Intent();
                                Operation operation = new Intent.OperationBuilder()
                                        .withAction(COMMON_UDP_INFO_CHANGED)//自定义字符串类型的action
                                        .build();
                                intent.setOperation(operation);
                                intent.setParam("result","commonEventData");
                                intent.setParam("isCommonEvent",true);
                                CommonEventData eventData = new CommonEventData(intent);
                                CommonEventManager.publishCommonEvent(eventData);
                                HiLog.info(LABEL, "PublishCommonEvent SUCCESS");
                            } catch (RemoteException e) {
                                HiLog.info(LABEL, "Exception occurred during publishCommonEvent invocation.");
                            }
                        }
                    }

                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

在收到数据更新的时候会发一个公共消息COMMON_UDP_INFO_CHANGED通知数据更新,不知道是否有其他更好的方式,欢迎指教。

                            try {
                                Intent intent = new Intent();
                                Operation operation = new Intent.OperationBuilder()
                                        .withAction(COMMON_UDP_INFO_CHANGED)//自定义字符串类型的action
                                        .build();
                                intent.setOperation(operation);
                                intent.setParam("result","commonEventData");
                                intent.setParam("isCommonEvent",true);
                                CommonEventData eventData = new CommonEventData(intent);
                                CommonEventManager.publishCommonEvent(eventData);
                                HiLog.info(LABEL, "PublishCommonEvent SUCCESS");
                            } catch (RemoteException e) {
                                HiLog.info(LABEL, "Exception occurred during publishCommonEvent invocation.");
                            }

3.4 北向手机端JSFA 调用PA的实现

详细的讲解可以论坛上搜索有很多JSFA 调用PA的介绍,这里我是用的是Ability调用方式,接口调用试用同步方式。

3.4.1 北向手机端JS FA端代码

DemoApp\entry\src\main\js\default\pages\index\index.js完整代码如下

// 定义常量 0-Ability、1-Internal Ability
const ABILITY_TYPE_EXTERNAL = 0;//Ability调用方式
const ABILITY_TYPE_INTERNAL = 1;//Internal Ability调用方式
// 接口调用同步或者异步
const ACTION_SYNC = 0;//同步方式
const ACTION_ASYNC = 1;//异步方式

// 业务码
const ACTION_MESSAGE_UDP_UPDATE = 1001;// 主动更新
const ACTION_MESSAGE_UDP_SUBSCRIBE = 1002;// 订阅Subscribe
const ACTION_MESSAGE_UDP_UNSUBSCRIBE = 1003;// 取消订阅Unsubscribe
const SUCCESS = 0;

var timer = null;

export default {
    data: {
        temp : "",
        humi : "",
        gas : ""
    },
    environmentStatus: function () {
        this.getEnvironmentStatus();
    },
    environmentStatusSubscribe: function () {
        this.startEnvironmentStatusSubscribe();
    },
    environmentStatusUnSubscribe: function () {
        this.startEnvironmentStatusUnSubscribe();
    },
    initAction: function (code) {
        var actionData = {};
        var action = {};
        action.bundleName = "com.soon.demoapp";
        action.abilityName = "com.soon.demoapp.UpdateDataServiceAbility";
        action.messageCode = code;
        action.data = actionData;
        action.abilityType = ABILITY_TYPE_EXTERNAL;
        action.syncOption = ACTION_SYNC;
        return action;
    },
    onInit() {
        console.info('Demo App onInit')
        this.environmentStatusSubscribe();
    },
    onShow(){
        console.info('Demo App onShow')
    },
    onActive(){
        console.info('Demo App onActive')
    },
    onDestroy(){
        console.info('Demo App onDestroy')
        this.environmentStatusUnSubscribe();
    },
    getEnvironmentStatus: async function() {
        var action = this.initAction(ACTION_MESSAGE_UDP_UPDATE); //给封装好的初始化函数传递操作码,确定要调用的业务
        var result = await FeatureAbility.callAbility(action);
        var ret = JSON.parse(result);

        if (ret.code == SUCCESS) {
            this.temp = ret.temp;
            this.humi = ret.humi;
            this.gas = ret.gas;
            console.info('getEnvironmentStatus temp is:' + JSON.stringify(ret.temp)
            + ' humi is:' + JSON.stringify(ret.humi)
            + ' gas is:' + JSON.stringify(ret.gas));
        } else {
            this.temp = "NA";
            this.humi = "NA";
            this.gas = "NA";
            console.error('getEnvironmentStatus error code:' + JSON.stringify(ret.code));
        }
    },
    startEnvironmentStatusSubscribe: async function () {
        try {
            var action = this.initAction(ACTION_MESSAGE_UDP_SUBSCRIBE); //给封装好的初始化函数传递操作码,确定要调用的业务
            var that = this;//that没改变之前仍然是指向当时的this,这样就不会出现找不到原来的对象
            var result = await FeatureAbility.subscribeAbilityEvent(action,function (requestEnvironmentStatus) { //调用订阅服务API
                var envInfo = JSON.parse(requestEnvironmentStatus).data;  //将Json字符串转换为对象,并获取接口返回数据
                that.temp = envInfo.temp;
                that.humi = envInfo.humi;
                that.gas = envInfo.gas;
                console.info('startEnvironmentStatusSubscribe temp is:' + JSON.stringify(that.temp)
                + ' humi is:' + JSON.stringify(that.humi)
                + ' gas is:' + JSON.stringify(that.gas));
            });
            console.info(" startEnvironmentStatusSubscribe result = " + result);
        } catch (pluginError) {
            console.error("startEnvironmentStatusSubscribe error : result= " + result + JSON.stringify(pluginError));
        }
    },
    startEnvironmentStatusUnSubscribe: async function () {
        try {
            var action = this.initAction(ACTION_MESSAGE_UDP_UNSUBSCRIBE);
            var result = await FeatureAbility.unsubscribeAbilityEvent(action);
            FeatureAbility.callAbility(action);
        } catch (pluginError) {
            console.error("startEnvironmentStatusUnSubscribe error : " + JSON.stringify(pluginError));
        }
    }
}

用ACTION_MESSAGE_UDP_UPDATE的方式需要在JS 里面设置定时器一直去刷新,
如果用ACTION_MESSAGE_UDP_SUBSCRIBE和ACTION_MESSAGE_UDP_UNSUBSCRIBE的方式则不需要手动更细在PA有变化之后会通知更新。
另外在实现的时候有遇到用以下写法UI一直没有刷新的问题,

            var result = await FeatureAbility.subscribeAbilityEvent(action,function (requestEnvironmentStatus) { //调用订阅服务API
                var envInfo = JSON.parse(requestEnvironmentStatus).data;  //将Json字符串转换为对象,并获取接口返回数据
                this.temp = envInfo.temp;
                this.humi = envInfo.humi;
                this.gas = envInfo.gas;
                console.info('startEnvironmentStatusSubscribe temp is:' + JSON.stringify(this.temp)
                + ' humi is:' + JSON.stringify(this.humi)
                + ' gas is:' + JSON.stringify(this.gas));
            });

后来才发现,需要将当下的this存到that,这样that没改变之前仍然是指向当时的this,这样就不会出现找不到原来的对象,导致无法更新了,最后改成下面写法解决问题

            var that = this;//that没改变之前仍然是指向当时的this,这样就不会出现找不到原来的对象
            var result = await FeatureAbility.subscribeAbilityEvent(action,function (requestEnvironmentStatus) { //调用订阅服务API
                var envInfo = JSON.parse(requestEnvironmentStatus).data;  //将Json字符串转换为对象,并获取接口返回数据
                that.temp = envInfo.temp;
                that.humi = envInfo.humi;
                that.gas = envInfo.gas;
                console.info('startEnvironmentStatusSubscribe temp is:' + JSON.stringify(that.temp)
                + ' humi is:' + JSON.stringify(that.humi)
                + ' gas is:' + JSON.stringify(that.gas));
            });

3.4.1 北向手机端PA端代码

主要代码在DemoApp\entry\src\main\java\com\soon\demoapp\UpdateDataServiceAbility.java中

// ohos相关接口包
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.event.commonevent.*;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.rpc.*;
import ohos.utils.zson.ZSONObject;

import java.util.HashMap;
import java.util.Map;

public class UpdateDataServiceAbility extends Ability {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, "Soon UpdateDataServiceAbility");

    private MyRemote remote = new MyRemote();
    private CommonEventSubscriber subscriber;

    ...
    private String tempValue;
    private String humiValue;
    private String gasValue;

    private static final int DEFAULT_TYPE = 0;
    private static final String COMMON_UDP_INFO_CHANGED = "UDP_INFO_CHANGED";

    @Override
    protected void onStart(Intent intent) {
        HiLog.info(LABEL, "DemoApp onStart");

        UpdateDataServiceAbility.GetStatusInfo getstatusinfo = new UpdateDataServiceAbility.GetStatusInfo();
        Thread t = new Thread(getstatusinfo, "getstatusinfoThread");
        t.start();
        HiLog.info(LABEL, "线程开始执行--> " + t.isAlive());//判断是否启动
        super.onStart(intent);
    }

    // FA在请求PA服务时会调用Ability.connectAbility连接PA,连接成功后,需要在onConnect返回一个remote对象,供FA向PA发送消息
    @Override
    protected IRemoteObject onConnect(Intent intent) {
        super.onConnect(intent);
        return remote.asObject();
    }

    class MyRemote extends RemoteObject implements IRemoteBroker {
        private static final int SUCCESS = 0;
        private static final int ERROR = 1;
        private static final int ACTION_MESSAGE_UDP_UPDATE = 1001;
        private static final int ACTION_MESSAGE_UDP_SUBSCRIBE = 1002;
        private static final int ACTION_MESSAGE_UDP_UNSUBSCRIBE = 1003;

        MyRemote() {
            super("MyService_MyRemote");
        }

        @Override
        public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
            switch (code) {
                case ACTION_MESSAGE_UDP_UPDATE: {
                    reply.writeString(getEnvironmentInfo());
                    break;
                }
                case ACTION_MESSAGE_UDP_SUBSCRIBE: {
                    subscribeEvent(data, reply, option);
                    break;
                }
                case ACTION_MESSAGE_UDP_UNSUBSCRIBE: {
                    unSubscribeEnvironmentEvent(reply);
                    break;
                }
                default: {
                    Map<String, Object> result = new HashMap<String, Object>();
                    result.put("abilityError", ERROR);
                    reply.writeString(ZSONObject.toZSONString(result));
                    return false;
                }
            }
            return true;
        }
        private void subscribeEvent(MessageParcel data, MessageParcel reply, MessageOption option) {
            MatchingSkills matchingSkills = new MatchingSkills();
            matchingSkills.addEvent(COMMON_UDP_INFO_CHANGED);
            IRemoteObject notifier = data.readRemoteObject();
            CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);
            subscriber = new CommonEventSubscriber(subscribeInfo) {
                @Override
                public void onReceiveEvent(CommonEventData commonEventData) {
                    replyMsg(notifier);
                }
            };
            if (option.getFlags() == MessageOption.TF_SYNC) {
                reply.writeString("subscribe common event success");
            }
            try {
                CommonEventManager.subscribeCommonEvent(subscriber);
                reply.writeString(" subscribe common event success");
            } catch (RemoteException e) {
                HiLog.info(LABEL, "%{public}s", "RemoteException in subscribeNotificationEvents!");
            }
        }
        private void replyMsg(IRemoteObject notifier) {
            MessageParcel notifyData = MessageParcel.obtain();
            notifyData.writeString(getEnvironmentInfo());
            try {
                notifier.sendRequest(DEFAULT_TYPE, notifyData, MessageParcel.obtain(), new MessageOption());
            } catch (RemoteException exception) {
                HiLog.info(LABEL, "%{public}s", "replyMsg RemoteException !");
            } finally {
                notifyData.reclaim();
            }
        }

        private String getEnvironmentInfo() {
            // 返回结果当前仅支持String,对于复杂结构可以序列化为ZSON字符串上报
            Map<String, Object> result = new HashMap<String, Object>();
            result.put("code", SUCCESS);
            result.put("temp", tempValue);
            result.put("humi", humiValue);
            result.put("gas", gasValue);
            return ZSONObject.toZSONString(result);
        }

        private void unSubscribeEnvironmentEvent(MessageParcel reply) {
            try {
                CommonEventManager.unsubscribeCommonEvent(subscriber);
                reply.writeString("Unsubscribe common event success!");
            } catch (RemoteException | IllegalArgumentException exception) {
                reply.writeString("Unsubscribe failed!");
                HiLog.info(LABEL, "%{public}s", "Unsubscribe failed!");
            }
            subscriber = null;
        }
        @Override
        public IRemoteObject asObject() {
            return this;
        }
    }
	...
}

在subscribeEvent 中会监听COMMON_UDP_INFO_CHANGED这个消息,收到这个消息后会通知更新。

以上本次分享内容,谢谢阅读!

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
thermostat.zip 23.01K 70次下载
src.zip 382.6K 44次下载
已于2022-4-21 12:41:18修改
7
收藏 5
回复
举报
1条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

感谢楼主假日分享,必须支持

回复
2022-4-6 10:42:38
回复
    相关推荐