I am new to arduino. I hope someone can help me with this problem. I have looked for days over the web trying to find out how to create sequence using non blocking code so that the ISR button can work. However, different answers was confusing even more, I have tried "if" loop but that messes up the sequence of the activation of the digital output. Things randomly switches on unexpectedly. I fell in to this vicious cycle of confusion.
The sequence work with delay() and but the interrupt button fails to acknowledge due to delay in the main loop. How do I turn this to a none blocking code or something that I can understand in simple programming terms. May thanks.
// Digital input switches
const int elt1 = 31;
const int elt2 = 32;
const int el1 = 33;
const int el2 = 34;
const int el3 = 35;
const int el4 = 36;
const int res = 37;
const int push_button1 = 53;
const int motor_brake = 52;
const int power_led = 51;
// global variables
volatile int button_flag;
int last_button1_state = 0;
int current_button1_state;
unsigned long int current_time = 0;
unsigned long int previous_time = 0;
unsigned long int counter = 0;
unsigned long int interval = 1000;
void setup() {
// setup pin modes
pinMode(push_button1, INPUT); // Push Button for enabling / dissabling programme
pinMode(elt1, OUTPUT); // Door Enable Switch (E1)
pinMode(elt2, OUTPUT); // E2 switch active high
pinMode(el1, OUTPUT); // DLS switch (E3)
pinMode(el2, OUTPUT); // DCS switch (E4)
pinMode(el3, OUTPUT);
pinMode(el4, OUTPUT);
pinMode(res, OUTPUT);
pinMode(power_led, OUTPUT);
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(push_button1), ISR_button, RISING);
current_button1_state = digitalRead(push_button1);
//while(!Serial); // wait for the serial connection
}
void loop() {
// put your main code here, to run repeatedly:
if(current_button1_state == 0 && button_flag){
if(button_flag == HIGH){
last_button1_state = button_flag;
Serial.print("Cycle count: ");
Serial.println(counter);
delay(5000); // On power up allow the motor to run for 5 sec
digitalWrite(motor_brake, HIGH); // activate motor brake
delay(4000); // Apply motor brake for 4 sec
digitalWrite(motor_brake, LOW); // de-activate motor brake
delay(3000); // wait 3 seconds before the next seuence
digitalWrite(el4, HIGH); // toggle open door switch for 2 sec
delay(2000); // wait 2 sec
digitalWrite(el4, LOW); // set EL4 low
delay(4000);
digitalWrite(motor_brake, HIGH); // activate motor brake
delay(4000);
digitalWrite(motor_brake, LOW); // de-activate motor brake
delay(2000);
digitalWrite(el2, HIGH); // activate EL2
delay(13000); // wait 13 sec for auto close
digitalWrite(el2, LOW);
counter++;
} else {
current_button1_state;
}
} else if (last_button1_state == 1 && button_flag == 0) {
// Do something else in this block
current_time = millis();
while(current_time - previous_time >= interval){
Serial.println("Button Deactive");
digitalWrite(power_led, button_flag);
previous_time = current_time;
last_button1_state = button_flag;
}
//delay(1000);
}
}
// Call function when switch is pressed.
void ISR_button(){
if(current_button1_state == LOW){
button_flag = !button_flag;
}
}
So the wanted functionality is you have a constant sequence of steps switching some IO-pins HIGH/LOW after different periods of time.
and whenever you press the button stop the sequence
This is best coded using lines of code that build a so called state-machine
state 1: wait 5000 milliseconds have passed by
state 2: switch motorbrake HIGH
state 3: wait 4000 milliseconds have passed by
state 4: switch switch motor-brake LOW
state 5: wait until 3000 milliseconds have passed by
state 6: switch e14 HIGH
state 7: wait 2000 milliseconds have passed by
state 8: switch e14 LOW
state 9: wait 4000 milliseconds have passed by
state 10: switch motorbrake HIGH
state 11: wait 4000 milliseconds have passed by
state 12: switch switch motor-brake LOW
state 13: wait 2000 milliseconds have passed by
state 14: switch e12 HIGH
state 15: wait 13000 milliseconds have passed by
state 16: switch e12 LOW and increase variable counter by one
writing this code uses the switch-case-break; statement
the break; is very important to make it work as intended
In your case The conditions to proceed to the next state is
either immitiately change to next state
or
change to the next state after a number of milliseconds have passed by
I perfectly understand the delay() is blocking the rest of the operation as it goes down the sequences. I just do not know how the go on about programming an alternative routine so that it can also acknowledge the button activation to enable the jump in to a section of the routine. I have tried it with "while" loop also and it did the same thing.
The push button switch is tied to input low by 10K resistor and going through a hardware de-bouncing. That part works fine as I have tried it with blinking LED and also observed that on serial monitor. The current_button_state is not set at the moment as the button_flag is toggled and currently I am taking the button_flag to control my routine.
Thats interesting, How would I set the case variable? Do I set this as time, e.g if I want 1 sec , do I set first variable as time1 = 1000; when the function returns 1000 the switch case will activate?
The essence is to rethink your strategy COMPLETELY.
Forget about your code being ‘time driven’… reimagine it as ‘event driven’
Things shouldn’t happen after a period of time, they should occur when the passage of certain interval “happens’. This is where a good understanding of millis() timing really makes your life easier.
More work, yes, but things happen when you want them to, not when the delay() wants them to.
inform "I initiate counting" (printing this message only ONCE)
counting up to 10 VERY fast (printing each number)
say "good bye" (printing this message only ONCE)
wait 5 seconds
but not only waiting!
in PARALLEL to the waiting a counter is counting up very fast
in PARALLEL to the waiting print a message once per second
repeat this pattern.
// this demo-program shows how to use the switch-case statement
// to create a functionality as described at the bottom of this file
unsigned long myCounter;
const byte sayHello = 0;
const byte startCounting = 1;
const byte countTo10 = 2;
const byte sayGoodbye = 3;
const byte wait5seconds = 4;
byte myStateVar;
unsigned long waitingTime = 5000;
unsigned long WaitingTimer; // timer variable for non-blocking timing
unsigned long oneSecondTimer; // timer variable for non-blocking timing
void setup() {
Serial.begin(115200);
Serial.print( F("\n Setup-Start \n") );
myCounter = 0;
// initialise state-variable to that state the state-machine
// shall start with
myStateVar = sayHello;
}
// helper-function for easy to use non-blocking timing
boolean TimePeriodIsOver (unsigned long &StartTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - StartTime >= TimePeriod ){
StartTime = currentMillis; // store timestamp when the new interval has started
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // return value false because LESS time than TimePeriod has passed by
}
void myStepChain() {
switch (myStateVar) {
case sayHello:
Serial.println( F("Hello user!") );
myStateVar = startCounting;
break;
case startCounting:
myCounter = 0; // reset counter-variable
Serial.println( F("I start counting very fast") );
myStateVar = countTo10;
break;
case countTo10:
// there is no slowing down through non-blocking timing
// in this state. These lines get executed very fast
// counting up to 10 in less than one MILLIsecond
myCounter++;
Serial.print( F("I'm counting up counter=") );
Serial.println(myCounter);
if (myCounter == 10) {
myStateVar = sayGoodbye;
}
break;
case sayGoodbye:
Serial.println( F("goodbye see you next round in 5 seconds") );
WaitingTimer = millis(); // store timestamp when the waiting STARTS
myStateVar = wait5seconds;
break;
case wait5seconds:
// this condition changes the state after 5000 milliseconds
if ( TimePeriodIsOver(WaitingTimer,5000) ) {
// more time than 5000 milliseconds has passed by
Serial.println( F("5 seconds waited starting new cycle.. ") );
Serial.println();
Serial.println();
myStateVar = sayHello; // reset to starting step to repeat
}
myCounter++; // counts up VERY fast because in a state-machine
// code-execution is always fast
// if you need to execute only from time to time
// this is done by non-blocking timing like coded below
// non-blocking timing:
if ( TimePeriodIsOver(oneSecondTimer,1000) ) {
// more than 1000 milliseconds have passed by
Serial.print( F("one second over") );
Serial.print( F(" value of myCounter=") );
Serial.println(myCounter);
}
break;
}
//delay(100);
}
void loop() {
myStepChain();
}
/* pre-ambel: programming something more complex than
switch LED on wait 1 second switch LED off wait 1 second
requires knowledge. More or less complex knowledge
And this needs a rather big minimum of words to explain
That is the reason why this text has more than two lines
in writing programs it is always a good idea to write down
the functionality of the program in NORMAL WORDS.
This code uses the serial monitor to make visible what the code
is doing. So no additional hardware is required
The functionality of this program is
1. say "Hello" (printing this message only ONCE)
2. inform "I initiate counting" (printing this message only ONCE)
3. counting up to 10 VERY fast (printing each number)
4. say "good bye" (printing this message only ONCE)
5. wait 5 seconds
but not only waiting!
in PARALLEL to the waiting a counter is counting up very fast
in PARALLEL to the waiting print a message once per second
repeat this pattern.
This means the program is doing different things in a defined order
and is able to do a thing A in parallel to a thing B and in parallel
to a thing C
This basic principle can be transferred to things like
- an LED is blinking while a display shows a message
"press button to start"
- a motor is switched on to run while in parallel a LED is blinking
and the motor stops after X seconds
- a motor is switched on to run while in parallel a LED is blinking
and the motor stops if a button is pressed / or a switch is closed
- a heating is switched on starting to heat and in parallel aquire
new temperature-measurings until a certain temperature is reached
then switch of the heating
while in parallel a humifier is switched on to increase humidity
and in parallel new humidity-measurings are aquired until
a certain level is reached and then switch off
where the times it takes do reach the right temperature-level
and humidity-level can be very different
###### everyday analogon
The basic principle behind this is to make a servant watching
the scene and when certain things happen take a short action
servant stay a little aside from the dining table and watch
all guests dining
if somebodies glas is empty go over and refill the glass
keep an eye on the soup-pot if the pot is empty shout to the cook
"bring a new pot"
The servant is changing his focus very quickly from task to task
this could be described as step in (a certain task) step out - repeat
check if a glas is empty
change to
check soup-level
change to
....
in opposite to:
walk over to the soup-pot fix your eyes on the soup-level and if
soup-level is low enough shout for the cook to bring a new pot
This means your program is entering a first "task"
does a single step and then quickly leaves the first "task"
to enter the second "task"
does a single step and then quickly leaves the second "task"
....
the repeating is taken to a higher level
loop() {
task1(); // enter and quickly leave again
task2(); // enter and quickly leave again
task3(); // enter and quickly leave again
...
}
if each task requires multiple steps to be done
this is done by a switch-case-statement
like refilling a glas
- open bottle
- bring bottle into position to pour
- turn neck of the bottle down
- pour wine into the glas checking the level
- if glas is filled
- turn neck of the bottle up
- close bottle
- move back to watching position
after opening the bottle and bring bottle into position
the servant is able to take a quick look to the soup-pot
seeing still enough soup
OK let's just pour the wine
or if soup-pot is empty shouting "cook bring a new pot with soup"
Which means he is entering and leaving a task quickly to do
a small step of another task
That is how multi-tasking programming works
and it is done by using one BIG L----O----O----P
loop() {
task1(); // enter and quickly leave again
task2(); // enter and quickly leave again
task3(); // enter and quickly leave again
...
}
and quickly jumping in/out functions where each function has its
own switch-case-statement to work through sequential steps
that must be done in a defined order
*/
1 event: simply set switch-variable immidiately to a new value
2 event: simply set switch-variable immidiately to a new value
3 event: if counter reaches value 10 set switch-variable to a new value
4 event: if function TimePeriodIsOver(WaitingTimer,5000) returns value true
set switch-variable to a new value
ported to your sequence of steps
state 0: initial state store actual value of millis() in a variable that is then used as the timing variable myWaitTimer = millis();
state 1: check if TimePeriodIsOver(myWaitTimer , 5000) returns true event if TimePeriodIsOver() returns true set switch-variable to state 2:
state 2:
switch motorbrake HIGH "event"
update timer-variable myWaitTimer = millis();
set switch-variable to state 3 (as everything of state2 is done with these 3 lines of code
state 3:
check if TimePeriodIsOver(myWaitTimer , 4000) returns true event if TimePeriodIsOver() returns true set switch-variable to state 4: