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

Catch advertising timeout in main

Hi,

I would like to catch in main.c the nobody connect after advertising event via ADV_SET_TERMINATED. 

I can see with breakpoint program jump into  ble_advertising_on_ble_evt inble_advertising.c but once traeted, it doesn't call the same function in main.c.


I have added the case case BLE_GAP_EVT_ADV_SET_TERMINATED in ble_evt_handler in mains.c. But this timeout event doesn' reach the ble_evt_handler function of may main.c.

I saw another topics where Joakim Jakobsen proposed a good solution, that should solve the problem, but it dosn't work for me. My code is based on ble_app_buttonless_dfu_pca10100.

devzone.nordicsemi.com/.../142407

static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    uint32_t err_code = NRF_SUCCESS;

    switch (p_ble_evt->header.evt_id)
    {
    
            case BLE_GAP_EVT_ADV_SET_TERMINATED:
               // AdvertisingTimeout, nobody connect after 1 minutes
               etat = EVENT_TIMEOUT;
            break;

Thanks a lot !

Parents
  • Hi,

    The buttonless dfu example attempts to enter System OFF (deep sleep) on adv. timeout if you've kept the default implementation. In that case, see if it works if you comment the call to sleep_mode_enter(); being made from on_adv_evt()::BLE_ADV_EVT_IDLE.

  • Great that's works i can reach the event in main.c now. I had also this feeling before to read your answer that program was stopped lol.

    Now i have a second issue: after advertising timeout, i set ma state machine to enter low power with the call 2time to function idle_state_handle();:

     case LOW_POWER:
    
                        NRF_LOG_INFO("State Machine : LOW POWER");
                
                        bma400_goto_mode_power(MODE_LOW_POWER);    // attention si echec recommencer 
    
                        idle_state_handle();
                        idle_state_handle();    //why two times ? but it works...
    

    From a power on sequence this go into low power mode works. But after timeout advertising, it jump over the two function wihtout going into low power mode.

    Is there a link with the fact that we were in timeout advertising event previously may be ?

    Thank you Vidar !

  • Hi,

    What kind of sleep are you trying to enter, System OFF or System ON. If it's system ON I would just recommend calling idle_state_handle() from the main loop like we do in our SDK examples. 

    Also noticed that you were using GPIOTE IN events. This increases the idle current in SYSTEM ON. You may consider using GPIOTE port events instead:

    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true); // <-- Set to false to use low power PORT events. GPIOTE IN vs PORT event: Sleep

  • Hi Vidar,

    I'm in system On to keep ram retention and clock because i should wake up every day. My system start, do all peripherals init, go in low power with idle_state_handle(); called two time because first time it jump over i don't know why.

    Then I wake up with GPIOTE ( accelerometer connected to one input pin). It toggle from low to high. Once wake up, nrf52833 start advertising. If nobody connect, it raise timeout event, and i call idle_state_handle() to enter in low power, but it doesn't work this time. It jump over the the function without going into low power mode.

    How to know why it is impossible to enter into sleep mode ?

    Thanks for the tips to use port event

  • Hi,

    Could you call idle_state_handle() from your main loop instead? If not you should consider placing it inside a spinlock loop that doesn't exit until the GPIOTE interrupt is triggered.

    idle_state_handle() may return immediately if you call it after servicing an interrupt because the internal event register is not cleared.

  • I call it in the main already, in my state machine, so i guess timeout advertising as raised an interrupt flag that as not been cleared as you said. How to know which flag has been set ? I think i have the same the issue at the first low power entering, because i need to call two times idle_state_handle();

    int main(void)
    {
        bool       erase_bonds;
        ret_code_t err_code;
    
      //  etat = START;
    
        for (;;)
        {
            switch (etat){
    
                case START:
    
                     log_init();
    
                    NRF_LOG_INFO("State Machine : START!");
    
                    // Initialize the async SVCI interface to bootloader before any interrupts are enabled.
                    err_code = ble_dfu_buttonless_async_svci_init();
                    APP_ERROR_CHECK(err_code);
    
                    timers_init();
    
                    power_management_init();
                    buttons_leds_init(&erase_bonds);
                    ble_stack_init();
                    peer_manager_init();
                    gap_params_init();
                    gatt_init();
    
                    services_init();
    
                    advertising_init(); //fle advertising init should be called after services init else error
                    conn_params_init();
    
    
                    // ACCEL INIT
    
                    acceel_init_spi();
                    accel_goto_mode_power(MODE_LOW_POWER);           
    
                    err_code = nrf_drv_gpiote_init();
                    APP_ERROR_CHECK(err_code);
    
                    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
                    in_config.pull = NRF_GPIO_PIN_NOPULL;
    
                    err_code = nrf_drv_gpiote_in_init(30, &in_config, in_pin_handler);
                    APP_ERROR_CHECK(err_code);
                    nrf_drv_gpiote_in_event_enable(30, true);
    
                        etat = LOW_POWER;
                        break;
    
                case LOW_POWER:
    
                        NRF_LOG_INFO("State Machine : LOW POWER");
    
    
                        accel_goto_mode_power(MODE_LOW_POWER);    // attention si echec recommencer 
                      
    
                        idle_state_handle();
                        idle_state_handle();    //why two times ? but it works...
                   
                        //ZZZZZZZZZZ... Program sleep here, will continu after this comment on wake up
    
                         bma400_goto_mode_power(MODE_SLEEP);    
    
                        etat = EVENT_WAKE_UP_MOVE;
    
                        break;
    
                    /**/case EVENT_WAKE_UP_MOVE:
                    
                            NRF_LOG_INFO("State Machine : WAKE UP on move!");
                            etat = ADVERTISING;
    
                            break;
    
                case ADVERTISING:
                    NRF_LOG_INFO("State Machine : ADVERTISING...");
    
                    // Start execution.
                    advertising_start(erase_bonds);
    
    
                    etat = WAIT_USER_CONNECT_OR_TIMEOUT;
                    NRF_LOG_INFO("State Machine : Wait USER CONNECT or TIMEOUT...");
                        break;
    
            /**/case WAIT_USER_CONNECT_OR_TIMEOUT:
              
    
                    
                        break;
    
                          /**/case EVENT_USER_BLE_CONNECT:
                                  NRF_LOG_INFO("State Machine : CONNECTED");
                                 
                              
                                  etat = INIT_SENSORS;
                                  break;
    
                          /**/case EVENT_ADVERTISING_TIMEOUT:
                                  NRF_LOG_INFO("State Machine : ADVERTISING TIMEOUT");
                              
                                  etat = LOW_POWER; //nobody is connected go back to sleep
                                  break;
    
                case INIT_SENSORS:
    
                       
                   
                        start_timer();
      
                        
                        etat = RUN;
                        break;
    
                case RUN:
                        //Sensors are measured in their timer interrupt
                        break;
    
                case EVENT_USER_BLE_DISCONNECT:
    
                      
                        NRF_LOG_INFO("State Machine : DISCONNECT");
    
                        etat = LOW_POWER;
                        break;
    
            }//end switch machine d'etat
       }//end for infinite
    }

  • So the problem is that you have to call the "idle" function to enter System ON idle? I think the safest method may be to add a spinlock loop in this case. Something like this maybe:

    static volatile bool m_acc_wakeup_triggered;
    
    void acc_wakeup_callback()
    {
        m_acc_wakeup_triggered = true;
        ...
    
    
    int main(void)
    {
        bool       erase_bonds;
        ret_code_t err_code;
    
      //  etat = START;
    
        for (;;)
        {
            switch (etat){
    
                ...
    
                case LOW_POWER:
    
                        NRF_LOG_INFO("State Machine : LOW POWER");
    
    
                        accel_goto_mode_power(MODE_LOW_POWER);    // attention si echec recommencer 
                      
    
                        while(!m_acc_wakeup_triggered)
                        {
                            __WFE();
                        }
                        
                        m_acc_wakeup_triggered = false;
                   
                        //ZZZZZZZZZZ... Program sleep here, will continu after this comment on wake up
    
                         bma400_goto_mode_power(MODE_SLEEP);    
    
                        etat = EVENT_WAKE_UP_MOVE;
    
                        break;
    
                    /**/case EVENT_WAKE_UP_MOVE:
                    
                            NRF_LOG_INFO("State Machine : WAKE UP on move!");
                            etat = ADVERTISING;
    
                            break;
    
                case ADVERTISING:
                    NRF_LOG_INFO("State Machine : ADVERTISING...");
    
                    // Start execution.
                    advertising_start(erase_bonds);
    
    
                    etat = WAIT_USER_CONNECT_OR_TIMEOUT;
                    NRF_LOG_INFO("State Machine : Wait USER CONNECT or TIMEOUT...");
                        break;
    
            /**/case WAIT_USER_CONNECT_OR_TIMEOUT:
              
    
                    
                        break;
    
                          /**/case EVENT_USER_BLE_CONNECT:
                                  NRF_LOG_INFO("State Machine : CONNECTED");
                                 
                              
                                  etat = INIT_SENSORS;
                                  break;
    
                          /**/case EVENT_ADVERTISING_TIMEOUT:
                                  NRF_LOG_INFO("State Machine : ADVERTISING TIMEOUT");
                              
                                  etat = LOW_POWER; //nobody is connected go back to sleep
                                  break;
    
                case INIT_SENSORS:
    
                       
                   
                        start_timer();
      
                        
                        etat = RUN;
                        break;
    
                case RUN:
                        //Sensors are measured in their timer interrupt
                        break;
    
                case EVENT_USER_BLE_DISCONNECT:
    
                      
                        NRF_LOG_INFO("State Machine : DISCONNECT");
    
                        etat = LOW_POWER;
                        break;
    
            }//end switch machine d'etat
       }//end for infinite
    }

Reply
  • So the problem is that you have to call the "idle" function to enter System ON idle? I think the safest method may be to add a spinlock loop in this case. Something like this maybe:

    static volatile bool m_acc_wakeup_triggered;
    
    void acc_wakeup_callback()
    {
        m_acc_wakeup_triggered = true;
        ...
    
    
    int main(void)
    {
        bool       erase_bonds;
        ret_code_t err_code;
    
      //  etat = START;
    
        for (;;)
        {
            switch (etat){
    
                ...
    
                case LOW_POWER:
    
                        NRF_LOG_INFO("State Machine : LOW POWER");
    
    
                        accel_goto_mode_power(MODE_LOW_POWER);    // attention si echec recommencer 
                      
    
                        while(!m_acc_wakeup_triggered)
                        {
                            __WFE();
                        }
                        
                        m_acc_wakeup_triggered = false;
                   
                        //ZZZZZZZZZZ... Program sleep here, will continu after this comment on wake up
    
                         bma400_goto_mode_power(MODE_SLEEP);    
    
                        etat = EVENT_WAKE_UP_MOVE;
    
                        break;
    
                    /**/case EVENT_WAKE_UP_MOVE:
                    
                            NRF_LOG_INFO("State Machine : WAKE UP on move!");
                            etat = ADVERTISING;
    
                            break;
    
                case ADVERTISING:
                    NRF_LOG_INFO("State Machine : ADVERTISING...");
    
                    // Start execution.
                    advertising_start(erase_bonds);
    
    
                    etat = WAIT_USER_CONNECT_OR_TIMEOUT;
                    NRF_LOG_INFO("State Machine : Wait USER CONNECT or TIMEOUT...");
                        break;
    
            /**/case WAIT_USER_CONNECT_OR_TIMEOUT:
              
    
                    
                        break;
    
                          /**/case EVENT_USER_BLE_CONNECT:
                                  NRF_LOG_INFO("State Machine : CONNECTED");
                                 
                              
                                  etat = INIT_SENSORS;
                                  break;
    
                          /**/case EVENT_ADVERTISING_TIMEOUT:
                                  NRF_LOG_INFO("State Machine : ADVERTISING TIMEOUT");
                              
                                  etat = LOW_POWER; //nobody is connected go back to sleep
                                  break;
    
                case INIT_SENSORS:
    
                       
                   
                        start_timer();
      
                        
                        etat = RUN;
                        break;
    
                case RUN:
                        //Sensors are measured in their timer interrupt
                        break;
    
                case EVENT_USER_BLE_DISCONNECT:
    
                      
                        NRF_LOG_INFO("State Machine : DISCONNECT");
    
                        etat = LOW_POWER;
                        break;
    
            }//end switch machine d'etat
       }//end for infinite
    }

Children
  • The problem is that i can enter without issue in System ON idle( what i call low power) the first time. But then , after advertsing timeout , i can't. If you go deeper in the idle function you can see that __WFE is called into nrf_pwr_mgmt_run so that's not the issue i think.

    I think the source of the probleme is that somthing is blocking the possibility to enter into System ON idle mode. But what or how can i know which interupt service is not served to clear the flag i don't know where to look in whitch register for example

  • __WFE will execute as a __NOP instruction after servicing an interrupt because the Event register will be set. Please take a look at the ARM documentation here for a more detailed explanation as to how this works: https://developer.arm.com/documentation/ka001269/1-0/?search=5eec6e71e24a5e02d07b259a

  • Hi Vidar,

    Thanks for the link.

    I have found the problem. As my software is based on sdk example, bsp function managing leds and button were called with timer. When i connected scope on debug sleep pin ,i have seen the uC going into sleep for around 0.3sec. Then I thought to someting linked to timer and i have found the leds stuff. So i have set LEDS_NUMBER to 0 and now i'm not waken by this anymore and product is sleeping as i want. 

    I should clean all the function linked to bsp button and leds to be sure it will not happen anymore.

    I'm still not sure whitch register to look at to mastered the enable source of wake up, and i still don't know why i have to call two times idle_state_handle function. I'm really afraid to put a mass production product without be sure of this. imagine for any reason even after calling two times this function product doesn t enter into low power mode ... 

  • The event register is internal and not exposed to the CPU. To clear it you must call __WFE/sd_app_evt_wait(). This is the reason why sd_app_evt_wait() has to be called twice after servicing an application interrupt.

    Olfox said:
    imagine for any reason even after calling two times this function product doesn t enter into low power mode ... 

     This is why I suggested that you placed the "wait for event" call in a spinlock loop earlier.  You want to be sure the correct interrupt/event was triggered before you transition to the next state.

  • Hi Vidar,

    Yes I see now I have implemented it, i try to run my software but i'm stuck with some configuration issue, i guess with settings page. I'm using softdevice s113, secure_bootloader_s113 to perform dfu that worked in the past ( ota worked, but i couldn't flash by jlink cable anymore lol ).

    I try to solve this and i come back to you to tell you if spinlock is working :) . Again, thanks Vidar for your support ! 

Related