This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Nordic didn't update custom characteristic after porting from Keil to SES

Hi,

I am trying to migrate my project file from Keil 5 to Segger Embedded Studio (SES).

After porting all my source code and configuration files, SES is able to compile and download code to my custom NRF52840 board without error. But I cannot update any data on my custom BLE characteristics - It is successfully initialised but didn't receive any notification data.

But with the same code compiled with Keil, I can update my custom BLE characteristic without any errors.

Here is my code when adding custom service and characteristics:

//Function to add custom Characteristics (1)
static uint32_t custom_char_add(ble_os2_t * p_custom_service, ble_uuid128_t CUSTOM_BASE_UUID, uint16_t CUSTOM_CHARACTERISTIC_UUID, uint8_t characteristic_channel)
{
    //Add a custom characteristic UUID
    uint32_t            err_code;
    ble_uuid_t          char_uuid;
    ble_uuid128_t       base_uuid = CUSTOM_BASE_UUID;
    char_uuid.uuid      = CUSTOM_CHARACTERISTIC_UUID;
    err_code = sd_ble_uuid_vs_add(&base_uuid, &char_uuid.type);
    APP_ERROR_CHECK(err_code);  

    
    //Add read/write properties to our characteristic
    ble_gatts_char_md_t char_md;
    memset(&char_md, 0, sizeof(char_md));
    char_md.char_props.read = 1;        //Enable Read
    char_md.char_props.write = 0;       //Enable Write
    
    
    //Configuring Client Characteristic Configuration Descriptor metadata and add to char_md structure
    ble_gatts_attr_md_t cccd_md;
    memset(&cccd_md, 0, sizeof(cccd_md));
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc                = BLE_GATTS_VLOC_STACK;    
    char_md.p_cccd_md           = &cccd_md;
    char_md.char_props.notify   = 1;
    
    
    //Configure the attribute metadata
    ble_gatts_attr_md_t attr_md;
    memset(&attr_md, 0, sizeof(attr_md));  
    attr_md.vloc        = BLE_GATTS_VLOC_STACK;
    
    //Set read/write security levels to our characteristic
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    //BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    
    //Configure the characteristic value attribute
    ble_gatts_attr_t    attr_char_value;
    memset(&attr_char_value, 0, sizeof(attr_char_value));
    attr_char_value.p_uuid      = &char_uuid;
    attr_char_value.p_attr_md   = &attr_md;
    
    //Set characteristic length in number of bytes
    attr_char_value.max_len     = BLE_NUS_MAX_DATA_LEN;
    attr_char_value.init_len    = BLE_NUS_MAX_DATA_LEN;
    uint8_t value[244]          = {0x12,0x34,0x56,0x78,0x90,0x91,0x92};
    value[243] = 0x99;
    attr_char_value.p_value     = value;
    
    //Add our new characteristic to the service
    switch (characteristic_channel)
    {
        case 1:
            err_code = sd_ble_gatts_characteristic_add(p_custom_service->service_handle,
                                   &char_md,
                                   &attr_char_value,
                                   &p_custom_service->char_handles_x);
            APP_ERROR_CHECK(err_code);
            break;
        case 2:
            err_code = sd_ble_gatts_characteristic_add(p_custom_service->service_handle,
                                   &char_md,
                                   &attr_char_value,
                                   &p_custom_service->char_handles_y);
            APP_ERROR_CHECK(err_code);
            break;
        case 3:
            err_code = sd_ble_gatts_characteristic_add(p_custom_service->service_handle,
                                   &char_md,
                                   &attr_char_value,
                                   &p_custom_service->char_handles_z);
            APP_ERROR_CHECK(err_code);
            break;
    }
    
    return NRF_SUCCESS;
}

//Function to iniliase customise service UUID
void custom_service_init_mode_2(ble_os2_t * p_custom_service, ble_uuid128_t CUSTOM_BASE_UUID, uint16_t CUSTOM_SERVICE)
{

    //Declare 16 bit service and 128 bit base UUIDs and add them to BLE stack table     
    uint32_t   err_code;
    ble_uuid_t        service_uuid;
    ble_uuid128_t     base_uuid = CUSTOM_BASE_UUID;
    service_uuid.uuid = CUSTOM_SERVICE;
    err_code = sd_ble_uuid_vs_add(&base_uuid, &service_uuid.type);
    APP_ERROR_CHECK(err_code);
    
    m_conn_handle = BLE_CONN_HANDLE_INVALID;
    
    //Add custom service
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                    &service_uuid,
                                    &p_custom_service->service_handle);
    APP_ERROR_CHECK(err_code);
    
    uint16_t characteristic_input = BLE_CHARACTERISTIC_X;
    custom_char_add(p_custom_service, base_uuid, characteristic_input, 1);
    
    characteristic_input = BLE_CHARACTERISTIC_Y;
    custom_char_add(p_custom_service, base_uuid, characteristic_input, 2);
    
    characteristic_input = BLE_CHARACTERISTIC_Z;
    custom_char_add(p_custom_service, base_uuid, characteristic_input, 3);
}


Here is the code when trying to initialise together with default BLE NUS.

// Function for initializing services that will be used by the application.
static void services_init(void)
{
    uint32_t           err_code;
    ble_nus_init_t     nus_init;
    nrf_ble_qwr_init_t qwr_init = {0};

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
    APP_ERROR_CHECK(err_code);

    // Initialize NUS.
    memset(&nus_init, 0, sizeof(nus_init));
    nus_init.data_handler = nus_data_handler;
    err_code = ble_nus_init(&m_nus, &nus_init);
    APP_ERROR_CHECK(err_code);
    
    //Adding customised service
    ble_uuid128_t uuid_input = BLE_BASE_UUID;
    uint16_t service_input = BLE_SERVICE_MODE_1;
    custom_service_init_mode_2(&ble_service_mode_2, uuid_input, service_input);
}

Here is the code to update data on specific characteristic.

// Function to be called when updating custom characteristic value
uint32_t mode_2_characteristic_update(ble_os2_t *p_custom_service, uint8_t *p_data, uint16_t *p_length, uint8_t channel, uint16_t conn_handle)
{
    ret_code_t                 err_code;
    ble_gatts_hvx_params_t     hvx_params;
    
    VERIFY_PARAM_NOT_NULL(p_custom_service);
    
    //Update characteristic value
    if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
    {
        memset(&hvx_params, 0, sizeof(hvx_params));
        
        switch (channel)
        {
            case 1:
                hvx_params.handle = p_custom_service->char_handles_x.value_handle;
                break;
            case 2:
                hvx_params.handle = p_custom_service->char_handles_y.value_handle;
                break;
            case 3:
                hvx_params.handle = p_custom_service->char_handles_z.value_handle;
                break;
            default:
                hvx_params.handle = p_custom_service->char_handles_x.value_handle;
                break;
        }
        hvx_params.p_data = p_data;
        hvx_params.p_len  = p_length;
        hvx_params.type   = BLE_GATT_HVX_NOTIFICATION; 
    }
    
    return sd_ble_gatts_hvx(m_conn_handle, &hvx_params);
}

And the I use the default ble_evt_handler with added BLE_GATTS_EVT_HVN_TX_COMPLETE to manage the activities on custom characteristics:

// Function for handling BLE events.
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    uint32_t err_code;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("Connected");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
            APP_ERROR_CHECK(err_code);
            ble_flag = true;
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("Disconnected");
            // LED indication will be changed when advertising starts.
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
            ble_flag = false;
            break;

        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY update request.");
            ble_gap_phys_t const phys =
            {
                .rx_phys = BLE_GAP_PHY_AUTO,
                .tx_phys = BLE_GAP_PHY_AUTO,
            };
            err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            // Pairing not supported
            err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
            // No system attributes have been stored.
            err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTC_EVT_TIMEOUT:
            // Disconnect on GATT Client timeout event.
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_TIMEOUT:
            // Disconnect on GATT Server timeout event.
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
            break;
        
        case BLE_GATTS_EVT_HVN_TX_COMPLETE:
            NRF_LOG_INFO("Packet sent");
            break;
        
        default:
            // No implementation needed.
            break;
    }
}
.

Here is my UUID definitions:

//BLE Customised Services & Characteristics
#define BLE_BASE_UUID                {0x23, 0xD1, 0x13, 0xEF, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x04, 0x6e} // 128-bit base UUID
#define BLE_SERVICE_MODE_1           0x0002 // Just a random, but recognizable value
#define BLE_SERVICE_MODE_2           0x0003 // Just a random, but recognizable value
#define BLE_CHARACTERISTIC_X         0x0004
#define BLE_CHARACTERISTIC_Y         0x0005
#define BLE_CHARACTERISTIC_Z         0x0006

typedef struct
{
    //uint16_t    conn_handle;        // Handle of current connection
    uint16_t    service_handle;     // Handle of custom service (as provided by the BLE stack).
    ble_gatts_char_handles_t    char_handles_x;
    ble_gatts_char_handles_t    char_handles_y;
    ble_gatts_char_handles_t    char_handles_z;
} ble_os2_t;

static ble_os2_t ble_service_mode_2;

Any advice are much appreciated. Thanks!

  • Hi.

    Which version of the nRF5 SDK are you working with?

    We have a guide on migrating an existing project from Keil -> SES:
    https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/segger-embedded-studio-a-cross-platform-ide

    Br,
    Joakim

  • Thanks Joakim,

    I am using nRF SDK 16.0.0.

    I had read the guide that you provided, and thought that it would be easier by copying and pasting my custom source code directly to the provided SES template file. (I used ble_app_uart example as template). Have also added required paths and updated memory size and location in the project settings.

    Perhaps I can try to do the migration and see the result again. Will update soon.

  • You are correct. If you are not using an older nRF SDK without native support for SES I wouldn't recommend following the guide I linked to.

    I would recommend using a template SES project instead of actually migrating the example.
    Do you see any errors or abnormal behavior if you debug the application with SES?

    Br,
    Joakim

  • Hi Joakim,

    Strange things happen when I am using the SES IDE:

    Case 1:

    Sometimes the system is able to trigger the flag (used to indicate BLE transmission activity) but sometime doesn't.

    // Function for handling BLE events.
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        uint32_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected");
                err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                APP_ERROR_CHECK(err_code);
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                ble_flag = true;
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected");
                // LED indication will be changed when advertising starts.
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
                ble_flag = false;
                break;
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                // Pairing not supported
                err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_SYS_ATTR_MISSING:
                // No system attributes have been stored.
                err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
            
            case BLE_GATTS_EVT_HVN_TX_COMPLETE:
                uart_busy_flag = false;
                packet_sent++;
                break;
            
            default:
                // No implementation needed.
                break;
        }
    }

    In which that ble_flag is declared as global variable:

    volatile static bool ble_flag = false;

    Case 2:

    In the case when ble_flag is triggered in "Release" project configuration, the system will return err_code = 0x05.

    Also, it won't return any error in "Debug" project configuration, which means that the system can send data to my phone without any problem.

    All of these problem didn't exist when I am using Keil.

    Looks like there are several misconfigurations here. I will need to check again with my code and update soon.

    Thanks.

  • Looks like I solved the problem by changing the Optimization Level from Level 2 to Level 0.

    But still curious on why this would change the performance behavior of the device. Any information on this will be much appreciated! =)

Related