Как написать большое количество текста, чтобы написать характеристику

1

Я пишу приложение для Android с Xamarin.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, но эта функция вызывается только один раз. Я должен что-то упустить с одной стороны?

Самое смешное, что, когда я тестирую это приложение для Android с версией приложения для iOS, у меня нет этой проблемы. У меня эта проблема возникает только тогда, когда оба устройства 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);
    }
}
Теги:
xamarin.android
bluetooth-lowenergy

3 ответа

2

Причина в том, что вы используете GattProperty.WriteNoResponse вместо GattProperty.Write. В варианте свойства без ответа клиент может использовать только команду ATT "Запись без ответа", которая ограничена MTU. В обычном варианте свойства записи клиент может использовать как запрос ATT "Запись с ответом", так и последовательность множественных подготовительных записей, за которыми следует запись выполнения, также известная как "Длинная запись". При длинных записях клиент (автоматически) разделяет запись на разные куски со смещениями. Обратите внимание, что длинная запись занимает значительно больше времени, чем если бы вы просто увеличивали MTU из-за необходимого количества многократных циклов.

  • 0
    Я установил его на GattProperty.Write но у меня все еще есть та же проблема. Я предпочел бы иметь «длинную запись», чем увеличивать MTU на данный момент.
  • 0
    Может быть, только некоторые устройства поддерживают длинные записи?
Показать ещё 3 комментария
1

Вот два момента здесь.

  1. почему он ограничен 20 байтами?

Базовая спецификация определяет MTU по умолчанию для ATT равным 23 байта. После удаления одного байта кода операции ATT и байта ручки ATT2 оставшиеся 20 байтов зарезервированы для GATT. Учитывая, что некоторые интеллектуальные устройства Bluetooth являются слабыми и не осмеливаются использовать слишком много места в памяти, спецификация ядра требует, чтобы каждое устройство поддерживало MTU 23. В начале соединения между двумя устройствами все как новые друг, я не знаю, другая сторона в порядке, поэтому строго следуйте порядку, то есть отправляйте до 20 байтов за раз, что является самой большой страховкой.

  1. Как прорваться через 20?

Поскольку максимальная длина ATT составляет 512 байтов, достаточно изменить MTU передаваемого ATT. В Android (API 21) интерфейс для изменения MTU ATT:

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)
  • 0
    Это хорошая информация. Но должен быть способ сделать «длинную запись»? Так как я могу сделать это с Android на iOS и Windows ...
  • 0
    Может быть, только некоторые устройства поддерживают длинные записи?
Показать ещё 1 комментарий
0

При чтении ответа у меня было нечто похожее - устройство BLE ограничивало каждое чтение до 20 байтов. Тем не менее, я мог бы преодолеть это, читая его в 20-байтовых чанках и затем ожидая завершающего символа перед обработкой данных.

Вы используете завершающий символ при написании? Когда вы пишете с вашей частичной строкой, вы получаете в результате событие OnCharacteristicChanged?

  • 0
    Я не разбиваю его на 20-байтовые фрагменты или что-то в этом роде; Я просто отправляю это как есть (это строка json). Я знаю, что размер MTU обычно составляет около 20 байтов, но, похоже, он управляется самим фреймворком, когда я тестирую его с Android <=> iOS и Android <=> Windows. Ошибка только при тестировании Android <=> Android. В моей версии iOS эквивалент «OnCharacteristicWriteRequest» имеет смещение, которое я использую для управления пакетами, и он вызывается столько раз, сколько необходимо для завершения всей строки. Но в моей версии для Android OnCharacteristicWriteRequest только один раз.
  • 0
    После еще OnCharacteristicWriteRequest тестирования мой OnCharacteristicWriteRequest вызов OnCharacteristicWriteRequest был вызван дважды. Но я смог воспроизвести это только один раз. Теперь он вернулся к тому, чтобы быть вызванным только один раз. Кажется, он ведет себя непредсказуемо ... Я попробовал его с третьим устройством Android на случай, если оно неисправно, но все равно вызывается только один раз.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню