我正在编写Xamarin.Android应用程序,但该问题通常适用于本机Android和BLE。我有一个可以写入的写入特征,只要我发送的字符数不超过600个,它就可以工作。超过600个字符的内容都会被截断。查看我的日志,可以看到该文本被拆分为20个字符的数据包,每个数据包都被调用OnCharacteristicWriteRequest
,但是在600个字符之后不再被调用。我正在测试2个Android平板电脑。我要编写的代码特征:
public override void OnServicesDiscovered(BluetoothGatt gatt, [GeneratedEnum] GattStatus status)
{
base.OnServicesDiscovered(gatt, status);
try
{
if (status != GattStatus.Success)
{
Log?.Invoke("discover services failed");
return;
}
Log?.Invoke("services discovered");
if(RequestForAddressExists(gatt.Device.Address))
{
lock (_requestsLocker)
{
Java.Util.UUID serviceUuid = GetRequestedServiceUuid(gatt.Device.Address);
Java.Util.UUID characteristicUuid = GetRequestedCharacteristicUuid(gatt.Device.Address);
BluetoothGattCharacteristic characteristic = gatt.GetService(serviceUuid).GetCharacteristic(characteristicUuid);
Log?.Invoke("characterisitic found");
var request = _requests.FirstOrDefault(r => r.DeviceAddress == gatt.Device.Address);
if (characteristic.Properties.HasFlag(GattProperty.Write))
{
Log?.Invoke("writing characteristic...");
string data = ((WriteCharacteristicRequest)request).Data;
characteristic.SetValue($"{data}{Constants.WriteCharacteristicEndDelimiter}");
characteristic.WriteType = GattWriteType.Default;
gatt.WriteCharacteristic(characteristic);
}
else
{
Log?.Invoke("GattProperty not supported");
_requests.Remove(request);
}
}
}
}
catch (Exception e)
{
Log?.Invoke(e.Message);
}
}
public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, [GeneratedEnum] GattStatus status)
{
base.OnCharacteristicWrite(gatt, characteristic, status);
if (status != GattStatus.Success)
{
Log?.Invoke($"OnCharacteristicWrite status not success: {status}");
}
else
{
Log?.Invoke("OnCharacteristicWrite success");
}
gatt.Disconnect();
gatt.Close();
lock (_requestsLocker)
{
var r = _requests.FirstOrDefault(x => x.DeviceAddress == gatt.Device.Address);
if (r != null)
{
_requests.Remove(r);
}
}
}
我的代码接受写请求:
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);
string characteristicId = new Guid(characteristic.Uuid.ToString()).ToString().ToUpperInvariant();
var record = _writeCharacteristicsReceived.FirstOrDefault(c => c.DeviceAddress == device.Address && c.CharacteristicId.ToUpperInvariant() == 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)
{
Log?.Invoke("end found");
_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, 0, value);
}
}
public override void OnExecuteWrite(BluetoothDevice device, int requestId, bool execute)
{
// need to override OnExecuteWrite and call SendResponse here as well,
// since the execute packet corresponds to the last ATT packet that the client sends as a "finish" marker,
// and the client expects a response to know that the server accepted the writes
base.OnExecuteWrite(device, requestId, execute);
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, 0, new byte[0]);
}
有趣的是,即使文本被截断,我的status == GattStatus.Success
中仍然有OnCharacteristicWrite
。为什么会被截断?是否可以发送最大数量的数据包?
两个设备都在写入BLE的同时不断在BLE上做广告和扫描...会引起问题吗?
答案 0 :(得分:1)
每个规范的特征值只能是512个字节长。不允许写入更长的值,即使某些堆栈显然不强制执行它也是如此。 当您写入的值长于MTU的值时(默认23字节减去3的报头),发送方蓝牙堆栈会将其拆分为多个块(“准备写入”),然后发送执行请求以提交。对于每个块,您都有offset参数,因此您知道在哪个偏移量下写入当前块。