Hi Tomek,

I agree with you, but in this first "incarnation" it will support only two
actions, but people are invited to improve it later.

Notice that I used INPUT_SBUTTON, so the "Dual Action" is just a Kconfig
message, easy to change. :-)

Now my idea is just to add the improvements that Michał Łyszczek added in
the review and add a debouncing, because after more tests I discovered that
sometimes it was returning invalid key press.

The driver itself is very simple, less than 300 LoC, so I hope everybody
can understand how it works.

BR,

Alan

On Fri, Jul 11, 2025 at 7:39 PM Tomek CEDRO <to...@cedro.info> wrote:

> 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