HarmonyOS基于LYEVK-3861開發(fā)火焰報警系統(tǒng)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
在各種災害中,火災是最經(jīng)常、最普遍地威脅公眾安全和社會發(fā)展的主要災害之一?;鸾o人類帶來文明進步、光明和溫暖。但是,有時它是人類的朋友,有時是人類的敵人。失去控制的火,就會給人類造成災難。說到火災的控制,一套火焰感應報警系統(tǒng)就有其價值了。那我們?nèi)绾稳z測火焰呢?
本文內(nèi)容主要講述基于LYEVK-3861物聯(lián)網(wǎng)開發(fā)板套件的火焰?zhèn)鞲衅?,開發(fā)一個具有火焰感應報警功能的HarmonyOS應用,主要實現(xiàn)藍牙設(shè)備掃描,連接,檢測火焰,設(shè)置報警閾值。
1.效果演示

2.環(huán)境準備
本貼不對實驗環(huán)境搭建做詳細說明。具體準備實驗環(huán)境請參考:
- 《HarmonyOS 官方文檔》
- LYEVK-3861 物聯(lián)網(wǎng)開發(fā)板套件
3.藍牙通信說明
3.1 藍牙通信協(xié)議:
3.2 藍牙通信流程:

3.3 數(shù)據(jù)透傳協(xié)議:
3.3.1 串口協(xié)議:
3.3.2 數(shù)據(jù)協(xié)議:
(permission P:APP下發(fā);G:設(shè)備請求;R:設(shè)備上報)
4.開發(fā)調(diào)試
藍牙交互封裝BleHelper工具類,通過BLE掃描和廣播提供的開放能力,可以根據(jù)指定狀態(tài)獲取外圍設(shè)備、啟動或停止BLE掃描、廣播。
4.1 進行BLE掃描
- MyBleCentralManagerCallback繼承BleCentralManagerCallback類實現(xiàn)scanResultEvent和scanFailedEvent回調(diào)函數(shù),用于接收掃描結(jié)果。
- BleCentralManager(BleCentralManagerCallback callback)接口獲取中心設(shè)備管理對象。
- 調(diào)用startScan()掃描藍牙設(shè)備。
- /**
- * 掃描設(shè)備
- * @param filters 設(shè)備過濾器
- * @since 2021-10-09
- */
- public void startScan(List<BleScanFilter> filters) {
- centralManager = new BleCentralManager(context, new MyBleCentralManagerCallback());
- centralManager.startScan(filters);
- }
- /**
- * 掃描設(shè)備回調(diào)
- *
- * @since 2021-10-09
- */
- private class MyBleCentralManagerCallback implements BleCentralManagerCallback {
- // 掃描結(jié)果的回調(diào)
- @Override
- public void scanResultEvent(BleScanResult bleScanResult) {
- if (mBleManagerCallback != null) {
- mBleManagerCallback.scanResultCallback(bleScanResult);
- }
- // 獲取廣播數(shù)據(jù)中的服務(wù)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();
- }
- }
- }
- // 掃描失敗回調(diào)
- @Override
- public void scanFailedEvent(int event) {
- HiLog.debug(loglabel, "掃描失敗 scanFailedEvent()");
- }
- // 組掃描成功回調(diào)
- @Override
- public void groupScanResultsEvent(List<BleScanResult> list) {
- // 使用組掃描時在此對掃描結(jié)果進行處理
- }
- }
4.2 建立連接
- 掃描成功,匹配服務(wù)UUID FFB0,調(diào)用bleConnect()連接開發(fā)板藍牙。
- 觸發(fā)connectionStateChangedEvent(int connectionState)回調(diào),connectionState=2連接成功,然后調(diào)用discoverServices()接口發(fā)現(xiàn)服務(wù)。
- 在回調(diào)servicesDiscoveredEvent(int status)中獲取外圍設(shè)備支持的服務(wù)和特征值,此時才能調(diào)用read和write方法讀取或者寫入對應特征值數(shù)據(jù)。
- characteristicChangedEvent(GattCharacteristic characteristic)特征變更的回調(diào)中,解析傳感器上報數(shù)據(jù)、校驗等。具體數(shù)據(jù)解析邏輯在ProtocolEntity中parseCharacteristic(String hexStr)方法。
- /**
- * 連接到BLE外圍設(shè)備
- *
- * @since 2021-10-09
- */
- public void bleConnect() {
- peripheralDevice.connect(false, new BlePeripheralCallback() {
- // 在外圍設(shè)備上發(fā)現(xiàn)服務(wù)的回調(diào)
- @Override
- public void servicesDiscoveredEvent(int status) {
- super.servicesDiscoveredEvent(status);
- if (status == BlePeripheralDevice.OPERATION_SUCC) {
- HiLog.debug(loglabel, "發(fā)現(xiàn)服務(wù)成功 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;
- }
- }
- }
- // 連接狀態(tài)變更的回調(diào)
- @Override
- public void connectionStateChangeEvent(int connectionState) {
- super.connectionStateChangeEvent(connectionState);
- if (connectionState == ProfileBase.STATE_CONNECTED && !isConnected) {
- HiLog.debug(loglabel, "連接成功 connectionStateChangeEvent() connectionState:" + connectionState);
- isConnected = true;
- // 連接成功在外圍設(shè)備上發(fā)現(xiàn)GATT服務(wù),部分手機發(fā)現(xiàn)服務(wù)失敗,需要延遲調(diào)用discoverServices()
- peripheralDevice.discoverServices();
- }
- }
- // 特征變更的回調(diào)
- @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 預警閾值下發(fā)
- 設(shè)置火焰報警距離閾值,tagId為0002,當火焰?zhèn)鞲衅靼l(fā)現(xiàn)火焰,并小于此設(shè)置的閾值時,設(shè)備上報預警。
- 通過發(fā)現(xiàn)服務(wù)servicesDiscoveredEvent()回調(diào)獲取的writeCharacteristic特征,寫入數(shù)據(jù)。數(shù)據(jù)下發(fā)格式按照3.3數(shù)據(jù)透傳協(xié)議。
- /**
- * 主動去獲取所Tag設(shè)備數(shù)據(jù),通過Write(消息類型0x01), Notify接收數(shù)據(jù)
- */
- public void readInitiativeByTags(List<String> tagIds) {
- String hex = getTagsCommandHexStr(tagIds);
- bleManagerWrite(hex);
- }
- /**
- * 寫Tag設(shè)備數(shù)據(jù),通過Write(消息類型0x01), Notify接收數(shù)據(jù)
- */
- 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數(shù)據(jù)的Write指令
- *
- * @param tagIds tag集
- */
- private String getTagsCommandHexStr(List<String> tagIds) {
- try {
- StringBuilder sb = new StringBuilder();
- String byte1 = PROTOCOL_ID; //串口協(xié)議標識
- 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數(shù)據(jù)的Write指令
- *
- * @param tagId 0001
- * @Param value 寫入值
- */
- private String getTagCommandHexStr(String tagId, int value) {
- try {
- StringBuilder sb = new StringBuilder();
- String byte1 = PROTOCOL_ID; //串口協(xié)議標識
- 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 火焰距離上報
- 火焰距離上報,通知應用,開始預警。
- 通過3.3數(shù)據(jù)透傳協(xié)議,解析設(shè)備上報的火焰距離。
- /**
- * 協(xié)議校驗 (從協(xié)議標識首節(jié)至協(xié)議內(nèi)容尾字節(jié)求累加和后再對 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);
- }
- /**
- * 根據(jù)串口協(xié)議解析
- */
- 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.結(jié)語
以上就是LYEVK-3861物聯(lián)網(wǎng)開發(fā)板火焰?zhèn)鞲衅鞯念A警功能,和應用程序交互的一個相對簡單的流程。場景的交互還有很多種,比如在此基礎(chǔ)上搭建遠程云端系統(tǒng),實現(xiàn)遠程火焰監(jiān)控實時預警。有興趣的伙伴也可以根據(jù)開發(fā)板其他傳感器組合成不同的智能場景。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)