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

sd_app_evt_wait() for power management

Hi my goal is to put the NRF into system on low power mode every other second. I have a repeated app_timer that increments counter variable m_custom_value every second. I thought of two possible ways to do this by calling sd_app_evt_wait() if/while m_custom_value % 2 == 0. 

1. I was wondering if there is any difference if I use an if statement vs while loop to call sd_app_evt_wait(). If I use an if statement then sd_app_evt_wait() is only called once every other second, but if I use a while loop then sd_app_evt_wait() is called continuously for the duration of one second (or approximately one second), every other second. Will using a while loop just interrupt the NRF CPU from sleep or is there no difference between calling sd_app_evt_wait() once or calling sd_app_evt_wait() continuously for the duration where I want the NRF CPU to be sleeping? 

2. Do I need to make sure the NRF is done advertising or force the NRF to stop advertising before calling sd_app_evt_wait() if I want the NRF CPU to go to system on low power mode?

I noticed that if I start BLE advertising then call sd_app_evt_wait() the NRF DK will continue advertising until the advertising timeout or idle power-management code is called. So my current assumption is that if BLE advertising is still active when sd_app_evt_wait() is called, then the NRF CPU will not go to sleep and the active BLE advertising will continue draining significant power. 

#define NOTIFICATION_INTERVAL           APP_TIMER_TICKS(1000)    
APP_TIMER_DEF(m_busy_wait_timer_id);
static uint8_t m_custom_value = 0;

static void notification_timeout_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
    ret_code_t err_code;
    
    // Increment the value of m_custom_value before notifying it.
    m_custom_value++;
}

static void timers_init(void)
{
    // Initialize timer module.
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    app_timer_create(&m_notification_timer_id, APP_TIMER_MODE_REPEATED, notification_timeout_handler);
}

static void application_timers_start(void)
{
       app_timer_start(m_notification_timer_id, NOTIFICATION_INTERVAL, NULL);

}
    

int main(void)
{
    bool erase_bonds;

    // Initialize.
    log_init();
    timers_init();
    application_timers_start();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    peer_manager_init();
    
    services_init();
    advertising_init();
    conn_params_init();
  
    NRF_LOG_INFO("Template example started.");
    advertising_start(erase_bonds);
    
    // Enter main loop.
  for (;;)
    {
        while(m_custom_value % 2 == 0){
            sd_app_evt_wait();
        }
        // vs? 
        if(m_custom_value % 2 == 0){
            sd_app_evt_wait();
        }
    } 
    
}

Parents
  • 1. I was wondering if there is any difference if I use an if statement vs while loop to call sd_app_evt_wait(). If I use an if statement then sd_app_evt_wait() is only called once every other second, but if I use a while loop then sd_app_evt_wait() is called continuously for the duration of one second (or approximately one second), every other second. Will using a while loop just interrupt the NRF CPU from sleep or is there no difference between calling sd_app_evt_wait() once or calling sd_app_evt_wait() continuously for the duration where I want the NRF CPU to be sleeping? 

    sd_app_wait() should be called in the infinite main loop to ensure that the CPU goes to sleep when its idling, i.e. not executing any code in interrupt context. 

    It is important to note that sd_app_wait() will not return unless there is an event/interrupt waking up the CPU. Hence,sd_app_wait() will only be called continuously if there are pending events/interrupts that immediately wake up the CPU again. 

    2. Do I need to make sure the NRF is done advertising or force the NRF to stop advertising before calling sd_app_evt_wait() if I want the NRF CPU to go to system on low power mode?

     No, you do not need to stop advertising before calling sd_app_evt_wait(). 

    I noticed that if I start BLE advertising then call sd_app_evt_wait() the NRF DK will continue advertising until the advertising timeout or idle power-management code is called. So my current assumption is that if BLE advertising is still active when sd_app_evt_wait() is called, then the NRF CPU will not go to sleep and the active BLE advertising will continue draining significant power. 

     If the SoftDevice is advertising, then calling sd_app_evt_wait() will make the nRF go to sleep untill the next interrupt/event wakes up the chip, i.e. the CPU will sleep until the next advertisement event. The same applies in connections, i.e. the CPU will sleep in between connection.

    Best regards

    Bjørn

  • Hi Bjørn,

    1. If I am calling sd_app_evt_wait() every other second within the infinite main loop ( for(;;) ), won't the CPU be woken up when the loop checks that 'm_custom_value' % 2 == 1? Or will 'm_custom_value' % 2 == 1 in the while loop/if statement (shown below ) not be checked after the first call to sd_app_evt_wait()?

    int main(void)
    {
        bool erase_bonds;
    
        // Initialize.
        log_init();
        timers_init();
        application_timers_start();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        gap_params_init();
        gatt_init();
        peer_manager_init();
        
        services_init();
        advertising_init();
        conn_params_init();
      
        NRF_LOG_INFO("Template example started.");
        advertising_start(erase_bonds);
        
        // Enter main loop.
      for (;;)
        {
            while(m_custom_value % 2 == 0){
                sd_app_evt_wait();
            }
            // or 
            if(m_custom_value % 2 == 0){
                sd_app_evt_wait();
            }
        } 
        
    }

    The reason I want my original code to repeatedly call sd_app_evt_wait() every other second is because I need the CPU to check connected sensors every other second. So the CPU has to be awake to call sensor_code() and check the sensors. But I want the CPU to save power and go back to system on low-power mode the next second after checking the sensors. Would the code below work better? 

    int main(void)
    {
        ...
        
        // Enter main loop.
      for (;;)
        {
            if(m_custom_value % 2 == 1){
            //sensor_code calls i2c sensors connected to the nrf
                sensor_code();
            }
            sd_app_evt_wait();
    
        } 
        
    }

    2. If the SoftDevice is still advertising after sd_app_evt_wait() is called, won't the BLE advertising still consume the normal amount of power even though the CPU is sleeping?

    And to clarify your last point, if the NRF is currently connected and paired to another device I can still call sd_app_evt_wait() and the NRF CPU will go to sleep without any issues? Example: if the NRF is currently transferring data over BLE to a phone, will calling sd_app_evt_wait() interrupt the data transfer or will the sd_app_evt_wait() call just be queued to start after the data transfer is finished? 

Reply
  • Hi Bjørn,

    1. If I am calling sd_app_evt_wait() every other second within the infinite main loop ( for(;;) ), won't the CPU be woken up when the loop checks that 'm_custom_value' % 2 == 1? Or will 'm_custom_value' % 2 == 1 in the while loop/if statement (shown below ) not be checked after the first call to sd_app_evt_wait()?

    int main(void)
    {
        bool erase_bonds;
    
        // Initialize.
        log_init();
        timers_init();
        application_timers_start();
        buttons_leds_init(&erase_bonds);
        power_management_init();
        ble_stack_init();
        gap_params_init();
        gatt_init();
        peer_manager_init();
        
        services_init();
        advertising_init();
        conn_params_init();
      
        NRF_LOG_INFO("Template example started.");
        advertising_start(erase_bonds);
        
        // Enter main loop.
      for (;;)
        {
            while(m_custom_value % 2 == 0){
                sd_app_evt_wait();
            }
            // or 
            if(m_custom_value % 2 == 0){
                sd_app_evt_wait();
            }
        } 
        
    }

    The reason I want my original code to repeatedly call sd_app_evt_wait() every other second is because I need the CPU to check connected sensors every other second. So the CPU has to be awake to call sensor_code() and check the sensors. But I want the CPU to save power and go back to system on low-power mode the next second after checking the sensors. Would the code below work better? 

    int main(void)
    {
        ...
        
        // Enter main loop.
      for (;;)
        {
            if(m_custom_value % 2 == 1){
            //sensor_code calls i2c sensors connected to the nrf
                sensor_code();
            }
            sd_app_evt_wait();
    
        } 
        
    }

    2. If the SoftDevice is still advertising after sd_app_evt_wait() is called, won't the BLE advertising still consume the normal amount of power even though the CPU is sleeping?

    And to clarify your last point, if the NRF is currently connected and paired to another device I can still call sd_app_evt_wait() and the NRF CPU will go to sleep without any issues? Example: if the NRF is currently transferring data over BLE to a phone, will calling sd_app_evt_wait() interrupt the data transfer or will the sd_app_evt_wait() call just be queued to start after the data transfer is finished? 

Children
  • thoric_fish said:
    1. If I am calling sd_app_evt_wait() every other second within the infinite main loop ( for(;;) ), won't the CPU be woken up when the loop checks that 'm_custom_value' % 2 == 1? Or will 'm_custom_value' % 2 == 1 in the while loop/if statement (shown below ) not be checked after the first call to sd_app_evt_wait()?

    It will be the latter.  If there are no pending events/interrupts after you call sd_app_evt_wait(), then the CPU will sleep until the next event/interrupt from either a peripheral or the SD. 

    thoric_fish said:
    The reason I want my original code to repeatedly call sd_app_evt_wait() every other second is because I need the CPU to check connected sensors every other second. So the CPU has to be awake to call sensor_code() and check the sensors. But I want the CPU to save power and go back to system on low-power mode the next second after checking the sensors. Would the code below work better? 

     If you want the nRF to check if m_custom_value % 2 == 1 every 2 seconds and run sensor_code() if that is the case, then my suggestion would be to set up a repeating app timer that calls the following code  in the callback

    if(m_custom_value % 2 == 1)
    {
        //sensor_code calls i2c sensors connected to the nrf
        sensor_code();
    }

     

    thoric_fish said:
    If the SoftDevice is still advertising after sd_app_evt_wait() is called, won't the BLE advertising still consume the normal amount of power even though the CPU is sleeping?

     Yes, the advertising will consume the same amount of current regardless if the CPU is sleeping or not, but the overall current consumption will be very high if you keep the CPU idling instead of sleeping inbetween the advertisment events. If you do not want to advertise to save power then you need to explicitly stop advertisement. Note, you can calculate the current consumption of the nRF device when its advertising using the Online Power profiler, see https://devzone.nordicsemi.com/nordic/power/

     

    thoric_fish said:
    And to clarify your last point, if the NRF is currently connected and paired to another device I can still call sd_app_evt_wait() and the NRF CPU will go to sleep without any issues? Example: if the NRF is currently transferring data over BLE to a phone, will calling sd_app_evt_wait() interrupt the data transfer or will the sd_app_evt_wait() call just be queued to start after the data transfer is finished? 

    The latter, if you are transferring data then the sd_app_evt_wait() call will be queued after the data transfer is finished.

    Best regards

    Bjørn

  • Hi Bjørn

    1. If I use a repeated app timer with the callback code below, would I just call sd_app_evt_wait() under the for loop in main() without encapsulating the sd_app_evt_wait() call in an if statement or while loop?

    if(m_custom_value % 2 == 1)
    {
        //sensor_code calls i2c sensors connected to the nrf
        sensor_code();
    }

    2. If I used a repeated app timer with the callback code below, would it have the same effect as the callback code above? Because, if I understand correctly, the RTC will keep running in system on low-power mode so app timer will still do callback when it expires. 

    if(m_custom_value % 2 == 0)
    {
        sd_app_evt_wait();
    }
    if(m_custom_value % 2 == 1)
    {
        sensor_code();
    }

  • thoric_fish said:
    1. If I use a repeated app timer with the callback code below, would I just call sd_app_evt_wait() under the for loop in main() without encapsulating the sd_app_evt_wait() call in an if statement or while loop?

     Yes, simply call sd_app_evt_wait() without a if or while statement inside the infinite main for loop. 

    thoric_fish said:
    If I used a repeated app timer with the callback code below, would it have the same effect as the callback code above? Because, if I understand correctly, the RTC will keep running in system on low-power mode so app timer will still do callback when it expires. 

     Yes, the RTC will keep running in low power mode and generate an interrupt that wakes up the chip and executes the callback. 

Related