This Tutorial explains blocking code from non-blocking code in common sense with code.
Years ago I wrote examples to show how to un-delay code with delays and make it sketch-combine compatible. The actual conversion is cut-and-dry techniques that let the non-delay code lines (what ran a GSM in one case.. 13 steps of it worked before, it works after too) copy straight through and run like no other code is in the sketch.
The Tutorial should explain enough to make sense of this ton-of-comments undelay-combine-sketches example.
[code]
// CombineSketchesDemo 2022 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 23, 2022 by GFS. Compiled on Arduino 2.1.0.5
// Free to post anywhere complete and as is.
// This sketch shows a general method to get rid of delays in code.
// You could upgrade code that delays to combine into this sketch.
// .. adding delays in loop cases
// revisions with forum help:
// dlloyd -- added state enums May 11. <--- changed as needed.
////////////////////////////////////////////////////////////////////////////
// This example/demo takes 3 sketches that use delay() and COMBINE THEM
// by using simple techniques the sketch demonstrates.
// Plus there's a user stop/go that violates how the 3 together work.
// Code that does not sit or loop in one spot to wait,
// does not block other code from executing during a wait.
// In 1 ms, code can use or lose 16000 cpu cycles. So don't block!
// The 3 delay-sketches void loop() code goes into 3 functions that run as tasks.
// Arduino void loop() runs the same functions over and over, these functions don't wait
// but instead check time and if time's not up then run the next function.
// if ( Serial.available() ) is just such a thing, not waiting around for data.
// Every delay() has a built-in timer. I replace it with a timer that only runs when set.
// The timer is first in the function so that the function can return until timeout.
// The timer only runs when the time to wait is set > 0, else the function continues.
// Where a delay was removed, the time is set and the timer runs for the next many loops.
// When the time is done, the time to wait set = 0. What was a delay() sets the timer.
// This demo also addresses loops inside of void loop(), which can hog cycles terribly.
// It also contains a state machine, a code tool of value beyond what the demo does.
// A State Machine is code written in steps of what to do according to what's been done.
// A state variable holds what to do next time the machine runs.
// perhaps the 1st state waits for input and when it gets it the code changes state..
// to run what to do with the input. State Machines can run inside of state machines.
// task StopGo. -- Block - unBlock on a keystroke to stop serial monitor
// task LoopCounter -- lets you know how often void loop() ran last second.
// LoopCounter value
#define microsInOneSecond 1000000UL
// task PrintNumbers -- prints from printNum to setNum, pnWait ms apart
// PrintNumbers variables
unsigned long pnStart;
unsigned long pnWait = 1000UL;
int setNum = 60;
int printNum = 0;
// task BlinkPattern -- blinks led13
// BlinkPattern variables
const byte ledPin = 13;
unsigned long blinkStart;
unsigned long blinkWait;
byte indexMax = 12;
byte index;
enum blinkStates {BLINK_1_ON, BLINK_1_OFF, BLINK_2_ON, BLINK_2_OFF, BLINK_3_ON, BLINK_3_OFF};
blinkStates blinkStep; // state tracking for BlinkPattern() below
void setup()
{
Serial.begin( 115200 );
Serial.println( F( "\n\n\n Combine Sketches Demo, free by GoForSmoke" ));
Serial.println( F( "This sketch shows how to combine sketches.\n" ));
Serial.println( F( "Press Enter to toggle monitor scrolling." ));
pinMode( ledPin, OUTPUT );
digitalWrite( ledPin, HIGH ); // 2 secs before the scroll starts
delay( 500 );
digitalWrite( ledPin, LOW );
delay( 500 );
digitalWrite( ledPin, HIGH );
delay( 500 );
digitalWrite( ledPin, LOW );
delay( 500 );
blinkStep = BLINK_1_ON; // actual value is 0
};
/* LoopCounter -- void loop() code to count loops per second
*
* delay( 1000 );
* Serial.println( "1" );
*/
void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
static unsigned long count, countStartMicros; // only this function sees these
count++; // adds 1 to count after any use in an expression, here it just adds 1.
if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
{
countStartMicros += microsInOneSecond; // for a regular second
Serial.println( count ); // 32-bit binary into decimal text = many micros
count = 0; // don't forget to reset the counter
}
}
/* PrintNumbers -- void loop() code to count from one value to another with wait between.
*
* for ( printNum = 0; printNum <= setNum; printNum++ )
* {
* Serial.print( F( "Number " ));
* Serial.print( printNum );
* Serial.print( F( " Time " ));
* Serial.println( millis() );
* delay( pnWait );
* }
*/
// this task runs once. suppose that StopGo made it start again?
void PrintNumbers() // a loop with a delay in it becomes...
{
if ( setNum < 1 ) return; // how to turn this task off, setNum = 0;
// This repeat timer replaces delay()
// start of repeat timer
if ( millis() - pnStart < pnWait ) // wait is not over
{
return; // instead of blocking, the undelayed function returns
}
Serial.print( F( "Number " ));
Serial.print( printNum );
Serial.print( F( " Time " ));
Serial.println( millis() );
pnStart += pnWait; // starting 1 sec after last start, not the same as = millis()
if ( ++printNum >= setNum )
{
setNum = printNum = 0;
}
}
/* BlinkPattern -- void loop() code to blink led13 using delay()
*
* digitalWrite( ledPin, HIGH ); -- BLINK_1_ON
* Serial.print( F( "BLINK_1_ON, time " ));
* Serial.println( millis());
* delay( 500 );
* digitalWrite( ledPin, LOW ); -- BLINK_1_OFF
* Serial.print( F( "BLINK_1_OFF, time " ));
* Serial.println( millis());
* delay( 500 );
* for ( i = 0; i < 12; i++ )
* (
* digitalWrite( ledPin, HIGH ); -- BLINK_2_ON
* Serial.print( F( "BLINK_2_ON, time " ));
* Serial.println( millis());
* delay( 250 );
* digitalWrite( ledPin, LOW ); -- BLINK_2_OFF
* Serial.print( F( "BLINK_2_OFF, time " ));
* Serial.println( millis());
* delay( 250 );
* }
* digitalWrite( ledPin, HIGH ); -- BLINK_3_ON
* Serial.print( F( "BLINK_3_ON, time " ));
* Serial.println( millis());
* delay( 1000 );
* digitalWrite( ledPin, LOW ); -- BLINK_3_OFF
* Serial.print( F( "BLINK_3_ON, time " ));
* Serial.println( millis());
* delay( 1000 );
*/
void BlinkPattern() // does the same as above without delay()
{
// This one-shot timer replaces every delay() removed in one spot.
// start of one-shot timer
if ( blinkWait > 0 ) // one-shot timer only runs when set
{
if ( millis() - blinkStart < blinkWait )
{
return; // instead of blocking, the undelayed function returns
}
else
{
blinkWait = 0; // time's up! turn off the timer and run the blinkStep case
}
}
// end of one-shot timer
// here each case has a timed wait but cases could change Step on pin or serial events.
switch( blinkStep ) // runs the case numbered in blinkStep
{
case BLINK_1_ON :
digitalWrite( ledPin, HIGH );
Serial.print( F( "BLINK_1_ON, time " ));
Serial.println( blinkStart = millis()); // able to set a var to a value I pass to function
blinkWait = 500; // for the next half second, this function will return on entry.
blinkStep = BLINK_1_OFF; // when the switch-case runs again it will be case 1 that runs
break; // exit switch-case
case BLINK_1_OFF :
digitalWrite( ledPin, LOW );
Serial.print( F( "BLINK_1_OFF, time " ));
Serial.println( blinkStart = millis());
blinkWait = 500;
blinkStep = BLINK_2_ON;
break;
case BLINK_2_ON :
digitalWrite( ledPin, HIGH );
Serial.print( F( "BLINK_2_ON, time " ));
Serial.println( blinkStart = millis());
blinkWait = 250;
blinkStep = BLINK_2_OFF;
break;
case BLINK_2_OFF :
digitalWrite( ledPin, LOW );
Serial.print( F( "BLINK_2_OFF, time " ));
Serial.println( blinkStart = millis());
blinkWait = 250;
// ****** this replaces the for-loop in non-blocking code. ******
if ( index++ < indexMax ) // index gets incremented after the compare
{
blinkStep = BLINK_2_ON;
}
else
{
index = 0;
blinkStep = BLINK_3_ON;
} // end of how to for-loop in a state machine without blocking execution.
break;
case BLINK_3_ON :
digitalWrite( ledPin, HIGH );
Serial.print( F( "BLINK_3_ON, time " ));
Serial.println( blinkStart = millis());
blinkWait = 1000;
blinkStep = BLINK_3_OFF;
break;
case BLINK_3_OFF :
digitalWrite( ledPin, LOW );
Serial.print( F( "BLINK_3_OFF, time " ));
Serial.println( blinkStart = millis());
blinkWait = 1000;
blinkStep = BLINK_1_ON; // start again
break;
}
}
void StopGo() // user keyboard block / unblock
{
if ( Serial.available() )
{
while ( Serial.available() ) // clearing the buffer
{
Serial.read();
delay(1); // don't care
}
}
else
{
return;
}
while ( ! Serial.available() ); // waiting for Go, ! is logical NOT
while ( Serial.available() ) // clearing the buffer
{
Serial.read();
delay(1); // don't care
}
}
void loop() // runs over and over, see how often
{
LoopCounter(); // the function runs as a task, the optimizer will inline the code.
PrintNumbers();
BlinkPattern();
StopGo();
}
[/code]