如何写大量文字来写特征

时间:2019-04-18 16:07:16

标签: android xamarin.android bluetooth-lowenergy

我正在用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应用程序时,没有这个问题。当两个设备都是Android时,我只有这个问题。

编辑

我对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);
    }
}

3 个答案:

答案 0 :(得分:3)

原因是您使用了GattProperty.WriteNoResponse而不是GattProperty.Write。使用无响应属性变量,客户端只能使用“写无响应” ATT命令,该命令受MTU限制。使用普通的Write属性变体,客户端可以使用“使用响应写入” ATT请求,也可以使用多次准备写入的顺序,然后执行写入,也称为“长时间写入”。使用长写时,客户端(自动)将写操作按偏移量分成不同的块。请注意,由于需要多次往返,因此长写入所花费的时间比仅增加MTU所花费的时间要多得多。

答案 1 :(得分:2)

这里有两点。

  1. 为什么限制为20个字节?

核心规范将ATT的默认MTU定义为23个字节。删除ATT操作码的一个字节和ATT handle2个字节后,剩余的20个字节将保留给GATT。 考虑到某些蓝牙智能设备较弱并且不敢过多使用内存空间,因此核心规格要求每个设备必须支持23的MTU。 在两个设备之间开始连接时,每个人都像一个新朋友,我不知道对方的罚款,因此请严格遵循例程,即一次最多发送20个字节保险。

  1. 如何突破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
    }
}

之后,您可以愉快地发送受支持的MTU数据的长度。 上是Java代码示例,这是Xamarin Android文档供参考。 https://developer.xamarin.com/api/member/Android.Bluetooth.BluetoothGatt.RequestMtu/p/System.Int32/

public Boolean RequestMtu (Int32 mtu)

答案 2 :(得分:0)

读取响应时,我有类似的东西-BLE设备将每次读取限制为20个字节。但是,我可以通过读取20个字节的块来克服该问题,然后在处理数据之前等待终止的char。

编写时是否使用终止符?用部分字符串编写时,是否会收到一个OnCharacteristicChanged事件?