You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:09:21