如何从字节数据读取蓝牙特性并将其转换为正确的值(蓝牙用于颤振)

Xen*_*nos 7 bluetooth dart flutter

我必须读取和写入一些值到使用 Flutter 的带有 BLE(低功耗蓝牙)的 Bike Smart 训练器。当我尝试从 GATT 特征 org.bluetooth.characteristic.supported_power_range(在 bluetooth.org 网站https://www.bluetooth.com/specifications/gatt/characteristics/上找到)读取值时,我得到一个 Int 的返回值列表 [0,0,200,0,1,0]。

GATT 特性说有 3 个 sint16 字段用于 Min., Max。和步长瓦特(功率)。

字节传输顺序还表示首先传输最低有效八位字节。

我的猜测是,这 3 个参数在一个 Int 数组中返回,每个数组都有 8 位值。但我无法解释 200 可能是最大功率设置。因为智能教练应该提供最大。2300W 瓦电阻(ELITE Drivo https://www.elite-it.com/de/produkte/home-trainer/rollentrainer-interaktive/drivo

此代码片段的输出结果:

device.readCharacteristic(savedCharacteristics[Characteristics.SUPPORTED_POWER_RANGE]).then((List<int> result) {
  result.forEach((i) {
    print(i.toString());
  });
});
// result: [0,0,200,0,1,0]
Run Code Online (Sandbox Code Playgroud)

也许你们中的某个人知道如何解释 flutter_blue 特征输出的二进制/十六进制/十进制值。或者一些提示会很棒

编辑

对于未来的读者,我得到了解决方案。我有点惭愧,因为我读错了特征。

返回值 [0,0,200,0,1,0] 用于支持阻力位。(这是 20%,200 显示 20%,分辨率为 0.1,如 GATT 规范中所述) 最大阻力水平,分辨率为 0.1

我还得到了支持的功率级别的返回值[0,0,160,15,1,0]。现在的解决方案是如何读取最大功率级别的 2 字节:您将获得 160,15 的规范 sais LSO(首先是最低有效八位字节,不要首先将其与最低有效LSB 混淆)。事实上,你必须像 15,160 一样阅读它。现在用第一个字节15*256 + 160 = 4000进行数学计算,这就是数据表中训练器的正确最大支持功率。

我希望我能帮助某人。感谢您的两个回复,他们也是正确的,并帮助我找到了我的错误。

小智 6

我用过 print(new String.fromCharCodes(value));并且对我有用。

value你的回报是来自 List<int> value = await characteristic.read();

我感谢 ukBaz 对这个问题的回答。将数据写入BLE设备并读取其响应抖动?


Sal*_*eno 5

我在连接 Polar H10 恢复 HR 和 RR 间隔时遇到了同样的问题。可能不是100%相同,但我认为我的案例可以指导你解决你的问题。

我收到的列表与您喜欢的这两个示例相同:

  1. [0,60]
  2. [16,61,524,2]

查看GATT 蓝牙心率服务的规格,我发现检索到的列表中的每个元素都与您所订阅的特征传输的数据的 1 个字节相匹配。对于该服务,第一个字节,即列表的第一个元素,有一些标志来指出HR值之后是否有RR值(16)或没有(0)。这只是根据标志值可能发生的许多不同情况中的两种情况,但我认为它显示了第一个字节的重要性。

之后,HR 值被编码为 8 位无符号整数 (UINT8),即 HR 值与前面所示列表的第二个元素匹配。然而,RR 间隔被编码为 16 位无符号整数 (UINT16),因此它使列表 #2 [16,61, 524,2 ] 的最后两个元素的转换变得复杂,因为我们应该使用 16 位来获得该值和字节的顺序不正确。

这是我们导入库dart:typed_data的时候

import 'dart:typed_data';

...

_parseHr(List<int> value) {

// First sort the values in the list to interpret correctly the bytes
List<int> valueSorted = [];
valueSorted.insert(0, value[0]);
valueSorted.insert(1, value[1]);
for (var i=0; i< (value.length-3); i++) {
  valueSorted.insert(i+2, value[i+3]);
  valueSorted.insert(i+3, value[i+2]);
}

// Get flags directly from list
var flags = valueSorted[0];

// Get the ByteBuffer view of the data to recode it later
var buffer = new Uint8List.fromList(valueSorted).buffer; // Buffer bytes from list

if (flags == 0) {
  // HR
  var hrBuffer = new ByteData.view(buffer, 1, 1); // Get second byte
  var hr = hrBuffer.getUint8(0);                  // Recode as UINT8
  print(hr);
}

if (flags == 16) {
  // HR
  var hrBuffer = new ByteData.view(buffer, 1, 1); // Get second byte
  var hr = hrBuffer.getUint8(0);                  // Recode as UINT8

  // RR (more than one can be retrieved in the list)
  var nRr = (valueSorted.length-2)/2; // Remove flags and hr from byte count; then split in two since RR is coded as UINT16
  List<int> rrs = [];
  for (var i = 0; i < nRr; i++) {
    var rrBuffer = new ByteData.view(buffer, 2+(i*2), 2); // Get pairs of bytes counting since the 3rd byte
    var rr = rrBuffer.getUint16(0);                       // Recode as UINT16
    rrs.insert(i,rr);
  }
  print(rrs);
}
Run Code Online (Sandbox Code Playgroud)

希望它有所帮助,关键是获取排序列表的缓冲区视图,获取所需的字节,然后按照标准指出的方式重新编码它们。