State Machine

When you want to make your code appear to do more than one thing at a time the answer is in the way you structure your code. You need a State Machine.

 
 

The delay function is very useful when used correctly, however it stops all processing and nothing can be done until the delay is finished. Sometimes this is exactly the function you want, but in a larger project it seldom is.


OK so you have got the LED blinking but you now want to extend it to have two LEDs on. You have put two pieces of code together and they just don't work one LED comes on and then the other. Or you want to do one thing when a button is pressed and another when it is pressed again and this code thing is getting more complex than you thought. Or you want to do two or more things seemingly at the same time, or you are from a programming background and are surprised that this machine is single threaded, what to do? Well you can go on the forum and they will rightly point you at the blink without delay example, but a few people find that they need more of an explanation as to what is going on.

Addicted to delays

How to make a Scheduler with an Arduino

Well to solve this sort of problem you need two things, a scheduler and a state machine lets see how they work. With a scheduler it is like writing down a list of when you are going to do something and a state machine is like keeping a record of how far through a task you have got.

If you use the function delay(1000) then the processor goes away and does nothing for one second. We say this delay() is a blocking function, that is it blocks further activity in the program. So in order for our code to do something whilst waiting for the delay time to expire, what we need is a scheduler, something that will call a specific task at a specific time. There are several ways to do this but this is perhaps the simplest. To make a scheduler we need to call on the aide of the system clock. This keeps on ticking every millisecond and we can access the current tick count with the millis() function. Note that this returns a long integer variable not the normal integer. What we need to do is to set up a variable called say goTime that contains the time in milliseconds when we need to do the next operation. That is made from simply the time now plus how long from now you want to do the next operation. Then all we do in the main loop is check if it is time to do our operation and if it is do it. Finally before we exit that routine we need to set our variable goTime to reflect when we want to do it again. Well that's it in words in code it looks like this:-

How to make a State Machine with an Arduino

int nextTime = 1000;  // Do this every second or 1000 milliseconds

long int goTime;


void setup(){

goTime = millis();

  }


void loop(){

if(millis() >= goTime) functionGo();

  }


void functionGo(){

//do what we want

goTime = millis() + nextTime; // set when to do it again

  }

Let's read through that code.

  1. 1)We start of with the definition of a variable that determines how often we want to do our task, int nextTime = 1000; in this case it is every second.

  2. 2)Then in the setup we initialise the variable goTime to be the same value as the system clock.

  3. 3)In the main loop all we do is check if it is time to do our function, it will be time when the current time given by millis() is greater or equal to the time we want to fire things off.

  4. 4)Finally when it is time, we call our function functionGo.

There is nothing much written in that function at the moment so it is not very exciting but the last statement sets the goTime variable to be the time we call this function again. This program is a bit simplified in that it doesn't consider what happens when the number of milliseconds since the Arduino was switched on exceeds what will fit in the long variable. This happens once every two months or so. When it does happen, there is a chance that it might fail if you don't check the time every millisecond, but for the sake of simplicity we won't consider that for the moment, just get your head round what is happening. Besides it is a rare program that runs for over two months without powering down.

Now for two LEDs

So why is there nothing in the functionGo()? This is because I need to introduce an other new concept, that of a state variable. Now a state variable is not one controlled by the government it is a variable that holds the current state of you program, in other words it tells you where you are up to in doing your task. Suppose you want to flash an LED, the state variable would be one that tells you the current state of the LED. In that way, when it is time, you know wether to switch the LED on or off. Yes I know there are other ways of doing this but it it is a good way of introducing the concept of a state variable. As it is only an LED we need only to indicate if it is on or off, so let's call it LEDstate and make it a Boolean variable, that's one with only two possible values. Using the on board LED on pin 13 is simple, we can add this to our code, with the new lines highlighted:-

boolean LEDstate = LOW;

int nextTime = 1000;  // Do this every second or 1000 milliseconds

long int goTime;


void setup(){

pinMode(13,OUTPUT);

goTime = millis();

}


void loop(){

if(millis() >= goTime) functionGo();

}


void functionGo(){

if(LEDstate == HIGH) {

digitalWrite(13,LOW);

LEDstate = LOW;

}

else {

digitalWrite(13,HIGH);

LEDstate = HIGH;

}

goTime = millis() + nextTime;

}

Most of the action here is in the functionGo() definition. We start by looking at the state variable to see if the LED is currently on or off. If it is equal to HIGH,  please note the two equals signs in the if statement, if you only have one it will not give you an error but it will not work either. If the state is high you make it low and change the state variable to low as well. On the other hand if it is already low we make it high and set LEDstate to HIGH.

So here we have a blinking LED sketch that looks a lot more complex that the original tutorial but this one can act as a skeleton to build other functions into it. As an exercise you can make this sketch have the LED on for twice the time it is off, this is done by simply putting the line that updates the goTime variable into the if two statement cases, so that goTime is set at one value when the LED is put HIGH and another when it is put LOW. Don't forget to delete the original line otherwise that will over ride any changes made earlier to this variable.

Doing something more

Now we are going to extend this to control two LEDs both flashing at a different rate. What you can do is have two functions, one that flashes one LED and another routine that flashes the other LED. You need to wire up another LED and resistor to pin 2 and have another state variable and time to go variable for the second LED.

Suppose you wanted to have an LED flashing at one speed, when you press a button it flashes faster, and press the button again and it flashes even faster. Then when the button is pressed for the third time it flashes at the original speed.


The difficulty here is that while you can easily tell what the current state of a button is by reading it, here you have to "remember" what state the flashing was set at and only when it changes from one state to another do you need to do something. To do this you need a state variable that holds the key to the flashing rate. In this example we need three values, let's use 0, 1 and 2. This variable needs to be incremented each time a button is pressed, what is more when we exceed the maximum value, it has to "wrap round". This means that one plus two equals zero. The simplest way of doing this is to write:-


variable++;

if(variable > 2) variable = 0;


We can change the flashing rate in our program if we just change the variable 'nextTime' so we need to add a function to look at the button and increment the state variable if the button changes from a pressed to unpressed state. That means we have to remember the state of the button from one loop to the next. Suppose you have a push button connected between ground and Pin 4 then the following code would work:-

boolean LEDstate1, LEDstate2 = LOW;

int nextTime1 = 1000;  // Do this every second or 1000 milliseconds

int nextTime2 = 600;  // Do this every 600 milliseconds

long int goTime1, goTime2;


void setup(){

pinMode(13,OUTPUT);

pinMode(2,OUTPUT);

goTime1 = millis();

goTime2 = millis();

}


void loop(){

if(millis() >= goTime1) functionGo1();

if(millis() >= goTime2) functionGo2();

}


void functionGo1(){

if(LEDstate1 == HIGH) {

digitalWrite(13,LOW);

LEDstate1 = LOW;

}

else {

digitalWrite(13,HIGH);

LEDstate1 = HIGH;

}

goTime1 = millis() + nextTime1;

}


void functionGo2(){

if(LEDstate2 == HIGH) {

digitalWrite(2,LOW);

LEDstate2 = LOW;

}

else {

digitalWrite(2,HIGH);

LEDstate2 = HIGH;

}

goTime2 = millis() + nextTime2;

}

In summery

boolean LEDstate = LOW;

byte button = 4;

int nextTime = 1000;  // Do this every second or 1000 milliseconds

int pushState = HIGH, flashState = 0;

long int goTime;


void setup(){

pinMode(button,INPUT);

digitalWrite(button, HIGH);  // enable the internal pull up resistors

pinMode(13,OUTPUT);

pushState = digitalRead(button);

goTime = millis();

}


void loop(){

if(millis() >= goTime) functionGo();

checkPush();

}


void functionGo(){

if(LEDstate == HIGH) {

digitalWrite(13,LOW);

LEDstate = LOW;

}

else {

digitalWrite(13,HIGH);

LEDstate = HIGH;

}

goTime = millis() + nextTime;

}


void checkPush(){

int buttonNow = digitalRead(button);

if(buttonNow != pushState) {   // something has changed

   pushState = buttonNow;    // this is the new state of the button to remember

   if(buttonNow == LOW) {   // button has gone from a not being pushed to pushed

   flashState++;  // increment the state variable

   goTime = millis(); // make the change immediately

   if(flashState > 2) {  // wrap round the state variable

    flashState = 0;

    nextTime = 1000;   // wrap round the speed

   } else {

   nextTime = nextTime / 2;  // make flashing twice the speed

   }

}

}

}

What we have done here is to add the checkPush function this first looks at the current state of the button and if nothing has changed the function is exited immediately. If however something is different with the button from last time we then check to see if the new state is low if it is this means we have detected a button press, otherwise we have a button release which in this application should be ignored. If we have a button pressed event we proceed to increment the state variable so we know how many times we have pressed the button and then we adjust the flashing speed by changing the nextTime value. We half it each time to increase the flashing speed or set it to the original value when we wrap round the state variable.


The only thing that might be wrong with this is contact bounce. This is where you don't get a clean signal on the button but a rapidly changing burst of changing push states, as the mechanical contacts bounce against each other in the few milli seconds after the button has been pushed. This depends very much on the type of push button you are using but the effect you will see it that the flashing rate appears to skip one or two settings. One way to get round this is to ignore the state of the button until it has been in the same state for a some time. This is known as the de-bounce time and is normally so small you can't tell it is there. There are many ways to implement this, the way I have chosen here is to note the time when the buttons are the same and only do something if the buttons have not been the same for over 20mS.

void checkPush(){

int buttonNow = digitalRead(button);

if((buttonNow != pushState) )

  if((millis() - buttonHoldTime) > 20) {   // something has changed and been steady for longer than 20mS

   pushState = buttonNow;    // this is the new state of the button to remember

   if(butonNow == LOW) {    // button has gone from a not being pushed to pushed

   flashState++;  // increment the state variable

   goTime = millis(); // make the change immediately

   if(flashState > 2) { flashState =0; // wrap round the state variable

    nextTime = 1000;   // wrap round the speed

   else {

   nextTime = nextTime / 2;  // make flashing twice the speed

   }

}

}

else{

buttonHoldTime = mills();

  }

}

}

Every time the current button state and the previous state are found to be the same the variable buttonHoldTime is reset to the current time. If they are not the same the the time when they were the same is subtracted from the current time and if this exceeds out threshold of 20 we consider the change to be not affected by contact bounce and so we can act on it.

So by making note of the next time something should happen and constantly checking if that time has arrived we can run a function without blocking the processor. We can easily extend this idea to checking the time for more than one thing by having a variable for each event holding the time when it next should happen. As your program gets more complex and does more things it is a simple matter to add an other variable and check if it is time to do that function.


The idea is that what ever the function is, the program should be in and out of it quickly. The state variable tells you where you are up to with this function and what bit of it to do next time you enter it.


This just doesn't only apply to flashing lights but to anything that consists of a number of stages, it is a different way of thinking about a program. The program doesn't hang about waiting for something to happen, it checks if something has happened and if it hasn't it exits. If it has happened, it changes its state variable so it can recognise that something has happened and do something about it.


This is a difficult concept to grasp for a beginner and I would be surprised if you get it on first reading, but once you have got it it opens up a whole new way of getting the Arduino to do a lot more than you thought it could.

Wrap round - wrap up

I said at the start there could be a problem with wrap round of the millis counter after a month or two. You can get round this by changing the concept of a variable which holds the absolute time to go and do the next function into a variable that holds the time interval between running a function. You then need a variable to hold the last time you ran the function, call it lastTime, then use the following code to see if it is time:-

if( millis() - lastTime > interval) functionGo();

The subtraction will automatically cope with the millis counter wrap round. If you look closely at the debounce function you will see that I used this technique there.

However, for most projects I think the concept of a go time is easer to understand.