Introduction: Using the Switch() Statement As Sequencing Control
In this Instructable, we'll be going over the "switch()" statement and it's use in programming. The switch statement is a powerful tool for organizing your program, easily allow you to move through complex flowcharts.
In our switch statement, we'll be going over some different input options to create a sequence of events. We'll start easy, with some timing delays to move actuators, then move to processing some inputs to trigger the next stage of motion. Finally, we'll put everything we learned together to make a simulated factory line!
For this project, we will need:
- Actuator
- Breadboard
- Push buttons for inputs (you can use a wire connected to ground to simulate buttons)
- Potentiometer (10k preferred, any value works)
- LEDs (can use the built in LED on pin 13)
Let's get started!
Step 1: The Switch() Statement
Before we dive into the coding, we need to understand what we are working with. The basics of the switch() statement is that you watch a variable, and depending what the variable's value is, a different case in the switch() is activated.
This property makes it very easy to make a program pointer that will move through your code, applying all the appropriate inputs and outputs at the correct time. There is also a default case, so that you can put a section of the program to happen when the variable you are watching does not match one of the programmed cases.
For the first example, we'll have a passive code. By watching the serial monitor, you can see the program count to 5 over 5 seconds. The switch statement sits in each case until 1 second has passed, then moves to the next. See the attached code, read the comments, and upload it to see how it works before moving on.
Since these are just test programs, you can leave the board plugged into the computer so the Arduino can receive power.
/* This code is to show the basic workings of a switch statement. It counts to 10 in the serial monitor using a delay.</p><p> Written by Progressive Automations Sept 21, 2015 This code is in the public domain */ int programCount = 0;//variable to move through the program void setup() { Serial.begin(9600);// initialize serial communication: programCount = 0;//start at the beginning }//end setup void loop() { switch (programCount) { case 0: delay(1000);//wait 1 second Serial.println("0");//print the number programCount = 1; break; case 1: delay(1000);//wait 1 second Serial.println("1");//print the number programCount = 2; break; case 2: delay(1000);//wait 1 second Serial.println("2");//print the number programCount = 3; break; case 3: delay(1000);//wait 1 second Serial.println("3");//print the number programCount = 4; break; case 4: delay(1000);//wait 1 second Serial.println("4");//print the number programCount = 5; break; case 5: delay(1000);//wait 1 second Serial.println("5");//print the number programCount = 6; break; default: Serial.print("Counting complete"); while(1); //freeze the program here }//end switch }//end loop
Step 2: Adding Basic Digital Inputs
Now that we know the basics of a switch statement, we can add in a basic digital input. We'll use a button to trigger the movement to the next state.
In the first case, we'll use a counter to store how long the button has been pressed. Once it has been held for 5 seconds, we'll move to the next state. Watch the serial monitor to see when the program is done.
See the code below, then upload it to see how it works before moving to the next step.
Since these are just test programs, you can leave the board plugged into the computer so the Arduino can receive power.
/* This code is to show how a digital input can move through cases in a switch statement. The button must be pressed 5 times to move on. Written by Progressive Automations Sept 21, 2015 This code is in the public domain */ const int button = 5;//attach the button on pin 5 int programCount = 0;//variable to move through the program int buttonCount = 0;//variable to hold the button presses int buttonState = 1;//variable to hold the state of the button void setup() { Serial.begin(9600);// initialize serial communication: programCount = 0;//start at the beginning buttonCount = 0;//set to 0 to start pinMode(button,INPUT);//set the button as an input digitalWrite(button, HIGH);//enable internal pullup resistor Serial.println("Hold the button for 5 seconds to complete the program"); }//end setup void loop() { switch (programCount) { case 0: buttonState = digitalRead(button); if (buttonState==0) { buttonCount = buttonCount+1;//count up every time the button is pressed delay(1);//small delay for counting milliseconds } if (buttonCount == 5000) programCount = 1;//once the button is pressed enough times, move on break; case 1: Serial.println("Button has been held for 5 seconds");//print the number delay(1000); programCount = 2; break; default: Serial.println("Program complete"); while(1); //freeze the program here }//end switch }//end loop
Step 3: Adding Basic Analog Inputs
The next input we'll try is an analog input. Most sensors give some sort of analog signal, for this example we will be using a potentiometer. The code will read the potentiometer, and only advance once the desired value is reached.
Wire the potentiometer according to the diagram, and upload the code below.
Since these are just test programs, you can leave the board plugged into the computer so the Arduino can receive power.
/* This code is to show how an analog input can be used to move through a switch statement. The potentiometer must be moved to the desired value before advancing to the next stage in the program. Written by Progressive Automations Sept 21, 2015 This code is in the public domain */ const int pot = A0;//attach the pot to pin A0 int potValue = 0;//variable to hold the pots reading int programCount = 0;//variable to move through the program void setup() { Serial.begin(9600);// initialize serial communication: pinMode(pot, INPUT); //set the pot as an input programCount = 0;//start at the beginning of the program Serial.println("Move the potentiometer to the desired locations to advance"); }//end setup void loop() { switch (programCount) { case 0: potValue = analogRead(pot);//read the pot's value Serial.println("Move the potentiometer fully to one side to get the max reading"); while (potValue <= 1000)//1023 is max value { potValue = analogRead(pot);//read the pot's value delay(10);//small delay between readings } Serial.println(potValue);//show what the reading is Serial.println("One limit reached!"); Serial.println("");//print an extra line if (potValue >= 1000) programCount = 1;//once the value is at the desired position, move on break; case 1: potValue = analogRead(pot);//read the pot's value Serial.println("Move the potentiometer to the middle"); while (potValue >= 500)//~512 is the middle { potValue = analogRead(pot);//read the pot's value delay(10);//small delay between readings } Serial.println(potValue);//show what the reading is Serial.println("Middle position reached!"); Serial.println("");//print an extra line if (potValue <= 500) programCount = 2;//once the value is at the desired position, move on break; case 2: potValue = analogRead(pot);//read the pot's value Serial.println("Move the potentiometer fully to the other side to get the min reading"); while (potValue >= 10)//0 is min value { potValue = analogRead(pot);//read the pot's value delay(10);//small delay between readings } Serial.println(potValue);//show what the reading is Serial.println("Final limit reached!"); Serial.println("");//print an extra line if (potValue <= 10) programCount = 3;//once the value is at the desired position, move on break; default: Serial.println("Program complete"); while (1); //freeze the program here }//end switch }//end loop
Step 4: Adding Outputs
Now that we've tested different inputs, we can see how outputs can be used in the switch() statements as well. We will use the MegaMoto to control an actuator to extend and retract. This example is similar to the first example, we're just using time delays.
See the wiring diagram, and upload the code. See the MegaMoto Instructable for more detail information about the motor controller. While the program is running, the motor will be moving, and you can watch the serial monitor to see what is happening.
Since these are just test programs, you can leave the board plugged into the computer so the Arduino can receive power.
/* This code is to show how outputs are used in a switch statement. The code moves an actuator in and out at varying speeds, according to the sequence. Written by Progressive Automations Sept 21, 2015 This code is in the public domain */ const int enable = 8; const int PWMA = 11; const int PWMB = 3;//pins for the MegaMoto int programCount = 0;//variable to move through the program void setup() { Serial.begin(9600);// initialize serial communication: programCount = 0;//start at the beginning pinMode(enable, OUTPUT); pinMode(PWMA, OUTPUT); pinMode(PWMB, OUTPUT); }//end setup void loop() { switch (programCount) { case 0: digitalWrite(enable, HIGH);//enable the control board Serial.println("Motor moving forwards at full speed"); analogWrite(PWMA, 255); analogWrite(PWMB, 0);//set the speed of the actuator delay(5000);//Move for 5 seconds programCount = 1; break; case 1: Serial.println("Motor moving backwards at full speed"); analogWrite(PWMA, 0); analogWrite(PWMB, 255);//set the speed of the actuator delay(5000);//Move for 5 seconds programCount = 2; break; case 2: Serial.println("Motor moving forwards at half speed"); analogWrite(PWMA, 128); analogWrite(PWMB, 0);//set the speed of the actuator delay(3000);//Move for 3 seconds programCount = 3; break; case 3: Serial.println("Motor moving forwards at full speed"); analogWrite(PWMA, 255); analogWrite(PWMB, 0);//set the speed of the actuator delay(2000);//Move for 2 seconds programCount = 4; break; case 4: Serial.println("Motor moving backwards at full speed"); analogWrite(PWMA, 0); analogWrite(PWMB, 255);//set the speed of the actuator delay(2000);//Move for 2 seconds programCount = 5; break; case 5: Serial.println("Motor moving backwards at half speed"); analogWrite(PWMA, 0); analogWrite(PWMB, 128);//set the speed of the actuator delay(3000);//Move for 3 seconds programCount = 6; break; default: Serial.print("Movement complete"); while(1); //freeze the program here }//end switch }//end loop
Step 5: Putting It All Together
Since we've now seen all the inputs and outputs in action, let's put it all together!
We will simulate a part moving along an assembly line, with different actions being applied. We will use limit switches and the potentiometer feedback from the actuator. This is the sequence we will follow:
- Wait for push button input to start the cycle
- Extend the first actuator at full speed for 5 seconds to move the part into place
- Turn on the LED to simulate a washing cycle being started
- Wait 5 seconds for washing, then turn off LED
- Retract the first actuator at half speed for 10 seconds to dry the part
- Extend the second actuator at full speed to position 1
- Turn on the other LED, wait 2 seconds, turn off the LED
- Extend the second actuator at full speed to position 2
- Turn on the other LED, wait 2 seconds, turn off the LED
- Extend the second actuator at full speed to position 3
- Turn on the other LED, wait 2 seconds, turn off the LED
- Retract the second actuator at full speed for 5 seconds to complete the cycle.
- Wait for push button input to restart the cycle
Wire the system according to the diagram, and upload the code to see how it works!
Since these are just test programs, you can leave the board plugged into the computer so the Arduino can receive power.
/* This code is to show how outputs are used in a switch statement. The code moves an actuator in and out at varying speeds, according to the sequence. Written by Progressive Automations Sept 21, 2015 This code is in the public domain */ const int enable1 = 8; const int PWMA1 = 11; const int PWMB1 = 3;//pins for the first MegaMoto(Actuator) const int enable2 = 12; const int PWMA2 = 9; const int PWMB2 = 10;//pins for the second MegaMoto(Actuator) const int potFeedback = A0;//pin for the second actuators potentiometer const int LED1 = 7; const int LED2 = 13;//two pins for LEDs const int button = 6;//restart button int programCount = 0;//variable to move through the program int buttonState = 1;//vairable to store the state of the button,initialise as HIGH int pos1 = 100; int pos2 = 500; int pos3 = 1000;//three positions to move to void setup() { Serial.begin(9600);// initialize serial communication: programCount = 0;//start at the beginning pinMode(enable1, OUTPUT); pinMode(PWMA1, OUTPUT); pinMode(PWMB1, OUTPUT);//set first MegaMoto as outputs pinMode(enable2, OUTPUT); pinMode(PWMA2, OUTPUT); pinMode(PWMB2, OUTPUT);//set second MegaMoto outputs pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT);//set LED's as outputs pinMode(button, INPUT);//set button as input digitalWrite(button, HIGH);//enable internal pullup pinMode(potFeedback, INPUT);//set potentiometer as input }//end setup void loop() { switch (programCount) { case 0: digitalWrite(enable1,LOW); digitalWrite(enable2,LOW);//disable both control boards so the actuators cant move Serial.println("Waiting to start sequence"); while (digitalRead(button) == 1){}//wait here until button is pressed programCount = 1;//once button is pressed, advance break; case 1: digitalWrite(enable1, HIGH);//enable the first actuators control board Serial.println("First Actuator moving forwards at full speed"); analogWrite(PWMA1, 255); analogWrite(PWMB1, 0);//set the speed of the actuator delay(5000);//Move for 5 seconds analogWrite(PWMA1, 0); analogWrite(PWMB1, 0);//stop the actuator programCount = 2; break; case 2: Serial.println("Washing the part"); digitalWrite(LED1, HIGH);//turn on LED programCount = 3; break; case 3: delay(5000); Serial.println("Washing complete"); digitalWrite(LED2, LOW);//turn off LED programCount = 4; break; case 4: Serial.println("First Actuator moving backwards at half speed"); analogWrite(PWMA1, 0); analogWrite(PWMB1, 128);//set the speed of the actuator delay(10000);//Move for 10 seconds analogWrite(PWMA1, 0); analogWrite(PWMB1, 0);//stop the actuator programCount = 5; break; case 5: Serial.println("Second Actuator moving at full speed to Position 1"); digitalWrite(enable2,HIGH);//enable the second actuators control board while (analogRead(potFeedback) <= pos1) { analogWrite(PWMA2, 255); analogWrite(PWMB2, 0);//set the speed of the actuator } analogWrite(PWMA2, 0); analogWrite(PWMB2, 0);//stop the actuator programCount = 6; break; case 6: Serial.println("First decal being applied"); digitalWrite(LED2, HIGH); delay(2000); digitalWrite(LED2, LOW); programCount = 7; break; case 7: Serial.println("Second Actuator moving at full speed to Position 2"); while (analogRead(potFeedback) <= pos2) { analogWrite(PWMA2, 255); analogWrite(PWMB2, 0);//set the speed of the actuator } analogWrite(PWMA2, 0); analogWrite(PWMB2, 0);//stop the actuator programCount = 8; break; case 8: Serial.println("Second decal being applied"); digitalWrite(LED2, HIGH); delay(2000); digitalWrite(LED2, LOW); programCount = 9; break; case 9: Serial.println("Second Actuator moving at full speed to Position 3"); while (analogRead(potFeedback) <= pos3) { analogWrite(PWMA2, 255); analogWrite(PWMB2, 0);//set the speed of the actuator } analogWrite(PWMA2, 0); analogWrite(PWMB2, 0);//stop the actuator programCount = 10; break; case 10: Serial.println("Third decal being applied"); digitalWrite(LED2, HIGH); delay(2000); digitalWrite(LED2, LOW); programCount = 11; break; case 11: Serial.println("Second Actuator moving backwards at full speed"); analogWrite(PWMA2, 0); analogWrite(PWMB2, 255);//set the speed of the actuator delay(5000);//Move for 5 seconds analogWrite(PWMA2, 0); analogWrite(PWMB2, 0);//stop the actuator Serial.println("Sequence Complete"); Serial.println(""); Serial.println("");//print some blank spaces to make serial monitor more read-able programCount = 0;//loop back to the beginning break; default: Serial.print("Error"); while (1); //freeze the program here }//end switch }//end loop
Step 6: Conclusion
In this Instructable, we learned how a switch() statement can be used to control the flow of a program. We went over digital inputs, analog inputs, and outputs, and finished with a simulated system that brought it all together. This code can be modified to make your actuators follow any sequence of events, using a variety of inputs and outputs.
If you'd like to take a look at our selection of linear actuators, motions control systems and microcontrollers then please visit us at www.progressiveautomations.com for all your actuator needs! We can even build a custom actuator or control system for you based on your own custom specifications with the help of our highly trained staff of engineers. You can learn more about the custom order process right here!
Follow, Like and Subscribe!
Twitter - twitter.com/ProgAutoInc
Facebook - www.facebook.com/ProgressiveAutomations
Youtube - www.youtube.com/user/MrActuators
17 Comments
7 years ago
should have made it set programCount to -1 in case 5. and it looks like your code would stay forever hung at the while statement. should make that a variable or bool that you can set based on something.
7 years ago
also a second actuator would be needed to close throttle...wch
7 years ago
ok I see your point ,however this program with a few mods could be used for an auto stop system in a car, sonic sensors on the front and /or rear bumper would sense distance to object/auto speed and activators would push brake pedal if speed exceeded preset amount...WCH
7 years ago
Hi Guys,
I just finished the first version of my 'Automaton' framework which works on the same state-machine principles as this instructable, like you I've become interested in state machines as a way of multitasking Arduino's. Automaton has about 0 users so far, and I'd love to get some feedback on it from people who are working who are interested in state machines and the like.
I had similar design decisions to make. I went for the enums and the state changes are controlled by a state transition table contained in a class. The switch statement from this instructable is was broken up per state machine (object) and split in events and actions. That makes for a nice modular approach that lends itself very well to multitasking.
https://github.com/tinkerspy/Automaton
Documentation & tutorial:
https://github.com/tinkerspy/Automaton/wiki
You should also be able to install it via the Arduino Library manager. I'd appreciate it if some of you could have a look and tell me what you think.
Thanks,
Tinkerspy
@ProgressiveAuto1: if you think I'm hijacking your instructable I apologize, in that case feel free to delete this post
Reply 7 years ago
No worries, it looks like a cool project!
7 years ago on Introduction
Another name for such a code block is "state machine" , and the switch statement statement is a good to implement one.
One "Pro Tip" is to make the cases 10,20,30... instead of 1,2,3... At least when you first write the code. That way when you realize there needed to be 2 more steps in between, you can call them 23 and 27, and still have the steps happening in numerical order. If you don't have the steps happen in numerical sequence, the code becomes very hard to maintain, as it is essentially another form of spaghetti code.
Reply 7 years ago on Introduction
Excellent ProTip. That's far easier then having to go back and re-number every state when you add new ones.
Reply 7 years ago
A better protip would be to use an enumeration.
typedef enum{
STEP_1 = 0,
STEP_2,
STEP_3
} MySteps
Then you can
case STEP_3:
After that all you do is add steps in your enumeration and not worry about what they're numbered.
Reply 7 years ago
This also had the benefit of naming your cases something meaningful.
Reply 7 years ago
I've never heard of enum, that's definitely something I'll look into.
Thank you for the suggestion!
Reply 7 years ago on Introduction
The code certainly shows some signs of aspiring to being a Finite State Machine (FSM). But it needs at least one other attribute to be more than just a simple linear sequencer: The ability to change its behaviour according to external events. That means that within each state (as coded in the example) it will sit in a loop looking for one of several possible things, say a timeout OR an external input, then switch it's state accordingly. In the process of switching state it would typically change an output.
The moment it has to check for more than one thing (e.g. timeout) we have to eliminate "blocking" calls such as Delay() and use something that doesn't tie up the processor. After all, if it's in a Delay() it can't be testing an input at the same time. For the Arduino there's a tutorial on creating "non-blocking" delays at https://www.arduino.cc/en/Tutorial/BlinkWithoutDel...
Once you've eliminated blocking calls such as Delay() you are well on your way to multitasking - having the program seemingly do several things at once. Write each FSM as a function that gets called, does its thing, and immediately exits again. String two or more of those together inside an endless loop, and you have what is known as a superloop program structure.
There's more detailed description of multitasking systems in a post I wrote here.
Reply 7 years ago on Introduction
You are correct.
We've done some instructables that cover the more "multitasking" aspect of programming. In our "Monitoring Load Feedback of an Actuator", we use if statements to check if the load is over the limit, allowing us to always monitor the current as well as keep the actuator moving. We also use statements like that to debounce buttons, using millis().
Interrupts are very useful when trying to implement "superloop" codes,
we'll be going over more programming methods in future Instructables! In the next couple weeks we'll be releasing a multi-part series of Instructables that uses interrupts to monitor multiple inputs at the same time.
Stay tuned!
7 years ago on Step 6
Very informative; I like the way you brought it all together one step at a time.
I am looking forward to viewing your other Instructables as well.
Build_it_Bob
Reply 7 years ago on Introduction
Thanks for the positive feedback! Stay tuned for more.
7 years ago on Introduction
Can you explain what the program gains from the Switch statements? The sequence will in any event stall (block) during the Delay() calls and the While loops that wait for external events. Why would the program not work if all the Switch/Case stuff was removed?
Reply 7 years ago on Introduction
Programs can achieve the same result with many different ways of coding it.The switch statements are good for easily seeing when your program moves from state to state. This make debugging much easier, you can easily see in which state where the program gets caught up.
You are correct, it will get "stuck" while it is waiting for the delays and inputs, you could achieve the same result with a series of if statements and have your program continuously loop. If you are monitoring multiple things at once, you would need to program to continuously check everything, but doing a linear program like this the switch works well for basic, linear programs.
If you check our other instructable, https://www.instructables.com/id/Monitoring-Load-Feedback-of-an-Actuator/, you can see an example of a program continuously looping, so it can check the current that the actuator is pulling, as well as keeping the actuator in motion.
7 years ago
Mechanical limit
switches can be a costly problem. Emerson electric made gearmotors
for dental chairs some time ago in which limit switches were failing
far too early in their life cycle, and were made by the venerable
Micro Switch corp. using their patented snap action leaf spring. We
found a substitute switch that used an entirely different actuation
method used by another vendor, and replaced all failures, achieving a
100% success rate of repair. Never could get an answer from Emerson
or Micro about what their problem was either.