I'm pretty new to programming and i'd like some advice on how the arduino sequences instructions.
What i understand is this:
any variable declarations outside the void setup() and void loop() and any other voids are global and available to all functions
void setup() is run once in sequence when the unit is started up
void loop() is looped indefinitely
what i don't quite understand is how functions within the void loop() are run.
consider the code:
void functionA ();
{
do first thing
do second thing
}
void functionB();
{
do third thing
do fourth thing
}
void functionC();
{
do fifth thing
do sixth thing
}
///////////////////////////////
void loop() {
if (button1 == HIGH)
{
do tenth thing
}
functionA();
functionB();
functionC();
set button1 back to LOW;
}
my understanding is that
things 1-6 are executed in sequence and then looped to start again.
if the button is pressed, thing 10 will happen and then things 1-6 will happen and keep on looping
the button state is only read in the instance between the end of thing 6 and the beginning of thing 1 when the main loop repeats?
if any of items 1-6 are occurring, then the button state will not be read and acted upon?
if i want to read the button state and initiate an action during any of the functions A-C, i need to put the button read code into those functions?
do the functionsA-C loop within themselves? Or do they carry out the instructions sequentially and then move on to the next function?
I'm building a machine that has a lot of sequential processes that i'd like to control with the arduino. each of the processes has its own function within the main void loop(). However, i'd like to add a pause button, so i think i may need to add the button read function within each of the process functions?
Thanks for your help folks. If you know of any good resources that can explain this, please point me in the right direction. Ta
I'm building a machine that has a lot of sequential processes that i'd like to control with the arduino. each of the processes has its own function within the main void loop(). However, i'd like to add a pause button, so i think i may need to add the button read function within each of the process functions?
It sounds like you need to use what is known as a state machine. At any one time the system will be in a particular state defined by the value of a variable. Each time through loop() you test what state the system is in and execute the code for it or part of the code for it, but the program must allow loop() to run freely and not block it, for example by using delay().
When the action to move to a new state is detected, perhaps by user input, a count or time passing you change the value of the state variable and start executing the code for the new state.
Because loop() is running freely you can read inputs, such as a pause button, frequently and act upon them, perhaps by entering a pause state until the end of pause action occurs.
We need more information on what is meant by a pause button. The simplest pause button would be:
while (digitalRead(pauseButton) == LOW);// or HIGH
This will hold the program in the while loop while the button condition is true. To answer your more general question, about sequencing, functions outside of void loop() are called when they are called. They can be called from inside the loop or inside another function or from void setup() etc. They can be called from multiple places. Functions execute when they are called.
things 1-6 are executed in sequence and then looped to start again.
if the button is pressed, thing 10 will happen and then things 1-6 will happen and keep on looping
the button state is only read in the instance between the end of thing 6 and the beginning of thing 1 when the main loop repeats?
if any of items 1-6 are occurring, then the button state will not be read and acted upon?
OK so far
if i want to read the button state and initiate an action during any of the functions A-C, i need to put the button read code into those functions?
You could do but there is a much better way - see below
do the functionsA-C loop within themselves? Or do they carry out the instructions sequentially and then move on to the next function?
They will not loop within themselves unless you have code within them that makes a loop - such as WHILE or FOR
You should NOT have code within the functions that creates a loop unless it is always completed in a few microseconds - for example setting the values in an array back to zero.
And if the functions all complete very quickly (as they should) the code will get back to the start of loop() and the line to read the buttonPin so quickly that it will appear immediate. Have a look at the demo Several Things at a Time. Note how each function runs very briefly and returns to loop() so the next one can be called. And there may be dozens of calls to a function before it is actually time for it to do anything.
If you have a complex sequence of actions the idea of a state machine, as mentioned by @UKHeliBob is almost certainly the simplest way to manage it. A state machine can be a simple as a char variable that uses a series of different letters to keep track of the progress through the system - for example Unlock-door Open-door Enter-room Close-door
Just as programs have since 1978, Arduino sketches "start" at main(). Sketches that use the Arduino core, include main.cpp which contains main() which contains the function calls to setup() and loop(). The sequence follows the structure.
Thanks for your thoughtful explanations (and yes, i put the ';' at the end of declaring the functions. oops. )
so consider this code:
void verticalMotorDrop ()
{
verticalMotorDropFinished = LOW; //sets that the motor hasn't finished
//Timer to move the motor for verticalDropDuration seconds (which will be 10minutes)
for ( uint32_t tStart = millis(); (millis() - tStart) < verticalMotorDropDuration; ) {
digitalWrite (verticalMotorDownPin, HIGH);
verticalMotorsMovingDown = HIGH; //motor attached to this pin
unsigned long currentMillis = millis();
hazardLEDFlash(); //also flash hazard lights when motor is moving
}
digitalWrite (verticalMotorDownPin, LOW); //switch motor off at the end of the timer
digitalWrite (hazardLEDYellowPIN, LOW); //switch both LEDs off at the end of the timer
digitalWrite (hazardLEDRedPIN, LOW);
verticalMotorDropFinished = HIGH; //sets that the motor has finished
}
void doTheThingBeforeTheVerticalMotor()
{
doTheThingBeforeTheVerticalMotorIsFinished = LOW;
do some Stuff that happens really quickly
doTheThingBeforeTheVerticalMotorIsFinished = HIGH;
}
void doSomeStuffAfterTheVerticalMotorDrop()
{
doSomeStuffAfterTheVerticalMotorDropFinished = LOW;
do some more stuff
doSomeStuffAfterTheVerticalMotorDropFinished = HIGH;
}
void loop() {
//pushbutton code to start doTheThingBeforeTheVerticalMotorIsFinished
// will go here to 'start the machine'
if ( doTheThingBeforeTheVerticalMotorIsFinished == HIGH)
{
verticalMotorDrop();
}
if (verticalMotorDropFinished = HIGH)
{
doSomeStuffAfterTheVerticalMotorDrop();
}
}
in this code, i want the motor to be active for up to 10mins and i've put it into a for loop.
for each function i've put a state that is low whilst it is running and high when it is finished.
in the main loop, i've put an 'if' statement asking the loop to only call the next function when the previous one has finished.
My understanding though is that with this motor code, will the code be tied up in the 'for' loop for 10minutes whilst the motor is running so that i can't check if a pause button has been activated?
or will the code do one pass through the 'for' loop and then run through the main void loop, repeat, do another pass through the for loop etc?
a lot of the processes in the machine (it's a physical machine) will take a fair amount of time from a few seconds to 10minutes. What is the best guide to code this and still have responsiveness in the void loop?
anandmak:
My understanding though is that with this motor code, will the code be tied up in the 'for' loop for 10minutes whilst the motor is running so that i can't check if a pause button has been activated?
That's not the right way to think about it. You should start the motor running and then check regularly if the 10 minutes has completed and only then go back and turn the motor off.
Think about how you use the kitchen clock to time the cooking of a chicken. You don't sit watching the oven for 90 minutes (or whatever). You note the time the chicken goes in and then you get on with cleaning the bedrooms. Every so often you check the time to see if the chicken is cooked. You might even get time to do the crossword in the morning paper.
So, I am looking at your code and trying to figure out why you have it broken into before during and after. If everything runs sequentially from a to b to c etc. there is no need for structuring it into function calls. Just put everything in order from a to z in the main loop. That's not the case, obviously, as you want to do multiple things, some things while also running a motor and nothing while the pause button is pressed. To this end, it is better to organize your code into functions that group things together. Although the following code doesn't do everything you want to do, I hope it will serve as a starting point for you, at least with the start button, the pause button, the motor and flashing (and steady) lights. I hope you can see the way I've structured it and expand this to complete your project.
/* Start and Pause are momentary push-buttons that make contact with ground; 10K pull-up resistors on each button pull pin to same 5 volt supply. Lights and
relay share same 5 volt supply and ground. They are switched on the low side, with LOW being the on state. Green light is steady on when motor is not in
the middle of a cycle and pause is off. Yellow light is steady on when motor is not in the middle of a cycle and pause is on. Yellow light flashes when
motor is in the middle of a cycle and pause is on. Red light flashes when motor is in the middle of a cycle and pause is off.
*/
const byte StartButtonPin = 2;
const byte PauseButtonPin = 3;
const byte MotorRelayPin = 5;
const byte GreenLightPin = 6;
const byte YellowLightPin = 7;
const byte RedLightPin = 8;
int lastStartButtonState;
unsigned long startButtonDebounceTimestamp;
byte startFlag = 0;
int lastPauseButtonState;
unsigned long pauseButtonDebounceTimestamp;
byte pauseFlag = 0;
unsigned long motorTimestamp;
unsigned long cycleTime;
byte runningFlag = 0;
unsigned long flashLightTimestamp;
void setup() {
pinMode(StartButtonPin, INPUT_PULLUP);//although redundant, internal resistor offers a bit more protection to the pin.
pinMode(PauseButtonPin, INPUT_PULLUP);
pinMode(MotorRelayPin, OUTPUT);
digitalWrite(MotorRelayPin, HIGH);// motor starts off
pinMode(GreenLightPin, OUTPUT);
digitalWrite(GreenLightPin, LOW);// green light starts on
pinMode(YellowLightPin, OUTPUT);
digitalWrite(YellowLightPin, HIGH);
pinMode(RedLightPin, OUTPUT);
digitalWrite(RedLightPin, HIGH);
Serial.begin(9600);
delay(2000);//2 seconds to let everything stabilize
Serial.println("Ready");
}
void loop() {
pollButtons();
manageMotor();
manageLights();
}
void pollButtons() {
int startButtonState = digitalRead(StartButtonPin);
if (startButtonState != lastStartButtonState && startButtonState == LOW) {
if (millis() - startButtonDebounceTimestamp >= 50UL) {// 50UL = 50ms = time for debounce
startFlag = 1;
lastStartButtonState = startButtonState;
startButtonDebounceTimestamp = millis();
}
}
else lastStartButtonState = startButtonState;
int pauseButtonState = digitalRead(StartButtonPin);
if (pauseButtonState != lastPauseButtonState && pauseButtonState == LOW) {
if (millis() - pauseButtonDebounceTimestamp >= 50UL) {// 50UL = 50ms = time for debounce
if (pauseFlag == 0) {
digitalWrite(MotorRelayPin, HIGH);
Serial.println("Pause...");
pauseFlag = 1;
if (runningFlag == 1) {
unsigned long elapsedTime = millis() - motorTimestamp;
if (elapsedTime >= cycleTime) runningFlag = 0;
else cycleTime -= elapsedTime;
}
}
else {// pauseFlag == 1
pauseFlag = 0;
if (runningFlag == 1) {
digitalWrite(MotorRelayPin, LOW);
Serial.println("Resuming");
motorTimestamp = millis();
}
}
lastPauseButtonState = pauseButtonState;
pauseButtonDebounceTimestamp = millis();
}
}
else lastPauseButtonState = pauseButtonState;
}
void manageMotor() {
if (startFlag == 1 && runningFlag == 0) {
digitalWrite(MotorRelayPin, LOW);
Serial.println("Running");
motorTimestamp = millis();
runningFlag = 1;
startFlag = 0;
cycleTime = 600000UL;// 10 minutes in milliseconds
}
else startFlag = 0;
if (runningFlag == 1 && pauseFlag == 0) {
if (millis() - motorTimestamp >= cycleTime) {
digitalWrite(MotorRelayPin, HIGH);
Serial.println("Cycle Complete");
runningFlag = 0;
}
}
}
void manageLights() {
if (runningFlag == 1 && pauseFlag == 0) {
flashLight(RedLightPin);
digitalWrite(GreenLightPin, HIGH);
digitalWrite(YellowLightPin, HIGH);
}
else if (runningFlag == 1 && pauseFlag == 1) {
flashLight(YellowLightPin);
digitalWrite(RedLightPin, HIGH);
digitalWrite(GreenLightPin, HIGH);
}
else if (runningFlag == 0 && pauseFlag == 1) {
digitalWrite(YellowLightPin, LOW);
digitalWrite(RedLightPin, HIGH);
digitalWrite(GreenLightPin, HIGH);
}
else {// runningFlag == 0 && pauseFlag == 0
digitalWrite(GreenLightPin, LOW);
digitalWrite(RedLightPin, HIGH);
digitalWrite(YellowLightPin, HIGH);
}
}
void flashLight(byte light) {
if (millis() - flashLightTimestamp >= 500UL) {// 1/2 second on 1/2 second off
digitalWrite(light, !digitalRead(light));
flashLightTimestamp = millis();
}
}