Hello all, I’m new to the Arduino world so forgive my ignorance.
I’m trying to build a program that will have 5 led’s flash if a condition is true for 3 seconds.
I know I need to incorporate the Millis function but am not entirely sure how to store the moment an event happens and compare it to time elapsed so than a condition becomes true.
Sorry if that’s confusing. Imagine how confused I am haha…
Hello teqlove86
Welcome to the worldbest Arduino forum ever.
Post your sketch, well formated, with well-tempered comments and in so called
code tags "< code >" to see how we can help.
Have a nice day and enjoy coding in C++.
Welcome to the forum
Save the value of millis() when the start action occurs then, if the condition is still true compare the current value of millis() with the saved value each time through loop(). If the difference is longer than the required period then run whatever code that you require
look this over
it captures a timeStamp and set the start flag when a button is pressed. it resets the start flag when the button is released. if start remains set for 3 secs it prints
const byte ButtonPin = A1;
byte butState;
boolean start;
unsigned long msecStart;
void
loop (void)
{
unsigned long msec = millis();
if (start && msec - msecStart > 3000) {
Serial.println ("timer expire");
start = false;
}
byte but = digitalRead (ButtonPin);
if (butState != but) { // state change
butState = but;
delay (20); // debounce
if (LOW == but) { // pressed
Serial.println ("button pressed");
msecStart = msec;
start = true;
}
else {
start = false;
}
}
}
void
setup (void)
{
Serial.begin (9600);
pinMode (ButtonPin, INPUT_PULLUP);
butState = digitalRead (ButtonPin);
}
Learning why to create timed events, and how, will allow you to interact with hardware. Here is a simulation that lets you start one event (input), the event occurs (process), then you get feedback (output) about the event. See how your project might also use I.P.O.
Hi @teqlove86
welcome to the arduino-forum.
Very normal reaction to be confused with these poor written and poorly explained examples given by the Arduino-IDE (and most tutorials) .
The code user gcjr has posted has zero explanations and non-selfexplaining names. Very likely to be confusing for a newcomer.
To understand how to apply non-blocking timing based on millis().
Read this tutorial. It explains the basic principle with an everyday example.
best regards Stefan
Is that intended to blink the LED 300 ms once per button push?
I see the LED on for all the time the button is pressed, and a report of something enduring for 300 ms.
My slow brain and fat fingers seem to press longer than 300 ms. The code does hold the LED on for the correct duration if you stab the button.
I added !ledState to the pushbutton test. But then it oscillates, relaunching the 300 ms blink period.
Placing a long delay where the LED is turned off shows the 300 ms blink, but then… keeping your finger in the button will relaunch the timed period. Besides wrecking your otherwise delay-free code.
Now I think you need to run two state variables, one so you can turn off the LED, and another so only getting off the button allows the blink to get launched again.
Or I have missed the point of you demo. If so, appy polly loggies.
@teqlove86 - IPO, yes. It really is a key structuring method for processes of many kinds.
See Wikipedia on IPO
a7
Please continue relentlessly self-promoting yourself in these fora. I am sure someone will benefit from all the work you put into your various tutorials.
But stifle the gratuitous observations on the contributions of others, who you seem to view as competitors… at best it is unseemly. It is not productive, and amounts to saying "don't listen to her, listen to me! I alone am the one who can make this easy for you!"
Everyone learns differently and it is good to have so many explanations all over these fora and the internet. Some ppl, I would include myself, need to vector in on something from multiple perspectives.
BTW it's "piece of cake", not "peace of cake". You might want to avoid attempting to use idioms in your not-first language.
TIA
a7
???
real newbee: ???????
I agree: explanations of what the code is doing
"Measure time that a condition is true to then trigger and action"
I find it easier to "Record time condition was last false. The time true is current time minus recorded time.
static unsigned long LastTimeConditionFalse = millis();
if (condition == false)
LastTimeConditionFalse = millis();
if (millis - LastTimeConditionFalse > Timeout)
do_action();
The intent was: Press the button. LED on for 300ms. LED off. It then showed button press time as LED ON time, LED OFF time and calculated the difference.
[edit]
@alto777 - I did not make the button watch for "long-press"
Hi @teqlove86,
I hope it's not a bad sign that you haven't appeared since your first post ![]()
You decribed the task like this
I’m trying to build a program that will have 5 led’s flash if a condition is true for 3 seconds.
This will usually be understood like this
- (Version 1) If a condition is true for 3 seconds then five leds shall start flashing.
However it might be that you wanted to say
- (Version 2) If a condition is true then five leds shall start flashing for three seconds.
In both cases certain additional information is missing.
Version 1:
- Do you mean exactly three seconds (on the msec???) or in minimum?
- What happens after the first event, shall the leds stop flashing
- after a certain time
- when the condition becomes false again
- or ... ?
Version 2:
- What happens if the condition is still true after the 3 seconds flashing
- will the flashing restart?
- does the condition have to become false again before further action?
Would you mind clarifying your objective(s)?
No, no you did not.
In fact here, "long press" is defined by this, I think
delay(150); // debounce the button press
which makes my typical button press long. If you press longer than that, the time the LED is on is not correct, but the priniting you do will cheerfully lie about that.
The LED is not even on for exactly the time constant after the button is released, which is what my first theory was just reading the code. I haven't figured out the exact relationship between the button press duration, the observed LED on duration, and the delay(150) you use for debouncing, this despite working the code and using the wokwi logic analyser to see exact what is happening. And I probably never will.
If you want an LED to go on for N milliseconds, you have to detect the button going down, and only at that state change of the button launch your timed LED. Which button will probably benefit from some real debouncing, or you could cheat by using a wokwi bounce-free pushbutton.
Then the LED enjoys its own timing mechanism, and it won't matter if you stab the button or hold it long after the LED is extinguished.
I tried a few quick fixes and arrived at nothing that did not closely resemble that algorithm. Two separate things that need to be tracked and accounted for. Button and LED.
a7
The printing reflects the values during the simulation. Even though "300" is compared for the code to stop the counter (and LED), 301 is the result, which could be made to be 300 by changing the 300.
Yes, I see.
This comment
delay(150); // debounce the button press
is misleading - it is actually the time the button must be ignored, and fixes the time at which a short press becomes long.
Excusing this on the basis of not having looked for long presses is correct, but 150 ms is a very short threshold for calling a press long. It may be an age thing. I timed some casual button pressing - in 20 presses, only 5 were shorter that 150 milliseconds. The average was 172 milliseconds.
Some button presses were 300 - 400 milliseconds. Getting consistent presses of less than 150 milliseconds duration felt like I was having to deliberately stab the button.
Without trying too hard to break your algorithm, it was easy to see it actually lighting the LED for 300 and 450 milliseconds. Clicks that yet did not seem at all long yielded 600 milliseconds on time. A button press of less than 500 ms.
A fix would be to increase the period of ignoring the button. But you can't delay for more than 300 ms or the nice millis() timer won't get a chance to see that the time has come to extinguish the LED.
I do not know what is, or even if ppl agree what makes for, a long press. I will argue that 150 milliseconds is very too short.
I did measured the LED on time in code.
unsigned long myTimer;
void checkLEDOn()
{
if (digitalRead(ledPin) == LOW) myTimer = millis();
}
void checkLEDOff()
{
myTimer = millis() - myTimer;
}
void printLEDTime()
{
Serial.print(" LED on "); Serial.println(myTimer);
}
I call checkLEDOn() right before you turn on the LED (which might already be on), and checkLEDOff() after you turn it off.
I call printLEDTime() after you print your calculated duration. You consistently report ~300 ms.
My thinking is that since it solves the problem and is something everyone should be able to do, or figure out, adding real debouncing and state change detection would make your demonstration more valuable.
TBH I only then noticed the delay() debounce somewhat late. I apologize for sharing half-baked theories. I should have rested, recovered and run this to ground, and only then presented the conclusions I am currently more confident about.
a7
As you point out, the delay() is not a good choice for a ringing button press. The "Hello, World!" of Arduino (blink.ino) uses delay() as an introduction only, not intending for its use to choke every sketch presented to the forum. The user soon discovers delay() is not desired more times than it is. In this code. I intentionally used delay() to put the focus on the "300" timer and not have lines of state change and timing clouding the view. The user will decide if timing-based debounce is right for their code.
(off topic - I read in some .h file the macro "_delay_ms" (or something like that) is a non-blocking delay... am I reading that correctly? I have no reference, as it was months ago, but only remembered now)
It is hard to introduce more than one thing at a time. As hard as it is to make an Arduino do more than one thing at a time. ![]()
That said, we do expect, at the point of learning key idea Z that the student has some good handle on the concepts of X and Y.
Nothing is more frustrating than thinking someone is getting it, only to find they haven't a good grasp on something basic like loops or a conditionally executed block of code. Or or or...
I don't suppose we all arrive at knowing things in the same order. Here I think it would be reasonable to require that a person know how to, for example, count button presses.
By the time you want to learn how to keep an LED on after a button press, it would be good to have mastered, or at least come to some understanding of, switch debouncing and state change transition detection.
So trying to add as little as possible, I modified @xfpd's sketch.
I placed a delay(10); hiding at the bottom of the loop. This effectively debounces any button presses.
I installed state change detection, adding the concept of the button state transition setting a flag for subsequent consumption later in the loop.
One blink adjustabel, accordian to taste, per button press. Try it here:
@xfpd's demo #5 w/ modifications
// https://wokwi.com/projects/373589174724221953
// https://forum.arduino.cc/t/measure-time-that-a-condition-is-true-to-then-trigger-and-action/1159366
unsigned long durationMillis = 500; // DURATION for LED ON after BUTTON is pressed
#define buttonPin 2 // the Arduino pin connected to the BUTTON
#define ledPin 3 // the Arduino pin connected to the LED
bool ledState; // store the state (ON/OFF) of the LED
bool buttonState; // store the state (pressed/not pressed) of the button
unsigned long ledOnMillis; // timer for LED ON time
unsigned long ledOffMillis; // timer for LED OFF time
void setup() {
Serial.begin(115200); // start Serial Monitor communications
pinMode (ledPin, OUTPUT); // set Arduino pin for the LED to OUTPUT
pinMode (buttonPin, INPUT_PULLUP); // set Arduino pin for the BUTTON to INPUT with PULLUP resistor
digitalWrite(ledPin, LOW); // LED OFF
welcome();
}
void loop() {
bool buttonReading = !digitalRead(buttonPin);
unsigned long now = millis();
bool buttonEvent = false; // unless...
if (buttonReading != buttonState) {
buttonState = buttonReading;
if (buttonState)
buttonEvent = true; // we saw the button go down
}
if (buttonEvent) {
ledOnMillis = millis(); // record LED ON time
digitalWrite(ledPin, HIGH); // LED ON
ledState = 1; // set LED flag meaning LED is ON
printOn(); // format and print current status
}
if ((millis() - ledOnMillis > durationMillis) && ledState) { // difference between button to now
ledOffMillis = millis(); // get LED OFF time
digitalWrite(ledPin, LOW); // LED OFF
ledState = 0; // clear LED flag
printOff(); // format and print current status
duration();
}
delay(10); // poor man's debouncing
}
//*************************************************************
// Formatted printout
//*************************************************************
void printOn() {
Serial.print("TIME ON ");
Serial.print(ledOnMillis);
Serial.print(" <-- the milliseconds clock");
Serial.println();
}
void printOff () {
Serial.print("TIME OFF ");
Serial.print(ledOffMillis);
Serial.print(" <-- DURATION plus current milliseconds clock");
Serial.println();
}
void duration () {
Serial.print("DURATION ");
Serial.print(ledOffMillis - ledOnMillis); // show duration
Serial.print(" <-- difference between ON and OFF");
Serial.println();
}
void welcome() {
Serial.println("Press the green button.");
}
Careful testing will reveal some work left to be done, even so. Now a button press while the LED is already on extends the time on period of the LED by the defined time constant, and leads to the duration() function still reporting only the last bit of on period so invoked, the time constant itself, now +/- the poor man's delay which messes up fine timing to the millisecond.
By the time the sketch we are talking about just now (#5 upthread) is fully fixed up, it would take an order of magnitude (binary at least) more pesky little details.
a7
Nice. A more compact button state change than I wrote (before using delay()).