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

ble_app_multilink_central example breaks with addition of app_timer.c

I've been slowly and incrementally adding changes to the ble_app_multilink_central example from nRF51-SDK-6.0.0. This is an exercise to understand where my other app stopped working.

As soon as I add app_timer.c to my project and also declare a timer id from the header file (which is just a uint32_t typedef) my central app breaks. I haven't added any timer init code at all. I've tested this dozens of times and it goes from a successful connection to an ASSERT every time.

Here is the debug output from a successful connection:

[APPL]:[0x00] >> DM_EVT_LINK_SECURED
[APPL]:[0x00] << DM_EVT_DEVICE_CONTEXT_LOADED
[APPL]:[0x00] >> DM_EVT_CONNECTION
[APPL]:[58 9D 13 DF B5 D5]: Connection Established

[APPL]:[CI 0x00]: Requesting GATT client create
[DB]: Starting discovery of service with UUID 0x4a30 for Connection handle 0
[APPL]:[0x00] << DM_EVT_CONNECTION
[DB]: Discovery of service with UUID 0x4a30 completed with success for Connectionhandle 0
[APPL]:[0x00] >> DM_LINK_SECURED_IND, result 0x00000000
[APPL]:[0x00] << DM_LINK_SECURED_IND
[APPL]:[0x00] >> DM_EVT_SECURITY_SETUP_COMPLETE, result 0x00000000
[APPL]:[0x00] << DM_EVT_SECURITY_SETUP_COMPLETE
[APPL]:[0x00] >> DM_EVT_DEVICE_CONTEXT_STORED
[APPL]:[0x00] << DM_EVT_DEVICE_CONTEXT_STORED

I add timer.c and the following line to main.c:

static app_timer_id_t                     m_soh_timer_id;                      /**< Advertisement sleep timer. */

and my connection setup looks like this:

[APPL]:[0x00] >> DM_EVT_LINK_SECURED
[APPL]:[0x00] << DM_EVT_DEVICE_CONTEXT_LOADED
[APPL]:[0x00] >> DM_EVT_CONNECTION
[APPL]:[58 9D 13 DF B5 D5]: Connection Established

[APPL]:[CI 0x00]: Requesting GATT client create
[APPL]: ASSERT: ..\client_handling.c, 86, error 0x00000011

The assert line indicated is from the following function:

/**@brief Function for service discovery.
 *
 * @param[in] p_client Client context information.
 */
static void service_discover(client_t * p_client)
{
    uint32_t   err_code;

    p_client->state = STATE_SERVICE_DISC;

    err_code = ble_db_discovery_start(&(p_client->srv_db),
                                      p_client->srv_db.conn_handle);
    APP_ERROR_CHECK(err_code);
}

The error returned is: NRF_ERROR_BUSY. Looking at the code, it appears that it believes that there is a discovery_in_progress

uint32_t ble_db_discovery_start(ble_db_discovery_t * const p_db_discovery,
                                uint16_t                   conn_handle)
{
    if (p_db_discovery == NULL)
    {
        return NRF_ERROR_NULL;
    }

    if (!m_initialized)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    if (m_num_of_modules_reg == 0)
    {
        // No user modules were registered. Hence no services to discover.
        return NRF_ERROR_INVALID_STATE;
    }

    if (p_db_discovery->discovery_in_progress)
    {
        return NRF_ERROR_BUSY;
    }

    ble_db_discovery_srv_t * p_srv_being_discovered;

    m_num_of_discoveries_made             = 0;
    m_pending_usr_evt_index               = 0;

    p_db_discovery->curr_srv_ind          = 0;
    p_db_discovery->conn_handle           = conn_handle;

    p_srv_being_discovered = &(p_db_discovery->services[p_db_discovery->curr_srv_ind]);

    p_srv_being_discovered->srv_uuid = m_registered_modules[p_db_discovery->curr_srv_ind].srv_uuid;
    
    DB_LOG("[DB]: Starting discovery of service with UUID 0x%x for Connection handle %d\r\n",
                    p_srv_being_discovered->srv_uuid.uuid, p_db_discovery->conn_handle);
    
    uint32_t err_code;

    err_code = sd_ble_gattc_primary_services_discover(p_db_discovery->conn_handle,
                                                      SRV_DISC_START_HANDLE,
                                                      &(p_srv_being_discovered->srv_uuid));
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    p_db_discovery->discovery_in_progress = true;

    return NRF_SUCCESS;
}

I don't understand the cause/effect happening here! Has anyone else seen this or can't anyone shed any light into what's going on here?

Thank you for any insight.

  • I have a couple of thoughts based on what you've described so far:

    1. The addition of the new variable could change the memory in which your service db is located. If the discovery_in_progress field is uninitialized, that could move it to a memory location that is set.

    2. You added the timer.c module, but you did not indicate that you called it initialization function. If you did, it is possible there is some sort of interaction there. It's easy enough to comment out the initialization and repeat your test.

  • I have not called any app_timer initialization functions.

    I was exploring the possibility of #1 and went back to the original SDK examples and recompiled them to experiment. I think it's possible that I'm running out of RAM with the addition of the app_timer.c library. Running within the debugger, I'm seeing random app restarts. I'm trying to determine a good way to detect stack overflow using keil. Any suggestions?

  • Well, since you are not actually using the timers yet the app_timer.c addition shouldn't consume that much RAM. It certainly wouldn't add to your stack consumption.

    The stack allocated by default is 2K bytes so you would need a pretty stout call depth to blow through that.

    You can use the memory map generated by the linker to see where your stack is located and what variables are next to it so you can examine them for corruption. If you really wanted to prove stack overflow, you could allocate a buffer variable next to the top of the stack and fill it with a pattern. If the pattern is intact after a reset you probably don't have a stack overflow.

    Due to the randomness you are describing I'd suspect a rogue pointer corrupting your variables.

  • Good points! You're probably right. I was not completely understanding how the stack was allocated on this platform. I played around with moving RAM from the heap to the stack and it didn't change the behavior. I ended up setting heap size to 0 and just freeing the extra RAM for the application since I haven't seen anything that uses dynamic allocation.

    I added initialization to the m_client structure in client_handling.c and it did solve the NRF_ERROR_BUSY assertion. Of course I still have very strange behavior randomly.

  • I am getting the same "NRF_ERROR_BUSY" from "ble_db_discovery_start()". Can you please post as a proper answer if you have figured it out? Was it a RAM issue using KEIL?

Related