Set Timer for LED strip individually for each LED

Hello togehter,

This is my first post so be patient with me.

First of all I wanted to say this is just to know if it is possible or feasable to do with an Arduino and what components I would need for it. And if you want you can give me some tips on the Code too.

So lets get to the meat of it:

Like the Title says I want to control a variable amount of LEDs on a LED strip like ws2812b. I need at least 40 but want it to be upscaleable to 100.

I want to be able to Press a Button, which turns the LEDs on, one after the other, and I want them to turn off after a set amount of time (40 minutes).

So far so good I kind of managed to do that, what I did not manage to do is:

Lets say I press the button every minute, the first LED turns on then the next and so on. 40 min later I want the first LED to turn off, then a minute Later the next turns off. Every time 40 min after I pressed the button for that specific LED.
Basically I want to time each button press with a corresponding LED. In practise this means I finish something which has to sit for 40 minutes, but I finish one of those every minute or so.(time can vary from 45-65seconds thats why I wanted to use a button press to indicate it.)

Is this possible? If yes could you kind of hint me in the right direction?

Also English is not my native language so be kind ;).

Greetings and Thanks

Depending on your skill level, it’s either easy, or a learning curve.
Before you start, figure out how you’re going to power the LEDs…
Using a canned library, declare and initialise an array of all the LEDs you’re going to use (3x8 bits per WS pixel)

The a background function will ‘pump’ the LED data at whatever refresh rate you decide upon.

In the ‘actual’ code, you’ll manipulate that array of RGB LED data as needed, and the LEDs will be refreshed by the library as you have set them.

1 Like

Yes.

You need two concepts.
First is the state change detection , you can find an example in the IDE under file-> examples -> 02.Basics
The next concept is also under the same menu called Blink without delay.

1 Like

Wow thanks for the fast feedback!

If I would guess the state change direction is the one i'm missing atm. Controlling the strip itself with a button is not really a problem. At this point I can press the button. The LED turns on and then after 20 seconds turns off again. If I press the button again the next LED turns on and off after 20 seconds. The problem I have is when I press the button multiple times in that timeframe only the last LED turns off again. I also know why that is I just dont know another solution.
The way I do it is I count a variable up 1 each time I press the button and turn on the corresponding LED.
How can I now know which LED to turn off 40 min later.

Hello
Did you post your current sketch?

No i didnt, because its a mess :slight_smile: but here you go
In this case the LED goes red and not turn off, but that doesnt matter i guess.

#include <Adafruit_NeoPixel.h>
#define PIN 6


Adafruit_NeoPixel strip = Adafruit_NeoPixel(20 , PIN, NEO_GRB + NEO_KHZ800);

int i;

unsigned long ontime;
unsigned long elapsedtime;


// Button
const int buttonPin = 9;   // Buttonpin
int buttonState;             
int lastButtonState = HIGH;  
 // Debounce
unsigned long lastDebounceTime = 0;  
unsigned long debounceDelay = 50;  

// Colors
uint32_t color1 = strip.Color(0, 0, 255);  //Blau 
uint32_t color2 = strip.Color(255, 0,0);  //ROT 
uint32_t color3 = strip.Color(0, 255, 0);  // Grün 
uint32_t color4 = strip.Color(255, 168, 0);  //ORANGE 
uint32_t black = strip.Color(0, 0, 0);   // Ausschalten der LED's




void setup() {
 
pinMode(buttonPin, INPUT_PULLUP);
  ontime = millis();
  ontime = elapsedtime;
  strip.begin();   //initialisieren
  strip.setBrightness(40);   //Golbale Helligkeit
  strip.show();  // Alles aus
  i=-1;
}



void loop()

{
//Buttonpress
  int reading = digitalRead(buttonPin);
 
  if (reading != lastButtonState) {lastDebounceTime = millis(); }
 
  if ((millis() - lastDebounceTime) > debounceDelay) {
    
    if (reading != buttonState) {
      buttonState = reading;
      
      if (buttonState == HIGH) {i++;
       ontime = millis();
       if(i>20){i=0;}     
       strip.setPixelColor(i, color3); 

       
      
    strip.show();
       }
      }
   }

  if( millis() - ontime > 500)
            {      
           strip.setPixelColor(i, color2) ;  // LED goes red
        
             
            }
            strip.show();
 lastButtonState = reading;
}

But I wanted to try something like this, with a ds3231 but it doesnt do what I want yet. I am not sure how i can tell him if i press the button to "safe" the time, or a time 10 min in the future so i can shut the led off then.

//Knopfdruck
  int reading = digitalRead(buttonPin);
 
  if (reading != lastButtonState) {lastDebounceTime = millis(); }
 
  if ((millis() - lastDebounceTime) > debounceDelay) {
    
    if (reading != buttonState) {
      buttonState = reading;
      
      if (buttonState == HIGH) {i++;
       ontime = millis();
       if(i>20){i=0;}     
       strip.setPixelColor(i, color3); 

       if(i=1){DateTime future1 (now + TimeSpan(0,0,0,10));} // TimeSpan( day,hour,minute,second )
   
      
    strip.show();
       
       
       if( DateTime now = DateTime future1){strip.setPixelColor(1, black);}
       }
      }
   }

You will need to keep track of the time that each LED.

You could use millis(), the number of milliseconds since reset or power on. Or you could use a different length of time more convenient as a unit. Below I suggest using minutes.

But you need to keep all those times, and check them to see if they have “expired”.

I don’t see any arrays in you code, so perhaps that’s the next little bit of C/C++ you should learn.

I use count down timers, so for example:

declare an array for the timer(s)

byte ledTimer[numberOfLEDs];

When an LED is turned on, put 40 into the corresponding array position.

    setPixel... // whatever. turn on the LED number i
    ledTimer[i] = 40;

Every minute, decrement all the array elements that are still positive, and turn off any LEDs whose counter became zero.

    if (ledTimer[i] > 0) {
        --ledTimer[i];
        if (ledTimer[i] == 0) {
            setPixel... // whatever. turn OFF the LED number i
        }
    }

a7

1 Like

Thanks will try this tomorrow. Seems really helpful!

I would use a timer library like TimeObj.h. It would be a lot cleaner than using millis() in our code.

You would set up a timer for each LED, and start the timer when the LED is turned on. When the timer expires, turn the LED off. The only limit to the number of timers you can have is memory.

There's another timer library called ticker.h that sets up a callback when the timer expires. I only experimented with this library briefly, but it is worth a look.

Libraries have their place. I do not think it is worth the time in this case.

I put my ideas into the OP’s code, and made a few adjustments.

There is one call to millis() at the top of the loop. Everything else is just code, idiomatic code from examples like “blink without delay” &c.

I believe there is value in getting something simple like this to work using code you write, not libraries you come to grips with.

I think doing leaves one in a better position to exploit libraries.

In the OP’s case, it is very close to finished. If I hadn’t been obsessed about tinkering with a few other things in the code, I could literally have fixed it up by dropping in about 9 lines of code.

Too close to abandon ship and take a new approach.

I am not saying that those libraries won’t work, nor that they might not make things prettier. I remain convinced that knowing how to do this kind of thing will always be valuable, and here is a perfect place to figure out something and get it going.

If the OP plans on going deeper into this, I recommend sticking away from libraries for this sketch.

a7

hey thank you alot! I made it work :slight_smile: . One question I have is,how can I substract 1 minute from every timer in my array. I dont seem to be able to make a for loop work.
and if I want to handle 100 LED's at once its annoing to copy every line.

#include <Adafruit_NeoPixel.h>

#define PIN 6

Adafruit_NeoPixel strip = Adafruit_NeoPixel(19 , PIN, NEO_GRB + NEO_KHZ800); // Anzahl pixel
//Array
int i;
int ledTimers[19];

// Button
const int buttonPin = 9;   // Buttonpin
int buttonState;             
int lastButtonState = HIGH;  

// Debounce
unsigned long lastDebounceTime = 0;  
unsigned long debounceDelay = 50;  

// Farben
uint32_t blue = strip.Color(0, 0, 255);  //Blau 
uint32_t red = strip.Color(255, 0,0);  //ROT 
uint32_t green = strip.Color(0, 255, 0);  // Grün 
uint32_t orange = strip.Color(255, 168, 0);  //ORANGE 
uint32_t black = strip.Color(0, 0, 0);   // Ausschalten der LED's

int zeitorange;
int zeitrot;


//Timer ablaufen
unsigned long previousMillis = 0;
const long interval = 100;

//Setup

void setup() {
 
pinMode(buttonPin, INPUT_PULLUP);
  
  strip.begin();   //initialisieren
  strip.setBrightness(20);   //Golbale Helligkeit
  strip.show();  // Alles aus
  i=-1;
zeitrot = 2000;
zeitorange = 5000;
  
}


void loop()

{
//Knopfdruck
  int reading = digitalRead(buttonPin);
 
  if (reading != lastButtonState) {lastDebounceTime = millis(); }
  if ((millis() - lastDebounceTime) > debounceDelay) {   
    if (reading != buttonState) {
      buttonState = reading;   
      if (buttonState == HIGH) {i++;     
       if(i>19){i=0;}     
       strip.setPixelColor(i, green); 
        ledTimers[i]=10000;
       
      
    strip.show();
       }
      }
   }

//Ablauf der Timer
unsigned long currentMillis = millis();

if(currentMillis-previousMillis>= interval){
  previousMillis=currentMillis;
 
  if(ledTimers[0] > 0){
ledTimers[0]=ledTimers[0] - interval;}
if(ledTimers[1] > 0){
ledTimers[1]=ledTimers[1] - interval;}
if(ledTimers[2] > 0){
ledTimers[2]=ledTimers[2] - interval;}
if(ledTimers[3] > 0){
ledTimers[3]=ledTimers[3] - interval;}
if(ledTimers[4] > 0){
ledTimers[4]=ledTimers[4] - interval;}
if(ledTimers[5] > 0){
ledTimers[5]=ledTimers[5] - interval;}
if(ledTimers[6] > 0){
ledTimers[6]=ledTimers[6] - interval;}
if(ledTimers[7] > 0){
ledTimers[7]=ledTimers[7] - interval;}
if(ledTimers[8] > 0){
ledTimers[8]=ledTimers[8] - interval;}
if(ledTimers[9] > 0){
ledTimers[9]=ledTimers[9] - interval;}
if(ledTimers[10] > 0){
ledTimers[10]=ledTimers[10] - interval;}
if(ledTimers[11] > 0){
ledTimers[11]=ledTimers[11] - interval;}
if(ledTimers[12] > 0){
ledTimers[12]=ledTimers[12] - interval;}
if(ledTimers[13] > 0){
ledTimers[13]=ledTimers[13] - interval;}
if(ledTimers[14] > 0){
ledTimers[14]=ledTimers[14] - interval;}  
if(ledTimers[15] > 0){
ledTimers[15]=ledTimers[15] - interval;} 
if(ledTimers[16] > 0){
ledTimers[16]=ledTimers[16] - interval;} 
if(ledTimers[17] > 0){
ledTimers[17]=ledTimers[17] - interval;} 
if(ledTimers[18] > 0){
ledTimers[18]=ledTimers[18] - interval;} 

if(ledTimers[0]<=zeitorange){            
 strip.setPixelColor(0, orange);  }        
if(ledTimers[1]<=zeitorange){            
 strip.setPixelColor(1, orange); }          
if(ledTimers[2]<=zeitorange){            
 strip.setPixelColor(2, orange);}
if(ledTimers[3]<=zeitorange){            
 strip.setPixelColor(3, orange);}    
if(ledTimers[4]<=zeitorange){            
 strip.setPixelColor(4, orange);}
if(ledTimers[5]<=zeitorange){            
 strip.setPixelColor(5, orange); }   
if(ledTimers[6]<=zeitorange){            
 strip.setPixelColor(6, orange);}
if(ledTimers[7]<=zeitorange){            
 strip.setPixelColor(7, orange);}               
if(ledTimers[8]<=zeitorange){            
 strip.setPixelColor(8, orange);}
if(ledTimers[9]<=zeitorange){            
 strip.setPixelColor(9,orange);}
if(ledTimers[10]<=zeitorange){            
 strip.setPixelColor(10, orange); }           
if(ledTimers[11]<=zeitorange){            
 strip.setPixelColor(11, orange);}
if(ledTimers[12]<=zeitorange){            
 strip.setPixelColor(12, orange);}        
if(ledTimers[13]<=zeitorange){            
 strip.setPixelColor(13, orange);}        
if(ledTimers[14]<=zeitorange){            
 strip.setPixelColor(14, orange);}
if(ledTimers[15]<=zeitorange){            
 strip.setPixelColor(15, orange);}
if(ledTimers[16]<=zeitorange){            
 strip.setPixelColor(16, orange);}        
if(ledTimers[17]<=zeitorange){            
 strip.setPixelColor(17, orange);}        
if(ledTimers[18]<=zeitorange){            
 strip.setPixelColor(18, orange);}

if(ledTimers[0]<=zeitrot){            
 strip.setPixelColor(0, red);  }        
if(ledTimers[1]<=zeitrot){            
 strip.setPixelColor(1, red); }          
if(ledTimers[2]<=zeitrot){            
 strip.setPixelColor(2, red);}
if(ledTimers[3]<=zeitrot){            
 strip.setPixelColor(3, red);}    
if(ledTimers[4]<=zeitrot){            
 strip.setPixelColor(4, red);}
if(ledTimers[5]<=zeitrot){            
 strip.setPixelColor(5, red); }   
if(ledTimers[6]<=zeitrot){            
 strip.setPixelColor(6, red);}
if(ledTimers[7]<=zeitrot){            
 strip.setPixelColor(7, red);}               
if(ledTimers[8]<=zeitrot){            
 strip.setPixelColor(8, red);}
if(ledTimers[9]<=zeitrot){            
 strip.setPixelColor(9, red);}
if(ledTimers[10]<=zeitrot){            
 strip.setPixelColor(10, red); }           
if(ledTimers[11]<=zeitrot){            
 strip.setPixelColor(11, red);}
if(ledTimers[12]<=zeitrot){            
 strip.setPixelColor(12, red);}        
if(ledTimers[13]<=zeitrot){            
 strip.setPixelColor(13, red);}        
if(ledTimers[14]<=zeitrot){            
 strip.setPixelColor(14, red);}
if(ledTimers[15]<=zeitrot){            
 strip.setPixelColor(15, red);}
if(ledTimers[16]<=zeitrot){            
 strip.setPixelColor(16, red);}        
if(ledTimers[17]<=zeitrot){            
 strip.setPixelColor(17, red);}        
if(ledTimers[18]<=zeitrot){            
 strip.setPixelColor(18, red);}

        
if(ledTimers[0]<=0){            
 strip.setPixelColor(0, black);  }        
if(ledTimers[1]<=0){            
 strip.setPixelColor(1, black); }          
if(ledTimers[2]<=0){            
 strip.setPixelColor(2, black);}
if(ledTimers[3]<=0){            
 strip.setPixelColor(3, black);}    
if(ledTimers[4]<=0){            
 strip.setPixelColor(4, black);}
if(ledTimers[5]<=0){            
 strip.setPixelColor(5, black); }   
if(ledTimers[6]<=0){            
 strip.setPixelColor(6, black);}
if(ledTimers[7]<=0){            
 strip.setPixelColor(7, black);}               
if(ledTimers[8]<=0){            
 strip.setPixelColor(8, black);}
if(ledTimers[9]<=0){            
 strip.setPixelColor(9, black);}
if(ledTimers[10]<=0){            
 strip.setPixelColor(10, black); }           
if(ledTimers[11]<=0){            
 strip.setPixelColor(11, black);}
if(ledTimers[12]<=0){            
 strip.setPixelColor(12, black);}        
if(ledTimers[13]<=0){            
 strip.setPixelColor(13, black);}        
if(ledTimers[14]<=0){            
 strip.setPixelColor(14, black);}       
if(ledTimers[15]<=0){            
 strip.setPixelColor(15, black);}
if(ledTimers[16]<=0){            
 strip.setPixelColor(16, black);}        
if(ledTimers[17]<=0){            
 strip.setPixelColor(17, black);}        
if(ledTimers[18]<=0){            
 strip.setPixelColor(18, black);} 
            
            
            strip.show();
 }
 lastButtonState = reading;
}

I’ll test your program later when I am at the big rig.

But that is indeed painful and won’t scale well for 100 lamps…

This would be handled best by a for loop, at a glance there are none in any code you posted so…

go learn about the C/C++ “for” loop - when you have a pattern like that of repeated mostly identical statements, a for loop might make it a bit more concise, and changing up to 100 from 20 a matter of changing a few numbers.

BTW I’ve been playing with my version of your project, sadly it is what I call fun as I have no life, but I did two things you might interest:

  • change the color from green to yellow for the last little bit of time before the LED goes red.

  • if all the LEDs are off, then pushing the button starts over at the first LED

a7

1 Like

That is actually in the code already. I postet the working bit of Code, here is my try at the for loop, which doesnt work:

#include <Adafruit_NeoPixel.h>

#define PIN 6

Adafruit_NeoPixel strip = Adafruit_NeoPixel(19 , PIN, NEO_GRB + NEO_KHZ800); // Anzahl pixel
//Array
int i;
int ledTimers[19];

// Button
const int buttonPin = 9;   // Buttonpin
int buttonState;             
int lastButtonState = HIGH;  

// Debounce
unsigned long lastDebounceTime = 0;  
unsigned long debounceDelay = 50;  

// Farben
uint32_t blue = strip.Color(0, 0, 255);  //Blau 
uint32_t red = strip.Color(255, 0,0);  //ROT 
uint32_t green = strip.Color(0, 255, 0);  // Grün
uint32_t orange = strip.Color(255, 168, 0);  //ORANGE 
uint32_t black = strip.Color(0, 0, 0);   // Ausschalten der LED's

int zeitorange;
int zeitrot;


//Timer ablaufen
unsigned long previousMillis = 0;
const long interval = 100;

//Setup

void setup() {
 
pinMode(buttonPin, INPUT_PULLUP);
  
  strip.begin();   //initialisieren
  strip.setBrightness(20);   //Golbale Helligkeit
  strip.show();  // Alles aus
  i=-1;
zeitrot = 2000;
zeitorange = 5000;
  
}

//Start des Codes
void loop()

{

int reading = digitalRead(buttonPin);
//Knopfdruck
  

  if (reading != lastButtonState) {lastDebounceTime = millis(); }
  if ((millis() - lastDebounceTime) > debounceDelay) {   
    if (reading != buttonState) {
      buttonState = reading;   
      if (buttonState == HIGH) {i++;     
       if(i>19){i=0;}     
       strip.setPixelColor(i, green); 
        ledTimers[i]=10000;
       
      
    strip.show();
       }
      }
   }
  for(i=0;i<19;i++){
  unsigned long currentMillis = millis();
if(currentMillis-previousMillis>= interval){
  previousMillis=currentMillis;
 
  if(ledTimers[i] > 0){
ledTimers[i]=ledTimers[i] - interval;}


if(ledTimers[i]<=zeitorange){            
 strip.setPixelColor(i, orange);  }        


if(ledTimers[i]<=zeitrot){            
 strip.setPixelColor(i, red);  }        

        
if(ledTimers[i]<=0){            
 strip.setPixelColor(i, black);  }        

            
            strip.show();



 }
 }

 lastButtonState = reading;


}

Thanks for the Help again, im off for today tho :slight_smile:

Yes, I see green -> orange -> red -> black. But it doesn't do the other thing, where if all the LEDs have gone black, it acts then like you restarted it - the first LED to come on will be number 0, not, e.g. #17 because that's where you left off. Small deal, it just means keeping track of how many are (still) illuminated at all, noticing that none is and resetting "i" to zero.

BTW "i" is a horrible name for a variable. It can be hard to find, since the character 'i' appears all over your code. A name like ledNumber would be easier to see and find and less likely to be confused or used elsewhere.

All the truer if we talking about a global variable as is i in your code.

Added: in fact it was your own reuse of 'i' in your for loop that broke the code - it is in conflict with your other uses of the variable elsewhere!

You got the for loop almost... instead of the for loop enclosing the millis() test against interval, the for loop should not run at all until the interval has passed.

    unsigned long currentMillis = millis();
    if(currentMillis-previousMillis>= interval){
        previousMillis=currentMillis;
 
        for(i=0;i<19;i++) {

                if(ledTimers[i] > 0){
                        ledTimers[i]=ledTimers[i] - interval;}


                if(ledTimers[i]<=zeitorange){            
                        strip.setPixelColor(i, orange);  }        


                if(ledTimers[i]<=zeitrot){            
                        strip.setPixelColor(i, red);  }        

        
                if(ledTimers[i]<=0){            
                        strip.setPixelColor(i, black);  }        
        
//            strip.show();  /* no need to do this every time! */
         }
    }

    strip.show();  /* just once, after all the LEDs have new (maybe) colors to display */

But the above still uses 'i', see your code with my minor fixes here:

You have a bit of confusion problem I am too lazy to untangle analyze - there is lastButtonState and a seemingly identical function for buttonState, I think, in any case something causes a spurious knopfdruck activation so one LED goes off all by itself at reset.

I let you figure that one out. :expressionless:

My version is mostly what you have come up with youself:

HTH

a7

1 Like

Hey thanks again. Seems to work now. I am not sure why i didnt get it to work before. I saw the problem with i beeing used in the code twice. Tried to use x, but didnt work either. My problem was probalbe the for loop enclosing millis.
Thanks again for the help.

Little bonus question:
How accurate would the timing be in a 40minute timeframe. So if I set the timer for the LED to 40 minutes, how far off would I be after 40 min.

Cheers

If you Arduino is clocked by a resonator, the error could be as much as 1 percent, or 24 seconds out after only 40 minutes.

if clocked by a crystal, the error at 40 minutes would be hard to see, certainly in this code which already only has a revolution of 0.1 seconds.

In either case, you have a constant in the code that determines the step size, this can be bumped or reduced to get +/- 1 percent changes in the time step.

const long interval = 100;

If you needed better accuracy, you could switch it all to micros() and have a larger constant to tune it with, but at that point you might just consider throwing a DS3231 RTC module on there to get accurate timing. You wouldn't need to use any features of the module other than getting a good 1 Hz signal, I forget whether that is easy or very easy. Easy in any case and inexpensive.

Now after all this time of playing with the circuit I must ask if you've ever described your need for such a device - I was entertained by some speculation amongst friends, we came up with some cray-cray ideas…

a7

i actually also had the idea with the DS 3231. I testet this code with 10 minutes and testet it with a stoppwatch... ( i know , but its the best i had at hand) the difference was 0.5 seconds which is on par with human error. If the error is under a few seconds its fine for me tho.

Well i have a production process. I get a "part" every minute or so. This can vary by +- 30 seconds. Those parts have to rest for 40-42 min. I have an apparatus to "store" the parts were i can put an LED strip on the side. With this little program I can now see when each of the parts have to be processed further. The Problem we have at the moment is: That the parts are processed to early or late which makes them either not cured or spoil.

I know its not very specific, but still quenches your curiosity :slight_smile:

Greetings

That'll do. So this thing you are building should be wee received.

I imagine if you were to say what these parts are, or for what they are to be used, you'd have to kill me, I understand. :expressionless:

BTW you can get individual neopixels, so you could space them differently if it would benefit your layout. A bit of a pain to wire, however...

a7

Well im not james bond or anything but its the internet after all you know :slight_smile: .

Our product is similar to the format of a credit card and filled with a substance wich has to be cured for that amount of time. I have a board with slots in it, 3D printed to the exact distance of the led strip. Its fairly convenient to stand them upright in the board an have a space saving method of storing them while curing and I dont have to solder them induvidually.

ive said too much already :scream:

Cheers
007