CombineSketchesDemo
The same sketches with delays replaced by timers and void loop drives iterations.
// 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();
}