Detecting a button double click?

We are new to Nordic and have been making great progress with our nrf52840DK board and successfully running many of the Nordic SDK examples. We can't seem to find a single example of an API that will notify on a variety of button events (more than just a single click). Our code needs to respond differently for single-click, double-click, press-and-hold. Sure seems that the Nordic SDK would have something for that. Yes? Any help would be appreciated.

Thanks,

Steve K, PuzL Labs, LLC

Parents Reply Children
  • Hi Priyanka,

    Uggggg, I took a look at the recent link. Again, I'm coming up empty handed. Frankly, it doesn't seem that the question I'm asking should be this difficult to get to the bottom of. I wish that I could be directed to a very specific block of logic that demonstrates how a double-click of a button can be detected. (buttons like any of the four buttons on the nrf52840-DK board). I've given an honest look at the collection of CAF content in my SDK as well as the documentation and I find it lacking. It is by no means obvious how to answer my question from the general pointers I've been given in the direction of CAF. I am imagining that there really wouldn't be that much code involved, but if I don't know the various interfaces available and the syntax it's hard to get anything working. I typically am very good at hunting things down and reverse engineering. It was not so difficult for me to locate/generate the logic required for a single button click (non-CAF), I'll show it below. It would be fantastic if someone could just present an example with this level of detail that I could then leverage to get Button Double-Click Detection.

    Button Single-Click Detection.

    #include <zephyr/kernel.h>

    #define Button_NODE DT_ALIAS(sw0)
    static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(Button_NODE, gpios);
    static struct gpio_callback button_cb;

    static void button_callback(const struct device *gpiob, struct gpio_callback *cb, gpio_port_pins_t pins)
    {
       // TBSL — do logic here to process single button press event.
    }

    static void Init_button()
    {
        if (gpio_is_ready_dt(&button)) {
           gpio_pin_configure_dt(&button, GPIO_INPUT);
           gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
           gpio_init_callback(&button_cb, button_callback, BIT(button.pin));
           gpio_add_callback(button.port, &button_cb);
        }
    }

    int main(void)
    {
       Init_button();
       while (true) {
          k_msleep(5000);
       }
       return 0;
    }

  • Hi,

    There does not seem an in-detail sample, but there is the CAF: Click detector module which can help. You can refer to the implementation details there, that does various click types, say, double click for instance. You can take a look at the click_detector code and copy the same method used here into your own application.

    Regards.

    Priyanka

  • Hi Steve.

    To address the CAF front, here's a sample I modified from the CAF sample to use single and double clicks: droidecahedron/caf_click

    However, you can alternatively continue using your i/o callback and add some logic with Timers — Zephyr Project Documentation to determine single vs double vs long. These can be pretty cheap.

    I have a generic i/o practice repo that utilizes the logic pretty similarly here to the CAF example: droidecahedron/nrf_io_practice

    (You can take whatever is used in io.c and roll it into your main.c however you like. I just abstracted it away to another file in that project to keep main empty.)

    Both need some clean up but either of them should get you most of the way there. I am partial to using timers over CAF for this use case. Pick whichever you prefer. One benefit of using timers is you can specify a bit more what the "double click" duration is in a straightforward way. Another is knowing pretty directly which peripherals are actually being used.

  • Hi Johnny N,

    Yes, thank you very much. I think that is a great starting point for me. I downloaded your sample application using:

    $ git clone https://github.com/droidecahedron/nrf_io_practice.git

    And then I used the Visual Studio "Create an existing application" and pointed it at that sample application and after Adding a Build Config it built/flashed and is running on my nrf52840-DK. I will fiddle with this working sample and alter as needed for the particular system we are developing. Thanks again.

    Steve K, PuzL Labs, LLC

  • Sounds great, Steve.

    As you'll see in the comments in that repo, there is some cleanup to do on my end in there, but it is functional. (You can see some of the cleanup type of changes I reference in the init comment here in this PR)

    The Zephyr Timer documentation page I linked has some other examples on timer usage, my timer implementation is certainly less than optimal for button detection and is quite barebones, but you can extrapolate for more complex and robust logic, the application logic is up to you. (Such as accounting for bouncy buttons, etc.)

    Glad it could be helpful to you! I like to keep the io.c as a separate file so you can drag/drop it into your other applications. (I also like to put my BLE services into their own source files as well.)

    Do not hesitate to reach out if there is anything further.

    Best regards,

Related