Hi! I have this simple application with WS2812 Led Ring, which makes a 'wheel' effect, lights the LEDs one after the other. I'm trying to learn how to use millis () with loops, the delay () function causes a pause in the operation of an application, or so I understood.
void loop() {
unsigned long currentTime = millis();
for(s = 0; s < 73; s++){ // 6 colors x 12 LEDs
Serial.println(s); // just print the value
for(s = 0; s < 12; s++){
// here I would write
if ( currentTime - previousTime >= eventTime) { // eventTime = 1000
for(i = 0; i < NUMPIXELS; i++){
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(bright,0,0)); // Moderately bright green color.
pixels.show(); // This sends the updated pixel color to the hardware.
//delay(delayval); // Delay for a period of time (in milliseconds).
}
if(i==NUMPIXELS){
pixels.clear();
break;}
previousTime = currentTime;}
}// 1st for
for(s = 11; s < 24; s++){
for(j = 0; j < NUMPIXELS; j++){
pixels.setPixelColor(j, pixels.Color(0,bright,0)); // Moderately bright green color.
pixels.show(); // This sends the updated pixel color to the hardware.
delay(delayval); // 1s delay
}
if(j==NUMPIXELS){
pixels.clear();
break;}
}// 2st for
//************** other for loops follow
if((s > 71) && (s < 73)){
pixels.setPixelColor(n, pixels.Color(bright,bright,bright)); // Moderately bright green color.
pixels.show(); // This sends the updated pixel color to the hardware.
delay(delayval);
} // if
}// BIG For
}// loop
You can see that I use several for loops, but I can't create the 1s delay between them with millis (). Where am I wrong ? Where should I put the millis () function?
Using millis () between for loops [solved]
Using millis() in a for loop as delay
c - Replace delay() with millis inside a for loop Arduino - Stack Overflow
Using millis() for timing. A beginners guide
Videos
Use static variables to retain state and timestamps of the events you want to be periodic. For example, the following will set an LED in a random column on row zero, and every 250ms will move it to the next row until after reaching row 3, it will restart at a new random column.
static int j = 0 ;
static int i = 0 ;
// When j == 0, set initial LED
if( j == 0 )
{
i = random(0, 2);
digitalWrite(LEDrows[i][j], HIGH ) ;
}
// Get current millisecond tick
unsigned long now = millis() ;
// Every 250ms move the LED
static unsigned long led_timestamp = millis() ;
if( now - led_timestamp > 250 )
{
led_timestamp = now ;
digitalWrite(LEDrows[i][j], LOW ) ;
j++ ;
if( j > 3 )
{
j = 0 ;
}
else
{
digitalWrite(LEDrows[i][j], HIGH ) ;
}
}
The general pattern is:
static unsigned long timestamp = millis() ;
unsigned long now = millis() ;
if( now - timestamp > PERIOD_MS )
{
timestamp = now ;
// do something every PERIOD_MS here
...
}
With multiple timestamp variables you can perform tasks at various independent intervals.
void toggle(uint8_t row,uint8_t col){
static long time[4][4];
long curr_time = millis(); //current system time
long last_time = time[row][col];
if(curr_time >= (last_time + toogle_delay)){
digitalWrite((LEDrows[row][col]),!digitalread((LEDrows[row][col])));
time[row][col] = curr_time;
}
else{}
}
I was wrong being too confident about my whole code. When people suggested posting my whole code, I should have just done it!!!
Anyway, here is the mistake I made. I initialized the variable flash_time as an 8-bit integer. Of course, 8 bit can only hold up to 255. I am trying to store 9000!!! The following code has this bug fixed.
const long sleep_interval = 3000;
const long awake_interval = 250;
const int ledPin = 3;
const uint16_t flash_time = 9000; //bug fixed by using uint16_t instead of uint8_t
const uint16_t t1 = 5;
const uint16_t t2 = 495;
void flash() {
unsigned long x = millis();
while(millis() - x < flash_time){
digitalWrite(ledPin, HIGH);
delay(t1);
digitalWrite(ledPin, LOW);
delay(t2);
}
}
void setup() {
Serial.begin(57600);
pinMode(ledPin, OUTPUT);
}
void loop() {
unsigned long x = millis();
while(millis() - x < awake_interval){
Serial.println("flash time!");
flash();
}
unsigned long y = millis();;
while (millis() - y < sleep_interval) { //sleep_interval = 3 secs
Serial.println("Sleep time!");
}
}
So, nested loops that use millis() to determine the terminating conditions work just fine, even if loop 1 runs for 250 ms and loop 2 (that's inside loop 1) runs for much longer, like 9 seconds.
Apologies if I have wasted too much of people's time.
Your logic is wrong. If you update previousMillis with millis() value in the first line, you are always subtracting the time difference between the the first and second lines of your code (which is the same you are doing in the loop inside flash()). If what you need is to get the time passed since the program started running, you need to have a fixed variable for that in setup() instead of updating it every time in loop().
Even so, I don't see why millis() is necessary in your case. Using delay() would be much more simple and still get the job done. Example:
void setup() {
delay(250);
}
void loop(){
for(int i = 0; i < 9; i++){
digitalWrite(LED, HIGH);
delay(time);
digitalWrite(LED, LOW);
delay(time);
delay(1000 - 2*time); //Delays the loop exactly one second per iteraction
}
}
I know that the code is completely wrong because once it is in the for loop it doesn't stop until all the decrement is done, but is there a way to make the decrement in the for loop happen once every second?
void loop() {
unsigned long currentTime = millis();
if (currentTime - previousTime >= interval) {
previousTime = currentTime;
for(i=10; i>0; i--) {
lcd.print(i);
}
}
You have made the code to go in the loop every “interval” ms; now you have two options:
-
create another “interval” and “previousTime” variable (changing the name of course) and place another condition inside
-
add a “delay(1000);” after the lcd.print(i).
The difference is the following:
-
you maintain the code multi-task oriented, that is, you can check if (a) you have to launch the lcd updating with the current code and (b) check if you indeed have to change the value shown in the lcd (with the new addition to the code). Whatever it is the state (update or not, change “i” or not), you can then do other things before checking again (that is, you can read a sensor or blink a LED or do whatever you need). This is the best practice to get the most out of the Microcontroller.
-
with the delay, however, your code will simply wait one second on every loop tick. This means that yOu will have 10 seconds spent (indeed, wasted) every time you get in that loop (which for a microcontroller is equivalent to years of time :D). Therefore, any other task will only happen, regardless your need, every 10 seconds. This is a waste of potential.
I avoided writing code examples to invite you to explore options. Let me know if you need some inspiration. I recommend you to give it a try, the “ingredients” are there already :)
//Timers
unsigned long timer1000ms = 0;
void setup() {
}
void loop() {
if (timer1000ms + 1000 < millis()) { //Run code every 1000ms (1s)
timer1000ms = millis();
}
You can do this, for quite simple sketches. It starts to become ugly if you add more features and conditions.
Therefore, the "correctTM" (read: professional) way is to implement a state machine. See in the IDE the non-delay blink example.
const unsigned long DELAY_MS = 1000;
int pin = 13;
enum State {
WAITING, WORKING,
};
State state;
unsigned long startTime;
void setup()
{
pinMode(pin, INPUT);
state = WAITING;
startTime = millis();
}
void loop()
{
switch (state) {
case WAITING:
// You can put both conditions in one "if" with an "||" operator.
// But it will look less clear.
if (millis() - startTime > DELAY_MS) {
state = WORKING;
} else if (digitalRead(pin)) {
state = WORKING;
}
break;
case WORKING:
// anything to do after the delay or the button
break;
default:
// any error handling you see fit
break;
}
}
The function loop() is designed to be called endlessly in repetition. Use this to your advantage.
What's bad about this?
It looks a bit clunky but I suppose there is nothing particularly bad about it (apart from your typo of digitalWrite rather than digitalRead).
I would do it more like:
unsigned long now = millis ();
button = LOW;
while ((millis () - now < 10000) && (button == LOW))
button = digitalRead (pin);