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 we need to have somewhere to go when we leave soff, so let's pick red int 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 Serial.begin(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 */ case 2: /* statements */ case 3: /* statements */ default: /* statements */ the end of the switch statement. You *have* to have a "" 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 Serial.println("off"); if (digitalread(onoffpin) == HIGH) currentstate = nextstate; this ends the state of "soff" case sred: start of the "red" case Serial.println("red"); if (digitalread(onoffpin) == LOW) else if (digitalread(colorpin) == HIGH) currentstate = sblue; else PulseLed(redLed); end of the red case. case sblue: Serial.println("blue"); if (digitalread(onoffpin) == LOW) else if (digitalread(colorpin) == HIGH) currentstate = sgreen; else PulseLed(blueLed); case sgreen: Serial.println("green"); if (digitalread(onoffpin) == LOW) else if (digitalread(colorpin) == HIGH) currentstate = sred; else PulseLed(greenLed); default: the default case, which is an error for us Serial.println("ERROR: default state"); this is the end of the case, where we go after we hit a
state code examples #2 4/8 void PulseLed(int pin) for (int i=0; i<=255; i++) analogwrite(pin, i); for (int i=255; i>=0; i--) analogwrite(pin, i); 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 we need to have somewhere to go when we leave soff, so let's pick red int 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. Serial.begin(9600); void loop() BlinkStatusLed(); switch(currentstate) case soff: Serial.println("off"); if (digitalread(onoffpin) == HIGH) currentstate = nextstate; case sred: Serial.println("red"); if (digitalread(onoffpin) == LOW) 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; case sblue: Serial.println("blue"); if (digitalread(onoffpin) == LOW) else if (PulseLedReadButton(blueLed, colorpin) == true) currentstate = sgreen;
state code examples #2 7/8 case sgreen: Serial.println("green"); if (digitalread(onoffpin) == LOW) else if (PulseLedReadButton(greenLed, colorpin) == true) currentstate = sred; default: the default case, which is an error for us Serial.println("ERROR: default state"); 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 for (int i=255; i>=0; i--) analogwrite(lpin, i); if (buttonpushed == false) if ( digitalread(bpin) == HIGH) buttonpushed = true; 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); for (int i=255; i>=0; i--) analogwrite(pin, i); void BlinkStatusLed() digitalwrite(statusled,high); delay(100); digitalwrite(statusled,low); ; delay(100);