我正在使用Xamarin.Android编写一个Android应用程序,但原生Android中的答案也会受到赞赏。在我的Android应用程序中,我有一个设备可以写入的BLE写入特性。它工作,但我不能发送超过20个字节,其余的被切断。我创建和添加服务/特性的代码:
BluetoothGattService service = new BluetoothGattService(Java.Util.UUID.FromString(MyServiceUuid), GattServiceType.Primary);
// write characteristic (write-only, supports subscriptions)
BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(Java.Util.UUID.FromString(MyCharacteristicUuid), GattProperty.WriteNoResponse | GattProperty.Notify, GattPermission.Write);
service.AddCharacteristic(writeCharacteristic);
_bluetoothGattServer.AddService(service);
我写的代码的代码:
public override void OnServicesDiscovered(BluetoothGatt gatt, [GeneratedEnum] GattStatus status)
{
base.OnServicesDiscovered(gatt, status);
characteristic = gatt.GetService(Java.Util.UUID.FromString(MyServiceUuid))
.GetCharacteristic(Java.Util.UUID.FromString(MyCharacteristicUuid));
if(characteristic.Properties.HasFlag(GattProperty.WriteNoResponse))
{
Log?.Invoke("writing characteristic...");
characteristic.SetValue(MyVeryLongString);
characteristic.WriteType = GattWriteType.NoResponse;
gatt.WriteCharacteristic(characteristic);
}
}
在接受写请求的一方:
public override void OnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, bool preparedWrite, bool responseNeeded, int offset, byte[] value)
{
base.OnCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
Log?.Invoke("OnCharacteristicWriteRequest");
string data = System.Text.Encoding.UTF8.GetString(value);
Log?.Invoke(data);
if(responseNeeded)
{
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, 0, Encoding.ASCII.GetBytes("ok"));
}
}
我看到有一个offset
,但这个函数只被调用一次。我一定是在遗漏一些东西?
有趣的是,当我用我的应用程序的iOS版本测试这个Android应用程序时,我没有这个问题。当两个设备都是Androids时,我只有这个问题。
编辑
我对OnCharacteristicWriteRequest
的新实现:
public override void OnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, bool preparedWrite, bool responseNeeded, int offset, byte[] value)
{
base.OnCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
Log?.Invoke("OnCharacteristicWriteRequest");
string data = System.Text.Encoding.UTF8.GetString(value);
Log?.Invoke(data);
Guid characteristicId = new Guid(characteristic.Uuid.ToString());
var record = _writeCharacteristicsReceived.FirstOrDefault(c => c.DeviceAddress == device.Address && c.CharacteristicId == characteristicId);
if(record != null)
{
record.Data += data;
}
else
{
record = new CharacteristicWriteReceived()
{
CharacteristicId = characteristicId,
DeviceAddress = device.Address,
Data = data
};
_writeCharacteristicsReceived.Add(record);
}
if (record?.Data.EndsWith(Constants.WriteCharacteristicEndDelimiter) == true)
{
_writeCharacteristicsReceived.Remove(record);
record.Data = record.Data.Substring(0, record.Data.Length - Constants.WriteCharacteristicEndDelimiter.Length); // remove the end delimeter
Log?.Invoke(record.Data);
OnCharacteristicWriteReceived?.Invoke(record);
}
if (responseNeeded)
{
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, offset, value);
}
}
原因是您使用GattProperty.WriteNoResponse而不是GattProperty.Write。使用无响应属性变体,客户端只能使用“无响应写入”ATT命令,该命令受MTU限制。使用正常的Write属性变体,客户端可以同时使用“Write with response”ATT请求以及多次准备写入的序列,然后执行write,也称为“Long write”。通过长写入,客户端(自动)将写入分成具有偏移的不同块。请注意,由于需要多次往返次数,因此只需增加MTU,Long write需要的时间要长得多。
这里有两点。
核心规范将ATT的默认MTU定义为23个字节。在移除ATT操作码的一个字节和ATT handle2字节之后,剩余的20个字节被保留用于GATT。考虑到一些蓝牙智能设备很弱并且不敢过多使用内存空间,核心规范要求每个设备必须支持23的MTU。在两个设备之间的连接开始时,每个人都像一个新的朋友,我不知道对方的罚款,所以严格按照惯例,即一次最多发送20个字节,这是最保险的。
由于ATT的最大长度是512字节,因此改变发送的ATT的MTU就足够了。在Android(API 21)上,更改ATT MTU的界面是:
public boolean requestMtu (int mtu)
#Added in API level 21
#Request an MTU size used for a given connection.
#When performing a write request operation (write without response), the data sent is truncated to the MTU size. This function may be used to request a larger MTU size to be able to send more data at once.
#A onMtuChanged(BluetoothGatt, int, int) callback will indicate whether this operation was successful.
#Requires BLUETOOTH permission.
#Returns true, if the new MTU value has been requested successfully
如果外围应用程序更改MTU并成功,则也将调用此回调。
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
this.supportedMTU = mtu;//local var to record MTU size
}
}
之后,您可以愉快地发送supportedMTU数据的长度。 Up是java代码示例,这里是Xamarin Android文档供参考。 https://developer.xamarin.com/api/member/Android.Bluetooth.BluetoothGatt.RequestMtu/p/System.Int32/
public Boolean RequestMtu (Int32 mtu)
我在读取响应时有类似的东西--BLE设备将每个读取限制为20个字节。但是,我可以通过在20个字节的块中读取它然后在处理数据之前等待终止字符来克服它。
你在写作时使用终止字符吗?当您使用部分字符串编写时,您会得到一个OnCharacteristicChanged事件吗?