#星光计划1.0# HarmonyOS 基于LYEVK-3861开发火焰报警系统 原创 精华
作者:胡领情
前言
在各种灾害中,火灾是最经常、最普遍地威胁公众安全和社会发展的主要灾害之一。火给人类带来文明进步、光明和温暖。但是,有时它是人类的朋友,有时是人类的敌人。失去控制的火,就会给人类造成灾难。说到火灾的控制,一套火焰感应报警系统就有其价值了。那我们如何去检测火焰呢?
本文内容主要讲述基于LYEVK-3861物联网开发板套件的火焰传感器,开发一个具有火焰感应报警功能的HarmonyOS应用,主要实现蓝牙设备扫描,连接,检测火焰,设置报警阈值。
1.效果演示
2.环境准备
本贴不对实验环境搭建做详细说明。具体准备实验环境请参考:
3.蓝牙通信说明
3.1 蓝牙通信协议:
UUID | 值 |
---|---|
服务uuid:(需要续平台保持一致) | 0000ffb0-0000-1000-8000-00805f9b34fb |
特征1uuid:(属性只写) | 0000ffb1-0000-1000-8000-00805f9b34fb |
特征2uuid:(属性只通知) | 0000ffb2-0000-1000-8000-00805f9b34fb |
3.2 蓝牙通信流程:
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开发板套件
更多原创内容请关注:开鸿 HarmonyOS 学院
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,共建鸿蒙生态,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
很详细的南向开发教学,给楼主点赞
不错!谢谢分享!