Flutter使用flutter_reactive_ble向BLE设备发送字节无响应问题咨询
问题描述
我正在使用Flutter的flutter_reactive_ble包与BLE设备通信,目前已实现设备扫描、连接功能,编写了字节转换与发送相关方法,但始终无法收到设备返回的预期响应。参考此前可正常与该设备通信的Xamarin版本应用逻辑实现指令收发:将十六进制格式指令转换为十进制数值供设备解析,相关实现代码如下:
原有代码实现
指令头与指令定义
//HEADERS List<String> _header_1 = ['46', '4D', '42', '58']; List<String> _header_2 = ['4d', '4b', '49', '53']; //COMMANDS TO SEND List<String> _cmdALL = ['00', '02']; List<String> _cmdINF = ['00', '1c']; List<String> _cmdGPS = ['00', '1e']; List<String> _cmdGSM = ['00', '20']; List<String> _cmdIO = ['00', '22']; List<String> _cmdOBD = ['00', '24']; List<String> _cmdCAN = ['00', '26'];
服务与特征值UUID定义
Uuid _UART_UUID = Uuid.parse("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); Uuid _UART_RX = Uuid.parse("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"); Uuid _UART_TX = Uuid.parse("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"); List<int> bytes = [];
字节解析与发送逻辑
void _parseData() { bytes = []; //RESETS BYTES _concatenatedMessage.map((e) { var intValue = int.tryParse(e, radix: 16); bytes.add(intValue!); }).toList(); } void _sendData(header, cmd, tx) { try { _concatenateMessage(header, cmd); _parseData(); print('Parsed data: $bytes'); flutterReactiveBle.writeCharacteristicWithResponse(_txCharacteristic, value: bytes); print('Sent Data'); print('$bytes'); _sentDataLog = "${_sentDataLog} $bytes\n"; refreshScreen(); } on Exception catch (e) { print("Couldn't Send Data"); } }
消息拼接逻辑
void _concatenateMessage(header, cmd) { _concatenatedMessage = header + cmd; }
数据接收回调
void onNewReceivedData(List<int> data) { _numberOfMessagesReceived += 1; _receivedData .add("$_numberOfMessagesReceived: ${String.fromCharCodes(data)}"); if (_receivedData.length > 5) { _receivedData.removeAt(0); } refreshScreen(); }
连接成功后执行逻辑
final _txCharacteristic = QualifiedCharacteristic( serviceId: _UART_UUID, characteristicId: _UART_TX, deviceId: event.deviceId, ); _sendData(_header_1, _cmdALL, _txCharacteristic); _sendData(_header_1, _cmdINF, _txCharacteristic); _sendData(_header_1, _cmdGPS, _txCharacteristic); _sendData(_header_1, _cmdGSM, _txCharacteristic); _sendData(_header_1, _cmdIO, _txCharacteristic); _sendData(_header_1, _cmdOBD, _txCharacteristic); _sendData(_header_1, _cmdCAN, _txCharacteristic);
当前实际发送的字节数据格式示例为[70, 77, 66, 88, 0, 38],按照设备协议预期应返回对应配置的字节串数据,但实际未收到任何有效响应。设备固件并非本人开发,上述逻辑参考自可正常通信的Xamarin旧应用。
问题排查与修正方案
代码存在4个核心错误,按影响优先级排序:
- 收发特征值完全搞反:你用的是标准Nordic UART服务,
6E400002(定义的_UART_RX)是手机发、设备收的写特征,所有发往设备的指令要写到这个特征上;6E400003(定义的_UART_TX)是设备发、手机收的通知特征,设备返回的数据从这个特征输出。你现在把写操作的目标特征设成了_UART_TX,这个特征默认是只读/通知属性,根本不支持写操作,指令根本没发出去。 - 未开启接收特征的通知订阅:BLE设备主动向手机推送数据,必须先对对应的接收特征开启通知权限,否则系统会直接丢弃设备发来的所有数据包。你全程没有调用
subscribeToCharacteristic()监听_UART_TX特征,写的接收回调根本没有绑定到BLE数据流上,不可能收到数据。 - 连接后瞬间连发指令无间隔:大部分BLE外设处理能力弱,连接建立后需要几十到上百毫秒完成服务初始化,且BLE写操作需要等上一次写入完成才能发下一条。你连接成功后无间隔连发7条指令,会直接导致数据包丢失,设备无响应。
- 接收数据解析逻辑错误:你直接把收到的二进制字节用
String.fromCharCodes()转成ASCII字符串,设备返回的二进制数据里大量非ASCII字节会被转成乱码,就算收到数据你也会误以为是无效内容。排查阶段应该直接打印字节数组的十六进制内容。
修正后核心代码示例
连接成功后初始化逻辑
// 区分写特征和接收通知特征 final writeCharacteristic = QualifiedCharacteristic( serviceId: _UART_UUID, characteristicId: _UART_RX, // 写操作用RX特征 deviceId: event.deviceId, ); final notifyCharacteristic = QualifiedCharacteristic( serviceId: _UART_UUID, characteristicId: _UART_TX, // 通知监听用TX特征 deviceId: event.deviceId, ); // 先订阅通知,再发指令 flutterReactiveBle .subscribeToCharacteristic(notifyCharacteristic) .listen((data) { // 直接打印十六进制字节,不要直接转字符串 print("收到设备数据: ${data.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}"); _numberOfMessagesReceived += 1; _receivedData.add("$_numberOfMessagesReceived: $data"); if (_receivedData.length > 5) { _receivedData.removeAt(0); } refreshScreen(); }, onError: (e) { print("订阅通知出错: $e"); }); // 指令逐条发送,每条间隔200ms等待设备处理 await _sendData(_header_1, _cmdALL, writeCharacteristic); await Future.delayed(const Duration(milliseconds: 200)); await _sendData(_header_1, _cmdINF, writeCharacteristic); await Future.delayed(const Duration(milliseconds: 200)); await _sendData(_header_1, _cmdGPS, writeCharacteristic); await Future.delayed(const Duration(milliseconds: 200)); await _sendData(_header_1, _cmdGSM, writeCharacteristic); await Future.delayed(const Duration(milliseconds: 200)); await _sendData(_header_1, _cmdIO, writeCharacteristic); await Future.delayed(const Duration(milliseconds: 200)); await _sendData(_header_1, _cmdOBD, writeCharacteristic); await Future.delayed(const Duration(milliseconds: 200)); await _sendData(_header_1, _cmdCAN, writeCharacteristic);
修正发送方法为异步等待写入完成
Future<void> _sendData(header, cmd, QualifiedCharacteristic targetChar) async { try { _concatenateMessage(header, cmd); _parseData(); print('待发送字节: $bytes'); // 等待写入完成再返回 await flutterReactiveBle.writeCharacteristicWithResponse( targetChar, value: bytes, ); print('发送完成'); _sentDataLog = "${_sentDataLog} $bytes\n"; refreshScreen(); } catch (e) { // 打印完整错误信息,不要吞异常 print("发送数据失败: $e"); } }
额外排查点:
- 如果改完还是无响应,先抓包确认手机实际发出的字节和Xamarin版本发出的字节完全一致,注意检查字节序、帧尾校验位是否和Xamarin逻辑对齐,部分设备协议会要求在指令末尾追加校验字节。
- 可以把
_parseData里的int.tryParse结果加判空逻辑,避免非法十六进制字符串导致的异常被catch吞掉,难以定位问题。- 部分设备对写类型有要求,如果
writeCharacteristicWithResponse写入失败,可以尝试换成writeCharacteristicWithoutResponse测试,和Xamarin版本的写类型保持一致即可。
内容的提问来源于stack exchange,提问作者Henrique Guimarães
相关产品推荐
相关产品推荐

