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

app_button library fails to recognize a button press if another button transitions within the debounce window.

HERE is the fix for this problem.  It has been reported before (by others) with no reply. I hope that this fix or its equivalent will be incorporated into the next SDK release so that others will avoid having to debug and fix this themselves.

File:  app_button.c

 *          Button depression (press & release) for shorter than the debounce timeout,
 *          will cancel the debounce of any other buttons that are in progress.
 * Root Cause:
 *          Debounce timer is mistakenly stopped uppon the detection of a
 *          glitch while other button debounce is active.
 * Function:
 *          gpiote_event_handler()
static void gpiote_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    uint32_t err_code;
    uint32_t pin_mask = 1 << pin;

    NRF_LOG_DEBUG("gpiote_event_handler:");

    // Start detection timer. If timer is already running, the detection period is restarted.
    // NOTE: Using the p_context parameter of app_timer_start() to transfer the pin states to the
    //       timeout handler (by casting event_pins_mask into the equally sized void * p_context
    //       parameter).

    if (!(m_pin_transition & pin_mask))
    {
        // First edge for button

        err_code = app_timer_stop(m_detection_delay_timer_id);
        if (err_code != NRF_SUCCESS)
        {
            // The impact in app_button of the app_timer queue running full is losing a button press.
            // The current implementation ensures that the system will continue working as normal.
            return;
        }

        if (nrf_drv_gpiote_in_is_set(pin))
        {
            m_pin_state |= pin_mask;
        }
        else
        {
            m_pin_state &= ~(pin_mask);
        }
        m_pin_transition |= (pin_mask);

        NRF_LOG_DEBUG("app_timer_stop/start:");

        err_code = app_timer_start(m_detection_delay_timer_id, m_detection_delay, NULL);
        if (err_code != NRF_SUCCESS)
        {
            // The impact in app_button of the app_timer queue running full is losing a button press.
            // The current implementation ensures that the system will continue working as normal.
        }
    }
    else
    {
        // Second edge for button
        // press shorter than debounce time ... ignore
        m_pin_transition &= ~pin_mask;

        // If no other pending debounce edges, stop timer
        if (m_pin_transition == 0)
        {
            NRF_LOG_DEBUG("app_timer_stop:");
            (void)app_timer_stop(m_detection_delay_timer_id);        }
        }
}
Related