#星光计划1.0# HarmonyOS 基于LYEVK-3861开发火焰报警系统 原创 精华

深开鸿开发板
发布于 2021-11-2 18:10
浏览
6收藏

作者:胡领情

前言

在各种灾害中,火灾是最经常、最普遍地威胁公众安全和社会发展的主要灾害之一。火给人类带来文明进步、光明和温暖。但是,有时它是人类的朋友,有时是人类的敌人。失去控制的火,就会给人类造成灾难。说到火灾的控制,一套火焰感应报警系统就有其价值了。那我们如何去检测火焰呢?
本文内容主要讲述基于LYEVK-3861物联网开发板套件的火焰传感器,开发一个具有火焰感应报警功能的HarmonyOS应用,主要实现蓝牙设备扫描,连接,检测火焰,设置报警阈值。

1.效果演示

#星光计划1.0# HarmonyOS 基于LYEVK-3861开发火焰报警系统-鸿蒙开发者社区

2.环境准备

本贴不对实验环境搭建做详细说明。具体准备实验环境请参考:

1.《HarmonyOS 官方文档》

2、LYEVK-3861 物联网开发板套件

3.蓝牙通信说明

3.1 蓝牙通信协议:

UUID
服务uuid:(需要续平台保持一致) 0000ffb0-0000-1000-8000-00805f9b34fb
特征1uuid:(属性只写) 0000ffb1-0000-1000-8000-00805f9b34fb
特征2uuid:(属性只通知) 0000ffb2-0000-1000-8000-00805f9b34fb

3.2 蓝牙通信流程:

#星光计划1.0# HarmonyOS 基于LYEVK-3861开发火焰报警系统-鸿蒙开发者社区

3.3 数据透传协议:

3.3.1 串口协议:

消息格式:

字段 长度 说明
协议标识 2 0xA55A:固定值,串口协议标识
协议版本 1 1~255:ver1-ver255
消息类型 1 标识消息的类型
数据长度 2 数据字段长度
数据 N 数据单元组
协议校验 1 从协议标识首节至协议内容尾字节求累加和后再对 256 取余数据单元组
字段 长度(byte) 说明
tagid 2 根据3.3.2数据协议,自定义
len 2 长度对应 value 的字节数
value 1/2/4/N hex 表示
3.3.2 数据协议:

(permission P:APP下发;G:设备请求;R:设备上报)

功能名称 tagid len value permission
火焰距离 0x0001 4 GR
火焰距离报警阈值 0x0002 4 PGR

4.开发调试

蓝牙交互封装BleHelper工具类,通过BLE扫描和广播提供的开放能力,可以根据指定状态获取外围设备、启动或停止BLE扫描、广播。

4.1 进行BLE扫描

1、MyBleCentralManagerCallback继承BleCentralManagerCallback类实现scanResultEvent和scanFailedEvent回调函数,用于接收扫描结果。

2、BleCentralManager(BleCentralManagerCallback callback)接口获取中心设备管理对象。

3、调用startScan()扫描蓝牙设备。

    /**
     * 扫描设备
     * @param filters 设备过滤器
     * @since 2021-10-09
     */
    public void startScan(List<BleScanFilter> filters) {
        centralManager = new BleCentralManager(context, new MyBleCentralManagerCallback());
        centralManager.startScan(filters);
    }

    /**
     * 扫描设备回调
     *
     * @since 2021-10-09
     */
    private class MyBleCentralManagerCallback implements BleCentralManagerCallback {
        // 扫描结果的回调
        @Override
        public void scanResultEvent(BleScanResult bleScanResult) {
            if (mBleManagerCallback != null) {
                mBleManagerCallback.scanResultCallback(bleScanResult);
            }
            // 获取广播数据中的服务uuids
            List<UUID> uuids = bleScanResult.getServiceUuids();
            for (UUID uuid : uuids) {
                if (SERVICE_UUID.equals(uuid.toString())) {
                    peripheralDevice = bleScanResult.getPeripheralDevice();
                    int length = peripheralDevice.toString().length();
                    String deviceId = peripheralDevice.toString().substring(length - CUT_LENGTH, length);
                    stopScan();
                    bleConnect();
                }
            }
        }

        // 扫描失败回调
        @Override
        public void scanFailedEvent(int event) {
            HiLog.debug(loglabel, "扫描失败 scanFailedEvent()");
        }

        // 组扫描成功回调
        @Override
        public void groupScanResultsEvent(List<BleScanResult> list) {
            // 使用组扫描时在此对扫描结果进行处理
        }
    }

4.2 建立连接

1、扫描成功,匹配服务UUID FFB0,调用bleConnect()连接开发板蓝牙。

2、触发connectionStateChangedEvent(int connectionState)回调,connectionState=2连接成功,然后调用discoverServices()接口发现服务。

3、在回调servicesDiscoveredEvent(int status)中获取外围设备支持的服务和特征值,此时才能调用read和write方法读取或者写入对应特征值数据。

4、characteristicChangedEvent(GattCharacteristic characteristic)特征变更的回调中,解析传感器上报数据、校验等。具体数据解析逻辑在ProtocolEntity中parseCharacteristic(String hexStr)方法。

     /**
     * 连接到BLE外围设备
     *
     * @since 2021-10-09
     */
    public void bleConnect() {
        peripheralDevice.connect(false, new BlePeripheralCallback() {
            // 在外围设备上发现服务的回调
            @Override
            public void servicesDiscoveredEvent(int status) {
                super.servicesDiscoveredEvent(status);
                if (status == BlePeripheralDevice.OPERATION_SUCC) {
                    HiLog.debug(loglabel, "发现服务成功 servicesDiscoveredEvent()");
                    for (GattService service : peripheralDevice.getServices()) {
                        checkGattCharacteristic(service);
                    }
                    if (mBleManagerCallback != null) {
                        mBleManagerCallback.connectCallback(status);
                    }
                }
            }

            private void checkGattCharacteristic(GattService service) {
                for (GattCharacteristic tmpChara : service.getCharacteristics()) {
                    if (tmpChara.getUuid().equals(UUID.fromString(NOTIFY_CHARACTER_UUID))) {
                        // 启用特征通知
                        peripheralDevice.setNotifyCharacteristic(tmpChara, true);
                    }
                    if (tmpChara.getUuid().equals(UUID.fromString(WRITE_CHARACTER_UUID))) {
                        // 获取GattCharacteristic
                        writeCharacteristic = tmpChara;
                    }
                }
            }

            // 连接状态变更的回调
            @Override
            public void connectionStateChangeEvent(int connectionState) {
                super.connectionStateChangeEvent(connectionState);
                if (connectionState == ProfileBase.STATE_CONNECTED && !isConnected) {
                    HiLog.debug(loglabel, "连接成功 connectionStateChangeEvent() connectionState:" + connectionState);
                    isConnected = true;
                    // 连接成功在外围设备上发现GATT服务,部分手机发现服务失败,需要延迟调用discoverServices()
                    peripheralDevice.discoverServices();
                }
            }

            // 特征变更的回调
            @Override
            public void characteristicChangedEvent(GattCharacteristic characteristic) {
                super.characteristicChangedEvent(characteristic);
                byte[] value = characteristic.getValue();
                if (value == null) return;
                String toHexStr = DataUtils.toHex(value);
                boolean isVerify = BleHelper.verifyProtocol(toHexStr);//校验
                if (isVerify) {
                    if (protocolEntity == null) {
                        protocolEntity = new ProtocolEntity();
                    }
                    protocolEntity.parseCharacteristic(toHexStr);
                    if (mBleManagerCallback != null) {
                        mBleManagerCallback.characteristicChangedCallback(protocolEntity, toHexStr);
                    }
                }

            }
        });
    }

4.3 预警阈值下发

1、设置火焰报警距离阈值,tagId为0002,当火焰传感器发现火焰,并小于此设置的阈值时,设备上报预警。

2、通过发现服务servicesDiscoveredEvent()回调获取的writeCharacteristic特征,写入数据。数据下发格式按照3.3数据透传协议。

    /**
     * 主动去获取所Tag设备数据,通过Write(消息类型0x01), Notify接收数据
     */
    public void readInitiativeByTags(List<String> tagIds) {
        String hex = getTagsCommandHexStr(tagIds);
        bleManagerWrite(hex);
    }

    /**
     * 写Tag设备数据,通过Write(消息类型0x01), Notify接收数据
     */
    public void writeBleByTag(String tagId, String value) {
        String hex = getTagCommandHexStr(tagId, Integer.parseInt(value));
        bleManagerWrite(hex);
    }

    private void bleManagerWrite(String hex) {
        if (peripheralDevice == null) {
            return;
        }
        writeCharacteristic.setValue(hex.getBytes(StandardCharsets.UTF_8));
        peripheralDevice.writeCharacteristic(writeCharacteristic);
    }

    /**
     * 获取所有Tag数据的Write指令
     *
     * @param tagIds tag集
     */
    private String getTagsCommandHexStr(List<String> tagIds) {
        try {
            StringBuilder sb = new StringBuilder();
            String byte1 = PROTOCOL_ID; //串口协议标识
            String byte2 = PROTOCOL_VERSION;
            String byte3 = TYPE_OBTAIN;
            int dataLen = 8 * tagIds.size();
            String byte4 = DataUtils.encodeHex(dataLen, 4);

            String byteTagLen = DataUtils.encodeHex(4, 4);
            String byteTagValue = "00000000";
            StringBuilder sbTag = new StringBuilder();
            for (String it : tagIds) {
                sbTag.append(it).append(byteTagLen).append(byteTagValue);
            }

            sb.append(byte1).append(byte2).append(byte3).append(byte4).append(sbTag);
            String hexStrSum = DataUtils.makeChecksum(sb.toString());
            int protocolVerify = DataUtils.decodeHEX(hexStrSum) % 256;
            String byteLast = DataUtils.encodeHex(protocolVerify);
            sb.append(byteLast);
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取单个Tag数据的Write指令
     *
     * @param tagId  0001
     * @Param value  写入值
     */
    private String getTagCommandHexStr(String tagId, int value) {
        try {
            StringBuilder sb = new StringBuilder();
            String byte1 = PROTOCOL_ID; //串口协议标识
            String byte2 = PROTOCOL_VERSION;
            String byte3 = TYPE_ISSUED;
            int dataLen = 8;
            String byte4 = DataUtils.encodeHex(dataLen, 4);

            String byteTagId = tagId;
            String byteTagLen = DataUtils.encodeHex(4, 4);
            String byteTagValue = DataUtils.encodeHex(value, 8);

            sb.append(byte1).append(byte2).append(byte3).append(byte4)
                   .append(byteTagId).append(byteTagLen).append(byteTagValue);

            String hexStrSum = DataUtils.makeChecksum(sb.toString());
            int protocolVerify = DataUtils.decodeHEX(hexStrSum) % 256;
            String byteLast = DataUtils.encodeHex(protocolVerify);
            sb.append(byteLast);
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

4.4 火焰距离上报

1、火焰距离上报,通知应用,开始预警。

2、通过3.3数据透传协议,解析设备上报的火焰距离。

    /**
     * 协议校验 (从协议标识首节至协议内容尾字节求累加和后再对 256 取余)
     *
     * @param hexStr 带空格的 A5 5A 01 00 00 08 00 02 00 04 00 00 00 8C
     */
    public static boolean verifyProtocol(String hexStr) {
        if (hexStr.isEmpty()) return false;
        String checkHex = hexStr.substring(0, hexStr.lastIndexOf(" "));
        String lastHex = hexStr.substring(hexStr.lastIndexOf(" ") + 1);
        String hexStrSum = DataUtils.makeChecksum(checkHex);
        return DataUtils.decodeHEX(hexStrSum) % 256 == DataUtils.decodeHEX(lastHex);
    }

    /**
     * 根据串口协议解析
     */
    public boolean parseCharacteristic(String hexStr) {
        try {
            String[] hexs = hexStr.split(" ");
            version = DataUtils.decodeHEX(hexs[2]);
            messageType = DataUtils.decodeHEX(hexs[3]);
            dataLen = DataUtils.decodeHEX(hexs[4] + hexs[5]);
            for (int i = 0; i < dataLen / 8; i++) {
                int startIndex = 6 + (i * 8);
                DeviceData deviceData = new DeviceData();
                deviceData.tagId = DataUtils.decodeHEX(hexs[startIndex] + hexs[startIndex + 1]);
                deviceData.len = DataUtils.decodeHEX(hexs[startIndex + 2] + hexs[startIndex + 3]);
                deviceData.value = DataUtils.decodeHEX(hexs[startIndex + 4] + hexs[startIndex + 5] +
                        hexs[startIndex + 6] + hexs[startIndex + 7]);
                deviceListMap.put(deviceData.tagId, deviceData);
            }
            protocolVerify = DataUtils.decodeHEX(hexs[6 + dataLen]);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

5.结语

以上就是LYEVK-3861物联网开发板火焰传感器的预警功能,和应用程序交互的一个相对简单的流程。场景的交互还有很多种,比如在此基础上搭建远程云端系统,实现远程火焰监控实时预警。有兴趣的伙伴也可以根据开发板其他传感器组合成不同的智能场景。

购买LYEVK-3861开发板套件

https://developer.huawei.com/consumer/cn/market/prod-detail?productId=8b2d9f0cd85445e0ace0410736977695&shopId=641dc12fabac47cdab3f03e7a

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,共建鸿蒙生态,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

【本文正在参与51CTO HarmonyOS技术社区创作者激励-星光计划1.0】

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
HarmonyOS 基于LYEVK-3861开发火焰报警系统.zip 1.6M 63次下载
已于2021-11-3 08:54:48修改
12
收藏 6
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

很详细的南向开发教学,给楼主点赞

2
回复
2021-11-2 21:18:19
longlong899
longlong899

不错!谢谢分享!

2
回复
2021-11-3 10:30:44
回复
    相关推荐