Last Updated: 08/03/2023

Arduino State Machine (Millis() controlled) code

Components/How To >> Arduino State Machine (Millis() controlled) code

State Machine (Millis() controlled) code

One of the constant issues I see is people running up against problems when their sensors are not working or btton presses are not being read because their other code is written in a blocking style.
In this video I will try and explain how to write code efficiently and in a way that allows inputs and outputs to function at the same time. It is often called State Machine of millis() controlled code.

Circuit Diagram

In the video I have simply attached a number of LEDs and buttons to the pins as follows

Pins
13 built in led
12 button for built in LED
11 button LEDs
10 Orange LED
9 Green LED

6 Red LED1
5 RED LED2

The LEDs have a 330ohm resitor and the buttons have 10K pull down resistors.

Example 1: Blocking Blink


Click to Download code: BlockedBlinkV1.ino

This sketch explains which part of the sketch is using up the processor time and the impact that has

 

 
/* BlockedBlinkV1
 *  
 *  Simple demonstration of blocking code.
 *  
 */

const int red2LED = 6;
const int redledButtonPin = 11;
byte flashState;

void setup() {
  Serial.begin(9600);
  Serial.println("BlockedBlinkV1");
  pinMode(redledButtonPin, INPUT);
  pinMode(red2LED, OUTPUT);
}

void loop() {
  if(flashState < 1){
    digitalWrite(red2LED,HIGH);
    delay(5000);//Wait 5 seconds
    digitalWrite(red2LED,LOW);
    delay(5000);//wait 5 seconds
  }
  //read the button status and turn flashing LED's on or off
   if(digitalRead(redledButtonPin)> 0){//button pressed-HIGH
   if(flashState > 0){
    flashState = 0; 
   }else{
    flashState = 1; 
   }
  }

}

Example 2: State Machine Example


Click to Download code: stateMachineV1.ino

This sketch demonstrates the basic principles of State Machine code and how all inputs and outputs are being updated at the same time.

 

 
/* stateMachineV1
   
   07/03/2023

   An example of how to write non blocking code, sometimes called a state machine or millis() timed code.

   The buttons could be replaced by sensors or some other type of trigger.
   The LED's could be servos or some other output item.

   At the bottom of the functino redLights() is a comment about using Serial.println();
   Uncomment this line to see the impact on timing comments can have on your code.

   
   Control 2 LED's fading in and out opposite to each other, stopped and started by a button.
   Control built in LED blinking fast controlled by button
   LED constantly blinking in the background.
   LED doing random fire flicker code.

   Pins
   13 built in led
   12 button for built in LED
   11 button LEDs
   10 Orange LED
   9 Green LED

   6 Red LED1
   5 RED LED2
*/



//Pins
const int builtinButtonPin = 12;
const int redledButtonPin = 11;
const int orangeLED = 10;//fire flicker
const int greenLED = 9;//will blink on and off at 1 sec intervals
const int servoSignal = 8;//controls servo
const int red1LED = 5;
const int red2LED = 6;
const int boardLED = 13;//the built in LED

//Timing
unsigned long currentMillis;//Stores the current board time in millis()

//GreenLED
unsigned long greenTimer;
byte greenState;

//Orange fireflicker LED
unsigned long fireTimer;
int fireDelay; //stores the current delay timing as this value changes

//Builtin LED
unsigned long boardLEDTimer;
byte boardLEDState;

//Red flashing LEDs
byte currentFadeLED;//which LED is being worked on
byte fadeValue; //holds the current fade value for the red leds
byte fadeDirection; //hold if the value of fadeValue is increasing or decreasing
unsigned long fadeTimer;

//Buttons
unsigned long builtinButtonTimer;//for debounce
byte builtinButtonState; //is the button turing the item on or off?
unsigned long redledButtonTimer;//for debounce
byte redledButtonButtonState; //is the button turing the item on or off?

//Start with the equivelent of the blink sketch
void greenLight() {
  int greenDelay = 1000; // 1 second
  if (currentMillis - greenTimer >= greenDelay) { //this runs every 10 milliseconds
    greenTimer = currentMillis;
    if (greenState > 0) {
      greenState = 0;
    } else {
      greenState = 1;
    }
    digitalWrite(greenLED, greenState);
  }
}

//The orange LED is going to simulate a fire so randomly changing brightness at a random timing
void fireFlicker() {
  if (currentMillis - fireTimer >= fireDelay) {
    fireTimer = currentMillis;
    fireDelay = random(30, 200); //random timing;
    analogWrite(orangeLED, random(0, 255)); //pick a random brightness
  }
}
/*
   unsigned long builtinButtonTimer;//for debounce
  byte builtinButtonState; //is the button turing the item on or off?
  unsigned long redledButtonTimer;//for debounce
  byte redledButtonButtonState; //is the button turing the item on or off?
*/


//checks for button presses.
void buttonPress() {
  int debounceDelay = 200; //0.2 secs.. too long and buttons can get laggy,too short and you get double presses
  byte buttonState;
  //first process the built in LED control button
  if (currentMillis - builtinButtonTimer >= debounceDelay) {
    buttonState = digitalRead(builtinButtonPin);
    if (buttonState > 0) { //button pressed
      builtinButtonTimer = currentMillis;
      if (builtinButtonState > 0) { //This changes the current state and stops the LED flashing if value is 1
        builtinButtonState = 0;
      } else {
        builtinButtonState = 1;
      }
    }
  }

  //Now the red flashing LED button
  if (currentMillis - redledButtonTimer >= debounceDelay) {
    buttonState = digitalRead(redledButtonPin);
    if (buttonState > 0) { //button pressed
      redledButtonTimer = currentMillis;
      if (redledButtonButtonState > 0) { //This changes the current state and stops the LED flashing if value is 1
        redledButtonButtonState = 0;
      } else {
        redledButtonButtonState = 1;
      }
    }
  }

}

void boardLEDFlash() {
  int boardDelay = 100; // 0.11 second
  if (builtinButtonState < 1) {//Only process if system is turned on by the button..this will leave the LED in it's current state on or off
    if (currentMillis - boardLEDState >= boardDelay) {
      boardLEDState = currentMillis;
      if (boardLEDState > 0) {
        boardLEDState = 0;
      } else {
        boardLEDState = 1;
      }
      digitalWrite(boardLED, boardLEDState);
    }
  }
}





void redLights() {
  //Arduino is 16mhz so 1 millisecond is one change every 16,000 clock cycles
  // if this function has to change a state it will run in approx 6 microseconds
  int fadeDelay = 1;//Causes a change in light state every millisecond
  if (redledButtonButtonState > 0) {//Lights to stop flashing
    //make sure both lighst are turned off
    fadeValue = 0;
    analogWrite(red1LED, fadeValue);
    analogWrite(red2LED, fadeValue);  
  }else{//Otherwise leave everything flashing
    if (currentMillis - fadeTimer >= fadeDelay) { //this runs every 10 milliseconds
      fadeTimer = currentMillis;
      //Serial.println(fadeTimer);
      if (fadeDirection < 1) {
        fadeValue++;
        if (fadeValue == 255) { //fade has reached max value so change direction
          fadeDirection = 1;
        }
      } else {
        fadeValue--;
        if (fadeValue == 0) { //fade has reached minimum value so change direction
          fadeDirection = 0;
          if (currentFadeLED > 0) {
            currentFadeLED = 0;
          } else {
            currentFadeLED = 1;
          }
        }
      }
      /* blocking code for single led
         This simple bit of code blocks the Arduino for 1.53 seconds....
         It then needs to be endlessly repeated.
         
         for(w = 0; w< 255;w++){
            analogWrite(red1LED, w);
            delay(3);
         }
         for(w = 255; w> 0;w--){
            analogWrite(red1LED, w);
            delay(3);
         }
         
         
       */

      /* The line below this comment

          Serial.println(fadeValue);

         has been commented out, it is there to show the problems
         with certain functions when high speed timing is critical

         If you uncomment the line you will see that the LED's flash at
         about 1/3 the normal speed.
         This is because normally the led is being adjusted every 1/1000 of second
         A simple Serial.println statement takes about 1/1000 sec at 9600 baud for every character sent.
         So sending a simple number takes about 3/1000 second, 3 times longer than our timer.

         Be very careful with Serial.print(), great for deugging but comment them out when you have finished
         with them to speed up your code.

         This is critical with certain items, for example LCD touch screen will get very laggy with endless
         Serial.print statements in the background.

      */
      //Line below to demonstrate how Serial.println() slows down the system.
      //Serial.println(fadeValue);

      if (currentFadeLED > 0) {
        analogWrite(red1LED, fadeValue);
      } else {
        analogWrite(red2LED, fadeValue);
      }
    }
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("stateMachineV1");

  pinMode(builtinButtonPin, INPUT);
  pinMode(redledButtonPin, INPUT);
  pinMode(orangeLED, OUTPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(red1LED, OUTPUT);
  pinMode(red2LED, OUTPUT);
  pinMode(boardLED, OUTPUT);



}

void loop() {
  //Global so will be used in all function
  //If nothing is triggered in any functions the whole loop will be repeated in 1 microsecond (1 millionth sec)
  currentMillis = millis();//called once at the beginning of every loop to control all timings
  
  //IN a situation when timing is critical, the most time critical item should be first in the list.
  //with model railways most of the time plus or minus a millisecond, let alone microseconds will not cause problems.
  greenLight();
  fireFlicker();
  boardLEDFlash();
  redLights();
  buttonPress();
//Other inputs/outputs can be added in here.


}

Additional Resource Links

Arduino Tutorial: Avoiding the Overflow Issue When Using millis() and micros()

Comments


This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site.
To add a comment or ask a question please email the address in this image: and use Arduino State Machine (Millis() controlled) code as a reference.