AWTK 的低功耗蓝牙 (BLE) 插件开发笔记
BLE 插件的实现还是挺麻烦的,过年期间断断续续的写了 4 天。除了代码量本身不小外 (1500 行+500 行 java),主要还是对 BLE 开发不熟。看了一些资料,这里记个笔记,供新手参考:
发送数据
发送数据通过写 Characteristic 的值来实现,写 Characteristic 的过程分为两步:
- 先设置 Value。
- 调用 writeCharacteristic 发送数据。
之前没有调用 writeCharacteristic,数据怎么也发不出去,后来看文档才知道。
String address = json.getString("address");
String uuid = json.getString("uuid");
String data = json.getString("data");
String type = json.getString("type");
BluetoothGatt conn = this.findConnectionByAddr(address);
if (conn != null) {
BluetoothGattCharacteristic c = findCharacteristic(conn.getServices(), uuid);
if (c != null) {
if (type.equals("hex")) {
c.setValue(hexToByteArray(data));
} else {
c.setValue(data);
}
conn.writeCharacteristic(c);
PluginManager.writeSuccess(this.callerInfo, this.action);
} else {
PluginManager.writeFailure(this.callerInfo, this.action, "not found BluetoothGattCharacteristic");
}
} else {
PluginManager.writeFailure(this.callerInfo, this.action, "please connect device first");
}
} catch (JSONException e) {
PluginManager.writeFailure(this.callerInfo, this.action, "bad parameters");
}
接收数据
接收数据一般是监听 Characteristic 变化事件,而不是读取 Characteristic。需要重载 onCharacteristicChanged:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.i("AWTK", "onCharacteristicChanged: " + characteristic.getUuid().toString());
byte[] bytes = characteristic.getValue();
String data = new String(bytes, StandardCharsets.UTF_8);
String hex = byteArrayToHex(bytes);
if (notifyReceiver != null) {
try {
JSONObject json = new JSONObject();
json.put("type", "onCharacteristicChanged")
.put("address", gatt.getDevice().getAddress())
.put("uuid", characteristic.getUuid().toString())
.put("hex", hex)
.put("data", data);
PluginManager.writeResult(notifyReceiver, json.toString());
} catch (JSONException e) {
Log.d("AWTK", e.toString());
}
}
}
但是我在用 HC-42 测试时,onCharacteristicChanged 函数怎么也不触发。在网上查了一下,说要调用 setCharacteristicNotification 函数,然而调用了 setCharacteristicNotification 之后还是没有效果。
后来使用 HC-Bluetooth 提供的 apk 测试,发现工作正常的。由于没有找到这个 apk 的源码,把它反编译出来研究了一下。才知道还要去设置 Descriptor 来开启通知:
反编译的源码如下:
public void setNotification(final BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic bluetoothGattCharacteristic, boolean z) {
if (bluetoothGatt == null || bluetoothGattCharacteristic == null) {
log("gatt == null || characteristic == null");
return;
}
boolean characteristicNotification = bluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, z);
Log.e("TAG", "setNotification: " + characteristicNotification);
if (characteristicNotification) {
for (final BluetoothGattDescriptor next : bluetoothGattCharacteristic.getDescriptors()) {
if ((bluetoothGattCharacteristic.getProperties() & 16) != 0) {
next.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
log("路线 1");
} else if ((bluetoothGattCharacteristic.getProperties() & 32) != 0) {
next.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
log("路线 2");
} else {
log("没有走");
}
this.mTimeHandler.postDelayed(new Runnable() {
public void run() {
bluetoothGatt.writeDescriptor(next);
BluetoothLeService bluetoothLeService = BluetoothLeService.this;
bluetoothLeService.log("监听的特征是:" + next.getUuid().toString());
}
}, 1000);
}
}
}
于是我也加了类似的代码:
public void enableCharacteristicNotify(final BluetoothGatt gatt, BluetoothGattCharacteristic c, boolean enable) {
boolean result = gatt.setCharacteristicNotification(c, enable);
Log.v("AWTK", "enable notify for " + c.getUuid());
if (result) {
int properties = c.getProperties();
for (final BluetoothGattDescriptor d : c.getDescriptors()) {
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
d.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
d.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
} else {
Log.v("AWTK", "bad");
}
mTimeHandler.postDelayed(new Runnable() {
public void run() {
gatt.writeDescriptor(d);
}
}, 1000);
}
}
}
测试了一下,HC-42 收发数据正常了,不过不太确定其它模块是否有效。