Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

ble_app_cli dies without battery_measurement_init()

If I just comment-out battery_measurement_init() in main():

/**@brief Function for application main entry.
 */
int main(void)

{
    bool erase_bonds;

    core_init();

    buttons_leds_init(&erase_bonds);

    ble_init();

    flashlog_init();

    //battery_measurement_init();            <<<< COMMENTED-OUT!

    APP_ERROR_CHECK(nrf_cli_ble_uart_service_init());

    NRF_LOG_INFO("BLE NUS CLI example started.");

    task_manager_start(idle_task, (void *)erase_bonds);
}

Advertising does not start, I see nothing on the UART CLI, and the RTT log just shows:

rtt_cli:~$ [00:00:00.000,000] <info> stack_guard: Stack Guard (128 bytes): 0x2000E000-0x2000E07F (total stack size: 8192 bytes, usable stack area: 8064 bytes)

[00:00:00.000,000] <info> pwr_mgmt: Init

[00:00:00.000,000] <info> task_manager: Task 0 created (name: 'rtt_cli:~$ ', stack: 0x20003F00-0x200042FF).

With battery_measurement_init() enabled, it all works fine; and the RTT log shows:

rtt_cli:~$ [00:00:00.000,000] <info> stack_guard: Stack Guard (128 bytes): 0x2000E000-0x2000E07F (total stack size: 8192 bytes, usable stack area: 8064 bytes)

rtt_cli:~$ [00:00:00.000,000] <info> pwr_mgmt: Init

rtt_cli:~$ [00:00:00.000,000] <info> task_manager: Task 0 created (name: 'rtt_cli:~$ ', stack: 0x20003F00-0x200042FF).

rtt_cli:~$ [00:00:00.000,000] <info> task_manager: Task 1 created (name: 'uart_cli:~$ ', stack: 0x20004400-0x200047FF).

rtt_cli:~$ [00:00:00.000,000] <info> app: BLE NUS CLI example started.

rtt_cli:~$ [00:00:00.000,000] <info> task_manager: Task 3 created (name: 'Idle Task', stack: 0x20004E00-0x200051FF).

rtt_cli:~$ [00:00:00.007,415] <info> app: Fast advertising.

rtt_cli:~$ 

So why does removing battery_measurement_init() break everything else?

I don't want the battery measurement (it's only a simulation, anyhow) - so what do I need to keep from battery_measurement_init() to make the rest of it work?

  • So if I leave battery_measurement_init()  in main, and have it just start the timer, and have the timer handler do absolutely nothing - it works again:

    /**@brief Function for initialization of ticker for periodical battery measurement.
     */
    static void battery_measurement_init(void)
    {
        ret_code_t err_code;
    
    #if 0
        m_battery_sim_cfg.min          = MIN_BATTERY_LEVEL;
        m_battery_sim_cfg.max          = MAX_BATTERY_LEVEL;
        m_battery_sim_cfg.incr         = BATTERY_LEVEL_INCREMENT;
        m_battery_sim_cfg.start_at_max = true;
    
        sensorsim_init(&m_battery_sim_state, &m_battery_sim_cfg);
    #endif
    
    
        // Create timers.
        err_code = app_timer_create(&m_battery_timer_id,
                                    APP_TIMER_MODE_REPEATED,
                                    battery_level_meas_timeout_handler);
        APP_ERROR_CHECK(err_code);
    }

    /**@brief Function for handling the Battery measurement timer timeout.
     *
     * @details This function will be called each time the battery level measurement timer expires.
     *
     * @param[in] p_context  Pointer used for passing some arbitrary information (context) from the
     *                       app_start_timer() call to the timeout handler.
     */
    static void battery_level_meas_timeout_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
    #if 0
    
        ret_code_t err_code;
        uint8_t  battery_level;
    
        battery_level = (uint8_t)sensorsim_measure(&m_battery_sim_state, &m_battery_sim_cfg);
    
        NRF_LOG_INFO("Battery level update: %d", battery_level);
        err_code = ble_bas_battery_level_update(&m_bas, battery_level, BLE_CONN_HANDLE_ALL);
        if ((err_code != NRF_SUCCESS) &&
            (err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != NRF_ERROR_BUSY) &&
            (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)
           )
        {
            APP_ERROR_HANDLER(err_code);
        }
    #endif
    }

    So there's clearly a big secret that the comments are not telling us, and that is not obvious just by looking at the code - something else is relying upon having that timer running!

  • OK - found it:

    The idle_task() calls battery_measurement_start(), which tries to start the timer created by battery_measurement_init()  - so, if battery_measurement_init() was not called, the timer doesn't exist and, therefore, can't be started.

    So disabling the battery reporting requires removing both the battery_measurement_init() call from main() and the battery_measurement_start() call from idle_task().

    Then it works.

    Slight smile

Related