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.