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

SDK 16.0 APP_BUTTON_RELEASE event not received when waking up from a button press

I just updated my project from SDK 15.3 to 16.0. I am using the nRF52840.

I am not receiving the APP_BUTTON_RELEASE event when waking up from a button press. It used to work on 15.3.

To help isolate the problem, I recreated it in the ble_app_template example project and ran it in both SDK 15.3 and 16.0 on the Nordic dev board. I made the following changes in each SDK:

In bsp.c, in the bsp_button_event_handler() , I added the following serial debug line of code:

case APP_BUTTON_RELEASE:
    (void)app_timer_stop(m_bsp_button_tmr);
    NRF_LOG_RAW_INFO("button release\r\n");

In main.c, I modified the sleep_mode_enter() with a serial debug line of code and removed the APP_ERROR_CHECK() to simulate sleep while debugging:

static void sleep_mode_enter(void)
{
    ret_code_t err_code;

    NRF_LOG_RAW_INFO("sleep_mode_enter\r\n");
    NRF_LOG_FLUSH();

    err_code = bsp_indication_set(BSP_INDICATE_IDLE);
    APP_ERROR_CHECK(err_code);

    // Prepare wakeup buttons.
    err_code = bsp_btn_ble_sleep_mode_prepare();
    APP_ERROR_CHECK(err_code);

    // Go to system-off mode (this function will not return; wakeup will cause a reset).
    err_code = sd_power_system_off();
    // APP_ERROR_CHECK(err_code);
    while (1){}
}

In main.c, I also decreased advertising time to 10 seconds for convenience so that it will go to sleep sooner:

#define APP_ADV_DURATION 1000

I run the app by:

  1. Start the ble_app_template example.
  2. Allow advertising to time out so that it goes into sleep mode.
  3. Press the button to wake it back up.

When I run the ble_app_template example in 15.3, I get the following Debug Terminal Output:

<info> app: Template example started.
<info> app: Fast advertising.
sleep_mode_enter
<info> app: Template example started.
<info> app: Fast advertising.
button release
sleep_mode_enter

The app resets upon button press as expected. Notice "button release". This means that I get the button release event.

When I run the ble_app_template example in 16.0, I get the following Debug Terminal Output:

<info> app_timer: RTC: initialized.
<info> app: Template example started.
<info> app: Fast advertising.
sleep_mode_enter
<info> app_timer: RTC: initialized.
<info> app: Template example started.
<info> app: Fast advertising.
sleep_mode_enter

The app resets upon button press as expected. Although, I do not get a button release event.

It is very important to my project to get this event. I need to detect the release event so that I can differentiate between a short and long press upon wakeup from a button press.

  • Hi Christopher, 

    I did some testing with just the app_button library in SDK 16.0.0, not using the BSP library and with the nRF52840 going to actual System OFF, not simulated. It turns out that the logic in the app_button library is not designed to keep track of state during a wakeup from system off.  The reason for this is that a wakeup from System OFF will reset the nRF52840. 

    So the button press that wakes up the chip will cause the GPIOTE IRQ handler to be called, however at this point the app_button library and the GPIOTE driver is not initialized. Hence, the interrupt will not trigger the app_button library's state machine and  push event is generated since none of the callbacks are set up when the GPIOTE IRQ handler fires on wakeup. 

    This is due to the following logic in the app_button library

    /* GPIOTE event is used only to start periodic timer when first button is activated. */
    static void gpiote_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
        app_button_cfg_t const * p_btn = button_get(pin);
        bool is_set = nrf_drv_gpiote_in_is_set(p_btn->pin_no);
        bool is_active = !((p_btn->active_state == APP_BUTTON_ACTIVE_HIGH) ^ is_set);
    
        /* If event indicates that pin is active and no other pin is active start the timer. All
         * action happens in timeout event.
         */
        if (is_active && (m_pin_active == 0))
        {
            NRF_LOG_DEBUG("First active button, starting periodic timer");
            timer_start();
        }
    }

    When a button is configured as active low, the first interrupt will not enter this handler as the callback is not set up at this point. When the button is released, the interrupt will trigger this callback, but the pin will now be high, the logic in the handler will therefore not start the periodic timer that polls the button state. 

    A workaround could be to configure the wakeup pin as a active high pin, when it in reality is active low ( or vice versa if its active high in reality) before going to System OFF. A wakeup from System OFF will as before cause the first interrupt to be missed, but when the button is released the event handler will read the button pin state, which will be high. The is_active variable will then evaluate to true as !( true ^ false) is true. The periodic timer will then start and later a APP_BUTTON_PUSH event is generated, so in this specific scenario after wake-upit means that the button was released.

    After the wake-up you reconfigure the app button library to use the active state during normal operation. 

    Best regards

    Bjørn

  • bjorn-spockeli,

    Thanks for looking into this for me. I tried configuring the wakeup pin as active high, but I ended up modifying app_button instead. This worked out smoother to time the button press. I normally don't like to touch an SDK library, but here is the function that I added to app_button.c:

    void app_button_override_to_button_pressed(uint8_t button_id)
    {
        uint8_t pin = mp_buttons[button_id].pin_no;
        state_set(pin, BTN_PRESSED);
        CRITICAL_REGION_ENTER();
        m_pin_active |= 1ULL << pin;
        CRITICAL_REGION_EXIT();
        timer_start();
    }

    At startup, I call that function something like this (although it is a little more complicated):

    if(bsp_board_button_state_get(0))
    {
        bsp_button_event_handler(BSP_BUTTON_0, APP_BUTTON_PUSH);
        app_button_override_to_button_pressed(0);
    }

    Let me know if there are any suggestions.

    Thanks,
    Chris

Related