onCharacteristicWrite() вызывается, но не всегда пишет

У меня есть специальное оборудование с чипом Bluetooth с низким энергопотреблением. Я настроил его с помощью массива с 500 u32, так что array[n] == n. Я работаю над приложением для Android, которое может подключаться к устройству, запрашивать длину массива, а затем запрашивать точки данных в массиве по одному.

Приложение для Android в основном работает нормально. Он подключается к устройству, запрашивает длину и продолжает запрашивать следующую часть данных ПОСЛЕ получения предыдущей части. Однако на полпути через массив (где-то от 2 до 450 элементов - кажется несогласованным) он напишет другую команду и дойдет до onCharacteristicWrite(), но никогда не получит ответа. У меня есть периферийное устройство BLE, подключенное к CoolTerm, и оно даже не получает команду. Вот фрагменты моего кода и журналов:

BLEService:

 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 нет журнала onCharacteristicWrite для запроса точки данных 51. Кажется, это происходит каждый раз прямо перед прекращением получения данных, но я не уверен, имеет ли это значение или это просто ошибка. проблема с буфером журнала.

Я провел довольно много тестов, пытаясь обнаружить какие-либо закономерности, и я заметил, что, похоже, появляется больше точек данных, когда устройство не подключено к отладочному кабелю. Моя единственная мысль на данный момент заключается в том, что, возможно, у меня есть проблема с чем-то асинхронным, прерывающим обратный вызов, но я не знаю, что это делает. Есть ли у кого-нибудь мысли о том, почему он не записывает данные после вызова onCharacteristicWrite?

Спасибо

РЕДАКТИРОВАТЬ:

Я последовал совету Эмиля и включил ведение журнала Bluetooth. Я поигрался с wireshark и понял, что происходит. Я снова попробовал свое приложение, и оно достигло индекса 102, пока не остановилось, после чего я отключил устройство. Я копался в пакетах в Wireshark и обнаружил, что мое устройство ДЕЙСТВИТЕЛЬНО получило данные о 102, но не отправило запрос на 103. Я дважды проверил свой журнал Android, и оператор журнала изнутри onCharacteristicWrite сказал, что он отправил команду 02 67 00, что является запросом на 103. Итак, похоже, что onCharacteristicWrite вызывается, но на самом деле характеристика не записывается.

Еще немного посмотрев и подумав, я совершенно уверен, что либо 1) onCharacteristicWrite вызывается неправильно, потому что данные никогда не записываются, либо 2) каким-то образом что-то асинхронное прерывает их и останавливает их передачу. Я понятия не имею, что будет делать это.

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ:

Несмотря на то, что, насколько я понимаю из спецификации, onCharacteristicWrite должен вызываться только тогда, когда выполняется надежная и успешная запись, я решил проверить возвращаемое значение из writeCharacteristic. Который я должен был проверить несколько часов назад. И что вы знаете, он возвращал false на последний запрос.

Я думаю, что тот факт, что onCharacteristicWrite был вызван, хотя возврат был ложным, является ошибкой. Я читал, что безопасно использовать вызов onCharacteristicWrite для записи следующего фрагмента данных. Либо они ошиблись, либо здесь происходит что-то странное. В любом случае, я думаю, неплохо было бы проверить эти возвращаемые значения вызова функции.


person Seth    schedule 23.08.2016    source источник
comment
Можете ли вы показать код, который на самом деле вызывает writeCharacteristic?   -  person Emil    schedule 23.08.2016
comment
Конечно, добавляю сейчас   -  person Seth    schedule 23.08.2016
comment
Я не изменяю это в Android, но на уровне прошивки моя характеристика определяется как CHAR_PROP_WRITE=0x08, что совпадает со значением PROPERTY_WRITE. Я еще не пробовал, я погуглю, как это сделать, и дам это попытка.   -  person Seth    schedule 23.08.2016
comment
@Emil Эмиль, я добавил в свой вопрос некоторую информацию о журнале отслеживания. Надеюсь, это прольет свет на проблему. Я совершенно озадачен в этот момент.   -  person Seth    schedule 24.08.2016
comment
Неа. Код, которым я поделился, является единственным кодом, который выполняет операции GATT.   -  person Seth    schedule 24.08.2016


Ответы (1)


Теперь я думаю, что вижу это. Иногда уведомление приходит до обратного вызова onWriteCharacteristic. Поскольку вы выдаете следующую запись в обратном вызове уведомления, предыдущая запись все еще находится в состоянии ожидания. Вы должны убедиться, что onWriteCharacteristic был вызван, прежде чем выполнять следующую запись.

person Emil    schedule 24.08.2016
comment
Ты уверен? например, если бы мне нужна точка данных 7, я бы написал этот запрос, затем onCharacteristicWrite будет вызываться, когда выполняется успешная запись, затем вызывается onCharacterisicChanged, когда я получаю ответ, и в этот момент я бы запрашивал точку данных 8. Возможно ли это? onCharacteristicWrite даже не вызывается до получения ответа? - person Seth; 24.08.2016
comment
Да, onCharacteristicWrite будет вызываться, когда ваше периферийное устройство отправит ответ записи GATT. Поскольку он одновременно отправляет уведомление, я предполагаю, что не определено, что сначала доставляется вашему приложению. В вашем журнале logcat последний ответ на запись извлекается после уведомления по сравнению с более ранними пакетами. Поскольку character.getValue() содержит последнее записанное или уведомленное значение, поэтому вы подумали, что это ответ на неотправленную запись (помните, что вы сами установили значение характеристики). В любом случае, посмотрите в своем snooplog, какой заказ отправляется по воздуху. - person Emil; 24.08.2016
comment
Да, это действительно интересно. Хотя имеет смысл. Спасибо за помощь в решении этой проблемы! +1 - person Seth; 24.08.2016