LarryD StateMachine
Jump to navigation
Jump to search
Test of image:
//********************************************************************** // 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