Go Down

Topic: code for multiple LEDs not working (Read 17182 times) previous topic - next topic

Lazer57

I wanted to be able to blink multiple LEDs, each on their own time frame but it isn't working. I've looked it over and don't know what the problem is, so while I look at it some more I figured I should post it in here. I have tried replacing the initital setting of time1 and time2 to 0 but that didn't help. Also, does millis() reset each time a new sketch is uploaded or does it reset with each time power is turned on?

Code: [Select]
/* Blink Multiple LEDs without Delay
*
* Turns on and off several light emitting diode(LED) connected to a digital  
* pin, without using the delay() function.  This means that other code
* can run at the same time without being interrupted by the LED code.
*/

int led1 = 13;                // LED connected to digital pin 13
int led2 = 12
int value = LOW;                // previous value of the LED
long time1 = millis();        
long time2 = millis();
long interval1 = 1000;           // interval at which to blink (milliseconds)
long interval2 = 500

void setup()
{
 pinMode(led1, OUTPUT);      // sets the digital pin as output
 pinMode(led2, OUTPUT);
}

void loop()
{
 if (millis() = time1 + interval1){
   time1 = millis();
   
   if (value == LOW)
     value = HIGH;
   else
     value = LOW;

   digitalWrite(led1, value);
 }
   
 if (millis() = time2 + interval2){
   time2 = millis();
   
   if (value == LOW)
     value = HIGH;
   else
     value = LOW;

   digitalWrite(led2, value);
 }
 
}

mikalhart

#1
Nov 02, 2008, 05:32 am Last Edit: Nov 02, 2008, 05:33 am by mikalhart Reason: 1
Lazer,

Getting multiple things to happen at the same time sometimes seems like a black art when you are first starting.  There are several solutions to your problem; I have posted one that is similar to your original code except for a few additional semicolons and tweaks here and there.  Once you understand thoroughly how the following works, you'll be well equipped to solve similar problems in the future on your own.

/* Blink Multiple LEDs without Delay
*
* Turns on and off several light emitting diode(LED) connected to a digital
* pin, without using the delay() function.  This means that other code
* can run at the same time without being interrupted by the LED code.
*/
int led1 = 13;                // LED connected to digital pin 13
int led2 = 12;
int value1 = LOW;                // previous value of the LED
int value2 = LOW;                // previous value of the LED
long time1 = millis();
long time2 = millis();

long interval1 = 1000;           // interval at which to blink (milliseconds)
long interval2 = 500;

void setup()
{
 pinMode(led1, OUTPUT);      // sets the digital pin as output
 pinMode(led2, OUTPUT);
}

void loop()
{
 unsigned long m = millis();

 if (m - time1 > interval1){
   time1 = m;

   if (value1 == LOW)
     value1 = HIGH;
   else
     value1 = LOW;

   digitalWrite(led1, value1);
 }

 if (m - time2 > interval2){
   time2 = m;

   if (value2 == LOW)
     value2 = HIGH;
   else
     value2 = LOW;

   digitalWrite(led2, value2);
 }
}

Cheers,

Mikal

Lazer57

Thanks mikalhart! Works like a charm. I am happy my code didn't require too much tweaking.

Correct me if i'm wrong:
It definitely required seperate "value"s because they blink at different rates. I figure you used unsigned long in order to increase the range since this code doesn't use negative numbers anyways. The equal sign in my if statement was wrong because its an assignment operator, while == is a comparison operator. But the > sign works just as well, if not better because it doesn't require an exact moment to complete the if statement.

Why use m instead of just using millis()? I put millis() back in place of m and it worked the same. I don't doubt that using m is better, just want to understand why.

mikalhart

Great questions all, Lazer,

The reason I made m an unsigned long is because the published return value for millis() is unsigned long, and I like to make sure things match up.  Lots of mistakes are made because someone used a variable that is too small to accomodate all the possible returns.  It's good programming practice.

Your observation about = vs. == is right on.  I changed it to > because if you use == you run the risk of missing an important event.  Consider for example the case where you were turning on an LED when millis turns precisely to 1000.  If millis() is 999 it won't trigger, but the next time through the loop, if millis() is 1001 (which it might be), then your LED will never illuminate.

It's just my instinct for performance tuning that makes me save the return value of millis() in a variable rather than calling millis() multiple times.  In this case it makes very little actual difference, but if I can avoid calling a function twice I will usually do that.

And to answer your earlier question, uploading a sketch causes a reset, which sets the millis() counter back to 0.

Good luck!

Mikal



trav603

Hello, I'm new to arduino and programming. I'm trying to make a project with LEDs and this thread was certainly helpful but just have a few questions, if you wouldn't mind.

1) when you call up unsigned long m = millis(); does it start counting right at the beginning of the program?

2) when does time1/time2 start counting? say 10ms goes by so m - time1 > interval1 -- 10 - 10 > 1000? im a little confused to say the least..

3) the project im trying to accomplish will probably use 20 LEDs, but symmetrical, would it be possible to wire 2 LEDs to 1 pin? that way only need to control 10 pins?

4) since i want to do so many LEDs im gonna have a ton of int led1 = 12; etc all the way to int led10, then also int value1 through int value10, correct? anyway to tidy all that up?

sorry if these questions are a little out of left field haha. thanks in advance!

trav603

actually, i can just use delay(); since i dont need to run any other code at the same time, im only driving LEDs BUT i wouldn't mind seeing answers to my crazy questions :)

this does lead me to another question: instead of doing like
 digitalWrite{led1, HIGH);
 delay(500);
 digitalWrite(led1, LOW);
 delay(500);
for every pin, is there an easier, shorter way to accomplish this? also, is there a way to fade an LED on arduino?

mikalhart

#6
Nov 03, 2008, 07:50 pm Last Edit: Nov 03, 2008, 08:00 pm by mikalhart Reason: 1
Hi trav, and welcome to Arduino.  I'm happy to try to answer your questions.

millis() is a system function that returns the number of milliseconds that have elapsed since the system was last reset or powered on.  For example, if the line "unsigned long m = millis();" is executed early in the program, you might find that m has the value 8 or 72 or something.

The loop function is called repeatedly, and each time through, the value of "m - time1" increases slightly.  For example, m might be 23 and time1 0.  Is 23 - 0 greater than 1000? No, so nothing happens with LED #1.  Next time through m might be 25.  Nothing again.  Eventually though -- after about a second -- m will reach 1001, and the if statement will trigger (because 1001-0 > 1000).  At that point the LED flips state and, significantly, time1 is set to 1001.  The next time through the loop we'll have m = 1003, and since 1003 - 1001 is not greater than 1000, nothing happens until the next second elapses.  A lot of words to describe it!  Sorry! :)

If you are going to follow the same strategy with multiple LEDs, rather than repeating very similarly named variables and lines of code, I'd recommend using arrays.  Here, for example, is some code that blinks 18 different LEDs at varying rates:

/* Blink Multiple LEDs without Delay
*
* Turns on and off several light emitting diode(LED) connected to a digital
* pin, without using the delay() function.  This means that other code
* can run at the same time without being interrupted by the LED code.
*/
const int NUMLEDS = 18;
byte pin[NUMLEDS] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
byte state[NUMLEDS] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
unsigned long interval[NUMLEDS] = {100, 500, 1000, 750, 750, 750, 750, 750, 5000, 250, 1000, 1000, 1000, 50, 7500, 500, 400, 250};
unsigned long time[NUMLEDS];

void setup()
{
 for (int i=0; i<NUMLEDS; ++i)
   pinMode(pin, OUTPUT);
}

void loop()
{
 unsigned long m = millis();

 for (int i=0; i<NUMLEDS; ++i)
   if (m - time > interval)
   {
     time = m;
     state = state == LOW ? HIGH : LOW;
     digitalWrite(pin, state);
   }
}

Mikal

mikalhart

Quote
actually, i can just use delay(); since i dont need to run any other code at the same time, im only driving LEDs BUT i wouldn't mind seeing answers to my crazy questions  

this does lead me to another question: instead of doing like
digitalWrite{led1, HIGH);
delay(500);
digitalWrite(led1, LOW);
delay(500);
for every pin, is there an easier, shorter way to accomplish this? also, is there a way to fade an LED on arduino?


Trav, if you use delay, then you will only be able to to blink one LED at a time.  I doubt that's what you want.  You can fade an LED by hooking it up to one of the Arduino pins that support PWM ("pulse width modulation") and using the analogWrite function to vary its brightness.  See http://arduino.cc/en/Reference/AnalogWrite.

Mikal

trav603

#8
Nov 04, 2008, 06:54 am Last Edit: Nov 04, 2008, 07:07 am by trav603 Reason: 1
oh i wasnt totally aware of that. arent there only 3 pins with PWM? in actuality one of the projects i wanna do i could get away with blinking one pin at a time but i wouldn't mind learning other ways.

basically im trying to animate a triangle. let me test my ASCII art skills.

     /\         O          /\         /\         /\
    /  \       /  \       O  O     /   \       /  \                 O = Lit LED
   /    \     /    \     /     \    O    O    /    \          <-- loop for as long as
  /      \   /      \   /       \  /       \  O      O              arduino is
                                                                         powered on

EDIT: and thanks for answering my questions! i appreciate it

mikalhart

#9
Nov 04, 2008, 07:24 am Last Edit: Nov 04, 2008, 07:25 am by mikalhart Reason: 1
Looking at your ASCII picture, it would appear that you have 7 LEDs?  If this is ALL you want to do, it may indeed be conceptually simpler to use delays, looping with something like this:

Code: [Select]
void loop()
{
 digitalWrite(pin4, HIGH);

 delay(200);

 digitalWrite(pin4, LOW); // turn off
 digitalWrite(pin3, HIGH);
 digitalWrite(pin5, HIGH);

 delay(200);

 digitalWrite(pin3, LOW); // turn off
 digitalWrite(pin5, LOW); // turn off
 digitalWrite(pin2, HIGH);
 digitalWrite(pin6, HIGH);

 delay(200);

 digitalWrite(pin2, LOW); // turn off
 digitalWrite(pin6, LOW); // turn off
 digitalWrite(pin1, HIGH);
 digitalWrite(pin7, HIGH);

 delay(200);

 digitalWrite(pin1, LOW); // turn off
 digitalWrite(pin7, LOW); // turn off
}


Mikal

trav603

thanks mikal, you make it look so easy :) haha. what i was actually thinking of is maybe 19 LEDs but wiring 2 LEDs per pin(except the top LED, that can be solo). my little ASCII presentation was just a small example of my idea but not too far off. either way, it seems like your code would work well for me if my 'wiring 2 leds to 1 pin' idea works.

mikalhart

I think you can drive 2 LEDs with one pin, but you'd better check with the HW boys and girls to make sure.

I think if I had 19 LEDs being driven by (presumably) 10 pins, I'd write simple code like this:

Code: [Select]
byte mypins[10] = {6,5,4,3,2,7,8,9,10,11}; // or whatever your 10 pins are, starting with triangle top

void setup()
{
 for (int i=0; i<10; ++i)
   pinMode(mypins[i], OUTPUT);
}

void loop()
{
 for (int i=0; i<10; ++i)
 {
   if (i > 0)
     digitalWrite(mypins[i-1], LOW); // turn off previous LEDs
   digitalWrite(mypins[i], HIGH);
   delay(200);
 }
 digitalWrite(mypins[9], LOW); // turn off the last LED
}


Does that seem reasonable?

Mikal

trav603

#12
Nov 04, 2008, 08:00 am Last Edit: Nov 04, 2008, 08:11 am by trav603 Reason: 1
wow, nice job Mikal :P thanks for all your help. im not much of a programmer but i think i can follow/understand the code you wrote. I gotta say its pretty cool to see this project i was kinda scratching my head about be summed up in like a dozen lines of code haha. thanks again. i think i could just wire 2 LEDs in parallel to a pin to control that many LEDs with 1 arduino. should be a good experiment, ill post pics when i get it done.


EDIT: just a question. if im not using pins 1,2,13 what happens when i is increased to 1 or 2? and if i wanna use pins 10,11,12 wouldnt i<10 skip those pins?

mikalhart

"i" isn't referring to the pin number here, it's the index into the "mypins" array.  So, using my example, when i is 0, pin 6 will turn on.  When i is 1, pin 6 will turn off and pin 5 turn on, and so forth up until i=9, which turns off pin 10 and on pin 11.  Your job is to fill up the mypins array with the 10 pins you have used, starting with the tip of the triangle and working down.

(I'm assuming that you'll have 19 LEDs attached to those 10 pins, two per pin except for the first pin.)

Savvy?

Mikal

trav603

yes! i get it. thank you and sorry for the million questions haha. i do appreciate tthe help!

Go Up