Hi guys!
I have a project where (before the game starts) there is a light wave. I have 11 LEDs next to each other. I put them into an array so I can go through them and turn ON or OFF. I had delay between the HIGH or LOW states. In the code there are delay comments, there were the delays and now I want to replace the delays with the millis logical if statements. (I go through the LEDs and turn them on or off but had delay between, so same backwards with an other for loop) . I understand why this code doesn't work and tried a lot of things but none of those worked and I don't know the solution. Can anyone help me a little bit?
//makes the animation light wave before strating and checks the buttons
//set the led pins
int led1 = 22;
int led2 = 24;
int led3 = 28;
int led4 = 32;
int led5 = 36;
int led6 = 40;
int led7 = 44;
int led8 = 48;
int led9 = 52;
int led_correct = 12;
int led_incorrect = 13;
int Led_Array[] = {led1, led2, led3, led4, led5, led6, led7, led8, led9, led_correct, led_incorrect};
unsigned long previousTime = millis();
long bef_game_delay = 500;
void bef_sta_ani() {
unsigned long currentTime = millis();
//go through all the LEDs from 1 to the last
for (byte i = 0; i < 11; i++) {
if ((currentTime - previousTime) > bef_game_delay) {
previousTime = currentTime;
digitalWrite(Led_Array[i], HIGH);
}
if ((currentTime - previousTime) > bef_game_delay) {
previousTime = currentTime;
//delay(50);
digitalWrite(Led_Array[i+1], HIGH);
digitalWrite(Led_Array[i], LOW);
}
//delay(50);
}
//go through all the LEDs from the last to 1
for (byte j = 11; j > 2 ; j--) {
if ((currentTime - previousTime) > bef_game_delay) {
previousTime = currentTime;
//delay(50);
digitalWrite(Led_Array[j], LOW);
digitalWrite(Led_Array[j-2], HIGH);
}
if ((currentTime - previousTime) > bef_game_delay) {
previousTime = currentTime;
//delay(50);
digitalWrite(Led_Array[j-2], LOW);
digitalWrite(Led_Array[j-3], HIGH);
}
//delay(50);
}
}
In a typical deployment, that code pattern will run many times, possibly thousands, before the test becomes true and a timed action is taken, and the timer reset.
Your for loop races past testing every step, probably not true at all for any, no chance to test again.
You have to lose the for loop and make your own function that
sees if it is time to (just like the above)
and if it is
handles the next LED, including bump along the timer
You'll need a variable to play the roll of the for loop index, incrementing with each timed LED and resetting to zero when you done all eleven or N.
A hand made loop, in a function you call very very frequently from the loop() as it, um, loops, most of the time that function will be doing nothing, as it won't be time to yet.
You kinda have to see this from a few perspectives, but once you Aha! it you'll wonder what the fuss was all about.
Like so many things it will be hard until it is easy.
/*
Forum: https://forum.arduino.cc/t/i-dont-really-understand-how-the-millis-work/1229674
Wokwi: https://wokwi.com/projects/391000332028236801
*/
byte leds[] = {2, 3, 4, 5, 6, 7};
const int noOfLeds = sizeof(leds) / sizeof(leds[0]);
const unsigned long ledDelay = 100;
unsigned long lastChange = 0;
int actLed = 0;
int direction = 1;
void setup() {
for (int i = 0; i < noOfLeds; i++) {
pinMode(leds[i], OUTPUT);
}
}
void loop() {
ledWave();
}
void ledWave() {
if (millis() - lastChange > ledDelay) {
lastChange = millis();
resetLeds();
digitalWrite(leds[actLed], HIGH);
actLed += direction;
if (actLed == noOfLeds - 1 || actLed == 0) {
direction = -direction;
}
}
}
void resetLeds() {
for (int i = 0; i < noOfLeds; i++) {
digitalWrite(leds[i], LOW);
}
}
The main issues that your sketch has are (as already partly mentioned above):
for (byte i = 0; i < 11; i++) {
// This loop is usually far too fast for anything with millis() inside
// Better use millis() as the "outside" and put the code that shall
// be performed once and a while to its "inside"
if ((currentTime - previousTime) > bef_game_delay) {
previousTime = currentTime;
// The values of these variables do never change inside the for-loop!!
// and when they change the statements inside this condition will only be
// performed ONCE for i == 0 as previousTime will be set to currentTime
// so all other values of i do not have any chance to get here
}
}
Hope that helps to improve your understanding ...
Good luck!
ec2021
The basic basic blink_without_delay example-code makes understanding non-blocking timing harder than it must be for three reasons:
the blink without delay-example does NOT mention that there is a fundamental difference between delay() and non-blocking timing.
Without explicitly explaining this difference newcomers are in danger to see a delay()-similar-Thing
And trying to see a delay()-similar thing will confuse to the maximum because there is NO similarity!
a part of the variable-names is badly chosen.
the demo-code distributes the variables to multiple places
There is a really fundamental difference between blocking timing with delay()
and
non-blocking timing based on function millis().
based on means: it is NOT a simple replacement of delay() with millis()
blocking timing with delay() works linear top down
non-blocking timing based on millis() works circular repeating
This video explains it pretty good
Lets take it out of context and bring it to simple numbers. We have this magic counter that increments once every second (time base) unless we deliberately stop it. To determine elapsed time we take a reading of that counter(timer/mills) and save it for later, 55. We now forget it and do our thing. When the event ends we again read the magic counter which now contains a bigger number, 65.
To get the elapsed time we subtract what it was in the beginning 55 from what we have now 65, the result is the time (counter ticks) that has passed since we started. For example we start and the counter is 55. When we finish it reads 65. Then 65 - 55 = 10 or ten seconds which is the the difference times the time base which was 1 second.
With this we can time events or whatever else we want to. In the Arduino world they use an unsigned 32 bit number, the largest standard number The Arduino would work with. They use an unsigned integer because once you have the most significant bit high it becomes a negative number if it is signed. if unsigned it just is another bit.
That bit is how computers determine a negative or positive number. You only get 1/2 the count positive but you also get 1/2 the count negative so it evens out and no sign to work with. We tell it to ignore the sign bit when specifying an unsigned integer, default is signed
.
// keep time values unsigned!
// unsigned long millis is good to 49.71-some days intervals.
// if you time with unsigned long millis the interval is 65.535 seconds.
You pick up how integer (not floating point or text) variables work.
Variable types long and unsigned long are 32-bits, 4 bytes long.
As type unsigned long it can count 0 to 4294967295.
The time formula End - Start = Elapsed depends on how unsigned math works. On a round 12 hour clock say we save Start as 9 and now it is 5. If I turn the hour hand 9 hours leftward, the subtract direction from 5 to 8, the elapsed time. The same code works across rollovers always as long as elapsed time fits in your time variables.
4294967295 millis... 4294967.295 seconds, 49.7102696181 days. You can sit a data collector out with batteries in an Altoid tin and get readings a month apart if that's your desire. It would need a crystal, not a drifty resonator (the Uno does) or an RTC but there used to be neat-logger projects here before.
A 16 bit type unsigned int can count from 0 to 65535.
As 65535 ms, the maximum wait-for time is 65.535 seconds. See?
16 bit variables on AVR chip use half the RAM as 32 bit variables..
and they subtract and compare twice as fast sooooo
If you don't need to wait > 1 minute, unsigned int timers are better.
For close timing where millis get < 20, the millis() function is +/- 1 ms by the way it counts. If it matters, use micros() instead of millis().
Unsigned long micros() rolls over every 71.some minutes, good for over an hour.
If you wanted to measure how long a pendulum blocks IR at the bottom of the swing to get the speed, time with micros!
Presumably this is not the full code? There is a void for bef_sta_ani() but no void loop()
The usigned long currentTime = millis() should be in the top block of code not in the void()
And all of my timers are checking current millis() against the unsigned long variable.
i.e if((millis() - previousTime >= bef_game_delay)
and at the end of the if test there would be a statment resetting the previosTime to current millis().
i.e previousTime = millis();
I wouldn't be using currentTime at all.
As presneted I don't think the code would compile, and if it did it wouldn't do anything.
This should compille ok (but it might not do what you want it to?
int led1 = 22;
int led2 = 24;
int led3 = 28;
int led4 = 32;
int led5 = 36;
int led6 = 40;
int led7 = 44;
int led8 = 48;
int led9 = 52;
int Led_Array[] = {led1, led2, led3, led4, led5, led6, led7, led8, led9,};
unsigned Timer = millis();
long bef_game_delay = 500;
void loop()
{
//go through all the LEDs from 1 to the last and turn them all on
for (byte i = 0; i < 8; i++)
{
if ((millis() - Timer) >= bef_game_delay) {
digitalWrite(Led_Array[i], HIGH);
}
//Then go back from the last one and turn them all off
Timer =millis();
for (byte i = 0; i < 8; i--)
if ((millis() - Timer) >= bef_game_delay) {
digitalWrite(Led_Array[i], LOW);
}
}
If the works you can go back in and add the
digitalWrite(Led_Array[j], LOW);
digitalWrite(Led_Array[j-2], HIGH); // should be i - 1
stuff which presumably is trying to achieve a floating LED that is off and all the rest saty on
Come here and use the / button in the message composition window toolbar and do like it sez:
type or paste code here
Post a complete sketch. You can't run that kind of timer pattern inside a loop over nine elements. That idiom expects to be executed very more frequently than the basic timing rate. delay() is what works in such a loop.
Of course I think post #3 goes into that in some detail.
Why? If this is just a one-off startup dance who cares?
1. Once the uploading of a sketch in Arduino UNO is done, an unsigned 32-bit counter (called millsCounter, Fig-1) is automatically started from zero, which keeps accumulatig 1 ms time period in the background on interrupt basis. The millisCounter will rollover (back to zero) after counting 232 milliseconds.
Figure-1:
2. The content of the millisCounter (current elapsed time from the point of Arduino start) can be read on-the-fly (without disturbing the counting process of the millisCounter) at any time by executing the following codes; where, the variable presentMillis holds the current elapsed time/content of millisCounter.
unsigned long int presentMillis = millis();
3. In order to make a delay of 2000 ms without blocking the MCU (the MCU cannot do any other task), the millisCounter/millis() are helpfule to offer the following codes. Here, the MCU is checking if 2000 ms has elapsed on not at 1-ms interval.
unsigned long int presentMillis = millis(); //time *t1* is recorded
if(millis() - presentMillis >= 2000) //2000 ms has elapsed
{
// do something like -- change the state of a LED1 (On to Off or Off to On)
}
else
{
//2000 ms has not elapsed; do another thing like -- check the Fire Alarm
}