Combine Sketches Method Demo

I have 2 sketches for this.

CombineSketchesBlocked
Turns 3 sketches that use delay into functions that run "in parallel" in void loop.

CombineSketchesDemo
The same sketches with delays replaced by timers and void loop drives iterations.
Code with lots of comments is in the next post.

**********************************************

First, 3 sketches with delays combined do not run as if parallel.

// CombineSketchesBlocked 2022 by GoForSmoke @ Arduino.cc Forum
// Free to post anywhere complete and as is.

// This is the delay-codes combined to show how bad it is.

// LoopCounter value
#define microsInOneSecond 1000000UL

// PrintNumbers variables
unsigned long pnWait = 1000UL; 
int setNum = 60;
int printNum = 0;

// BlinkPattern variables
const byte ledPin = 13;
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 Blocked, free by GoForSmoke" ));
  Serial.println( F( "This sketch shows how NOT 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
void LoopCounter() // tells the average response speed of void loop()
{ 
  delay( 1000 );
  Serial.println( "1" );
}


// PrintNumbers -- void loop() code to count from one value to another with wait between.
void PrintNumbers()  // a loop with a delay in it becomes...
{
  for ( printNum = 0; printNum <= setNum; printNum++ )
  {
    Serial.print( F( "Number " ));
    Serial.print( printNum );
    Serial.print( F( "   Time " ));
    Serial.println( millis() );
    delay( pnWait );
  }

  printNum = 0;
}


// BlinkPattern -- void loop() code to blink led13 using delay()
void BlinkPattern()  // does the same as above without 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 ( byte 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 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();
}
1 Like

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();
}
1 Like

And I'm looking for sketches with delay(s) or other blocking code that can't easily be converted.

It is very good to show programming principles. This is what every example-code does.

Though if it is easy to understand depends on

  • giving an overview about the functionality
  • very carefully chosen self-explaining variablenames
  • extending comments (much more extended than "professionals" would write them)

otherwise for a newcomer a lot of questions arise and the code remains not understood

The best experts for the quality of a text that shall explain in an esay to understand way are newcomers.
If this newcomer is very selfconfident she/he will start to ask questions.

But such newcomers are rare.

I will start to comment on your demo-code with the function BlinkPattern():

  // 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

You call this part of the code "oneshot-timer"
For a real newcomer reading these two words "oneshot" and "timer" creates a picture of

  • something that executes only once.
  • after some time or for some time

And this is completely different than what this part of the code does:
This part is called and executed over and over again.

and with the variable-names you have chosen the reader must have some more knowledge about programming-techniques and implicit naming-habits

double-use of a variable zero / non-zero value itself

  • variable-value 0 is used as "I'm deactivated values greater than zero are used as
    I am active
    The value itself is used as yeah what? what is the meaning of "BlinkWait"

A real newcomer is asking:

  • does it blink?
  • does it wait?
    these both words are put together.
  • Does this mean it blinks then wait?
  • does it wait for blinking (but then it would be the same as a delay() - - - ???

using such a name requires the implicit knowledge
"it is a typical name for a non-blocking timing. The first part describes the action that shall be timed "blink" the second part indicates the amount of milliseconds that shall be waited in code written in the typical BWD-style

A real newcomer does not have this knowledge !
This means this information must be added right here at this place

(professionals will whine "oh man! This makes my code look so bloated!" )
My suggestion:

  • leave away all comments
  • but instead add an abbreviation at the top of your code "HTUC-NSI"
    I'll leave you guessing what that abbreviation means

So here is the explanation I would write

void BlinkPattern(){  // does the same as above without delay()
  // This first part of the code does two things:
  // 1. check if timing is activated 
  // 2. if activated then check am I still too EARLY to take action?
  // if still too early RETURN immidiately from this function "Blinkpattern()" 

  // timing IS activated in case variable "blinkWaitMillisecs" has a value > 0
  
  // timing is DE-activated if variable "blinkWaitMillisecs" HAS value zero

  // very IMPORTANT!
  // in case of timing is DE-activated go on executing the code below
  if ( blinkWaitMillisecs > 0 )   {
    // if timing is activated
    // calculate how much time has passed by since timing started: "millis() - blinkStartTime"
    // compare this difference to amount of milliseconds that shall be waited ".." < blinkWaitMillisecs
    if ( millis() - blinkStartTime < blinkWaitMillisecs ) { 
       // if still too early for taking action
      return; // instead of blocking, return from function BlinkPattern() 
    }
    else { 
      // the amount of milliseconds of variable "blinkWaitMillisecs" HAS passed by
      blinkWait = 0; // DE-activate the timer through assigning value 0
      // very IMPORTANT!! Here is NO return ! This means execution of code GOES ON
    }
  }

meaning of "HTUC-NSI" Hard to understand code newcomer skip it

This pro went by "the code is the comments" after working on OPC.

I tried googling the abbreviation "OPC"
As I'm german google came up with
Oligomere Proantho-Cyanidine
I'm sure in this case OPC has a very different meaning. Though I don't know it.
...
googling new
OPC Open Platform Communications

Other People's Code

I think that comments explaining variable names is in order and
a lot more technical detail too.

I put the open brace
{
at the same level as the closing brace
}
because it makes matching braces easier and faster by far. It's an eye thing.

I also prefer that. But it seems to be a personal preference for most and the default Arduino IDE auto-format keeps that "as is" (version 1.8.x, version 2 is a totally different story).

1 Like

What is the actual status of version 2? has it left early embryonic alpha status?

Release candidate (I think rc6 at this stage).

That's what they call it. But what is the level of usability?

I did a short test loading the blink sketch saved to a different filename added a variable and an if-condition badly indented then hit Ctrl-T
it took clang more than a minute to "indexing" "building sketch"

The German school system has grades from 1 to 6
1 very good
6 insufficient

with such a behaviour needing more than a minute to execute autoformat this is
insufficient for "releasing". Early embryonatic alpha

i got it through RPi package manager.

2.1.0.5+dfsg2-4.1

Well that depends on how they specified the requirement. If the spec says it has to do an autoformat in 5 minutes, then if it takes 1-2m it would score as "very good" :wink:

(Like when public transport companies improve their figures by relaxing the definition of "arrived on time" ....)

And when I'm counting gear teeth go by on a 60 tooth gear turning at 5000 RPM...
that's 5 teeth per ms, Arduino Real Time.

You can post about it in IDE 2.x - Arduino Forum; this however has been discussed before in there.

But I think we're derailing the topic.

1 Like

It seems as if I need to stretch the demonstration across more than two sketches.

Each blocked sketch to be combined needs its own workup before combining.

Naming conventions have to be explained, references addressed.
Maybe name original delayed sketches to combine by how they block.
And try to get an example for every kind, a method to unblock that kind in general.

You can Quick&Dirty get one thing to work then convert that to run with others.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.