Go Down

Topic: timed sequences of events without delay() (Read 209 times) previous topic - next topic

johnerrington

Hi I hope you can help I'm having trouble getting my head around this.
I'm trying to send charcters to the PC emulating a keyboard.
The code I am using is based on this which follows:
(from https://www.dinotools.de/en/2016/11/09/use-an-arduino-board-to-emulate-a-keyboard/)
 I'd prefer not to use the delay function. However other options either a) dont work or b)take up a LOT of code space.

The commands do need to be sent in a strict sequence; and I'm planning to use other different command sequences which because they have some ordinary and some combined keypresses cant just be sent as a string.

All suggestions welcomed.


Keyboard.begin();
  delay(10000);
  Keyboard.press(KEY_LEFT_GUI);
  Keyboard.press('r');
  delay(100);
  Keyboard.releaseAll();
  delay(500);
  Keyboard.print("notepad.exe");
  Keyboard.press(KEY_RETURN);
  delay(100);
  Keyboard.releaseAll();
  delay(2000);
  Keyboard.print("Hello World");

Idahowalker

Code: [Select]
Keyboard.begin();
  delay(10000);
  Keyboard.press(KEY_LEFT_GUI);
  Keyboard.press('r');
  delay(100);
  Keyboard.releaseAll();
  delay(500);
  Keyboard.print("notepad.exe");
  Keyboard.press(KEY_RETURN);
  delay(100);
  Keyboard.releaseAll();
  delay(2000);
  Keyboard.print("Hello World");



What have you tried?

Sil83

You can try to use the function millis() https://www.arduino.cc/reference/en/language/functions/time/millis/

Check between the example, I think you can find something useful for you!

Silvano
Eng. Silvano Bertoldo, PhD

johnerrington

Thanks for your help; I revisited one of my ideas;
I set a timer tick interrupt, and just increment a volatile int counter.

in setup:

OCR0A = 0xAF;  // set compare register to #175, interrupt on match
   TIMSK0 |= _BV(OCIE0A);


// Timer tick interrupt is called once a millisecond,
ISR(TIMER0_COMPA_vect)
{
  timerCount+=1;
}

Then in my loop Ihave a blink Led routine,

void blinkLed()
  {
    flashRate=readA2(); //read the speed from the potentiometer
    if(flashRate<=100)flashRate=100; //set a maximum rate toallow PC to respond.
    waitFor(flashRate);
    ledValue=1;
    digitalWrite(ledPin, ledValue);
    waitFor(flashRate );
    ledValue=0;
    digitalWrite(ledPin, ledValue); 
 }
which calls a function waitFor(); as follows:

  void waitFor(int msec)
  {
    do{ int i = 0;
    } while (timerCount<=msec);
    timerCount=0;
  }

I dont see why I need an operation (int i=0) in the do loop but it does not seem to work properly without.

Not sure exactly what I have achieved by avoiding using delay() though!

Blackfin

Consider a state-machine approach. I don't see your entire code so this may not do what you want (as well, I only compiled it, didn't test it...)

Give it a try.

Code: [Select]
#include <Keyboard.h>

#define ST_INIT             0
#define ST_INIT_DELAY       1
#define ST_RELEASE_DELAY    2
#define ST_NOTEPAD_DELAY    3
#define ST_RELEASE_2_DELAY  4
#define ST_HELLOWORLD_DELAY 5
#define ST_END              6

const byte pinLED = LED_BUILTIN;

void setup()
{
    Keyboard.begin();
    pinMode( pinLED, OUTPUT );
    digitalWrite( pinLED, LOW );   

}//setup

void loop()
{
    Keyboard_StateMachine();

}//loop

void Keyboard_StateMachine( void )
{
    static bool
        bLEDFlag = false;
    static byte
        stateKSM = ST_INIT;
    static unsigned long
        timeKSM;
    unsigned long
        timeNow;

    timeNow = millis();
   
    switch( stateKSM )
    {
        case    ST_INIT:
            timeKSM = timeNow;
            stateKSM = ST_INIT_DELAY;
        break;

        case    ST_INIT_DELAY:
            if( timeNow - timeKSM >= 10000U )
            {
                Keyboard.press(KEY_LEFT_GUI);
                Keyboard.press('r');               
                timeKSM = timeNow;
                stateKSM = ST_RELEASE_DELAY;
            }//if
           
        break;

        case    ST_RELEASE_DELAY:
            if( timeNow - timeKSM >= 100U )
            {
                Keyboard.releaseAll();
                timeKSM = timeNow;
                stateKSM = ST_NOTEPAD_DELAY;

            }//if
           
        break;
       
        case    ST_NOTEPAD_DELAY:
            if( timeNow - timeKSM >= 500U )
            {
                Keyboard.print("notepad.exe");
                Keyboard.press(KEY_RETURN);
                timeKSM = timeNow;
                stateKSM = ST_RELEASE_2_DELAY;

            }//if
           
        break;

        case    ST_RELEASE_2_DELAY:
            if( timeNow - timeKSM >= 100U )
            {
                Keyboard.releaseAll();
                timeKSM = timeNow;
                stateKSM = ST_HELLOWORLD_DELAY;

            }//if
       
        case    ST_HELLOWORLD_DELAY:
            if( timeNow - timeKSM >= 2000U )
            {
                Keyboard.print("Hello World");
                Keyboard.press(KEY_RETURN);
                timeKSM = timeNow;
                stateKSM = ST_END;

            }//if
           
        break;

        case    ST_END:
            //at the end of the SM just blink the built-in LED
            if( timeNow - timeKSM >= 250u );
            {
                bLEDFlag ^= true;
                digitalWrite( LED_BUILTIN, (bLEDFlag ? HIGH:LOW) );
                timeKSM = timeNow;
               
            }//if
       
        break;

    }//switch
   
}//Keyboard_StateMachine

johnerrington

Thanks BlackFin;

I got so far thinking about how to implement a state machine approach, your example has shown me the "light at the end of the tunnel"!


I'll need to do a bit more thinking but I think I'm right in believing it would work in a multitasking setup, where my approach would not.  Also its applicable to many other sequential systems.

I dont  think I need that for my current project, but its something I've learnt for the future.

Following up on State machine implementations I found these two links that look useful, so if anyone else is reading this thread here they are:

https://github.com/j-bellavance/Tutorials/tree/master/State%20machines%20Tutorial
https://www.norwegiancreations.com/2017/03/state-machines-and-arduino-implementation/

Robin2

I set a timer tick interrupt, and just increment a volatile int counter.
Unless you need timing at the microsecond level there is no need to use a hardware timer and interrupts.


The demo Several Things at a Time illustrates the use of millis() to manage timing without blocking. It may help with understanding the technique.

Have a look at Using millis() for timing. A beginners guide if you need more explanation.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

johnerrington

Blackfin - could i ask your help again?
Another part of the program is to send all printable ascii characters with a space and delay between.
I cant see an ASM with 100 lines in the case - should I create a class "sendascii" with a member functions "sendnext" and resetto0 and put that in a simple state machine switch-case construct eg
switch (next or stop)
case next .. sendascii ... etc break
case stop .. resetto0 ...  etc break.

sorry not very precise but presently on holiday with limited resources except thinking time!

Blackfin

There's lot of ways to do this. Here's a simple way that I think does what you ask:

Code: [Select]
void setup()
{
    Serial.begin(9600);
   
}//setup

void loop()
{
    SendASCII();

}//loop

void SendASCII( void )
{
    static byte
        asciiVal = 0x20;
    static unsigned long
        timeASCII = 0;

    //wait 500mS between character prints
    if( (millis() - timeASCII) <= 500 )
        return;

    //send character
    Serial.write( asciiVal );
    //incremement printable character variable until '~' (not printing
    //"delete"; if you want that too, change to 0x7f
    asciiVal++;
    if( asciiVal > 0x7e )
    {
        Serial.print( "\n" );
        asciiVal = 0x20;
       
    }//if

    //set up timeASCII for the delay for this character
    timeASCII = millis();
   
}//SendASCII

Go Up