Home > Net >  Android ESP32 BLE read after write not working
Android ESP32 BLE read after write not working

Time:11-08

I'm writing an embedded system which doesn't have an internet connection, so the main interaction is using BLE from an Android device. (ESP32 is using the NimBLE-Arduino library)

I have some write characteristics and some read characteristics. Each one individually works well, but when I try to read immediately after write (or vice versa), only the first callback in the ESP32 is called

ESP32 code

// Setup function
void Bluetooth::setupCharacteristic()
{        
    NimBLEService *pService = m_pServer->createService(SERVICE_UUID);
    NimBLECharacteristic *getStationsChr = pService->createCharacteristic(GET_STATIONS_CHR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
    NimBLECharacteristic *setTimeChr = pService->createCharacteristic(SET_TIME_CHR_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC | NIMBLE_PROPERTY::WRITE_AUTHEN);

    getStationsChr->setCallbacks(this);
    setTimeChr->setCallbacks(this);
    pService->start();
}

// Read/Write callbacks
void Bluetooth::onRead(NimBLECharacteristic* pCharacteristic){
    log_i("%s", pCharacteristic->getUUID().toString().c_str());
    log_i(": onRead(), value: %s",pCharacteristic->getValue().c_str());

    if (m_pCallback)
    {
        auto value = pCharacteristic->getValue();
        //m_pCallback->onMessageReceived(&value);
    }
    ...
}

void Bluetooth::onWrite(NimBLECharacteristic* pCharacteristic) {
    std::string characteristicStr = pCharacteristic->getUUID().toString();
    std::string value_str(pCharacteristic->getValue().c_str());
    log_i("%s: onWrite(), value: %s (%s)", characteristicStr.c_str(), pCharacteristic->getValue().c_str(), value_str.c_str());

    // Process incoming value and call the callback
    // ...
    
    if (m_pCallback)
    {
        auto value = pCharacteristic->getValue();
        //m_pCallback->onMessageReceived(&value);
    }
    ...
}

Android code

private fun updateTime() {
    val calendar = GregorianCalendar()
    val ts = Instant.now().epochSecond
    val timeZone = timezonesMap?.get(calendar.timeZone.id) ?: ""

    val timeMessage = TimeMessage(ts, 0, timeZone)
    val setTimeChar = bluetoothGatt?.getService(SERVICE_UUID)?.getCharacteristic(SET_TIME_UUID)
    if (setTimeChar?.isWritable() == true) {
        val timeStr = gson.toJson(timeMessage)
        Log.d("updateTime", "setting time to $timeStr")
        sendToCharacteristic(setTimeChar, timeStr)
    }

}

// This function is used to break long message to multiple messages, this is a simplified version
// for brevity
private fun sendToCharacteristic(characteristic: BluetoothGattCharacteristic?, value: String)
{
    Log.d(TAG, "Sending to write characteristic ${characteristic?.uuid} value ($value)")
    characteristic?.value = value.toByteArray()
    bluetoothGatt?.writeCharacteristic(characteristic)
    TimeUnit.MILLISECONDS.sleep(500)
}

private fun getStations() {
    Log.d("getStations", "getting stations")
    val getStationsChar = bluetoothGatt?.getService(SERVICE_UUID)?.getCharacteristic(GET_STATIONS_UUID)
    if (getStationsChar?.isReadable() == true) {
        Log.d("getStations", "reading")
        bluetoothGatt?.readCharacteristic(getStationsChar)
    }
}

The issue is when calling my sync() function

fun sync() {
    Log.d("sync", "syncing")
    bluetoothGatt?.apply {
        if (device.bondState != BluetoothDevice.BOND_BONDED) {
            device.createBond()
        }
        else {
            updateTime()
//                getStations()
        }
    }
}

If I uncomment the getStations, the read callback isn't called. If I comment the updateTime and uncomment the getStations, the read callback is called. switching the order doesn't make a difference. First called function works, second doesn't. Also adding a 1 second delay between the calls doesn't work

CodePudding user response:

Your problem lies on the Android side.

In GATT you may only have one outstanding request. This means you must wait for the previous callback before executing a new operation. In particular, you must in this case wait for onCharacteristicWrite on your BluetoothGattCallback object after performing a write operation, before you can execute a next operation.

Adding a sleep doesn't work since you block the thread and hence prevent the result callback from running, whenever a response is received from the remote device.

  • Related