You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@nuttx.apache.org by "Alan C. Assis" <ac...@gmail.com> on 2025/07/09 13:16:10 UTC

New type of input idea: an action button

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

Re: New type of input idea: an action button

Posted by "Alan C. Assis" <ac...@gmail.com>.
Thank you Tomek!

I devices to change the name from Dual Action to Multi Actions as
you suggested, two is already multi! :-D

I implemented all improvements suggested and added debouncing and the
option to let the user define the codes for keys being generated.

BR,

Alan


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

> Very cool thank you Alan :-)
>
> On Sat, Jul 12, 2025 at 12:57 AM Alan C. Assis <ac...@gmail.com> wrote:
> >
> > 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 <ac...@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 <ac...@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
> > >
>
>
>
> --
> CeDeROM, SQ7MHZ, http://www.tomek.cedro.info
>

Re: New type of input idea: an action button

Posted by Tomek CEDRO <to...@cedro.info>.
Very cool thank you Alan :-)

On Sat, Jul 12, 2025 at 12:57 AM Alan C. Assis <ac...@gmail.com> wrote:
>
> 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 <ac...@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 <ac...@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
> >



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

Re: New type of input idea: an action button

Posted by "Alan C. Assis" <ac...@gmail.com>.
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 <ac...@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 <ac...@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
>

Re: New type of input idea: an action button

Posted by Tomek CEDRO <to...@cedro.info>.
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 <ac...@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 <ac...@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

Re: New type of input idea: an action button

Posted by "Alan C. Assis" <ac...@gmail.com>.
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 <ac...@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
>

Re: New type of input idea: an action button

Posted by "Alan C. Assis" <ac...@gmail.com>.
Hi Tomek,

I think the Single Button Actions will be a good name (maybe in future it
could have a dual click function, like used in mouses).

Yes, the idea is to use some generic system timer to get it working on all
MCUs.

BR,

Alan

On Wed, Jul 9, 2025 at 11:03 AM Tomek CEDRO <to...@cedro.info> wrote:

> Hey there Alan :-)
>
> Very cool and useful idea! I did something like this long time ago in
> pure assembly and C but it was returning press time and based on that
> actions were performed by the handling routine.
>
> In NuttX this would work on various platforms where different
> capabilities (i.e. irq, timers, etc) may be used for hardware
> acceleration, but I think generic approach based on system timer that
> would work on everything could be the first step :-)
>
> Good self-explanatory name for such module would be nice to have, what
> do you think about "Single Button Actions" or similar? Singe Button
> tells only one button is used to perform various Actions.
>
> Thanks :-)
> Tomek
>
>
> On Wed, Jul 9, 2025 at 3:17 PM Alan C. Assis <ac...@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
>

Re: New type of input idea: an action button

Posted by Tomek CEDRO <to...@cedro.info>.
Hey there Alan :-)

Very cool and useful idea! I did something like this long time ago in
pure assembly and C but it was returning press time and based on that
actions were performed by the handling routine.

In NuttX this would work on various platforms where different
capabilities (i.e. irq, timers, etc) may be used for hardware
acceleration, but I think generic approach based on system timer that
would work on everything could be the first step :-)

Good self-explanatory name for such module would be nice to have, what
do you think about "Single Button Actions" or similar? Singe Button
tells only one button is used to perform various Actions.

Thanks :-)
Tomek


On Wed, Jul 9, 2025 at 3:17 PM Alan C. Assis <ac...@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

Re: New type of input idea: an action button

Posted by "Alan C. Assis" <ac...@gmail.com>.
Hi Sebastien,

For now it will depend on GPIO IRQ, just like the
digital joystick (ref.: lpc17_40_djoystick.c)

But probably someone could extend it in the future to support polling mode.

BR,

Alan

On Wed, Jul 9, 2025 at 11:22 AM Sebastien Lorquet <se...@lorquet.fr>
wrote:

> Hello,
>
> Can this be wired in the GPIO system?
>
> There are gpio interrupts iirc. these could be used to trigger a signal.
>
> If a system does not have gpio interrupts, polling can do the same trick.
>
> Just my two cents!
>
> Sebastien
>
>
> On 09/07/2025 15:16, Alan C. Assis 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
> >
>

Re: New type of input idea: an action button

Posted by Nathan Hartman <ha...@gmail.com>.
On Wed, Jul 9, 2025 at 12:38 PM Gregory Nutt <sp...@gmail.com> wrote:

> The time delay that you are concerned about is simply the context
> switching time and this is necessary to instantiate the thread's stack and
> other operating context.  As a general rule this restoration of thread
> context cannot really be omitted.



I don't think it's even necessary to worry about the time of context
switching. The application in question is a button which might be short
pressed or long pressed. This is measured in hundreds of milliseconds or
even seconds. The context switching time is not perceptible to the human
who is pushing the button.

Cheers,
Nathan

Re: New type of input idea: an action button

Posted by Gregory Nutt <sp...@gmail.com>.
The time delay that you are concerned about is simply the context switching time and this is necessary to instantiate the thread's stack and other operating context.  As a general rule this restoration of thread context cannot really be omitted.
________________________________
From: Gregory Nutt <sp...@gmail.com>
Sent: Wednesday, July 9, 2025 9:34 AM
To: dev@nuttx.apache.org <de...@nuttx.apache.org>
Subject: Re: New type of input idea: an action button

In general, callbacks from the OS to application code is not possible in NuttX.

That will not work at all in PROTECTED or KERNAL builds due to address space conflicts between the OS and the application.  In some FLAT build cases it could work, provided that there is no dependency on the properties of the application thread.  In particular, the application callback could not use and thread-specific data or TLS.

When a application callback is performed, the callback runs with the full identity of the callee.  The stack used by callback is the stack of the callee.  TLS data in the stack — such as file descriptors — would be unusable by the callback code and could result is some really peculiar behavior.

I would recommend that we follow the POSIX programming model:  no callbacks can be used; other IPCs such as signals are the only mechanism available.  This is just as things work in Linux, BSD, and any other POSIX system.

________________________________
From: Matteo Golin <ma...@gmail.com>
Sent: Wednesday, July 9, 2025 8:13 AM
To: dev@nuttx.apache.org <de...@nuttx.apache.org>
Subject: Re: New type of input idea: an action button

I believe the GPIO system does in fact trigger a signal event. I've used
this for a project before.

However, I think it would be nice to be able to register a callback instead
of blocking for a signal, since that introduced A LOT more delay. This
would probably need to be done with the low priority work queue so we can
maintain responsiveness even with slower callbacks.

Matteo

On Wed, Jul 9, 2025, 10:22 AM Sebastien Lorquet <se...@lorquet.fr>
wrote:

> Hello,
>
> Can this be wired in the GPIO system?
>
> There are gpio interrupts iirc. these could be used to trigger a signal.
>
> If a system does not have gpio interrupts, polling can do the same trick.
>
> Just my two cents!
>
> Sebastien
>
>
> On 09/07/2025 15:16, Alan C. Assis 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
> >
>

Re: New type of input idea: an action button

Posted by Gregory Nutt <sp...@gmail.com>.
In general, callbacks from the OS to application code is not possible in NuttX.

That will not work at all in PROTECTED or KERNAL builds due to address space conflicts between the OS and the application.  In some FLAT build cases it could work, provided that there is no dependency on the properties of the application thread.  In particular, the application callback could not use and thread-specific data or TLS.

When a application callback is performed, the callback runs with the full identity of the callee.  The stack used by callback is the stack of the callee.  TLS data in the stack — such as file descriptors — would be unusable by the callback code and could result is some really peculiar behavior.

I would recommend that we follow the POSIX programming model:  no callbacks can be used; other IPCs such as signals are the only mechanism available.  This is just as things work in Linux, BSD, and any other POSIX system.

________________________________
From: Matteo Golin <ma...@gmail.com>
Sent: Wednesday, July 9, 2025 8:13 AM
To: dev@nuttx.apache.org <de...@nuttx.apache.org>
Subject: Re: New type of input idea: an action button

I believe the GPIO system does in fact trigger a signal event. I've used
this for a project before.

However, I think it would be nice to be able to register a callback instead
of blocking for a signal, since that introduced A LOT more delay. This
would probably need to be done with the low priority work queue so we can
maintain responsiveness even with slower callbacks.

Matteo

On Wed, Jul 9, 2025, 10:22 AM Sebastien Lorquet <se...@lorquet.fr>
wrote:

> Hello,
>
> Can this be wired in the GPIO system?
>
> There are gpio interrupts iirc. these could be used to trigger a signal.
>
> If a system does not have gpio interrupts, polling can do the same trick.
>
> Just my two cents!
>
> Sebastien
>
>
> On 09/07/2025 15:16, Alan C. Assis 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
> >
>

Re: New type of input idea: an action button

Posted by Matteo Golin <ma...@gmail.com>.
I believe the GPIO system does in fact trigger a signal event. I've used
this for a project before.

However, I think it would be nice to be able to register a callback instead
of blocking for a signal, since that introduced A LOT more delay. This
would probably need to be done with the low priority work queue so we can
maintain responsiveness even with slower callbacks.

Matteo

On Wed, Jul 9, 2025, 10:22 AM Sebastien Lorquet <se...@lorquet.fr>
wrote:

> Hello,
>
> Can this be wired in the GPIO system?
>
> There are gpio interrupts iirc. these could be used to trigger a signal.
>
> If a system does not have gpio interrupts, polling can do the same trick.
>
> Just my two cents!
>
> Sebastien
>
>
> On 09/07/2025 15:16, Alan C. Assis 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
> >
>

Re: New type of input idea: an action button

Posted by Sebastien Lorquet <se...@lorquet.fr>.
Hello,

Can this be wired in the GPIO system?

There are gpio interrupts iirc. these could be used to trigger a signal.

If a system does not have gpio interrupts, polling can do the same trick.

Just my two cents!

Sebastien


On 09/07/2025 15:16, Alan C. Assis 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
>