onCharacteristicWrite() 被调用,但并不总是写入
onCharacteristicWrite() is being called, but it doesn't always write
我有一个带有蓝牙低功耗芯片的定制硬件。我用一个有 500 个 u32 的数组来设置它,这样 array[n] == n。我正在开发一个 android 应用程序,它可以连接到设备,请求数组的长度,然后一次请求一个数组中的数据点。
android 应用似乎大部分工作正常。它连接到设备,请求长度,并在收到前一条数据后继续请求下一条数据。然而,在数组的中途(从 2 到 450 个元素的任何地方 - 似乎不一致),它将写入另一个命令,并且它将一直到 onCharacteristicWrite(),但它永远不会收到响应。我将我的 BLE 外围设备连接到 CoolTerm,它甚至从未收到命令。以下是我的代码和日志中的片段:
BLE服务:
private final BluetoothGattCallback bleGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.d("onCharacteristicRead", byteArrToHex(characteristic.getValue()));
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if(status != BluetoothGatt.GATT_SUCCESS){
Log.d("onCharacteristicWrite", "Failed write, retrying");
gatt.writeCharacteristic(characteristic);
}
Log.d("onCharacteristicWrite", byteArrToHex(characteristic.getValue()));
super.onCharacteristicWrite(gatt, characteristic, status);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d("onCharacteristicChanged", byteArrToHex(characteristic.getValue()));
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
我省略了与描述符写入、连接状态更改等相关的回调中不必要的部分。广播数据时,在 MainActivity 的这部分接收到:
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String receivedUUID = intent.getStringExtra("uuid");
byte[] data = intent.getByteArrayExtra("data");
Log.d("messageReceiver", "received intent in mainActivity with uuid " + receivedUUID.toString());
if(receivedUUID.equals(READ_LEN_UUID.toString()) && currentlyReading) {
datapoints = new ArrayList<Long>();
numberOfDatapoints = 0;
numberOfDatapoints |= (data[0] & 0xff);
numberOfDatapoints |= (data[1] & 0xff) << 8;
numberOfDatapoints |= (data[2] & 0xff) << 16;
numberOfDatapoints |= (data[3] & 0xff) << 24;
Log.d("RECEIVER TEST:", "number of datapoints = " + numberOfDatapoints);
if(numberOfDatapoints > 0) {
bleService.requestDatapoint(0);
}
} else if (receivedUUID.equals(READ_DATAPOINT_UUID.toString()) && currentlyReading){
long message = 0;
message |= (data[0] & 0xff);
message |= (data[1] & 0xff) << 8;
message |= (data[2] & 0xff) << 16;
message |= (data[3] & 0xff) << 24;
Log.d("Datapoint Recieved", "Index " + datapoints.size() + " = " + message);
datapoints.add(message);
if(datapoints.size() < numberOfDatapoints){
bleService.requestDatapoint(datapoints.size());
}
}
}
};
调用writeCharacteristic的代码:
public void requestDatapoint(int index){
Log.d("requestDatapoint", "Requested datapoint at " + index);
BluetoothGattCharacteristic commandChar = this.gattService.getCharacteristic(WRITE_UUID);
byte[] request = new byte[3];
// command - 2 = get index
request[0] = (byte) (2 & 0xff);
// index
request[1] = (byte) ((index) & 0xff);
request[2] = (byte) ((index >> 8) & 0xff);
commandChar.setValue(request);
bleGatt.writeCharacteristic(commandChar);
}
我相当确定发送命令太快没有问题。它实际上非常慢,我是故意这样做的,这样我就可以在继续我的项目的下一部分之前更轻松地进行测试。
我的一个调试日志中的一个片段:
08-23 12:08:18.470 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 49
08-23 12:08:18.570 16753-16765/sethp.datalogcollector D/onCharacteristicWrite: 02 31 00
08-23 12:08:18.570 16753-16765/sethp.datalogcollector D/onCharacteristicChanged: 31 00 00 00
08-23 12:08:18.570 16753-16765/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:08:18.575 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:08:18.575 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 49 = 49
08-23 12:08:18.575 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 50
08-23 12:05:55.585 16753-16765/sethp.datalogcollector D/onCharacteristicWrite: 02 32 00
08-23 12:05:55.585 16753-16765/sethp.datalogcollector D/onCharacteristicChanged: 32 00 00 00
08-23 12:05:55.585 16753-16765/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.585 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.590 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 50 = 50
08-23 12:05:55.590 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 51
08-23 12:05:55.680 16753-16845/sethp.datalogcollector D/onCharacteristicWrite: 02 33 00
08-23 12:05:55.685 16753-16764/sethp.datalogcollector D/onCharacteristicChanged: 33 00 00 00
08-23 12:05:55.685 16753-16764/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.685 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.685 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 51 = 51
08-23 12:05:55.685 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 52
08-23 12:05:55.785 16753-16765/sethp.datalogcollector D/onCharacteristicChanged: 34 00 00 00
08-23 12:05:55.785 16753-16765/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.785 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.785 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 52 = 52
08-23 12:05:55.785 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 53
08-23 12:05:55.790 16753-16765/sethp.datalogcollector D/onCharacteristicWrite: 02 35 00
和我对应的 CoolTerm 日志片段:
command: 02
index: 0031
command = 2
datapoint at 49 = 49
attempting to send 49
command: 02
index: 0032
command = 2
datapoint at 50 = 50
attempting to send 50
command: 02
index: 0033
command = 2
datapoint at 51 = 51
attempting to send 51
command: 02
index: 0034
command = 2
datapoint at 52 = 52
attempting to send 52
请注意,在我的外设日志中,它似乎没有收到对数据点 53 的请求。
作为参考,onCharacteristicWrite 调试中的第一个十六进制字节是命令。命令 02 只是意味着我在接下来的 2 个字节包含的任何索引处请求一个数据点。
我注意到在 Android 日志中,没有用于请求数据点 51 的 onCharacteristicWrite 日志。这似乎每次都在它停止获取数据之前发生,但我不确定如果这很重要,或者这只是日志缓冲区的问题。
我进行了 运行 相当多的测试来尝试发现任何模式,并且我注意到当设备未连接到调试电缆时似乎获得了更多数据点。在这一点上我唯一的想法是,也许我遇到了异步中断回调的问题,但我不知道会发生什么。有没有人想过为什么在调用 onCharacteristicWrite 之后它似乎没有实际写入数据?
谢谢
编辑:
我听从了 Emil 的建议,打开了蓝牙记录。我玩了一下 wireshark 并弄清楚发生了什么。我再次尝试我的应用程序,它 运行 直到索引 102 停止,此时我断开了设备。我在 Wireshark 中挖掘数据包,发现我的设备确实收到了关于 102 的数据,但它没有发送 103 的请求。我仔细检查了我的 android 日志,并且来自 onCharacteristicWrite 内部的日志语句说它发送了命令 02 67 00,这是对 103 的请求。因此,看起来正在调用 onCharacteristicWrite,但实际上并未写入特征。
经过更多的观察和思考,我很确定要么 1) onCharacteristicWrite 被不正确地调用,因为数据永远不会被写入,要么 2) 不知何故,异步的东西正在中断它并从 t运行击打。我不知道会做什么。
最终编辑:
尽管据我从规范中了解到,onCharacteristicWrite 只应在进行可靠、成功的写入时调用,但我还是决定检查 writeCharacteristic 中的 return 值。我应该在几个小时前检查一下。你知道吗,上次请求是 returning false。
我认为即使 return 为 false 也调用了 onCharacteristicWrite 这一事实是一个错误。我读过使用 onCharacteristicWrite 调用写入下一块数据是安全的。要么他们错了,要么这里发生了一些古怪的事情。无论哪种方式,我想检查那些函数调用 return 值是个不错的主意。
现在我想我明白了。有时通知会在 onWriteCharacteristic 回调之前到达。由于您在通知回调中发出下一次写入,因此之前的写入仍处于待处理状态。您必须确保在发出下一次写入之前调用了 onWriteCharacteristic。
我有一个带有蓝牙低功耗芯片的定制硬件。我用一个有 500 个 u32 的数组来设置它,这样 array[n] == n。我正在开发一个 android 应用程序,它可以连接到设备,请求数组的长度,然后一次请求一个数组中的数据点。
android 应用似乎大部分工作正常。它连接到设备,请求长度,并在收到前一条数据后继续请求下一条数据。然而,在数组的中途(从 2 到 450 个元素的任何地方 - 似乎不一致),它将写入另一个命令,并且它将一直到 onCharacteristicWrite(),但它永远不会收到响应。我将我的 BLE 外围设备连接到 CoolTerm,它甚至从未收到命令。以下是我的代码和日志中的片段:
BLE服务:
private final BluetoothGattCallback bleGattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.d("onCharacteristicRead", byteArrToHex(characteristic.getValue()));
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if(status != BluetoothGatt.GATT_SUCCESS){
Log.d("onCharacteristicWrite", "Failed write, retrying");
gatt.writeCharacteristic(characteristic);
}
Log.d("onCharacteristicWrite", byteArrToHex(characteristic.getValue()));
super.onCharacteristicWrite(gatt, characteristic, status);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d("onCharacteristicChanged", byteArrToHex(characteristic.getValue()));
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
};
我省略了与描述符写入、连接状态更改等相关的回调中不必要的部分。广播数据时,在 MainActivity 的这部分接收到:
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String receivedUUID = intent.getStringExtra("uuid");
byte[] data = intent.getByteArrayExtra("data");
Log.d("messageReceiver", "received intent in mainActivity with uuid " + receivedUUID.toString());
if(receivedUUID.equals(READ_LEN_UUID.toString()) && currentlyReading) {
datapoints = new ArrayList<Long>();
numberOfDatapoints = 0;
numberOfDatapoints |= (data[0] & 0xff);
numberOfDatapoints |= (data[1] & 0xff) << 8;
numberOfDatapoints |= (data[2] & 0xff) << 16;
numberOfDatapoints |= (data[3] & 0xff) << 24;
Log.d("RECEIVER TEST:", "number of datapoints = " + numberOfDatapoints);
if(numberOfDatapoints > 0) {
bleService.requestDatapoint(0);
}
} else if (receivedUUID.equals(READ_DATAPOINT_UUID.toString()) && currentlyReading){
long message = 0;
message |= (data[0] & 0xff);
message |= (data[1] & 0xff) << 8;
message |= (data[2] & 0xff) << 16;
message |= (data[3] & 0xff) << 24;
Log.d("Datapoint Recieved", "Index " + datapoints.size() + " = " + message);
datapoints.add(message);
if(datapoints.size() < numberOfDatapoints){
bleService.requestDatapoint(datapoints.size());
}
}
}
};
调用writeCharacteristic的代码:
public void requestDatapoint(int index){
Log.d("requestDatapoint", "Requested datapoint at " + index);
BluetoothGattCharacteristic commandChar = this.gattService.getCharacteristic(WRITE_UUID);
byte[] request = new byte[3];
// command - 2 = get index
request[0] = (byte) (2 & 0xff);
// index
request[1] = (byte) ((index) & 0xff);
request[2] = (byte) ((index >> 8) & 0xff);
commandChar.setValue(request);
bleGatt.writeCharacteristic(commandChar);
}
我相当确定发送命令太快没有问题。它实际上非常慢,我是故意这样做的,这样我就可以在继续我的项目的下一部分之前更轻松地进行测试。
我的一个调试日志中的一个片段:
08-23 12:08:18.470 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 49
08-23 12:08:18.570 16753-16765/sethp.datalogcollector D/onCharacteristicWrite: 02 31 00
08-23 12:08:18.570 16753-16765/sethp.datalogcollector D/onCharacteristicChanged: 31 00 00 00
08-23 12:08:18.570 16753-16765/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:08:18.575 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:08:18.575 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 49 = 49
08-23 12:08:18.575 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 50
08-23 12:05:55.585 16753-16765/sethp.datalogcollector D/onCharacteristicWrite: 02 32 00
08-23 12:05:55.585 16753-16765/sethp.datalogcollector D/onCharacteristicChanged: 32 00 00 00
08-23 12:05:55.585 16753-16765/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.585 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.590 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 50 = 50
08-23 12:05:55.590 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 51
08-23 12:05:55.680 16753-16845/sethp.datalogcollector D/onCharacteristicWrite: 02 33 00
08-23 12:05:55.685 16753-16764/sethp.datalogcollector D/onCharacteristicChanged: 33 00 00 00
08-23 12:05:55.685 16753-16764/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.685 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.685 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 51 = 51
08-23 12:05:55.685 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 52
08-23 12:05:55.785 16753-16765/sethp.datalogcollector D/onCharacteristicChanged: 34 00 00 00
08-23 12:05:55.785 16753-16765/sethp.datalogcollector D/BLEService: Characteristic found. UUID: 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.785 16753-16753/sethp.datalogcollector D/messageReceiver: received intent in mainActivity with uuid 00020000-5f5f-4a49-4847-464544434241
08-23 12:05:55.785 16753-16753/sethp.datalogcollector D/Datapoint Recieved: Index 52 = 52
08-23 12:05:55.785 16753-16753/sethp.datalogcollector D/requestDatapoint: Requested datapoint at 53
08-23 12:05:55.790 16753-16765/sethp.datalogcollector D/onCharacteristicWrite: 02 35 00
和我对应的 CoolTerm 日志片段:
command: 02
index: 0031
command = 2
datapoint at 49 = 49
attempting to send 49
command: 02
index: 0032
command = 2
datapoint at 50 = 50
attempting to send 50
command: 02
index: 0033
command = 2
datapoint at 51 = 51
attempting to send 51
command: 02
index: 0034
command = 2
datapoint at 52 = 52
attempting to send 52
请注意,在我的外设日志中,它似乎没有收到对数据点 53 的请求。 作为参考,onCharacteristicWrite 调试中的第一个十六进制字节是命令。命令 02 只是意味着我在接下来的 2 个字节包含的任何索引处请求一个数据点。
我注意到在 Android 日志中,没有用于请求数据点 51 的 onCharacteristicWrite 日志。这似乎每次都在它停止获取数据之前发生,但我不确定如果这很重要,或者这只是日志缓冲区的问题。
我进行了 运行 相当多的测试来尝试发现任何模式,并且我注意到当设备未连接到调试电缆时似乎获得了更多数据点。在这一点上我唯一的想法是,也许我遇到了异步中断回调的问题,但我不知道会发生什么。有没有人想过为什么在调用 onCharacteristicWrite 之后它似乎没有实际写入数据?
谢谢
编辑:
我听从了 Emil 的建议,打开了蓝牙记录。我玩了一下 wireshark 并弄清楚发生了什么。我再次尝试我的应用程序,它 运行 直到索引 102 停止,此时我断开了设备。我在 Wireshark 中挖掘数据包,发现我的设备确实收到了关于 102 的数据,但它没有发送 103 的请求。我仔细检查了我的 android 日志,并且来自 onCharacteristicWrite 内部的日志语句说它发送了命令 02 67 00,这是对 103 的请求。因此,看起来正在调用 onCharacteristicWrite,但实际上并未写入特征。
经过更多的观察和思考,我很确定要么 1) onCharacteristicWrite 被不正确地调用,因为数据永远不会被写入,要么 2) 不知何故,异步的东西正在中断它并从 t运行击打。我不知道会做什么。
最终编辑:
尽管据我从规范中了解到,onCharacteristicWrite 只应在进行可靠、成功的写入时调用,但我还是决定检查 writeCharacteristic 中的 return 值。我应该在几个小时前检查一下。你知道吗,上次请求是 returning false。
我认为即使 return 为 false 也调用了 onCharacteristicWrite 这一事实是一个错误。我读过使用 onCharacteristicWrite 调用写入下一块数据是安全的。要么他们错了,要么这里发生了一些古怪的事情。无论哪种方式,我想检查那些函数调用 return 值是个不错的主意。
现在我想我明白了。有时通知会在 onWriteCharacteristic 回调之前到达。由于您在通知回调中发出下一次写入,因此之前的写入仍处于待处理状态。您必须确保在发出下一次写入之前调用了 onWriteCharacteristic。