Nordic NRF52840 BLE Characteristic limit.

ljaynes wrote on June 10, 2019:

I am trying to add 4 characteristics with notifications to my custom BLE service. I can add up to 3 before my program will not longer run without immediately crashing. I can’t find anything about a limit but it seems as tho I am crashing due to a size issue or a predefined limit somewhere. Is this something that’s done on purpose that I can increase in freertos or is this a Nordic specific problem?

Gaurav-Aggarwal-AWS wrote on June 12, 2019:

There is no limit on the number of characteristics that you can add. It should only be limited by the amount of memory.

Would you please share the code snippet where you are adding these characteristics to help us debug the issue?

Thanks.

ljaynes wrote on June 13, 2019:

Here is my complete code for my ble service. I updated to the latest version of the release-1.5 and now I can only get it to work if I only add one characteristic.



#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * FreeRTOS includes.
 */
#include "FreeRTOS.h"
#include "FreeRTOSConfig.h"
#include "semphr.h"
#include "task.h"


/* AWS IoT include */
#include "iot_ble.h"

#include "SEGGER_RTT.h"

/*Characteristic Read Queues*/

xQueueHandle vehicle_meta_data_ble_queue;
xQueueHandle device_meta_data_ble_queue;
xQueueHandle auth_data_ble_queue;

/*Characteristic data storage*/

uint8_t vehicle_meta_data_buffer[256] = {0};
uint8_t device_meta_data_buffer[256]  = {0};
uint8_t auth_data_buffer[256]         = {0};

/*------------------------------------------------------------------------------------*/


#define app_SERVICE_BASE_UUID    0x00, 0xAA
#define app_SERVICE_UUID_MASK    0x32, 0xF9, 0x79, 0xE6, 0xB5, 0x83, 0xFB, 0x4E, 0xAF, 0x48, 0x68, 0x11, 0x7F, 0xAE
#define app_SERVICE_UUID         {app_SERVICE_BASE_UUID, app_SERVICE_UUID_MASK }

/**
 * @brief Service, characteristic and descriptor UUIDS for Device information Service
 */
#define app_CHAR_UUID_BASE             app_SERVICE_UUID
#define app_VEHICLE_META_DATA          { 0x01, 0xAA, app_SERVICE_UUID_MASK }
#define app_DEVICE_META_DATA           { 0x02, 0xAA, app_SERVICE_UUID_MASK }
#define app_AUTH_DATA                  { 0x03, 0xAA, app_SERVICE_UUID_MASK }
#define app_SERIALIZED_DATA            { 0x04, 0xAA, app_SERVICE_UUID_MASK }
#define app_CHAR_CFG_UUID              0x2902     

/**
 * @brief Structure used for Device Information Service
 */
typedef struct appService
{
    BTService_t * pBLEService;
    uint16_t CCFGVal;
    uint16_t BLEConnId;
    uint16_t BLEMtu;
} appService_t;


static appService_t app_service =
{
    .pBLEService = NULL,
    .BLEConnId  = 65535,
    .BLEMtu     = 0,
    .CCFGVal    = 0
};


#define app_VEHICLE_META_UUID_TYPE \
{ \
    .uu.uu128 = app_VEHICLE_META_DATA,\
    .ucType   = eBTuuidType128 \
}

#define app_DEVICE_META_UUID_TYPE \
{ \
    .uu.uu128 = app_DEVICE_META_DATA,\
    .ucType   = eBTuuidType128 \
}

#define app_AUTH_DATA_UUID_TYPE \
{ \
    .uu.uu128 = app_AUTH_DATA,\
    .ucType = eBTuuidType128\
}

#define app_SERIALIZED_DATA_UUID_TYPE \
{ \
    .uu.uu128 = app_SERIALIZED_DATA,\
    .ucType = eBTuuidType128\
}

#define app_CHAR_CFG_UUID_TYPE \
{ \
    .uu.uu16 = app_CHAR_CFG_UUID,\
    .ucType = eBTuuidType16\
}

#define xappSvcUUID \
{ \
    .uu.uu128 = app_SERVICE_UUID,\
    .ucType   = eBTuuidType128\
}


typedef enum {
  ATTR_SERVICE,
  ATTR_CHAR_VEH_META,
  ATTR_VEH_META_DESCR,
  ATTR_CHAR_DEV_META,
  ATTR_DEV_META_DESCR,
  ATTR_CHAR_AUTH_DATA,
  ATTR_AUTH_DATA_DESCR,
  ATTR_CHAR_SERIALIZED_DATA,
  ATTR_SERIALIZED_DATA_DESCR,
  ATTR_NUMBER
} app_attr_t;


static uint16_t app_handles_buffer[ATTR_NUMBER];




static const BTAttribute_t pAttributeTable[] = {
  {    
     .xServiceUUID =  xappSvcUUID
  },

  {
    .xAttributeType = eBTDbCharacteristic,
    .xCharacteristic = 
    {
      .xUuid = app_VEHICLE_META_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM ),
      .xProperties  = ( eBTPropRead | eBTPropNotify )
    }
  },
  {
    .xAttributeType = eBTDbDescriptor,
    .xCharacteristicDescr =
    {
      .xUuid        = app_CHAR_CFG_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM )
    }
  },

  {
    .xAttributeType = eBTDbCharacteristic,
    .xCharacteristic = 
    {
      .xUuid = app_DEVICE_META_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM ),
      .xProperties  = ( eBTPropRead | eBTPropNotify )
    }
  },

  {
    .xAttributeType = eBTDbDescriptor,
    .xCharacteristicDescr =
    {
      .xUuid        = app_CHAR_CFG_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM )
    }
  },

  {
    .xAttributeType = eBTDbCharacteristic,
    .xCharacteristic = 
    {
      .xUuid = app_AUTH_DATA_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM ),
      .xProperties  = ( eBTPropRead | eBTPropNotify )
    }
  },

  {
    .xAttributeType = eBTDbDescriptor,
    .xCharacteristicDescr =
    {
      .xUuid        = app_CHAR_CFG_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM )
    }
  },

  {
     .xAttributeType = eBTDbCharacteristic,
     .xCharacteristic = 
     {
        .xUuid = app_SERIALIZED_DATA_UUID_TYPE,
        .xPermissions = ( IOT_BLE_CHAR_READ_PERM ),
        .xProperties  = ( eBTPropRead | eBTPropNotify )
     }
  },
  {
    .xAttributeType = eBTDbDescriptor,
    .xCharacteristicDescr =
    {
      .xUuid        = app_CHAR_CFG_UUID_TYPE,
      .xPermissions = ( IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM )
    }
  },
};


static const BTService_t appService = 
{
  .xNumberOfAttributes = ATTR_NUMBER,
  .ucInstId = 0,
  .xType = eBTServiceTypePrimary,
  .pusHandlesBuffer = app_handles_buffer,
  .pxBLEAttributes = (BTAttribute_t *)pAttributeTable
};


static void vehicle_meta_data_read_callback( IotBleAttributeEvent_t * pEventParam );

static void device_meta_data_read_callback( IotBleAttributeEvent_t * pEventParam );

static void auth_data_read_callback( IotBleAttributeEvent_t * pEventParam );

static void enable_characterisic_notifictions( IotBleAttributeEvent_t * pEventParam );

/**
 * @brief Callback invoked MTU for the BLE connection changes.
 *
 * Updates the value for MTU characteristic. Sends a notification to GATT client if notification is enabled
 * via Client Characteristic Configuration descriptoe
 *
 * @param connId[in] Connection Identifier
 * @param usMtu[in] New MTU for the connection
 *
 */
static void mtu_change_callback( uint16_t connId,
                                 uint16_t usMtu );

/**
 * @brief Callback invoked on a BLE connect and disconnect event
 *
 * Stores the connection ID of the BLE client for sending notifications.
 *
 * @param status BLE status of the operation
 * @param connId Connection ID for the connection
 * @param bConnected Is Connected or disconnected
 * @param pxRemoteBdAddr Remote address of the client
 */
static void connection_callback( BTStatus_t status,
                                 uint16_t connId,
                                 bool bConnected,
                                 BTBdaddr_t * pxRemoteBdAddr );


static const IotBleAttributeEventCallback_t pxCallBackArray[ATTR_NUMBER] =
 {
  NULL,
  vehicle_meta_data_read_callback,
  enable_characterisic_notifictions,
  device_meta_data_read_callback,
  enable_characterisic_notifictions,
  auth_data_read_callback,
  enable_characterisic_notifictions,
  auth_data_read_callback,
  enable_characterisic_notifictions,
};




/*-----------------------------------------------------------*/

void vehicle_meta_data_read_callback( IotBleAttributeEvent_t * pEventParam )
{
    IotBleAttributeData_t attrData = { 0 };
    IotBleEventResponse_t resp;
    IotBleWriteEventParams_t * pxWriteParam;
    uint8_t ucEvent;

    resp.pAttrData = &attrData;
    resp.rspErrorStatus = eBTRspErrorNone;
    resp.eventStatus = eBTStatusFail;
    resp.attrDataOffset = 0;



    if( pEventParam->xEventType == eBLERead )
    {
        
        xQueueReceive(vehicle_meta_data_ble_queue, vehicle_meta_data_buffer, 0);

        attrData.handle = pEventParam->pParamRead->attrHandle;
        attrData.pData = vehicle_meta_data_buffer;
        attrData.size = strlen( vehicle_meta_data_buffer );

        IotBle_SendResponse( &resp, pEventParam->pParamRead->connId, pEventParam->pParamRead->transId );
    }

}


void device_meta_data_read_callback( IotBleAttributeEvent_t * pEventParam )
{
    IotBleAttributeData_t attrData = { 0 };
    IotBleEventResponse_t resp;


    if( pEventParam->xEventType == eBLERead )
    {

        xQueueReceive(device_meta_data_ble_queue, device_meta_data_buffer, 0);
        
        attrData.handle = pEventParam->pParamRead->attrHandle;
        attrData.pData = device_meta_data_buffer;
        attrData.size = strlen( device_meta_data_buffer );

        resp.pAttrData = &attrData;
        resp.attrDataOffset = 0;
        resp.eventStatus = eBTStatusSuccess;
        resp.rspErrorStatus = eBTRspErrorNone;

        IotBle_SendResponse( &resp, pEventParam->pParamRead->connId, pEventParam->pParamRead->transId );

    }
}


void auth_data_read_callback( IotBleAttributeEvent_t * pEventParam )
{
    IotBleAttributeData_t attrData = { 0 };
    IotBleEventResponse_t resp;


    if( pEventParam->xEventType == eBLERead )
    {

        xQueueReceive(auth_data_ble_queue, auth_data_buffer, 0);
        
        attrData.handle = pEventParam->pParamRead->attrHandle;
        attrData.pData = auth_data_buffer;
        attrData.size = strlen( auth_data_buffer );

        resp.pAttrData = &attrData;
        resp.attrDataOffset = 0;
        resp.eventStatus = eBTStatusSuccess;
        resp.rspErrorStatus = eBTRspErrorNone;

        IotBle_SendResponse( &resp, pEventParam->pParamRead->connId, pEventParam->pParamRead->transId );

    }
}


/*-----------------------------------------------------------*/


void enable_characterisic_notifictions( IotBleAttributeEvent_t * pEventParam )
{
    IotBleWriteEventParams_t * pWriteParam;
    IotBleAttributeData_t attrData = { 0 };
    IotBleEventResponse_t resp;

    resp.pAttrData = &attrData;
    resp.rspErrorStatus = eBTRspErrorNone;
    resp.eventStatus = eBTStatusFail;
    resp.attrDataOffset = 0;

    if( ( pEventParam->xEventType == eBLEWrite ) || ( pEventParam->xEventType == eBLEWriteNoResponse ) )
    {
        pWriteParam = pEventParam->pParamWrite;
        attrData.handle = pWriteParam->attrHandle;

        if( pWriteParam->length == 2 )
        {
            app_service.CCFGVal = ( pWriteParam->pValue[ 1 ] << 8 ) | pWriteParam->pValue[ 0 ];
            resp.eventStatus = eBTStatusSuccess;
        }

        if( pEventParam->xEventType == eBLEWrite )
        {
            attrData.pData = pWriteParam->pValue;
            attrData.size = pWriteParam->length;
            resp.attrDataOffset = pWriteParam->offset;
            IotBle_SendResponse( &resp, pWriteParam->connId, pWriteParam->transId );
        }
    }
    else if( pEventParam->xEventType == eBLERead )
    {
        attrData.handle = pEventParam->pParamRead->attrHandle;
        attrData.pData = ( uint8_t * ) &app_service.CCFGVal;
        attrData.size = 2;
        resp.attrDataOffset = 0;
        resp.eventStatus = eBTStatusSuccess;

        IotBle_SendResponse( &resp, pEventParam->pParamRead->connId, pEventParam->pParamRead->transId );
    }
}


static void mtu_change_callback( uint16_t connId,
                                 uint16_t usMtu )
{
  if( usMtu != app_service.BLEMtu )
  {
    SEGGER_RTT_printf(0, "MTU:%d
", usMtu);
  }
}


static void connection_callback( BTStatus_t status,
                                 uint16_t connId,
                                 bool bConnected,
                                 BTBdaddr_t * pxRemoteBdAddr )
{
  if( status == eBTStatusSuccess )
  {
    if( bConnected == true )
    {
      app_service.BLEConnId = connId;  

    }
  }
}


size_t app_ble_send(uint8_t attribute,
                    uint8_t * pData,
                    size_t len)
{

  IotBleAttributeData_t attrData = { 0 };
  IotBleEventResponse_t response =
  {
      .pAttrData      = &attrData,
      .attrDataOffset = 0,
      .eventStatus    = eBTStatusSuccess,
      .rspErrorStatus = eBTRspErrorNone,
  };

  bool status = true;

  attrData.handle = appService.pusHandlesBuffer[attribute];
  attrData.uuid   = appService.pxBLEAttributes[attribute].xCharacteristic.xUuid;
  attrData.pData  = pData;
  attrData.size   = len;


  if(  IotBle_SendIndication( &response, app_service.BLEConnId, false ) != eBTStatusSuccess )
  {
      status = false;
  }

  return status;

}

/*-----------------------------------------------------------*/

BaseType_t ble_init( void )
{
    BTStatus_t status = eBTStatusFail;
    BaseType_t error = pdFAIL;
    IotBleEventsCallbacks_t callback;

    /* Select the handle buffer. */
    status = IotBle_CreateService( (BTService_t *)&appService, (IotBleAttributeEventCallback_t *)pxCallBackArray );

	if( status == eBTStatusSuccess )
	{
		callback.pMtuChangedCb = mtu_change_callback;
		status = IotBle_RegisterEventCb( eBLEMtuChanged, callback );
	}

	if( status == eBTStatusSuccess )
	{
		callback.pConnectionCb = connection_callback;
		status = IotBle_RegisterEventCb( eBLEConnection, callback );
	}

  if( status == eBTStatusSuccess )
  {
    error = pdPASS;
    vehicle_meta_data_ble_queue = xQueueCreate( 1, sizeof(vehicle_meta_data_buffer) );
    device_meta_data_ble_queue  = xQueueCreate( 1, sizeof(device_meta_data_buffer) );
    auth_data_ble_queue         = xQueueCreate( 1, sizeof(auth_data_buffer) );
  }

    return error;
}

/*-----------------------------------------------------------*/



void IotBle_AddCustomServicesCb(void)
{
  ble_init();
}

hbouvier-AWS wrote on June 17, 2019:

Hello,

There is indeed a limit to the characteristic you can create.
I tested your code and made it work by increasing that limit.
You have to change the following parameter from the Nordic SDK:
https://github.com/aws/amazon-freertos/blob/b76006df130102971b5f3575b7a85356c5439394/vendors/nordic/boards/nrf52840-dk/aws_demos/config_files/sdk_config.h#L12159
You will also have to increase the memory you allocate to the nrf stack. It is the ram start parameter.
RAM_START=*
RAM_SIZE=*

Please find more info on Nordic guide:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v14.1.0%2Fgroup__nrf__sdh__ble.html&resultof="p_app_ram_start"%20

ljaynes wrote on June 18, 2019:

Thanks. Increasing the Nordic limit and ram parameters fixed my issues.