Help with multi-color LED

Hello everyone, new to this forum. Done a few Arduino projects before. I am wondering if anyone can help me out. I'm trying to control an RGB LED, I want it to cycle through all possible colors for 20 seconds and then pick a random color to hold after this point. Ideally, this would also be activated using a button. I've gotten all these separate things working, and some somewhat working together...but not perfectly.

Main problem right now, button isn't functioning and after it cycles through the colors, it just executes multiple random colors and doesn't pick one to remain.

Take a look at my code and let me know what you think is going wrong.

// Output
int redPin = 10; // Red LED, connected to digital pin 9
int greenPin = 9; // Green LED, connected to digital thatpin 10
int bluePin = 11; // Blue LED, connected to digital pin 11
int inPin = 0; // choose the input pin (for a pushbutton)

// Program variables
int redVal = 1; // Variables to store the values to send to the pins
int greenVal = 255; // Initial values are Red full, Green and Blue off
int blueVal = 1;
int pinVal = 0;
int hold = 2000;

int i = 0; // Loop counter
int wait = 3; // 50ms (.05 second) delay; shorten for faster fades
int timer = 0;
int touch = 0;
int redNew = 0;
int blueNew = 0;
int greenNew = 0;

void setup()
{
pinMode(redPin, OUTPUT); // sets the pins as output
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(inPin, INPUT);
timer = 0;
i = 0;
}

// Main program
void loop(){main:

timer += 1;
i += 1; // Increment counter
if (i < 255) // First phase of fades
{
redVal -= 1; // Red down
greenVal += 1; // Green up
blueVal = 1; // Blue low

}
else if (i < 509) // Second phase of fades
{
redVal = 1; // Red low
greenVal -= 1; // Green down
blueVal += 1; // Blue up
}
else if (i < 764) // Third phase of fades
{
redVal += 1; // Red up
greenVal = 1; // Green low
blueVal -= 1; // Blue down
}
else {
if (timer < 3){
redVal = redNew;
greenVal = greenNew;
blueVal = blueNew;
redNew = random(255);
greenNew = random(255);
blueNew = random(255);
delay(5000);
touch = analogRead(inPin);
if(touch > 10){
i = 0;
}
}
else{
i = 0;
}
}

analogWrite(redPin, redVal); // Write current values to LED pins
analogWrite(greenPin, greenVal);
analogWrite(bluePin, blueVal);
delay(wait);
analogRead(inPin);
}

One quick observation I have is that the clause

if (timer < 3){
... }

will never execute because the condition is tested only when i>=764. But since i and timer are incremented in tandem, timer will never be less than 3 when i>=764 (at least not until it rolls over, but I doubt that's what you meant).

Mikal

How would something like this be? I changed the button read code to a digitalRead (which is typically what you do for buttons, but feel free to change it back). This code tracks what phase you are in (0, 1, or 2) if the "trigger" has not yet occurred. If the 20 seconds expires or the button is pressed, this sets a "trigger" variable to true. If trigger is true, then it calculates a random color and displays it.

One note: your fade routines do not actually display every possible color. For example, R=128, G=128, B=128 will never been generated.

// Output
int redPin   = 10;   // Red LED,   connected to digital pin 9
int greenPin = 9;  // Green LED, connected to digital thatpin 10
int bluePin  = 11;  // Blue LED,  connected to digital pin 11
int inPin    = 0;   // choose the input pin (for a pushbutton)

// Program variables
int redVal   = 255; // Variables to store the values to send to the pins
int greenVal = 1;   // Initial values are Red full, Green and Blue off
int blueVal  = 1;
int wait     = 3; // 3ms (.003 second) delay; shorten for faster fades
int phase    = 0; // phase number for fades
bool triggered = false; // records whether the button has been pressed (or time expired)

void setup()
{
 pinMode(redPin, OUTPUT);   // sets the pins as output
 pinMode(greenPin, OUTPUT);   
 pinMode(bluePin, OUTPUT);
 pinMode(inPin, INPUT);
}

// Main program
void loop()
{
  // Have we triggered the random color by pressing a button (or by time expiring)?
  if (!triggered && (digitalRead(inPin) == LOW || millis() > 20000))
  {
    redVal = random(255);
    greenVal = random(255);
    blueVal = random(255);
    triggered = true;
  }
  
  if (!triggered)
  {
    if (phase == 0)
    {
      redVal--; // Red down
      greenVal++; // Green up
      if (greenVal == 255)
        phase = 1;
    } 
    else if (phase == 1)
    {
      greenVal--; // Green down
      blueVal++; // Blue up
      if (blueVal == 255)
        phase = 2;
    }
    else if (phase == 2)
    {
      blueVal--; // Blue down
      redVal++; // Red up
      if (redVal == 255)
        phase = 0;
    }
 }
 analogWrite(redPin,   redVal);   // Write current values to LED pins
 analogWrite(greenPin, greenVal); 
 analogWrite(bluePin,  blueVal); 
 delay(wait);
}

Mikal

Wow, thank you so much Mikal. Your edits to my code made it much smoother, and closer to what I'm trying to see happen here. However, I'm still having problems with the button, I wanted to button to restart the loop where it cycles through all colors and lands on one. The other problem seems to be the random function, it doesn't choose a random color, in fact the color is always a light blue-purple. I've read that there are problems with random functions in this version of arduino, and tried to implement code I saw you suggested for others but I can't get it working.

Again, thanks for your help!!!! You're a lifesaver.

I'm glad we're making progress. To address your problems, I changed the button code. Now, bringing the button low changes the triggered state to false, which should effectively start the looping process over.

Assuming you don't have analog pin 0 hooked up to anything, you can get a pretty good random by calling srand(analogRead(0)) (see the code and read about srand in the documentation).

Try this:

// Output
int redPin = 10; // Red LED, connected to digital pin 9
int greenPin = 9; // Green LED, connected to digital thatpin 10
int bluePin = 11; // Blue LED, connected to digital pin 11
int inPin = 0; // choose the input pin (for a pushbutton)

// Program variables
int redVal = 255; // Variables to store the values to send to the pins
int greenVal = 1; // Initial values are Red full, Green and Blue off
int blueVal = 1;
int wait = 3; // 3ms (.003 second) delay; shorten for faster fades
int phase = 0; // phase number for fades
unsigned long start_time = 0;
bool triggered = false; // records whether the button has been pressed (or time expired)

void setup()
{
pinMode(redPin, OUTPUT); // sets the pins as output
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(inPin, INPUT);
srand(analogRead(0));
}

// Main program
void loop()
{
// Have we reset the loop with a button?
if (digitalRead(inPin) == LOW)
{
triggered = false;
start_time = millis();
redVal = 255;
greenVal = 1;
blueVal = 1;
phase = 0;
}

if (!triggered && millis() - start_time > 20000)
{
redVal = random(255);
greenVal = random(255);
blueVal = random(255);
triggered = true;
}

if (!triggered)
{
if (phase == 0)
{
redVal--; // Red down
greenVal++; // Green up
if (greenVal == 255)
phase = 1;
}
else if (phase == 1)
{
greenVal--; // Green down
blueVal++; // Blue up
if (blueVal == 255)
phase = 2;
}
else if (phase == 2)
{
blueVal--; // Blue down
redVal++; // Red up
if (redVal == 255)
phase = 0;
}
}
analogWrite(redPin, redVal); // Write current values to LED pins
analogWrite(greenPin, greenVal);
analogWrite(bluePin, blueVal);
delay(wait);
}

Mikal

this one really helps me too. Thanks

Oops: one of the hazards with coding blind is that little errors creep in.

if (!triggered && start_time - millis() > 20000))
should be

if (!triggered && millis() - start_time > 20000)

(I fixed it in the original post.) There may be more mistakes, but we're getting close. :wink:

Mikal

OK,
Definitely making so much progress. I can't thank you enough for taking the time to help me with this! That srand function is amazing, reading the analog pin...I didn't even know that was possible. So after playing with the code I got the button to trigger random colors, and when it is pressed down completely it will cycle through the colors. However, no loop and then solid random color...if that makes sense. With your latest edit, the button no longer did anything. Strange. Another thing, with the first rendition the cycle would loop, when the button was depressed it would pick a random color and attempt to do the loop again when the button was let up...however, the colors were choppy and haphazard, not smooth like the first cycle.

Let me know if this isn't clear,
thank you!

One more little bug I see is that when the button is down it should reset the colors back to their original states, so

if (digitalRead(inPin) == LOW)
 {
   triggered = false;
   start_time = millis();
 }

should become

if (digitalRead(inPin) == LOW)
 {
   triggered = false;
   start_time = millis();
   redVal = 255;
   greenVal = 1;
   blueVal = 1;
   phase = 0;  
 }

I'll eyeball it and see if I can't find more problems.

Mikal

OK, I got it working! I looked up how other people have used the srand function and tried a few things out. I added % 255 to the end of the random() statements. It cycles through each color for 20 seconds and picks a random color and holds it. When the button is pressed, the red color shows and it cycles through it again. I'm not sure why red shows when the button is depressed but I can live with it. Yaaaaay!!!!!! Here is the final code:

// Output
int redPin = 10; // Red LED, connected to digital pin 9
int greenPin = 9; // Green LED, connected to digital thatpin 10
int bluePin = 11; // Blue LED, connected to digital pin 11
int inPin = 2; // choose the input pin (for a pushbutton)

// Program variables
int redVal = 255; // Variables to store the values to send to the pins
int greenVal = 1; // Initial values are Red full, Green and Blue off
int blueVal = 1;
int wait = 3; // 3ms (.003 second) delay; shorten for faster fades
int phase = 0; // phase number for fades
unsigned long start_time = 0;
bool triggered = false; // records whether the button has been pressed (or time expired)

void setup()
{
pinMode(redPin, OUTPUT); // sets the pins as output
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
pinMode(inPin, INPUT);
srand(analogRead(0));
}

// Main program
void loop()
{
// Have we triggered the random color by time expiring or reset with a button?
if (digitalRead(inPin) == HIGH)
{
triggered = false;
start_time = millis();
redVal = 255;
greenVal = 1;
blueVal = 1;
phase = 0;
}

if (!triggered && millis() - start_time > 20000)
{
redVal = random() % 255;
greenVal = random() % 255;
blueVal = random() % 255;
triggered = true;
}

if (!triggered)
{
if (phase == 0)
{
redVal--; // Red down
greenVal++; // Green up
if (greenVal == 255)
phase = 1;
}
else if (phase == 1)
{
greenVal--; // Green down
blueVal++; // Blue up
if (blueVal == 255)
phase = 2;
}
else if (phase == 2)
{
blueVal--; // Blue down
redVal++; // Red up
if (redVal == 255)
phase = 0;
}
}
analogWrite(redPin, redVal); // Write current values to LED pins
analogWrite(greenPin, greenVal);
analogWrite(bluePin, blueVal);
delay(wait);
}

You're the best Mikal!
thanks
Jessica

Actually, I got a little too excited. The randoms don't seem all that random...they are various shades of red + blue usually. Occasionally I'll get a white-ish green. However, there is alot of white in the randoms for the majority of times...hmm. I'll keep tinkering with it. I'm trying to figure out exactly what the % operation does...looks like it is subtracting from the original values? Know of a better thing to put in there?

Also, I figured out the red comes from the red led set to 255 when the button is pressed...duh.

It's a little confusing, but there are TWO different random() functions. random() (with no parameters) is built into the standard library. It returns a number between 0 and RAND_MAX (32767), so to slice it down to 255, you use the modulus operator (% 255). random(long max) is an Arduino function that returns a number between 0 and max.

If I were you, I'd put it back the way I originally had it:

redVal = random(255);
...

(If you don't, srand() doesn't affect the value.) Either put them back or call srandom(analogRead(0)); srandom seeds the standard library version of random.That should make the color more random, although I suspect that generating a random color using random R, G, and B components will not usually produce satisfying colors. If you don't like the washed out whites, it might be a good idea to develop an array of predetermined satisfying colors, and choose randomly from among them.

Mikal

// Output
int redPin   = 10;   // Red LED,   connected to digital pin 9
int greenPin = 9;  // Green LED, connected to digital thatpin 10
int bluePin  = 11;  // Blue LED,  connected to digital pin 11
int inPin    = 2;   // choose the input pin (for a pushbutton)

Comments shouldn't just be a rephrasing of the code. In this case, your comments don't even jive with the code, it looks like you changed your mind in the code but didn't correct the comments to reflect the new values. What's on pin 9 again? And there's a typo in there as well ("thatpin"). This just sows confusion when you're trying to remember things later.

Comments should indicate the purpose of something, so you can remember why the line is there. Provide additional information, but not redundant information.

// Output pin designations
int redPin   = 10;  // LED
int greenPin = 9;   // LED
int bluePin  = 11;  // LED
int inPin    = 2;   // pushbutton

Mostly, choosing good variable names is the best way of being clear about the code. You picked good names for these. Variables called tmp1 or value or mydummy tell you nothing about what the code is trying to compute, nor why. Variables called redPin are much more descriptive.

Hey thanks for the critique. I'm still learning, I grabbed this code from someone else and edited it. Sorry it was so confusing.

No worries, flobotic. It didn't confuse me, but it's good to develop proper coding standards. Just like learning to solder clean joints, it's important to have proper software discipline.

Hey Mikal, do you have any experience with pyserial and Arduino? I'm trying to get python to speak to Arduino and multicolor LED...so when I type in 1 to python it changes to a blue color, type 2 changes to green. Etc. However with the following code, it only responds with a green shade, no matter what number I'm putting in. Also, when I was reading the serial the output was always 48! No idea whats going on...lemme know if you have any insight. Thanks!

// Output
int redPin = 10; // Red LED
int greenPin = 9; // Green LED
int bluePin = 11; // Blue LED

// Program variables
int serVal = 0; // variable to store the data from the serial port
int redVal = 0; // Variables to store the values to send to the pins
int greenVal = 0;
int blueVal = 0;

void setup()
{
pinMode(redPin, OUTPUT); // sets the pins as output
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
Serial.begin(9600);
}

// Main program
void loop() {
serVal = Serial.read(); // read the serial port

if (serVal > '0' && serVal <= '4' ) {
serVal = serVal - '0'; // convert from character to number
for(int i=0; i < serVal; i++) {
{
if (serVal = '0')
{
redVal = 10;
greenVal = 255;
blueVal = 0;
}
else if (serVal = '1')
{
redVal = 15;
greenVal = 165;
blueVal = 170;
}
else if (serVal = '2')
{
redVal = 255;
greenVal = 230;
blueVal = 0;
}
else if (serVal = '3')
{
redVal = 220;
greenVal = 90;
blueVal = 0;
}
else if (serVal = '4')
{
redVal = 255;
greenVal = 0;
blueVal = 0;
}
else
{
redVal = 0;
greenVal = 0;
blueVal = 0;
}
}
analogWrite(redPin, redVal); // Write current values to LED pins
analogWrite(greenPin, greenVal);
analogWrite(bluePin, blueVal);
}
}
}

flobotic--

I don't know the first thing about pyserial, but I can tell you why your code is failing. You are using the assignment operator = where you want the equality test operator ==. Change all the

if (serVal = '0')

to

if (serVal == '0')

I'll leave it as a exercise to the reader to figure out why the current code only displays green. :slight_smile:

Mikal

Ok I tried changing those, but now no colors show up! Ha, who knows...

Ha, who knows...

I do! :slight_smile: When you wrote

if (serVal > '0' && serVal <= '4' ) {
   serVal = serVal - '0';          // convert from character to number

you "converted from character to number"! But when you do

 if (serVal == '0')

you're still comparing against the character! Change that to

 if (serVal == 0)

etc., and it will work fine.

Mikal

WOW Mikal
you are pretty much my favorite person right now!!
Thank you! :smiley: