Go Down

Topic: New State machine tutorial (Read 41076 times) previous topic - next topic

GoForSmoke

Someone new to the state machine concept are not necessarily new to programming. This tutorial is not about C++. It is about implanting a state machine in Arduino's environment which happens to be written in C++.

I my mind, using a "switch" construct is not easier than using enum, since I have met on this site, a few persons that only uses "if" constructs, "since you can to everything with that construct" and won't have anything to do with "switch". But I insist in using it because the "switch" construct is , IMHO, ideal to describe a state machine's behaviour.

A switch-case state machine is a good way to break a large or long top-down process into small pieces and wait intervals. A function with a state machine can achieve a whole process by running over and over. Your void loop() may run > 60 times a millisecond, over and over can get somewhere fast at that rate.

I have a demo showing how to use switch-case to remove calls to delay(). It hit me while I was de-blocking a greenhouse automation with a lot of wait-for's that one come-back-later code before the switch could replace any number of cases with timing code (the original GSM code had 10 delays).

Code: [Select]

// add-a-sketch_un-delay 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/18 by GFS. Compiled on Arduino IDE 1.6.9.
// This sketch shows a general method to get rid of delays in code.
// You could upgrade code with delays to work with add-a-sketch.

#include <avr/io.h>
#include "Arduino.h"

const byte ledPin = 13;
unsigned long delayStart, delayWait;

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Un-Delay Example, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch shows how to get rid of delays in code.\n" ));

  pinMode( ledPin, OUTPUT );
};


/* The section of the original sketch with delays:
 *
 * digitalWrite( ledPin, HIGH );   --  0
 * delay( 500 );
 * digitalWrite( ledPin, LOW );    --  1
 * delay( 250 );
 * digitalWrite( ledPin, HIGH );   --  2
 * delay( 250 );
 * digitalWrite( ledPin, LOW );    --  3
 * delay( 250 );
 * digitalWrite( ledPin, HIGH );   --  4
 * delay( 1000 );
 * digitalWrite( ledPin, LOW );    --  5
 * delay( 1000 );
 */

byte blinkStep; // state tracking for BlinkPattern() below

void BlinkPattern()
{
  // This one-shot timer replaces every delay() removed in one spot. 
  // start of one-shot timer
  if ( delayWait > 0 ) // one-shot timer only runs when set
  {
    if ( millis() - delayStart < delayWait )
    {
      return; // instead of blocking, the undelayed function returns
    }
    else
    {
      delayWait = 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 0 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 0 doing something unspecified here at " ));
    Serial.println( delayStart = millis()); // able to set a var to a value I pass to function
    delayWait = 500; // for the next half second, this function will return on entry.
    blinkStep = 1;   // when the switch-case runs again it will be case 1 that runs
    break; // exit switch-case

    case 1 :
    digitalWrite( ledPin, LOW );
    Serial.println( F( "Case 1 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 2;
    break;

    case 2 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 2 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 3;
    break;

    case 3 :
    digitalWrite( ledPin, LOW );
    Serial.println( F( "Case 3 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 4;
    break;

    case 4 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 4 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 1000;
    blinkStep = 5;
    break;

    case 5 :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "Case 5 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 1000;
    blinkStep = 0;
    break;
  }
}


void loop()  // runs over and over, see how often
{           
  BlinkPattern();
}


A full state machine tutorial would show a state machine embedded in a state machine to handle delays indented to the ones you just replaced.
   
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

blomcrestlight

#31
Mar 24, 2020, 11:53 am Last Edit: Mar 24, 2020, 01:46 pm by blomcrestlight
Having belatedly noticed the author's pdf, which includes all the necessary diagrams, I'm deleting this and the subsequent redundant posts with the diagrams I was making.


blomcrestlight

And so having belatedly noticed jbellavance's pdf (containing code, narrative and state diagrams) and having looked at it (admittedly quickly), I'm inclined to suggest it as "the" tutorial. Maybe Robin2, it's not necessary for you to do another one?


Go Up