Very cool thanks Alan! :-)

I think there may be more actions with one button:
1. single click.
2. double click.
3. triple click.
4. short press.
5. long press.
etc

So it is not only dual action button, there may be more actions :-)

It would be great if the driver could return event code or have return
codes configurable (build time?) on selected actions. That would make
things really versatile :-)

Thanks :-)
Tomek


On Fri, Jul 11, 2025 at 11:26 PM Alan C. Assis <acas...@gmail.com> wrote:
>
> Hi Everyone,
>
> The driver is done: https://github.com/apache/nuttx/pull/16714
>
> I started using drivers/input/spq10kbd.c as reference, but then I realized
> that there was already a keyboard_upper.c that I could use to send key
> events/strokes.
>
> After that the driver was easier to implement than I thought initially.
>
> BR,
>
> Alan
>
> On Wed, Jul 9, 2025 at 10:16 AM Alan C. Assis <acas...@gmail.com> wrote:
>
> > Hi Everyone,
> >
> > Some years ago a customer asked me to develop a project with NuttX for
> > this board that used a small OLED display and a single button.
> > That button should be used to navigate in the menu, as a side note:
> > initially he suggested: quick press and release will work as a ENTER and
> > long press will work as TAB (move to other option),
> > but while testing the application I released the was faster if quick press
> > work as TAB and long press as enter.
> >
> > This video implements the same idea on a ESP32-Devkit board that also has
> > only a single button: https://www.youtube.com/shorts/vfQLW-a2JhA
> >
> > The daemon that detect the button and define the type of input is
> > something like this:
> >
> >   /* Define the notifications events */
> >
> >   btnevents.bn_press   = supported;
> >   btnevents.bn_release = supported;
> >
> >   btnevents.bn_event.sigev_notify = SIGEV_SIGNAL;
> >   btnevents.bn_event.sigev_signo  = CONFIG_INPUT_BUTTONS_SIGNO;
> >
> >   /* Register to receive a signal when buttons are pressed/released */
> >
> >   ret = ioctl(fd, BTNIOC_REGISTER,
> >               (unsigned long)((uintptr_t)&btnevents));
> >   if (ret < 0)
> >     {
> >       int errcode = errno;
> >       printf("button_daemon: ERROR: ioctl(BTNIOC_SUPPORTED) failed: %d\n",
> >              errcode);
> >       goto errout_with_fd;
> >     }
> >
> >   /* Ignore the default signal action */
> >
> >   signal(CONFIG_INPUT_BUTTONS_SIGNO, SIG_IGN);
> >
> >   /* Now loop forever, waiting BUTTONs events */
> >
> >   for (; ; )
> >     {
> >       struct siginfo value;
> >       sigset_t set;
> >
> >       bool timeout;
> >       int nbytes;
> >
> >       /* Wait for a signal */
> >
> >       sigemptyset(&set);
> >       sigaddset(&set, CONFIG_INPUT_BUTTONS_SIGNO);
> >       ret = sigwaitinfo(&set, &value);
> >       if (ret < 0)
> >         {
> >           int errcode = errno;
> >           printf("ERROR: sigwaitinfo() failed: %d\n", errcode);
> >           goto errout_with_fd;
> >         }
> >
> >       sample = (btn_buttonset_t)value.si_value.sival_int;
> >
> >       if (sample & 0x01)
> >         {
> >           /* Button pressed, start measuring time */
> >
> >           clock_gettime(CLOCK_REALTIME, &start);
> >         }
> >       else
> >         {
> >           /* Button released, calculate the elapsed time */
> >
> >           clock_gettime(CLOCK_REALTIME, &curr);
> >
> >           elapsed.tv_sec  = curr.tv_sec - start.tv_sec;
> >           if (curr.tv_nsec >= start.tv_nsec)
> >             {
> >               elapsed.tv_nsec = curr.tv_nsec - start.tv_nsec;
> >             }
> >           else
> >             {
> >               unsigned long borrow = 1000000000 - start.tv_nsec;
> >               elapsed.tv_sec--;
> >               elapsed.tv_nsec = curr.tv_nsec + borrow;
> >             }
> >
> >  unsigned long ftime = (elapsed.tv_sec * 1000000000) + elapsed.tv_nsec;
> >
> >  /*printf("Elapsed miliseconds = %d\n", ftime / 1000000);*/
> >  if (ftime < 500000000)
> >             {
> >               input_event(INPUT_TAB);
> >             }
> >  else
> >             {
> >               input_event(INPUT_ENTER);
> >             }
> >         }
> >
> >       /* Make sure that everything is displayed */
> >
> >       fflush(stdout);
> >
> >       usleep(1000);
> >     }
> >
> > But today while taking a shower and thinking about other subjects that
> > fact came to mind and I asked myself: how to develop that thing in a more
> > standard way to avoid the "kludge" above? (in fact it is not kludge, but we
> > can improve, right?)
> >
> > So, I realized it would be nice to have a kind of "action button" driver
> > that generates this kind of event (detecting long press and short press)
> > automatically returning INPUT_ENTER or INPUT_TAB.
> >
> > In fact the driver will be more similar to djoystick.c than the ordinary
> > buttons driver itself (for simplicity).
> >
> > Please let me know if someone already implemented something similar or
> > other suggestions before I implement it.
> >
> > BR,
> >
> > Alan
> >



-- 
CeDeROM, SQ7MHZ, http://www.tomek.cedro.info

Reply via email to