state code examples #2 1/8
Using switch/case as the core of your state machine
// a state machine with an on/off switch and a button that changes the
// LED color each time you press the button.
// our states. We could do these with #defines once we've learned
// them "static" means this variable's value cannot be changed once
// it's set, it's a good way to prevent yourself from accidentally
// changing the value in your code.
//
// These *must* be declared "const int" and not just "int", don't ask
// why, just do it or your code will not compile.
//
const int sOff = 0;
const int sRed = 1;
const int sBlue = 2;
const int sGreen = 3;
// the current state we're in at any given time. We want to start in
// "off", so we set the variable to sOff when we declare it.
int currentState = sOff;
// we need to have somewhere to go when we leave sOff, so let's pick
// red
int nextState = sRed;
// our on/off switch
int onOffPin = 2;
// the momentary switch we use to change the color
int colorPin = 4;
// the LEDs
int redLed = 10;
int greenLed = 11;
int blueLed = 9;
int statusLed = 13;
void setup()
{
pinMode(onOffPin, INPUT);
pinMode(colorPin, INPUT);
pinMode(statusLed, OUTPUT);
pinMode(redLed, OUTPUT);
pinMode(blueLed, OUTPUT);
pinMode(greenLed, OUTPUT);
// if we didn't set the initial state when we created the variable,
// we'd do it here. The initial state is "off" or "start" in most
// state machines, but if you only have active/on states, pick an
// appropriate starting state.
state code examples #2 2/8
//
// currentState = sOff;
[Link](9600);
}
void loop()
{
BlinkStatusLed();
// Every time through the loop we check the state we're in with a
// switch/case statement.
// switch/case statements are replacement for using lots of
// if/else statements. Instead of doing this:
// if (a == 1) then { /* statements*/}
// else if (a == 2) then { /* statements*/}
// else if (a == 3) then { /* statements*/}
//we can use switch/case and do this:
// switch(a) { // start of the switch statement
// case 1:
// /* statements */
// break;
// case 2:
// /* statements */
// break;
// case 3:
// /* statements */
// break;
// default:
// /* statements */
// break;
// } // the end of the switch statement.
//You *have* to have a "break;" at the end of each "case".
//When the Arduino gets to a "break" statement, it exits the switch
//statement
// the "default" case is what the switch statement should do if
// nothing is matched. In the above example, if a is 1, 2, or 3 we do
// something. But if a was 4, it wouldn't match the first three cases
// and the "default" case is used. For our state machines, we should
// not have a default case that does anything other than generate an
// error message and reset the state machine
// for our state machine, each state is contained in its own case
// statement. in each case, the first thing we do is check to see
// if we should be in a different state. If not, then we go ahead
// and do whatever we need to do in this state.
switch(currentState) { // this is the start of the switch
case sOff: // this starts the state of "sOff"
// and this is what we do if currentState == sOff
state code examples #2 3/8
[Link]("off");
if (digitalRead(onOffPin) == HIGH) {
currentState = nextState;
}
break; // this ends the state of "sOff"
case sRed: // start of the "red" case
[Link]("red");
if (digitalRead(onOffPin) == LOW) {
currentState = sOff;
nextState = sRed;
}
else if (digitalRead(colorPin) == HIGH) {
currentState = sBlue;
}
else {
PulseLed(redLed);
}
break; // end of the red case.
case sBlue:
[Link]("blue");
if (digitalRead(onOffPin) == LOW) {
currentState = sOff;
nextState = sRed;
}
else if (digitalRead(colorPin) == HIGH) {
currentState = sGreen;
}
else {
PulseLed(blueLed);
}
break;
case sGreen:
[Link]("green");
if (digitalRead(onOffPin) == LOW) {
currentState = sOff;
nextState = sRed;
}
else if (digitalRead(colorPin) == HIGH) {
currentState = sRed;
}
else {
PulseLed(greenLed);
}
break;
default: // the default case, which is an error for us
[Link]("ERROR: default state");
currentState = sOff;
nextState = sRed;
break;
} // this is the end of the case, where we go after we hit a break;
state code examples #2 4/8
void PulseLed(int pin) {
for (int i=0; i<=255; i++) {
analogWrite(pin, i);
delay(2);
}
for (int i=255; i>=0; i--) {
analogWrite(pin, i);
delay(2);
}
}
void BlinkStatusLed()
{
digitalWrite(statusLed,HIGH);
delay(100);
digitalWrite(statusLed,LOW);
;
delay(100);
}
state code examples #2 5/8
Reading a Switch While Pulsing an LED
// a state machine with an on/off switch and a button that changes the
// LED color each time you press the button.
// as was demonstrated in class with state_machine_switch.pde, there's
// a bug: if some pushes the "change" button while the PulseLed()
// subroutine is happening, the button is ignored. PulseLed() is busy
// making the LED get brighter and darker and does not actually read
// the button.
//
// one solution is to read the button while you're in PulseLed(). But
// if you read the button, how do you get that information back to
// your state machine so it can do the right thing?
//
// We will solve this by changing PulseLed so that it reads the button
// while pulsing, then returns a value of "true" if the button was
// pushed. Compare this program with state_machine_switch.pde and
// look at the differences in what we do. We're still implementing
// the same state machine as before, but we read the button more often
// and have a better interaction experience with the button pusher.
//
// Also, see the case for sRed for some comments on how we change
// reading the button in our state machine now that we can read the
// button while pulsing the LED.
const int sOff = 0;
const int sRed = 1;
const int sBlue = 2;
const int sGreen = 3;
// the current state we're in at any given time. We want to start in
// "off", so we set the variable to sOff when we declare it.
int currentState = sOff;
// we need to have somewhere to go when we leave sOff, so let's pick
// red
int nextState = sRed;
// our on/off switch
int onOffPin = 2;
// the momentary switch we use to change the color
int colorPin = 4;
// the LEDs
int redLed = 10;
int greenLed = 11;
int blueLed = 9;
int statusLed = 13;
void setup()
{
state code examples #2 6/8
pinMode(onOffPin, INPUT);
pinMode(colorPin, INPUT);
pinMode(statusLed, OUTPUT);
pinMode(redLed, OUTPUT);
pinMode(blueLed, OUTPUT);
pinMode(greenLed, OUTPUT);
// if we didn't set the initial state when we created the variable,
// we'd do it here. The initial state is "off" or "start" in most
// state machines, but if you only have active/on states, pick an
// appropriate starting state.
//
// currentState = sOff;
[Link](9600);
}
void loop()
{
BlinkStatusLed();
switch(currentState) {
case sOff:
[Link]("off");
if (digitalRead(onOffPin) == HIGH) {
currentState = nextState;
}
break;
case sRed:
[Link]("red");
if (digitalRead(onOffPin) == LOW) {
currentState = sOff;
nextState = sRed;
}
// now that we can read the colorPin while we're pulsing
// the LED, we no longer need to explicitly read it here.
// We just pulse the LED, and if the button was pushed
// while we were reading, we change the state.
else if (PulseLedReadButton(redLed, colorPin) == true) {
currentState = sBlue;
}
break;
case sBlue:
[Link]("blue");
if (digitalRead(onOffPin) == LOW) {
currentState = sOff;
nextState = sRed;
}
else if (PulseLedReadButton(blueLed, colorPin) == true) {
currentState = sGreen;
}
state code examples #2 7/8
break;
case sGreen:
[Link]("green");
if (digitalRead(onOffPin) == LOW) {
currentState = sOff;
nextState = sRed;
}
else if (PulseLedReadButton(greenLed, colorPin) == true) {
currentState = sRed;
}
break;
default: // the default case, which is an error for us
[Link]("ERROR: default state");
currentState = sOff;
nextState = sRed;
break;
}
// we've made two major changes to PulseLed (which is still here so
// you can compare them)
//
// 1) our new function returns a value. Just like digitalRead()
// returns a value of HIGH or LOW, our new function can return "true"
// or "false". We use a "boolean" variable for this, can only be
// "true" or "false". Anything that calls PulseLedReadButton has to
// do something with this value. (If you want to pulse an led but not
// check the button, call PulseLed() instead.)
//
// 2) When you call PulseLedReadButton, you have to tell it what pin
// the button is on.
//
//
boolean PulseLedReadButton(int lPin, int bPin) {
// we need to keep track of whether or not a button was pushed.
// The default is that it was not pushed.
boolean buttonPushed = false;
// now, every time through each loop, we read the pin to see if
// the button is being pushed. If it is, save that information.
//
// we also do something a bit clever to make the program more
// efficient. If the button *has* been pushed, there's no need to
// read it again
for (int i=0; i<=255; i++) {
analogWrite(lPin, i);
if (buttonPushed == false) {
if ( digitalRead(bPin) == HIGH) {
buttonPushed = true;
}
}
state code examples #2 8/8
delay(2);
}
for (int i=255; i>=0; i--) {
analogWrite(lPin, i);
if (buttonPushed == false) {
if ( digitalRead(bPin) == HIGH) {
buttonPushed = true;
}
}
delay(2);
}
// the "return" statement is how we get the information back to
// the code that called us.
return buttonPushed;
}
// the previous version of PulseLed
void PulseLed(int pin) {
for (int i=0; i<=255; i++) {
analogWrite(pin, i);
delay(2);
}
for (int i=255; i>=0; i--) {
analogWrite(pin, i);
delay(2);
}
}
void BlinkStatusLed()
{
digitalWrite(statusLed,HIGH);
delay(100);
digitalWrite(statusLed,LOW);
;
delay(100);
}