HarmonyOS基于LYEVK-3861開發火焰報警系統
前言
在各種災害中,火災是最經常、最普遍地威脅公眾安全和社會發展的主要災害之一。火給人類帶來文明進步、光明和溫暖。但是,有時它是人類的朋友,有時是人類的敵人。失去控制的火,就會給人類造成災難。說到火災的控制,一套火焰感應報警系統就有其價值了。那我們如何去檢測火焰呢?
本文內容主要講述基于LYEVK-3861物聯網開發板套件的火焰傳感器,開發一個具有火焰感應報警功能的HarmonyOS應用,主要實現藍牙設備掃描,連接,檢測火焰,設置報警閾值。
1.效果演示

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

3.3 數據透傳協議:
3.3.1 串口協議:
3.3.2 數據協議:
(permission P:APP下發;G:設備請求;R:設備上報)
4.開發調試
藍牙交互封裝BleHelper工具類,通過BLE掃描和廣播提供的開放能力,可以根據指定狀態獲取外圍設備、啟動或停止BLE掃描、廣播。
4.1 進行BLE掃描
- MyBleCentralManagerCallback繼承BleCentralManagerCallback類實現scanResultEvent和scanFailedEvent回調函數,用于接收掃描結果。
- BleCentralManager(BleCentralManagerCallback callback)接口獲取中心設備管理對象。
- 調用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 建立連接
- 掃描成功,匹配服務UUID FFB0,調用bleConnect()連接開發板藍牙。
- 觸發connectionStateChangedEvent(int connectionState)回調,connectionState=2連接成功,然后調用discoverServices()接口發現服務。
- 在回調servicesDiscoveredEvent(int status)中獲取外圍設備支持的服務和特征值,此時才能調用read和write方法讀取或者寫入對應特征值數據。
- 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 預警閾值下發
- 設置火焰報警距離閾值,tagId為0002,當火焰傳感器發現火焰,并小于此設置的閾值時,設備上報預警。
- 通過發現服務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 火焰距離上報
- 火焰距離上報,通知應用,開始預警。
- 通過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物聯網開發板火焰傳感器的預警功能,和應用程序交互的一個相對簡單的流程。場景的交互還有很多種,比如在此基礎上搭建遠程云端系統,實現遠程火焰監控實時預警。有興趣的伙伴也可以根據開發板其他傳感器組合成不同的智能場景。