Arduino should rename delay() to. GoAwayCoverYourEyesAndCountTo()
It would make things more obvious to noobs and the length would make people not want to type it out and instead use timers
Using "Dragon88" code, I re-arranged few things and I finally have what I wanted! It is a one time sequence:
LED1 ON, delay 1sec, LED2 ON, delay 1 sec, LED3 ON.
If that's all you need to do, you can simplify the code quite a bit. I thought you wanted it to cycle through and repeat while waiting for the button press to turn them off.
i.e.
for (int i = 0; i <= 2; i++) ;For pins 0 through 2
{
digitalWrite(i, HIGH);
delay(1000);
}
Run that when the button is pressed, then just wait for another button press and turn everything off.
Thank you Dragon88, I tried that and it also works. But once again, delay is my enemy. If I press and hold the button down, it rolls over the "delay(200)" button debounce function and the loop goes marry go round. I will have to figure out a different debounce function for the button so I can eliminate "delay()" in that loop.
If you want to be able to hold the button down, there are plenty of ways to do that. You could start a counter, set for 3 seconds equivalent or whatever you want. Then when you check for another button press, also check that counter to see if it has reached zero. You could also flip a boolean and have some future condition that flips it back, i.e. the last LED gets turned on in the sequence. Again, compare against that boolean when checking for a "turn off" button press to see if it should be allowed yet.
For several months I am toying with the idea of writing an article about simple multi tasking with Arduino that will target beginners. Something along these lines:
Each task has two core methods, setup() and loop(). There are several options for implementing tasks, e.g. classes, namespaces, or name prefixes. With the name prefix approach a task may look like
// ----- button task
... some button state variables
// ---- Core functions
void button_setup() { ... }
void button_loop() { ... }
// ---- API functions
boolean button_isPressed() {} // post debouncing
boolean button_consumeClickEvent() { }
boolean button_consumeLongPressEvent() { }
// -----
and then the main will look like
void setup() {
button_setup();
task2_setup();
task3_setup();
}
void setup() {
button_loop();
task2_loop();
task3_loop();
}
Some useful library tasks will be timers, debouncers, leds (on, off, blink, etc), serial line readers, and buttons (with debouncers and event detection), and the examples will include a few state machine based apps such as this one.
I think this can be made simple enough for beginners to understand and follow.
Here are a few examples from a previous project
https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/button.hhttps://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/passive_timer.hhttps://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/byte_debouncer.hhttps://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/action_led.h
For more complex tasks, you'll need a statemachine per task to keep track of what you were doing and what the next step (state) will be...
For more complex tasks, you'll need a statemachine per task to keep track of what you were doing and what the next step (state) will be...
Wouldn't you do the FSM inside each task?
One really simple and nifty thing would be able to schedule tasks for later execution. Maybe have the return code from the "loop()" indicate how many milliseconds to wait until it is activated again. Anybody who returns 0 can just fight it out for whatever CPU cycles are left....
One really simple and nifty thing would be able to schedule tasks for later execution. Maybe have the return code from the "loop()" indicate how many milliseconds to wait until it is activated again. Anybody who returns 0 can just fight it out for whatever CPU cycles are left....
For most beginners programs, a simple timer check in the task's loop () is good enough.
The main point is having those small state machines in the tasks as you pointed out.
Wouldn't you do the FSM inside each task?
I thought I said that
I check the time at the start of each loop and use that to determine if specific Tasks waiting for a certain period are up for execution yet when they come to check.
The state machine in my library is implemented by a switch statement and a private var (hidden behind some macro's). This is one of the nicest ways I was able to discover to do cooperative multi tasking with very low overhead. It allows you to write code like this (template classes):
Task_Begin(Execute)
{
while(true)
{
Task_YieldUntil(DelaysT::Wait(BaseT::getId(), Timeout));
BaseT::OnTimeout();
}
}
Task_End
private:
int _task;
This is a part of the TimeoutTask in my library. The Task_ macros implement the switch statement. DelaysT::Wait is a way to have your task wait for a certain period. All you have to do is call the Execute method (of this class) from the loop() repeatedly. No, that while(true) loop will not hang your program - it's magic!
Wouldn't you do the FSM inside each task?
I thought I said that
I check the time at the start of each loop and use that to determine if specific Tasks waiting for a certain period are up for execution yet when they come to check.
The state machine in my library is implemented by a switch statement and a private var (hidden behind some macro's). This is one of the nicest ways I was able to discover to do cooperative multi tasking with very low overhead. It allows you to write code like this (template classes):
Task_Begin(Execute)
{
while(true)
{
Task_YieldUntil(DelaysT::Wait(BaseT::getId(), Timeout));
BaseT::OnTimeout();
}
}
Task_End
private:
int _task;
This is a part of the TimeoutTask in my library. The Task_ macros implement the switch statement. DelaysT::Wait is a way to have your task wait for a certain period. All you have to do is call the Execute method (of this class) from the loop() repeatedly. No, that while(true) loop will not hang your program - it's magic!
Can you explain the semantic of this code? (it doesn't matter how it's implemented).
Can the task consider other inputs or events while it is delaying?
DelayT::Wait returns a bool to indicate if the timeout specified has expired. In the main loop DelaysT is fed the current (delta) time. It maintains a list of waiting tasks (by Id) and counts each task down.
The Task_YieldUntil will exit the Execute method if the wait is not over yet. If the wait is over, the Execute method continues and calls the OnTimeout() method implemented by the base class (BaseT is a template parameter used as base class).
Then the while(true) loop takes it all to the top and the wait starts again.
The Execute method is called in the loop() function where other Task-methods can be called in sequence. Each task-method will perform a short/small piece of code and the exit again from its method (using the Task_Yield macros).
You could have multiple Timeout Tasks all doing different Timeout values blinking leds on different pins.
More?
So, I had more time to play with the code to take the "delay()" completely (except 1ms at the end) out of "debouce" function and replaced it with debounce function I used from Jeremy's book. That works exactly as I want it. The button will execute(change the statement) just one time when pressed and held down. Unlike it was going in circles before.
I might give it more tweaks later, time permitted.
Thank you guys for helping me.
#define redLED 0 //define IO
#define greenLED 1
#define blueLED 2
#define BUTTON 3
boolean lightsOn = false; // state variables
boolean lastButton = LOW; // Variable containing the previous button state.
boolean currentButton = LOW; // Variable containing the current button state.
int whichLED = 0;
int counter = 0; // main counter
void setup()
{
pinMode(redLED, OUTPUT); // set up IO
pinMode(greenLED, OUTPUT);
pinMode(blueLED, OUTPUT);
pinMode(BUTTON, INPUT);
}
/*
* Debouncing function
* Pass it to previous button state,
* and get back the current debounced button state.
*/
boolean debounce (boolean last)
{
boolean current = digitalRead (BUTTON); // Read the button state
if (last != current) // if it's different...
{
delay (10); // wait 10ms
current = digitalRead (BUTTON); // read it again
return current; // return the current value.
}
}
void loop ()
{
currentButton = debounce (lastButton); // read debounced state
if (lastButton == LOW && currentButton == HIGH) // if it was pressed...
{
lightsOn = ! lightsOn;
} // toggle the LED value
lastButton = currentButton; // reset button value
digitalWrite (whichLED, lightsOn); // change the LED state
if (lightsOn == false) // turn LED off and clean up
{
counter = 0;
digitalWrite(redLED, LOW);
digitalWrite(greenLED, LOW);
digitalWrite(blueLED, LOW);
whichLED = 0;
} else { // Or if we are activating LEDs
digitalWrite(whichLED, HIGH); // Turn first LED on to start
}
if (lightsOn == true) // Manage LED state
{
counter++; // Increment counter
if (counter >= 1000) // ~ 1 second has passed
{ // So increment LED state
counter = 0;
digitalWrite(whichLED, HIGH); // Don't forget to turn old LED off
whichLED++;
if (whichLED >= 3) // Don't let whichLED go to invalid state
whichLED = 3;
digitalWrite(whichLED, HIGH); // Turn new LED on
}
}
delay(1); // 1 ms delay for 1000 count = ~ 1 second
}
For several months I am toying with the idea of writing an article about simple multi tasking with Arduino that will target beginners. Something along these lines:
Each task has two core methods, setup() and loop(). There are several options for implementing tasks, e.g. classes, namespaces, or name prefixes. With the name prefix approach a task may look like
// ----- button task
... some button state variables
// ---- Core functions
void button_setup() { ... }
void button_loop() { ... }
// ---- API functions
boolean button_isPressed() {} // post debouncing
boolean button_consumeClickEvent() { }
boolean button_consumeLongPressEvent() { }
// -----
and then the main will look like
void setup() {
button_setup();
task2_setup();
task3_setup();
}
void setup() {
button_loop();
task2_loop();
task3_loop();
}
Some useful library tasks will be timers, debouncers, leds (on, off, blink, etc), serial line readers, and buttons (with debouncers and event detection), and the examples will include a few state machine based apps such as this one.
I think this can be made simple enough for beginners to understand and follow.
Here are a few examples from a previous project
https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/button.h
https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/passive_timer.h
https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/byte_debouncer.h
https://github.com/zapta/power-monitors/blob/master/pmon_3v8/arduino/action_led.h
I think the most difficult part for beginners it to understand the raw basic structure.
When do I use } ? Do I have to use a "{" before "for" or "else" ?......
I can picture the task in my head but even though I write it down on piece of paper in a simple way before I begin to write the actual code, it won't help me much later anyway. How do I even begin when I don't know things like control structures, data types and further syntax? Some of those I have never seen before and have no clue what they mean.
Now, there are conversions, variable scope & qualifiers.... I don't even want to go further. I couldn't compile the code because of one crazy "}"
There are so many combinations, it's just not easy for beginner. It took me a while just to wrap my head around the debounce function.....
What works the best for me are examples, so I can see the actual working structure that can be modified.
But that is not programming any more, is it ?
I firmly believe in learning programming with the aid of examples. Imagine trying to learn how to drive without ever having seen a car driven by someone else. It would seem completely strange and perhaps impossible, because you don't know the process AND you don't know what the end result should look like. But with an example, you have a context in your mind for what you need to do. Granted, after a certain point you should crossover and start creating original code on your own, but the first step is learning the building blocks and mastering the basics from good instruction and good examples.
But before you can really drive, you have to learn the traffic rules - same with coding: you have to know the syntax of the language of choice. If you still struggle with the '{' and the like, you need to learn that. If that stuff is automatic (enough) than you can focus on the parts that matter (the logic you wrote down on a piece of paper)...
it's that darned single missing semicolon or curly brace...
nested deep inside a hundred lines of new code!
Comment out huge chunks until it jumps out at you as the most obvious error in the world!!
There are so many combinations, it's just not easy for beginner. It took me a while just to wrap my head around the debounce function.....
There is some kerning that you must go through but if different functionalities are decoupled and separated from each other is easier to understand. This is actually how the pros can handle complex programs, by breaking them to small and simple chunks.
The button read/denounce function is a good example for such separation. BTW, I wouldn't even require the 'last' argument.
Anyway, good job on being (almost) delay free. Stay clean
it's that darned single missing semicolon or curly brace...
nested deep inside a hundred lines of new code!
Comment out huge chunks until it jumps out at you as the most obvious error in the world!!
Some tools are better than other providing useful error messages.
Guys, I need your help again, please.
After I successfully implemented LM35 temperature sensors to my code for overheat shut down , I am kind of stuck on one more task I would like to add. When LM35 temp sensor shuts down all LEDs (this works OK), I want to disable the push BUTTON if (val > LOWER_BOUND). I tried to add some code at the very bottom but no luck. As it is right now with "do" and "while", the button gets disabled but never gets enabled again.
#define redLED 0 //define IO
#define greenLED 1
#define blueLED 2
#define BUTTON 4
#define TEMP 3
const int LOWER_BOUND = 53;
const int UPPER_BOUND = 56;
int val = 0;
boolean lightsOn = false; // state variables
boolean lastButton = LOW; // Variable containing the previous button state.
boolean currentButton = LOW; // Variable containing the current button state.
int whichLED = 0;
int counter = 0; // main counter
void setup()
{
pinMode(redLED, OUTPUT); // set up IO
pinMode(greenLED, OUTPUT);
pinMode(blueLED, OUTPUT);
pinMode(BUTTON, INPUT);
pinMode (TEMP, INPUT);
}
/*
* Debouncing function
* Pass it to previous button state,
* and get back the current debounced button state.
*/
boolean debounce (boolean last)
{
boolean current = digitalRead (BUTTON); // Read the button state
if (last != current) // if it's different...
{
delay (10); // wait 10ms
current = digitalRead (BUTTON); // read it again
return current; // return the current value.
}
}
void loop ()
{
currentButton = debounce (lastButton); // read debounced state
if (lastButton == LOW && currentButton == HIGH) // if it was pressed...
{
lightsOn = ! lightsOn;
} // toggle the LED value
lastButton = currentButton; // reset button value
digitalWrite (whichLED, lightsOn); // change the LED state
if (lightsOn == false) // turn LED off and clean up
{
counter = 0;
digitalWrite(redLED, LOW);
digitalWrite(greenLED, LOW);
digitalWrite(blueLED, LOW);
whichLED = 0;
}
else
{ // Or if we are activating LEDs
digitalWrite(whichLED, HIGH); // Turn first LED on to start
}
if (lightsOn == true) // Manage LED state
{
counter++; // Increment counter
if (counter >= 1000) // ~ 1 second has passed
{ // So increment LED state
counter = 0;
digitalWrite(whichLED, HIGH); // Don't forget to turn old LED off
whichLED++;
if (whichLED > 2) // Don't let whichLED go to invalid state
whichLED = 2;
}
}
delay(1); // 1 ms delay for 1000 count = ~ 1 second
val=analogRead (TEMP);
if (val >= UPPER_BOUND)
{
if (lightsOn)
{
counter = 0;
digitalWrite (redLED, LOW);
digitalWrite (greenLED, LOW);
digitalWrite (blueLED, LOW);
lightsOn = false;
}
}
else
{
lightsOn == true; // It is good up to here
}
val=analogRead (TEMP); //I am trying to disable button through "! lightsOn"
if (val > UPPER_BOUND)
{
if (! lightsOn)
{
do
{
lightsOn = false; // "lightsOn" goes to false state and it stays false
}
while (val > LOWER_BOUND);
}
}
else // When the temperature drops under LOWER_BOUND,
//lightsOn should be true, but it stays false and button
{ // is hence disabled
lightsOn == true;
}
}
You could just stop calling the main-good-loop and call a different main-bad-loop...?
The main-bad-loop would do nothing but check for ok-temps...?
As it is right now with "do" and "while", the button gets disabled but never gets enabled again.
you have to read the value again to see if it has gone below the threshold.
I assume this should work. I dont know though, when I copied it to text editor, the curlies wound up all over the place.
#define redLED 0 //define IO
#define greenLED 1
#define blueLED 2
#define BUTTON 4
#define TEMP 3
const int LOWER_BOUND = 53;
const int UPPER_BOUND = 56;
int val = 0;
boolean lightsOn = false; // state variables
boolean lastButton = LOW; // Variable containing the previous button state.
boolean currentButton = LOW; // Variable containing the current button state.
int whichLED = 0;
int counter = 0; // main counter
void setup()
{
pinMode(redLED, OUTPUT); // set up IO
pinMode(greenLED, OUTPUT);
pinMode(blueLED, OUTPUT);
pinMode(BUTTON, INPUT);
pinMode (TEMP, INPUT);
}
/*
* Debouncing function
* Pass it to previous button state,
* and get back the current debounced button state.
*/
boolean debounce (boolean last)
{
boolean current = digitalRead (BUTTON); // Read the button state
if (last != current) // if it's different...
{
delay (10); // wait 10ms
current = digitalRead (BUTTON); // read it again
return current; // return the current value.
}
}
void loop ()
{
currentButton = debounce (lastButton); // read debounced state
if (lastButton == LOW && currentButton == HIGH) // if it was pressed...
{
lightsOn = ! lightsOn;
} // toggle the LED value
lastButton = currentButton; // reset button value
digitalWrite (whichLED, lightsOn); // change the LED state
if (lightsOn == false) // turn LED off and clean up
{
counter = 0;
digitalWrite(redLED, LOW);
digitalWrite(greenLED, LOW);
digitalWrite(blueLED, LOW);
whichLED = 0;
}
else
{ // Or if we are activating LEDs
digitalWrite(whichLED, HIGH); // Turn first LED on to start
}
if (lightsOn == true) // Manage LED state
{
counter++; // Increment counter
if (counter >= 1000) // ~ 1 second has passed
{ // So increment LED state
counter = 0;
digitalWrite(whichLED, HIGH); // Don't forget to turn old LED off
whichLED++;
if (whichLED > 2) // Don't let whichLED go to invalid state
whichLED = 2;
}
}
delay(1); // 1 ms delay for 1000 count = ~ 1 second
val=analogRead (TEMP);
if (val >= UPPER_BOUND)
{
if (lightsOn)
{
counter = 0;
digitalWrite (redLED, LOW);
digitalWrite (greenLED, LOW);
digitalWrite (blueLED, LOW);
lightsOn = false;
}
}
else
{
lightsOn == true; // It is good up to here
}
val=analogRead (TEMP); //I am trying to disable button through "! lightsOn"
if (val > UPPER_BOUND)
{
if (! lightsOn)
{
do
{
lightsOn = false; // "lightsOn" goes to false state and it stays false
val=analogRead (TEMP);
}
while (val > LOWER_BOUND);
}
}
else // When the temperature drops under LOWER_BOUND,
//lightsOn should be true, but it stays false and button
{ // is hence disabled
lightsOn == true;
}
}
As it is right now with "do" and "while", the button gets disabled but never gets enabled again.
you have to read the value again to see if it has gone below the threshold.
I assume this should work. I dont know though, when I copied it to text editor, the curlies wound up all over the place.
#define redLED 0 //define IO
#define greenLED 1
#define blueLED 2
#define BUTTON 4
#define TEMP 3
const int LOWER_BOUND = 53;
const int UPPER_BOUND = 56;
int val = 0;
boolean lightsOn = false; // state variables
boolean lastButton = LOW; // Variable containing the previous button state.
boolean currentButton = LOW; // Variable containing the current button state.
int whichLED = 0;
int counter = 0; // main counter
void setup()
{
pinMode(redLED, OUTPUT); // set up IO
pinMode(greenLED, OUTPUT);
pinMode(blueLED, OUTPUT);
pinMode(BUTTON, INPUT);
pinMode (TEMP, INPUT);
}
/*
* Debouncing function
* Pass it to previous button state,
* and get back the current debounced button state.
*/
boolean debounce (boolean last)
{
boolean current = digitalRead (BUTTON); // Read the button state
if (last != current) // if it's different...
{
delay (10); // wait 10ms
current = digitalRead (BUTTON); // read it again
return current; // return the current value.
}
}
void loop ()
{
currentButton = debounce (lastButton); // read debounced state
if (lastButton == LOW && currentButton == HIGH) // if it was pressed...
{
lightsOn = ! lightsOn;
} // toggle the LED value
lastButton = currentButton; // reset button value
digitalWrite (whichLED, lightsOn); // change the LED state
if (lightsOn == false) // turn LED off and clean up
{
counter = 0;
digitalWrite(redLED, LOW);
digitalWrite(greenLED, LOW);
digitalWrite(blueLED, LOW);
whichLED = 0;
}
else
{ // Or if we are activating LEDs
digitalWrite(whichLED, HIGH); // Turn first LED on to start
}
if (lightsOn == true) // Manage LED state
{
counter++; // Increment counter
if (counter >= 1000) // ~ 1 second has passed
{ // So increment LED state
counter = 0;
digitalWrite(whichLED, HIGH); // Don't forget to turn old LED off
whichLED++;
if (whichLED > 2) // Don't let whichLED go to invalid state
whichLED = 2;
}
}
delay(1); // 1 ms delay for 1000 count = ~ 1 second
val=analogRead (TEMP);
if (val >= UPPER_BOUND)
{
if (lightsOn)
{
counter = 0;
digitalWrite (redLED, LOW);
digitalWrite (greenLED, LOW);
digitalWrite (blueLED, LOW);
lightsOn = false;
}
}
else
{
lightsOn == true; // It is good up to here
}
val=analogRead (TEMP); //I am trying to disable button through "! lightsOn"
if (val > UPPER_BOUND)
{
if (! lightsOn)
{
do
{
lightsOn = false; // "lightsOn" goes to false state and it stays false
val=analogRead (TEMP);
}
while (val > LOWER_BOUND);
}
}
else // When the temperature drops under LOWER_BOUND,
//lightsOn should be true, but it stays false and button
{ // is hence disabled
lightsOn == true;
}
}
YES!
It works!
It is good to know that I was in the right ball park with "do" and "while". I know there are many many other ways to do it. I was looking yesterday on Arduino reference page and scratching my head as I had no clue what to use from control structures and further syntax.
Many thanks.
You try to weave the lightsOn logic through the button and led logic. I don't think you have to do that.
Consider this:
void loop()
{
int temp = readDigital(TEMP);
if (temp < MAX_THRESHOLD)
{
ScanButtonAndDriveLeds();
}
else
{
DisableOutput();
WaitForTempToLower();
}
}
The ScanButtonAndDriveLeds() function is the code you had before you tried to incorporate the temp logic. DisableOutput is the code that switches of the Leds and WaitForTempToLower() performs a loop that reads the temp value and only exists if its below the Max (or any other value).
I think your code will be more readable and maintainable. Remember - it is likely that you will not understand your own code if you revisit it after a couple of months (spending doing something else)...
[2c]
Thanks, Yes, probably. The code will need revision that is for sure because I will be using ATtiny84 instead of 85, adding another temp sensor and using one more pin for LED status ON/OFF/overheat.
Thank you for your suggestion.
Congratulations on getting it working the way you wanted it to!
This was a great example of a 'rookie' asking and receiving - with the appropriate amount of learning on the way through!
Lesson 1: Avoid delay() at all costs, unless you're prepared for the consequences!
Lesson 2: Ask the right questions, don't skimp on detail, or you'll get the wrong answer!
You should now have a lot better understanding of how the code and flow operates and will be able to make magic with a micro!