Queuing up or delaying button presses

Hi all,

I am making a project which emulates a keyboard. That's all fine, but I have a block which I am trying to solve.

Lets start with a single button acting as a single key.

Basically, each button press needs to activate keystroke of about 300 milliseconds to register properly on the host application.

For example when a button is pressed, I am running this:

//if button press detected do:
Keyboard.write('a');
    delay(300);
Keyboard.release('a');

The issue is, the user may press the button very fast and the blocking delay of 300 causes some button presses to be missed.

Is there a way to overcome this. Perhaps queue up the requests of keystrokes or do it some other non blocking way? not sure this is possible with an Arduino?

Any ideas much appreciated.

One way to approach this would be to record the value of millis() when the button press is detected, let's say in a variable called pressTime, then test for 300ms passing since the button press in loop...

if( millis() - pressTime >= 300 )
{
Keyboard.release('a');
}

or something like that ( probably ).

I have a thing that needs doing now but I'll post a working example in a bit.

or do it some other non blocking way? not sure this is possible with an Arduino?

Take a look at Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

Thanks guys so far.

Here is another example of the problem.

I have a potentiometer with 8 'positions'. You can spin the pot in less than a seconds, but of course Arduino needs to spend 300millis sending each position of the pot to the host app.

So if you spin it fast, it only sends 1 or 2 positions because of the blocking when sending, as its not reading. So it needs to read all positions and then spend time 'later' sending each 300millisecond command.

When the pot becomes close to one of the trigger positions save the position number or the pot value in an array. Once the pot value stablises for a significant period send the position numbers or post positions

As promised...

This or a variation of this is what I use for switch debouncing

const uint8_t  pin = A0;

void setup()
{
  Serial.begin(115200);
  while(!Serial);
  pinMode( pin , INPUT_PULLUP );
}
void loop()
{
  static uint32_t stateChanged = 0;
  uint32_t debouncePeriod = 10;
  static int state = 1;

  int reading = digitalRead( pin );
  if ( reading != state ) stateChanged = millis();
  state = reading;
  if ( stateChanged && ( millis() - stateChanged ) >= debouncePeriod )
  {
    stateChanged = 0;
    if ( state == 0 )
    {
//      Switch closed code here
    }
    else
    {
//      Switch open code here
    }
  }
}

it's more or less the same as the example from the IDE except I use the timer variable to double as a flag / state variable. it makes sense to my addled old brain and it works so I'm sticking with it.

Here's the version that demonstrates what I said in my other post

#include<Keyboard.h>
const uint8_t  pin = A0;

void setup()
{
  Serial.begin(115200);
  while(!Serial);
  pinMode( pin , INPUT_PULLUP );
}
void loop()
{
  static uint32_t stateChanged = 0;
  uint32_t debouncePeriod = 10;
  static int state = 1;

  static uint32_t pressTime = 0;      
  uint32_t pressPeriod = 300;

  int reading = digitalRead( pin );
  if ( reading != state ) stateChanged = millis();
  state = reading;
  if ( pressTime && ( millis() - pressTime ) >= pressPeriod )
  {
    pressTime = 0;
    Keyboard.release('a');
  }
  if ( stateChanged && ( millis() - stateChanged ) >= debouncePeriod )
  {
    stateChanged = 0;
    if ( state == 0 )
    {
      pressTime = millis();
      Keyboard.write('a');
    }
    else
    {
//      Switch open code here
    }
  }
}

The difference between the two is basically just the addition of a timer not unlike the debounce timer

on line 16

static uint32_t pressTime = 0;
  uint32_t pressPeriod = 300;

is the timer / flag / state variable pressTime and pressPeriod should be obvious

on line 32

pressTime = millis();
Keyboard.write('a');

when the transition from open to closed is detected we record the time and call Keyboard.write

line 22

if ( pressTime && ( millis() - pressTime ) >= pressPeriod )
  {
    pressTime = 0;
    Keyboard.release('a');
  }

when the timer runs out, reset the flag and call Keyboard.release.

Hope that works as advertised and makes sense.

There's a very real chance that I'm having one of my turns and this all came to me in a seizure-dream.

Best o' luck

Circular buffer application? Create an array to hold the switch closures. When a closure is detected 'push' it into the buffer. Meanwhile, monitor the buffer and when there's a value in the first position pull it out and send to host. Do the time delay thing. Check first position, lather, rinse, repeat. delay() is not compatible with this sort of thing

Site search 'circular buffer', 'fifo'. There are libraries available to implement these concepts.

Thanks all.
so in the end I used this library: GitHub - EinarArnason/ArduinoQueue: A lightweight linked list type queue implementation, meant for microcontrollers.
And then millis to stop blocking.

#include "Queue.h"
#include <Keyboard.h>

//timer stuff
long startTime;
int pressTime = 230;
bool timer;
char key;
unsigned long currentTime;

//queue
DataQueue<char> intQueue(20);

//throttle
int current_range;
int throttle;
int value;
int controller;
int last_range = 0;
const int potPin = A0;




void setup() {
  
  
  Serial.begin(9600);
  delay(1000);
  Serial.println("Class 66 controller v0.1");
}

void loop() {
//Throttle section
  value = analogRead(potPin);          //Read and save analog value from potentiometer
  value = map(value, 0, 1023, 0, 255); //Map value 0-1023 to 0-255 (PWM)
  

//set range numbers
   if (value <99 )
    { controller = -4;
    }
  else if (value >= 100 && value <109)
    { controller = -3;
    }
  else if (value >= 110 && value <119)
    { controller = -2;
    }
  else if (value >= 120 && value <129)
    { controller = -1;
    }
  else if (value >= 130 && value <139 )
    { controller = 0;
    }
  else if (value >= 140 && value <149 )
    { controller = 1;
    }
  else if (value >= 150 && value <159 )
    { controller = 2;
    }
  else if (value >= 160 && value <169 )
    { controller = 3;
    }
  else if (value >= 170 )
    { controller = 4;
    }

  
  current_range = controller;
  if (current_range > last_range)
   {
    //add item
    unsigned int n = intQueue.item_count();
    int queueSize = n;
    intQueue.enqueue('a');
    Serial.println("a");
   }
  if (current_range < last_range)
  {
     unsigned int n = intQueue.item_count();
    int queueSize = n;
    intQueue.enqueue('d');
    Serial.println("d");
  }
    last_range = current_range;

  
  // millis delay code
  currentTime = millis();
  
    //check if queue has items:
    if (!intQueue.isEmpty() && timer == false)
    {
      //Serial.println("press key");
      //Read and remove first item in queue
      key = intQueue.dequeue();
      
      //get current tume
      startTime = millis();

      //start keyboard press
      Keyboard.press(key);
      timer = true;
      
    }
    else
    {
      //Serial.println("empty queue");
    }

    //stop timer if enough time
    if (currentTime - pressTime >= startTime && timer == true)//
    {
      Keyboard.release(key);
      //Serial.println("time elapsed - stop pressing");
      startTime = currentTime;
      timer = false;
    }


}

Good job!

Thanks for the followup, too.