5 years, 2 months ago.

BLE: Writing into a WriteOnlyCharasteristic in the NRF52-DK, only supports 20 bytes

Hi!

I´m trying to write into a GATT Service in a NRF52-DK device. The charasteristic is a OnlyWrite type. I can send via NRF Connect App more than 20 bytes (a big array with more than 200 bytes). However, when I connect both boards (2 NRF52-DK using Mbed RTOS), the client only can send 20 bytes, if it send more the server doesn´t notice about that (even the writeChar callback is not called).

I have updated the Mbed RTOS to the latest version at the moment, and the stack I´m using is a SoftDevice s132.

I hope someone can help me, and I think is a client problem.

Best regards.

P.S. There is some of the code I am using.

Client Code

#define END_OF_JSON          0xFE  // Caracter final enviado por el Serial para indicar fin de JSON
#define VERBOSE_MODE            1  // Habilita la generacion de logs por el puerto USB   

#include "mbed.h"
#include "picojson.h"

/* Librerías para BLE - Servicio GATT */
#include "ble/BLE.h"
#include "ble/Gap.h"
#include "ble/DiscoveredCharacteristic.h"
#include "ble/DiscoveredService.h"

char                     chimueloTst[]          = "There is more than 20 characters so it wont work asdfadsasfadsfasdf"; //de mas de 20 caracteres hola que ase chimuelaco como vas OK?";
static bool   discoveredCharacteristic          = false;

DigitalOut led3Test(LED3);
DigitalOut led4BLE(LED4);
Serial pcSerial(USBTX, USBRX); // Abrimos conexión serial con el puerto USB

EventQueue eventQueue;
DiscoveredCharacteristic tofCharacteristic;

Thread threadLED(250);
Thread threadBLE(2500);

template<typename arg>
void printLog(const char * log, arg data) {
    if(VERBOSE_MODE) printf(log, data);
}

void printLog(const char * log) {
    if(VERBOSE_MODE) printf(log);
}

void scanCallback(const Gap::AdvertisementCallbackParams_t *params) {
    if (params->peerAddr[0] != 0x7C) {
        return;
    }
    printf("adv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, isScanResponse %u, AdvertisementType %u\r\n",
           params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0],
           params->rssi, params->isScanResponse, params->type);

    BLE::Instance().gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
}

void serviceDiscoveryCallback(const DiscoveredService *service) {
    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
        printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
    } else {
        printf("S UUID-");
        const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
        for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
            printf("%02x", longUUIDBytes[i]);
        }
        printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
    }
}

void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) {
    printf("  C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getUUID().getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast());
    if (characteristicP->getUUID().getShortUUID() == 0xA001) {
        tofCharacteristic        = *characteristicP;
        discoveredCharacteristic = true;
    }
}

void discoveryTerminationCallback(Gap::Handle_t connectionHandle) {
    printf("terminated SD for handle %u\r\n", connectionHandle);
}

/**
 * Thread encargado de parpadear un LED continuamente
 */
void blinkLED3() {
    while(true) {
        led3Test = !led3Test;
        wait(0.8);
    }
}

void blinkLED4() {
    led4BLE = !led4BLE;
    wait(0.1);
    led4BLE = !led4BLE;
    wait(0.1);  
} 

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) {
    printLog("Desconectado. Se comienza el escaneo de nuevo\r\n");
    discoveredCharacteristic = false;
    BLE::Instance().gap().startScan(scanCallback); 
}

void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
    printLog("Conectado al servidor\r\n");
    if (params->role == Gap::CENTRAL) {
        BLE::Instance().gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
        BLE::Instance().gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, 0xa000, 0xa001);
    }  
}

/**
 * Esta función se llama si ha habido algún error en el proceso de inicialización del BLE
 */
void onBleInitError(BLE &ble, ble_error_t error) {
    printLog("Ha ocurrido un error al inicializar la configuracion del BLE\n");
}

void periodicWriteCharacteristic() {
    if(discoveredCharacteristic) {
        printLog("Se escribe el dato en la char\r\n");
        tofCharacteristic.write(sizeof(chimueloTst),(uint8_t*) chimueloTst);            
    }    
}
/**
 * Callback triggered when the ble initialization process has finished
 */
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
    BLE &ble          = params->ble;
    ble_error_t error = params->error;
    
    if (error != BLE_ERROR_NONE) {
        return;
    }
    
    ble.gap().onConnection(connectionCallback);
    ble.gap().onDisconnection(disconnectionCallback);

    ble.gap().setScanParams(500, 400);
    ble.gap().startScan(scanCallback);
}

void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    BLE &ble = BLE::Instance();
    eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
} 

void BLEServiceManagment() {
    BLE &ble = BLE::Instance();
    ble.onEventsToProcess(scheduleBleEventsProcessing); 
    ble.init(bleInitComplete);
    
    eventQueue.dispatch_forever();
}

int main() { 
    srand(time(NULL));
    eventQueue.call_every(2000, periodicWriteCharacteristic);
    threadLED.start(callback(blinkLED3));
    threadBLE.start(callback(BLEServiceManagment));
            
    threadLED.join();
    threadBLE.join();
}

ToF Service

#ifndef __BLE_TOF_SERVICE_H__
#define __BLE_TOF_SERVICE_H__

#include "ble/BLE.h"

class TOFService {
    public:
        const static uint16_t   TOF_CHAR_ARRAY_SIZE                   =    100;
        const static uint16_t   CUSTOM_TOF_SERVICE_UUID               = 0xA000;
        const static uint16_t   TOF_CHAR_WRITE_CHARACTERISTIC_UUID    = 0xA001;
        
        TOFService(BLE& _ble) :
            ble(_ble),
            writeCharArrayCharacteristic(TOF_CHAR_WRITE_CHARACTERISTIC_UUID, writeBuffer)
        {
            static bool serviceAdded = false;
            if (serviceAdded) {
                return;
            }
    
            GattCharacteristic *charTable[] = {&writeCharArrayCharacteristic};
    
            GattService TOFService(CUSTOM_TOF_SERVICE_UUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
    
            ble.gattServer().addService(TOFService);
            serviceAdded = true;
        }
        
        GattAttribute::Handle_t getValueHandle() const {
            return writeCharArrayCharacteristic.getValueHandle();
        }
        
    private:
        BLE& ble;
    
        uint8_t writeBuffer[TOF_CHAR_ARRAY_SIZE];
    
        WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(writeBuffer)> writeCharArrayCharacteristic;
};

#endif /* #ifndef __BLE_TOF_SERVICE_H__*/

1 Answer

5 years, 2 months ago.

Hi Javier

When a gatt connection is made the att mtu size is negotiated, selected by the min of the value requested and the value supported by the client. Android and iOS devices normally request quite a large (>185) byte att mtu - on Android there is also an API in the gatt to request att mtu size.

In your case when you make a connection between 2 mbed devices it seems that the lowest value is chosen, ie 23 bytes which leaves 20 bytes for payload.

Please note that for best performance the att mtu size shall fit into the connection interval time - and if you want backwards compability you need be able to handle dynamic payload sizes (20 bytes and upwards)

You can read more here: https://os.mbed.com/forum/electronics/topic/28414/?page=1#comment-59548

Hope this helps

Hi Richard.

Oh, I see.

Do you know how to increase the minimum value supported by a client in Mbed for the NRF52-DK? I mean, if there is some target configuration file or some parameter in the mbed_app.json.

Thanks for you response.

posted by Javier Vargas 12 Feb 2019