# HDevBoard_Fire火焰案例开发详解 **Repository Path**: zsj2022/hdev-board-fire ## Basic Information - **Project Name**: HDevBoard_Fire火焰案例开发详解 - **Description**: 1.HarmonyOS 基于KHDVK-3861开发火焰报警系统实例应用 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 6 - **Created**: 2025-06-24 - **Last Updated**: 2025-06-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README @[toc](目录) 作者:胡领情 # 前言 在各种灾害中,火灾是最经常、最普遍地威胁公众安全和社会发展的主要灾害之一。火给人类带来文明进步、光明和温暖。但是,有时它是人类的朋友,有时是人类的敌人。失去控制的火,就会给人类造成灾难。说到火灾的控制,一套火焰感应报警系统就有其价值了。那我们如何去检测火焰呢? 本文内容主要讲述基于KHDVK-3861物联网开发板套件的火焰传感器,开发一个具有火焰感应报警功能的HarmonyOS应用,主要实现蓝牙设备扫描,连接,检测火焰,设置报警阈值。 ## 1.效果演示 效果图 ## 2.环境准备 本贴不对实验环境搭建做详细说明。具体准备实验环境请参考: 1.《HarmonyOS 官方文档》,获取地址: https://developer.harmonyos.com/cn/docs/documentation/doc-guides/installation_process-0000001071425528 2、KHDVK-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 蓝牙通信流程: ![](https://gitee.com/skh_7_0/hdev-board-fire/raw/master/img/蓝牙通信时序图.png) #### 3.3 数据透传协议: ##### 3.3.1 串口协议: 消息格式: | 字段 | 长度 | 说明 | | -------- | ---- | ------------------------------------------------------------ | | 协议标识 | 2 | 0xA55A:固定值,HiLink 串口协议标识 | | 协议版本 | 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()扫描蓝牙设备。 ```java /** * 扫描设备 * @param filters 设备过滤器 * @since 2021-10-09 */ public void startScan(List 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 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 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)方法。 ```java /** * 连接到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数据透传协议。 ```java /** * 主动去获取所Tag设备数据,通过Write(消息类型0x01), Notify接收数据 */ public void readInitiativeByTags(List 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 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、通过数据透传协议,解析设备上报的火焰距离。 ```java /** * 协议校验 (从协议标识首节至协议内容尾字节求累加和后再对 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); } ``` ```java /** * 根据串口协议解析 */ 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.结语 以上就是KHDVK-3861物联网开发板火焰传感器的预警功能,和应用程序交互的一个相对简单的流程。场景的交互还有很多种,比如在此基础上搭建远程云端系统,实现远程火焰监控实时预警。有兴趣的伙伴也可以根据开发板其他传感器组合成不同的智能场景。