LarryD StateMachine

From ArduinoInfo
Jump to navigation Jump to search
Test of image:

Breadboard.jpg

//**********************************************************************
// StateMachineSimple.ino
//
// Simple 'State Machine' skeleton 
// (Demonstrating one way to write a StateMachine)
//
// LarryD
//
// Version   YY/MM/DD   Comments
// 1.00      16/01/15   Running code
// 1.01      16/01/30   Added Blocking LED check
// 1.02      17/12/26   Added 'restart' and 'timer enabled' flags
// 1.03      17/12/27   Added switch handling
// 1.04      18/01/09   Added the WhileTimer() function
// 1.05      18/01/10   Example of what is needed for a new State and Timer i.e. State6              1.05
//
//
// For this example you need: 
// Six LEDs, each with a series resistor ~220R, wired so O/P pin HIGH turns on the LED.
// OutputPin -----LedAnode-->|--LedCathode---[220R]----GND
//
// One switch, wired so a push gives a LOW on the pin.
// (+5V--INPUT_PULLUP--)InputPin----SwitchPin--[Switch]--SwitchPin----GND
//
// LED      Function
// 13       Flashing indicates no code blocking is happening
// 12       toggles every 75ms
// 11       toggles every 150ms
// 10       toggles every 300ms
// 09       toggles every 600ms
// 08       turns on for 5 seconds when the switch is pressed
//
// Switch
// 02       wired so a push gives a LOW on the pin
//
//
//**********************************************************************
//Timers in this sketch: 
//Note, lines in this sketch with 1.05 at the end are needed to add a new Timer ans Machine State
//
//With reference to functions CheckTimer() and WhileTimer():
//(You could use a CLASS or STRUCT instead of the following discussion.)
//
//                                      StartTime   Interval     Restart?        Enabled?
//Each timer is made up of 4 variables: VarMillis, VarInterval, VarRestart and VarEnableFlag.
//Where Var is the name of your timer.
//VarInterval can be replaced by a constant, ex: 2*60*1000UL
//When referring to a timer, VarRestart and VarEnableFlag can be defaulted to 'ture or false'.
//
//some example calls to the CheckTimer() function.
//            StartTime   Interval     Restart?     Enabled?
// CheckTimer(VarMillis, VarInterval, VarRestart, VarEnableFlag);
// CheckTimer(VarMillis, VarInterval, true, false);
// CheckTimer(VarMillis, VarInterval, true, true);
// CheckTimer(VarMillis, 60*60*1000UL, false, VarEnableFlag);  // One hour
//
//**********************************************************************
//
// In this sketch,each timer has an entry in the enumeration 'enum States',
// however, not all 'machine states' need a timer.
//
//**********************************************************************
//
// Use <CTRL><F> to change the names as needed.
// example: change all 'State1' to 'StateRunning'
//
//**********************************************************************
//Sketch timers
//
//Start time, used to calculate when this Timer has expired
unsigned long State1Millis;
unsigned long State2Millis;
unsigned long State3Millis;
unsigned long State4Millis;
unsigned long State5Millis;
unsigned long State6Millis; //                                                                       1.05
//add more as needed

//Interval/delay when/where this Timer expires
unsigned long State1Interval = 75UL;   //75ms
unsigned long State2Interval = 150UL;  //150ms
unsigned long State3Interval = 300UL;  //300ms
unsigned long State4Interval = 600UL;  //600ms
unsigned long State5Interval = 5000UL; //5 seconds
unsigned long State6Interval = 50UL;   //50ms                                                        1.05  
//add more as needed

//Repeat this Timer timing, true = restart
boolean State1Restart = true;
boolean State2Restart = true;
boolean State3Restart = true;
boolean State4Restart = true;
boolean State5Restart = false;     //Do not automatically restart this Timer
boolean State6Restart = true;      //                                                                1.05 
//add more as needed

//Is this Timer enabled/disabled, true = enabled
boolean State1EnableFlag = true;
boolean State2EnableFlag = true;
boolean State3EnableFlag = true;
boolean State4EnableFlag = true;
boolean State5EnableFlag = false;  //This Timer will not be enabled at power up
boolean State6EnableFlag = true;   //                                                                1.05
//add more as needed

//define the available states in this sketch
enum States
{
  //add more states as needed
  StateStart, State1, State2, State3, State4, State5, State6 //                                      1.05
};

States mState = StateStart; //we will start out in this machine state (mState)

//**********************************************************************
//Debug stuff
unsigned long testMicros;                   //Used for debugging, remove in final version
//Serial.println(micros() - testMicros);    //Print time to come back to this same point
//testMicros = micros();


unsigned long currentMillis;
unsigned long BlockingMillis;
unsigned long SwitchMillis;

const unsigned long debounceDelay = 50UL;   //50ms
const unsigned long LEDonTime     = 5000UL; //5 seconds

//LEDs used to give visual feedback
const byte BlockingLED  = 13;
const byte FirstLED     = 12;
const byte SecondLED    = 11;
const byte ThirdLED     = 10;
const byte FourthLED    = 9;
const byte testLED      = 8;

const byte Outputs[]    = {8, 9, 10, 11, 12, 13};

const byte ToggleSwitch = 2;               //Pushing the switch puts a LOW on the pin
byte lastToggleSwitchState;

//**********************************************************************


//                            s e t u p ( )
//======================================================================
void setup()
{
  Serial.begin(9600);

  //initialize 'output' pins
  for (byte x = 0; x < sizeof(Outputs); x++)
  {
    digitalWrite(Outputs[x], HIGH);
    pinMode(Outputs[x], OUTPUT);
  }

  //initialize 'input' pins
  pinMode(ToggleSwitch, INPUT_PULLUP);
  lastToggleSwitchState = digitalRead(ToggleSwitch);

} //END of:                   s e t u p ( )


//                             l o o p ( )
//======================================================================
void loop()
{
  // keep this line of code at this location
  currentMillis = millis();

  //******************************************
  //Some code to check for blocking code, LED should flash at 1 Hertz rate.
  if (CheckTimer(BlockingMillis, 500UL, true, true))
  {
    //Toggle the Blocking LED
    digitalWrite(BlockingLED, !digitalRead(BlockingLED));
  }


  //******************************************
  //Put other non-blocking stuff here
  //******************************************


  //******************************************
  //Is it time to check the switches?
  if (CheckTimer(SwitchMillis, debounceDelay, true, true))
  {
    //it is time to read the switches
    CheckSwitches();
  }

  //******************************************
  //Service the 'State Machine'
  StateMachine();

} //END of:                     l o o p ( )


//======================================================================
//                           F U N C T I O N S
//======================================================================


//                        C h e c k T i m e r ( )
//======================================================================
// Used when actions are needed 'after' a period of time.
boolean CheckTimer(unsigned long &lastMillis, const unsigned long Interval, boolean restart, boolean enableFlag)
{
  //lastMillis = the time this "timer" was (re)started
  //Interval   = interval/delay (mS) we are looking for. You can use math, ex. 60*60*1000 for 1 hour
  //restart    = do we restart "this timer" again and again
  //enableFlag = is "this timer" enabled/allowed to be accessed

  //Is this timer enabled and has it expired?
  if (enableFlag == true && currentMillis - lastMillis >= Interval)
  {
    //should this timer start again?
    if (restart == true)
    {
      lastMillis = currentMillis;  //get ready for the next iteration
      //or use
      //lastMillis += Interval;    //get ready for the next iteration
    }

    //This Timer did expired
    return true;
  }

  return false;

} //END of:               C h e c k T i m e r ( )

//                        W h i l e T i m e r ( )
//======================================================================
// Used when actions are needed 'during' a period of time.
boolean WhileTimer(unsigned long &lastMillis, const unsigned long Interval, boolean restart, boolean enableFlag)
{
  // lastMillis = the time this "timer" was (re)started
  // Interval   = interval/delay (mS) we are looking for. You can use math, ex. 60*60*1000 for 1 hour
  // restart    = do we restart "this timer" again and again
  // enableFlag = is "this timer" enabled/allowed to be accessed

  //Is this timer enabled and are we still timing?
  if (enableFlag == true && currentMillis - lastMillis < Interval)
  {
    //This Timer is still timing
    return true;
  }

  else
    //should this timer start again?
    if (restart == true)
    {
      lastMillis = currentMillis;  //get ready for the next iteration
      //or use
      //lastMillis += Interval;    //get ready for the next iteration
    }

  return false;

} //END of:              W h i l e T i m e r ( )

//                    C h e c k S w i t c h e s ( )
//======================================================================
// No minimum switch press time is validated with this code (i.e. No glitch filter).
// For de-bounce, call this function every ~50ms.
// You add code to this function to handle all configured switches
void CheckSwitches()
{
  boolean switchPosition;

  //******************************************
  //check if this switch has changed state
  switchPosition = digitalRead(ToggleSwitch);

  if (switchPosition != lastToggleSwitchState)
  {
    //update to the new switch state
    lastToggleSwitchState = switchPosition;

    //This switch position has changed, let's do some stuff

    //"HIGH condition code"
    //switch goes from LOW to HIGH
    if (switchPosition == HIGH)
    {
      //Do some HIGH stuff here
    }

    //"LOW condition code"
    //switch goes from HIGH to LOW
    else
    {
      if (State5EnableFlag == false)   //If we 'are not' timing, proceed.
      {
        State5Millis = currentMillis;  //Initialize the timer
        State5EnableFlag = true;       //enable State5 timing
        //On switch closure, the LED on D08 will go on for 5 Seconds the go off. 
      }

    } //END of:   else

  } //END of:    ToggleSwitch code

  //******************************************
  // Similar code for other switches goes here
  //******************************************

} //END of:          C h e c k S w i t c h e s ( )

//                    S t a t e M a c h i n e ( )
//======================================================================
void StateMachine()
{
  switch (mState)
  {
    //******************************************
    case StateStart:
      //code for this state goes here

      //example below, how to change the machine state
      mState = State1; //now switching to State1

      break;

    //******************************************
    case State1:
      //is it time to run this section of code?
      //              Start Time   Interval/Delay    Restart?         Enabled?
      if (CheckTimer(State1Millis, State1Interval, State1Restart, State1EnableFlag))
      {
        Task1();
        //other 'timer related' state code goes here
      }

      //non-timer related State1 code goes here

      mState = State2;

      break;

    //******************************************
    case State2:
      //              Start Time   Interval/Delay    Restart?         Enabled?
      if (CheckTimer(State2Millis, State2Interval, State2Restart, State2EnableFlag))
      {
        Task2();
      }

      mState = State3;

      break;

    //******************************************
    case State3:
      //              Start Time   Interval/Delay    Restart?         Enabled?
      if (CheckTimer(State3Millis, State3Interval, State3Restart, State3EnableFlag))
      {
        Task3();
      }

      mState = State4;

      break;

    //******************************************
    case State4:
      //              Start Time   Interval/Delay    Restart?         Enabled?
      if (CheckTimer(State4Millis, State4Interval, State4Restart, State4EnableFlag))
      {
        Task4();
      }

      mState = State5;

      break;

    //******************************************
    case State5:
      //              Start Time   Interval/Delay    Restart?         Enabled?
      if (WhileTimer(State5Millis, State5Interval, State5Restart, State5EnableFlag))
      {
        //Code for 'while' the timer is running goes here.

        //We will do the work here rather than going to a Task5() function
        digitalWrite(testLED, LOW);    //turn "ON' the LED
      }

      else
      {
        //was the timing running?
        if (State5EnableFlag == true)
        {
          //Timing is now over, do finishing work.
          digitalWrite(testLED, HIGH); //turn 'OFF' the LED
          State5EnableFlag = false;    //disable timing for this timer
        }
      }

      //non-timer State5 code goes here.

      mState = State6; //                                                                            1.05

      break;

    //******************************************                                                     1.05 
    case State6:
      //              Start Time   Interval/Delay    Restart?         Enabled?
      if (CheckTimer(State6Millis, State6Interval, State6Restart, State6EnableFlag))
      {
        Task6();
      }

      //Back to the beginning                                     <----<<<<   NOTE
      mState = State1;

      break;

    //******************************************
    default:
      // default code goes here
      break;

  } // end of switch/case

} //END of:            S t a t e M a c h i n e ( )

//                           T a s k 1 ( )
//======================================================================
void Task1()
{
  //example code
  //we will toggle a LED, but you could just as easily scan a sensor
  digitalWrite(FirstLED, !digitalRead(FirstLED));
  //Other stuff

} //END of:                  T a s k 1 ( )

//                           T a s k 2 ( )
//======================================================================
void Task2()
{
  //example code
  digitalWrite(SecondLED, !digitalRead(SecondLED));
  //Other stuff

} //END of:                  T a s k 2 ( )

//                           T a s k 3 ( )
//======================================================================
void Task3()
{
  //example code
  digitalWrite(ThirdLED, !digitalRead(ThirdLED));
  //Other stuff

} //END of:                  T a s k 3 ( )

//                           T a s k 4 ( )
//======================================================================
void Task4()
{
  //example code
  digitalWrite(FourthLED, !digitalRead(FourthLED));
  //Other stuff

} //END of:                  T a s k 4 ( )

//                           T a s k 5 ( )
//======================================================================
void Task5()    //This task is not used in this sketch
{

} //END of:                  T a s k 5 ( )

//                           T a s k 6 ( )                                                           1.05
//======================================================================
void Task6()
{
  //Task6 stuff

} //END of:                  T a s k 6 ( )

//======================================================================



//======================================================================
//                           END OF CODE
//======================================================================







zz