# How to Make Proper Rainbow and Random Colors With the RGB Color Model

37,705

83

18

## Introduction: How to Make Proper Rainbow and Random Colors With the RGB Color Model

It would seem that such simple things as a rainbow effect or smoothly changing random colors on an RGB LED are pretty trivial. However, time and again I come upon projects using strange approaches to this matter. The most common strangeness is the use of the HSV color model for RGB LED programming. Really, people! An RGB LED consists of three LEDs: red, green and blue. What’s better suited to control such a thing than the red, green and blue color model? Why complicate matters by additional algorithms converting data from a very different set of rules to the native one? Still, HSV persists; it’s even present in an excellent ShiftPWM library...

In any case, one of the readers asked for a code in my previous instructable dealing with RGB LED strips connection to an Arduino, and I promised an article. Here it goes, hopefully it’s not too late for some of the readers to make some quick Christmas lights. Here you’ll learn of different possible approaches to an RGB rainbow, random colors and their transitions, as well as some bits on the usefulness of a sine wave and lookup tables.

You’ll need just an Arduino and an RGB LED to run the sketches present.

But first, let’s deal with the HSV. If you want to get to the sketches ASAP, you may skip to the second step now.

## Step 1: To HSV or Not to HSV

The most important HSV achievement is bringing together two color worlds: the light-emitting one (RGB) and the light-reflecting one (CMYK). These worlds are very different: for example, mixing red and green on the screen results in pure yellow, but try mixing them on paper and you’ll get a dirty blot. Thus HSV is pretty useful for graphic designers —they can do everything in one model and be reasonably sure that the result will look the same on the screen and on paper. However, as with all the other universal solutions, some constraints are imposed, thus, people who work exclusively with printed designs have to rely on CMYK, and people dealing with LEDs are better off with the RGB model. It’s just way better suited for light-emitting sources!

Let me dig a bit deeper into this. HSV stands for Hue, Saturation and Value (or Brightness). Hue is a circle, it has values from 0 to 360. Saturation and Value are measured in percents (0 to 100). Not exactly suited for a world of bytes... And what’s more, two of these values are almost useless when dealing with LEDs, because they are made for complex hi-res designs, not single pixels (aka RGB LEDs).

Saturation can be used to produce soft, pastel colors, without the complex calculations needed with RGB model. But with LEDs you don’t want pastel colors (they look just like different shades of white, in fact): you need your colors as bright and clear as possible, so Saturation is at 100% almost all the time (excluding the animation/transformation moments, but they are done with algorithms; more on that later).

With Brightness you can achieve some dark colors on screen. Lower brightness in red (H=0) and you get burgundy, lower brightness in orange (H=40) and get brown. Do the same with LEDs and you’ll get the same red and yellow colors, just a bit dimmer; there’s no such thing as a ‘brown LED’. You see deep dark red on the screen because there are different colors present there; try to fill it with dark red entirely, turn off all the lights and you’ll see a red rectangle; you won’t be able to tell whether it’s ‘burgundy’ or ‘scarlet’ due to absence of any reference. Same with LEDs: even if you have a bunch of them, they are still separate. Thus the use of Value in HSV model with RGB LEDs is limited to setting global brightness, fadeouts and transitions; all this can be easily done in RGB by simple simultaneous division applied to each channel.

Another thing that makes HSV Value/Brightness useless with LEDs is the fact that diodes, unlike computer monitors, are not calibrated, and are slow; their brightness is not linear. At 50% they look almost the same as at 100%, which, in fact, is very good, because it helps control power consumption.

Which leaves us with Hue: a nice circle containing all the colors of a rainbow in a neat 0-360° sequence. Seems like producing a rainbow with it is the easiest thing imaginable: the simple for (int k=0; k<360; k++) cycle will do the trick. What can be wrong with that?

See the picture with normal HSV graph above. As the model was made to contain every possible color, it considers yellow (HSV = 60, 100, 100) to have both red and green of the RGB model at maximum (255, 255, 0). With RGB LED it means that both red and green diodes are fully on. Which means that every composite color (yellow, cyan, magenta) consumes two times more current than a base one (red, green, blue). Not good, especially if you’re dealing with long LED strips or a matrix of RGB LEDs powered by USB. The second graph (‘Power-conscious HSV’) looks better, but it’s not the pure Hue of HSV, and is easier implemented with RGB model.

The third graph shows a sine wave rainbow. In my opinion it’s the best one, as it produces deeper base colors and is devoid of spikes. And it can be implemented only in the RGB model. On to the next step.

## Step 2: Rainbow Time!

The video above shows same five LEDs running rainbow in three modes: regular HSV (top), ‘power-conscious’ HSV (middle) and sine wave (bottom). Filming LEDs is not an exactly rewarding experience, but hopefully you can see the difference between different modes. There’s a B&W footage at the end, it clearly shows the spikes of HSV modes. In any case, here is the code that’ll let you repeat the experience yourself:

```// uint8_t is the same as byte// uint16_t is unsigned int
// I just noticed that I mixed these in this sketch, sorryconst uint8_t lights[360]={
0,   0,   0,   0,   0,   1,   1,   2,
2,   3,   4,   5,   6,   7,   8,   9,
11,  12,  13,  15,  17,  18,  20,  22,
24,  26,  28,  30,  32,  35,  37,  39,
42,  44,  47,  49,  52,  55,  58,  60,
63,  66,  69,  72,  75,  78,  81,  85,
88,  91,  94,  97, 101, 104, 107, 111,
114, 117, 121, 124, 127, 131, 134, 137,
141, 144, 147, 150, 154, 157, 160, 163,
167, 170, 173, 176, 179, 182, 185, 188,
191, 194, 197, 200, 202, 205, 208, 210,
213, 215, 217, 220, 222, 224, 226, 229,
231, 232, 234, 236, 238, 239, 241, 242,
244, 245, 246, 248, 249, 250, 251, 251,
252, 253, 253, 254, 254, 255, 255, 255,
255, 255, 255, 255, 254, 254, 253, 253,
252, 251, 251, 250, 249, 248, 246, 245,
244, 242, 241, 239, 238, 236, 234, 232,
231, 229, 226, 224, 222, 220, 217, 215,
213, 210, 208, 205, 202, 200, 197, 194,
191, 188, 185, 182, 179, 176, 173, 170,
167, 163, 160, 157, 154, 150, 147, 144,
141, 137, 134, 131, 127, 124, 121, 117,
114, 111, 107, 104, 101,  97,  94,  91,
88,  85,  81,  78,  75,  72,  69,  66,
63,  60,  58,  55,  52,  49,  47,  44,
42,  39,  37,  35,  32,  30,  28,  26,
24,  22,  20,  18,  17,  15,  13,  12,
11,   9,   8,   7,   6,   5,   4,   3,
2,   2,   1,   1,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0,
0,   0,   0,   0,   0,   0,   0,   0};const uint8_t HSVlights[61] =
{0, 4, 8, 13, 17, 21, 25, 30, 34, 38, 42, 47, 51, 55, 59, 64, 68, 72, 76,
81, 85, 89, 93, 98, 102, 106, 110, 115, 119, 123, 127, 132, 136, 140, 144,
149, 153, 157, 161, 166, 170, 174, 178, 183, 187, 191, 195, 200, 204, 208,
212, 217, 221, 225, 229, 234, 238, 242, 246, 251, 255};const uint8_t HSVpower[121] =
{0, 2, 4, 6, 8, 11, 13, 15, 17, 19, 21, 23, 25, 28, 30, 32, 34, 36, 38, 40,
42, 45, 47, 49, 51, 53, 55, 57, 59, 62, 64, 66, 68, 70, 72, 74, 76, 79, 81,
83, 85, 87, 89, 91, 93, 96, 98, 100, 102, 104, 106, 108, 110, 113, 115, 117,
119, 121, 123, 125, 127, 130, 132, 134, 136, 138, 140, 142, 144, 147, 149,
151, 153, 155, 157, 159, 161, 164, 166, 168, 170, 172, 174, 176, 178, 181,
183, 185, 187, 189, 191, 193, 195, 198, 200, 202, 204, 206, 208, 210, 212,
215, 217, 219, 221, 223, 225, 227, 229, 232, 234, 236, 238, 240, 242, 244,
246, 249, 251, 253, 255};uint8_t outputPins[6] = {3, 5, 6, 9, 10, 11}; // PWM pins// setRGBpoint (0, ...) for pins 3, 5, 6; setRGBpoint (1, ...) for pins 9, 10, 11.
// See array above
void setRGBpoint(byte LED, uint8_t red, uint8_t green, uint8_t blue)
{
// this code is for common anode LEDs. If you use common cathode ones,
// remove the '255-' bits.
analogWrite(outputPins[LED*3], 255-red);
analogWrite(outputPins[LED*3+1], 255-green);
analogWrite(outputPins[LED*3+2], 255-blue);
}// the real HSV rainbow
void trueHSV(byte LED, int angle)
{
byte red, green, blue;  if (angle<60) {red = 255; green = HSVlights[angle]; blue = 0;} else
if (angle<120) {red = HSVlights[120-angle]; green = 255; blue = 0;} else
if (angle<180) {red = 0, green = 255; blue = HSVlights[angle-120];} else
if (angle<240) {red = 0, green = HSVlights[240-angle]; blue = 255;} else
if (angle<300) {red = HSVlights[angle-240], green = 0; blue = 255;} else
{red = 255, green = 0; blue = HSVlights[360-angle];}
setRGBpoint(LED, red, green, blue);
}// the 'power-conscious' HSV rainbow
void powerHSV(byte LED, int angle)
{
byte red, green, blue;
if (angle<120) {red = HSVpower[120-angle]; green = HSVpower[angle]; blue = 0;} else
if (angle<240) {red = 0;  green = HSVpower[240-angle]; blue = HSVpower[angle-120];} else
{red = HSVpower[angle-240]; green = 0; blue = HSVpower[360-angle];}
setRGBpoint(LED, red, green, blue);
}// sine wave rainbow
void sineLED(byte LED, int angle)
{
setRGBpoint(LED, lights[(angle+120)%360], lights[angle],  lights[(angle+240)%360]);
}void setup() {
}void loop() {

for (int k=0; k<360; k++)
{// uncomment the mode (or modes) you need below.
// with all six PWM outputs connected you may use 2 modes, change one 0 to 1.trueHSV(0, k);//powerHSV(0, k);//sineLED(0, k);delay(30);
}
}```

Just connect one or two RGB LEDs to your Arduino and uncomment the needed routines in the loop(). Note that this code was written for common anode LEDs, if you have common cathode ones remove all three ‘255-‘ from the setRGBpoint() function.

The setRGBpoint() function itself is included for easier transition between PWM outputs and LED drivers. Change it accordingly if you’re using the latter. Note that you can still use one byte values, just multiply them in the function with ‘<<4’ for 12-bit output and ‘<<8’ for 16-bit one.

This code is enough to get you started, but if you want it explained a bit, read on (and yes, the random colors will follow shortly).

## Step 3: Lookup Tables

A large first part of the code in previous step consists of big constant arrays. These are lookup tables storing pre-calculated values for sine wave and HSV functions. Of course, it is possible instead to calculate the exact value of each LED the time it is turned on, for example, for normal HSV:

```byte red, green, blue;  if (angle<60) {red = 255; green = round(angle*4.25-0.01); blue = 0;} else
if (angle<120) {red = round((120-angle)*4.25-0.01); green = 255; blue = 0;} else
if (angle<180) {red = 0, green = 255; blue = round((angle-120)*4.25-0.01);} else
if (angle<240) {red = 0, green = round((240-angle)*4.25-0.01); blue = 255;} else
if (angle<300) {red = round((angle-240)*4.25-0.01), green = 0; blue = 255;} else
{red = 255, green = 0; blue = round((360-angle)*4.25-0.01);}
setRGBpoint(LED, red, green, blue);```

These calculations will free some dynamic memory, but at the cost of program memory and processing time. While it is more or less OK with simple multiplication of HSV, doing sine waves in realtime requires operations with floating point and most certainly should be avoided, hence the lookup tables.

Note that the sine wave lookup table even contains an array of zeroes at the end – that’s because I use these tables a lot and found that a hundred more filled bytes of dynamic memory is a good trade-off for a cleaner code.

You can calculate these tables during setup() instead of inserting them in the code – again, as I use them a lot, I prefer to do them once and then just copy-paste the arrays.

You may want to make your own table of different size or with different values, so here’s the sketch. It will print all three arrays in the Serial Monitor window, it’s easy to copy-paste them from there.

```uint8_t lights[360];uint16_t kkk;void setup() {Serial.begin(9600);  //sine wave
Serial.println("const uint8_t lights[360]={");
for (float k=PI; k<(3*PI); k=k+PI/120)
{
lights[kkk]=int((cos(k)+1)*127.7); // I use cosinus
if (lights[kkk]<10) Serial.print(" "); // I like to keep the table clean
if (lights[kkk]<100) Serial.print(" "); // told you I use them a lot!
Serial.print(lights[kkk]);
if (kkk<255) Serial.print(", "); //'if' portion is useful if you're not filling the
// rest of the table with zeroes. It makes sure there's
// no extra comma after the final value of array.
// I keep it here just for that situation
if (kkk%8==7) Serial.println();  // new line for cleaner table!
kkk++;
}for (; kkk<360; kkk++) // fill the rest with zeroes
{
Serial.print("  0");
if (kkk<359) Serial.print(", ");
if (kkk%8==7) Serial.println();
}
Serial.println("};");// HSV
// note that unlike the previous one these two just print out the numbers without storing them
// change the Serial.print to lights[k]= if you need to store the values in lights[] array
// for later use in the loop()
Serial.println("const uint8_t HSVlights[61] = {");
for (int k=0; k<61; k++)
{
Serial.print(round(k*4.25-0.01));
if (k<60) Serial.print(", ");
}
Serial.println("};");

//power-conscious HSV
Serial.println("const uint8_t HSVpower[121] = {");
for (int k=0; k<121; k++)
{
Serial.print(round(k*2.125-0.01));
if (k<120) Serial.print(", ");
}
Serial.println("};");
}void loop() {
}```

It is a good idea to keep the lookup-table-generating code in the setup() portion of your sketch until you are perfectly satisfied with their results, then copy-paste the const table and remove the code.

## Step 4: Random Colors

Ok, rainbow done, let’s do some nice random colors. What can be easier? Just

```byte r, g, b;r = random(255);
g = random(255);
b = random(255);
setRGBpoint(LED, r, g, b);```

will do, right? I guess you already see that this will result in uncontrollable power fluctuations. What’s more, this thing mostly looks like just slightly tinted shades of white, as it fills all three channels with some garbage (in HSV model that will be an average of 50% Saturation – but we want 100%!).

Look at this:

```r = random(256);g = random(256-r);
b = (255-r-g);
setRGBpoint(LED, r, g, b);```

Here there is no problem with power consumption, but you can see that this particular algorithm favors the first channel (red): it gets 50% of random values, and the other half is shared between the two remaining ones. This is not as bad as it looks and you should not dismiss this algorithm. Human eye is not very good in dealing with blue and green colors (in fact, I’ve read somewhere that we learned to distinguish those colors not that long ago; for ancient Egyptians they were the same). But we are pretty OK with reds. We can easily tell apart scarlet, orange, ochre, yellow and lemon – these colors are between red and green in the RGB model. Same is true for violet, purple, fuchsia, magenta, rose and pink. But can you remember the same amount of cyan tints? Emerald? Err… sky-blue?.. So, cheating a bit with red in your random algorithm can be perfectly ok if you know what you’re doing.

Let’s try a real random that doesn’t favor any single color channel. For this I prefer to use a small array of bytes and a counter that’s incremented each time the function is run:

```byte color[3];byte count, a0, a1, a2;
color[count]=random(256);
a0=count+random(1)+1;
color[a0%3]=random(256-color[count]);
color[(a0+1)%3]=255-color[a0%3]-color[count];
setRGBpoint(LED, color[0], color[1], color[2]);
count+=random(15); // to avoid repeating patterns
count%=3;```

It’s ok, but these three randoms are not saturated enough because we fill three channels with some values. If we want nice deep colors we should fill only two of them, leaving the last one at zero. This algorithm will produce such colors (in fact, it totally corresponds to the ‘power-conscious’ HSV model giving us clear Hues at 100% Saturation):

```color[count]=random(256);a0=random(1);
a1=((!a0)+count+1)%3;
a0=(count+a0+1)%3;
color[a0]=255-color[count];
color[a1]=0;
setRGBpoint(LED, color[0], color[1], color[2]);
count+=random(15); // to avoid repeating patterns
count%=3;```

What about the sine-wave lookup table we did earlier? It can also be used, and it produces even deeper and clearer colors, shifted a bit more towards base red, green and blue:

```a0=random(240);color[count]=lights[a0];
a1=random(1);
a2=((!a1)+count+1)%3;
a1=(count+a1+1)%3;
color[a1]=lights[(a0+100)%240];
color[a2]=0;
setRGBpoint(4, color[0], color[1], color[2]);
count++;
count%=3;```

These are just a few examples, test them out to find which one suits you best. There are tons of ways to do random colors, and it’s a good idea to mix them for better results.

## Step 5: Color Shifting

Now that you’re doing your favorite run of random colors let’s make them change seamlessly. Again the HSV model is of no help: it will make us go through the neighboring spectrum colors. The RGB model provides a shortcut: you have three color channels and you just need to move each of them from their starting points to their targets. We’re avoiding floating point operations, so need to change the types of some variables:

```uint16_t color[3], nextColor[3];long colorStep[3];
byte count, a0, a1, a2;void setNextColor(){
nextColor[count]=random(256)<<8;
a0=random(1);
a1=((!a0)+count+1)%3;
a0=(count+a0+1)%3;
nextColor[a0]=(255-nextColor[count])<<8;
nextColor[a1]=0;
}void loop() {setNextColor();
for (byte k=0; k<3; k++) colorStep[k]=((long)nextColor[k] - color[k])/100;
for (byte k=0; k<100;k++)
{
for (byte i=0; i<3; i++) color[i]+=colorStep[i];
setRGBpoint(0, color[0]>>8, color[1]>>8, color[2]>>8);
delay(10);
}
for (byte k=0; k<3; k++) color[k] = nextColor[k];
setRGBpoint(0, color[0]>>8, color[1]>>8, color[2]>>8);
delay(500);
count+=random(15);
count%=3;
}```

And what about using the lookup table of the sine waves? Here we go, this one is even better as it doesn’t need any stops to show the exact color we’re moving to (it gets enough showtime from the sine wave):

```void setNextColorSine(){
a0=random(240);
nextColor[count]=lights[a0]<<8;
a1=random(1);
a2=((!a1)+count+1)%3;
a1=(count+a1+1)%3;
nextColor[a1]=lights[(a0+100)%240]<<8;
nextColor[a2]=0;
}void loop() {setNextColorSine();
for (byte k=0; k<3; k++) colorStep[k]=((long)nextColor[k] - color[k])/255;
for (byte k=0; k<120;k++)
{
setRGBpoint(0, (color[0]+colorStep[0]*lights[k])>>8,
(color[1]+colorStep[1]*lights[k])>>8,
(color[2]+colorStep[2]*lights[k])>>8);
delay(10);
}
for (byte k=0; k<3; k++) color[k] = nextColor[k];
setRGBpoint(0, color[0]>>8, color[1]>>8, color[2]>>8);
delay(10); // no need for extra delay, the sine wave will keep the color long enough
count++;
count%=3;
}```

Hopefully by this time you get the idea how to do fade-outs and fade-ins…

And that’s it for today! Next time I hope to finally get to my favorite topic of OnePixel information displays (yes, the RGB model is vital there too)...

Until then — comments and questions welcome.

82 3.4K
260 18K
85 8.1K
293 26K

## 18 Discussions

Hello what schematic and code would I need for this?

Functional:
• Full-color RGBW spectrum output and 6,000 lumens white
• Voltage 10-30vDC, 12vDC amps up to 5.0A, 24vDC amps up to 2.5A
• Over-temperature protection
• Dimmable
• Timed toggle protocol (TTP) - make controlled by simple on/off toggle to turn on the light,
• start in white mode
• run 15 sec color cycle
• followed by a 3 min color cycle changing colors for fine tuning of color selection until desired color is selected including white light mode so no external controller is needed.
• color is selected by brief off/on toggle of the switch
• Turning light off for 3 sec resets start of color cycle

Physical: The board should be ring-shaped w/ 0.9215" ID and 2.2285" OD with LED's mounted on the top (white-colored) and electronics mounted on the bottom. LED's should be small footprint high-intensity like shown in the PCB BOM below. To accomplish this, we estimate a BOM similar to:

Qty

Description20Resistor - various19Capacitor - various1Inductor - 100uH, 1842, 10x107Diode - various1Ferrite Bead16White LED - CREE XBDAWT-00-0000-00000BF5118Red LED - Lite-on L1SP-DRD0002F0000014Green LED - Lite-on L1SP-LME0002F0000014Blue LED - Lite-on L1SP-RYL0002F000001LDO - Micrel MIC5233 L333 3.3V LDO, Vin = 2.3 to 36V1FET - Infineon BSC098N10NS5 power mosfet4FET - Diodes DMN3404L N-channel mosfet, SOT231FET - Vishay (Siliconix) PowerPair SiZ328DT dual N-channel mosfet, 3x3, 8 pins1Boost Controller - TI TPS43060, Vo = 60V max, Vin = 4.5V to 38V, 16 pins1Microprocessor - Freescale (NXP) MKL05Z32VFM4, 48MHz, 32 pins, QFN, 32k flash, 4k SRAM1Crystal Oscillator -

Hi Ontaelio,
Like to say I just lifted your code straight into a PSoc design and it all worked perfectly. Well done you...
Cheers
Paul

Hi , I'm new to this. How is your circuit wired? How do you make one RGB's colors flow into the following RGB seamlessly and so on?? Are you using an IC? Incredibly great article and code. Unique because it actually addresses the full spectrum of possible colors from these LED's. But in your video, it would appear that you have the colors flowing from one RGB to the next . How is this done? - you don't provide any schematic. My ultimate goal is the RGB Cube. I wanted to understand one RGB LED before trying to a Kevin Darrah project ( which is impossible at this point) If anyone can help with this one question of getting the colors to flow sequentially in a row, this is 'My' Holy Grail. Thank You Very Much Arduino Community. I will check back often for replies. Thanks Again

Hi and thanks.
1) There's no magic in flowing colors from one LED to the other. Each LED is running a simple rainbow loop, as described in the article, there's just a time shift between them. Thus when the first LED reaches red, the next one is still pink, the one before it is blue and so on.
2) To run five RGB LEDs I used a DM634 LED driver (I wrote an instructable on it), but with your cube, you won't need one as cubes rely on charlieplexing and that can be done only with microcontroller pins due to their tri-state nature.
Hope that helps.

Hi Ontaelio and thank you for answering so quickly. I will look into the DM634 LED Driver right away. Question : does the loop code need to be modified for each LED ? Or is this a function of the DM634 driver? And could you provide or describe the schematic? Again , seeing the full rainbow sequence from these LED's is fascinating. Yours is the only tutorial dealing with this complex problem. Thank You So Much Jim

The DM634 is just a LED driver (one of the ways to connect a lot of LEDs to an Arduino or other controllers), it provides 16 PWM outputs and can be chained. More on it, as well as some schematics, in my old instructable: https://www.instructables.com/id/Hundreds-of-LEDs-on-Arduino-a-New-Way-From-the-Pas/
Each LED shows the same rainbow sequence, they're just a bit out of sync with each other. Here's the example code from my library (no sine wave for simplicity):

do
{
for (int k = 0; k<numLeds; k++)
{
int tcount = (count + k * (765/numLeds))%765;
if (tcount < 256) {red = 255 - tcount; green = tcount; blue = 0;}
else if (tcount < 512) {red = 0; green = 511 - tcount; blue = tcount-256;}
else {red = tcount - 512; green = 0; blue = 765 - tcount;}
Test.setRGBpoint(k, red, green, blue);
}
Test.sendAll();
count++;
delay(SPEED);
}
while (count<765);

numLeds is the number of RGB LEDs connected, Test.setRGBpoint() writes RGB values to memory and Test.sendAll() sends all the values to the library. Here's how it works with 32 RGB LEDs:

Hi, thanks for the great article. I am completely new to Arduino and just try to solve a practical task I have at hand. I've been experimenting with these sine waves and I notice that this (last) piece of code completely avoids basic colors, just shifting between cyan -> yellow -> magenta -> white. Why is it so? Is it because I have to add these mysterious "fade-ins and fade-outs" somewhere?

Ah. Cathodes and anodes, all messed up. Sorry for the stupid question. Here's another one:
If I want the diode to first "fade-in" in red, then do the rainbow cycle ending with violet, then "fade-in" in white, "fade-out" completely and start the whole cycle over again by "fading-in" in red, is there any way of doing it with your beautifully neat setRGBpoint function? Or will I have to just add some code before and after the setRGBpoint cycle? Thanks in advance.

Hi there.

Fade in first, going from 0 to 255 in the red channel, then do the cycle starting with 255,0,0 (the start of the sine wave in the first picture) and stop it with 127, 0, 127 (almost the end of the sine wave where red and blue cross while green is zero). Fade in the green channel from 0 to 127. Fade out all three channels.

White will be a bit too bright and not 'power-conscious' enough (it will consume more than 20 mA), so you can tweak that a bit if needed by using something like:

```

uint16_t max = 255;

{

uint32_t valsum = (red + green + blue);

if (valsum > max)

{

red = ((uint32_t)red * max) / valsum;

green = ((uint32_t)green * max) / valsum;

blue = ((uint32_t)blue * max) / valsum;

}

}

```

(note that I copied this from my dm634 library, thus uint32_t types, you'll be ok with simple int)

Hope that's what you're asking and the answer helps. Feel free to ask again if not : )

Thanks for the answer, Ontaelio. I guess what I was trying to ask is whether it is possible to do all my fade-ins and fade-outs within this cycle of yours:

setRGBpoint(LED, lights[(angle+120)%360], lights[angle], lights[(angle+240)%360]);

by somehow shifting the starting/ending points, angles, whatever. Just to keep the code to this single line :)

Hi. No, fade ins/outs won't fit into a single line. The sine lookup table can be used, but fixed values are still needed (like setRGBpoint( LED, lights[angle], 0, 0); )

Many thanks :) If I may ask one more:

I am now fiddling with the random sine waves. In fact I have two LEDs (well, groups of LEDs) which I need to make shift colors in an out-of-sync way. I have made some clumsy additions to your code which now looks like this:

const uint8_t lights[360] = {
0, 0, 0, 0, 0, 1, 1, 2,
2, 3, 4, 5, 6, 7, 8, 9,
11, 12, 13, 15, 17, 18, 20, 22,
24, 26, 28, 30, 32, 35, 37, 39,
42, 44, 47, 49, 52, 55, 58, 60,
63, 66, 69, 72, 75, 78, 81, 85,
88, 91, 94, 97, 101, 104, 107, 111,
114, 117, 121, 124, 127, 131, 134, 137,
141, 144, 147, 150, 154, 157, 160, 163,
167, 170, 173, 176, 179, 182, 185, 188,
191, 194, 197, 200, 202, 205, 208, 210,
213, 215, 217, 220, 222, 224, 226, 229,
231, 232, 234, 236, 238, 239, 241, 242,
244, 245, 246, 248, 249, 250, 251, 251,
252, 253, 253, 254, 254, 255, 255, 255,
255, 255, 255, 255, 254, 254, 253, 253,
252, 251, 251, 250, 249, 248, 246, 245,
244, 242, 241, 239, 238, 236, 234, 232,
231, 229, 226, 224, 222, 220, 217, 215,
213, 210, 208, 205, 202, 200, 197, 194,
191, 188, 185, 182, 179, 176, 173, 170,
167, 163, 160, 157, 154, 150, 147, 144,
141, 137, 134, 131, 127, 124, 121, 117,
114, 111, 107, 104, 101, 97, 94, 91,
88, 85, 81, 78, 75, 72, 69, 66,
63, 60, 58, 55, 52, 49, 47, 44,
42, 39, 37, 35, 32, 30, 28, 26,
24, 22, 20, 18, 17, 15, 13, 12,
11, 9, 8, 7, 6, 5, 4, 3,
2, 2, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};

uint8_t outputPins[6] = {3, 5, 6, 9, 10, 11}; // PWM pins
uint16_t color_1[3], color_2[3], nextColor_1[3], nextColor_2[3];
long colorStep_1[3], colorStep_2[3];
byte count, a0, a1, a2;
unsigned long PastMill;
int interval=100;

// setRGBpoint (0, ...) for pins 3, 5, 6; setRGBpoint (1, ...) for pins 9, 10, 11.
// See array above
void setRGBpoint(byte LED, uint8_t red, uint8_t green, uint8_t blue)
{
// this code is for common anode LEDs. If you use common cathode ones,
// remove the '255-' bits.
analogWrite(outputPins[LED * 3], red);
analogWrite(outputPins[LED * 3 + 1], green);
analogWrite(outputPins[LED * 3 + 2], blue);
}

void setup() {
}

void setNextColorSine()
{
a0 = random(240);
nextColor_1[count] = lights[a0] << 8;
a1 = random(1);
a2 = ((!a1) + count + 1) % 3;
a1 = (count + a1 + 1) % 3;
nextColor_1[a1] = lights[(a0 + 100) % 240] << 8;
nextColor_1[a2] = 0;
a0 = random(240);
nextColor_2[count] = lights[a0] << 8;
a1 = random(1);
a2 = ((!a1) + count + 1) % 3;
a1 = (count + a1 + 1) % 3;
nextColor_2[a1] = lights[(a0 + 100) % 240] << 8;
nextColor_2[a2] = 0;
}

void loop() {
setNextColorSine();
for (byte k = 0; k < 3; k++) colorStep_1[k] = ((long)nextColor_1[k] - color_1[k]) / 255;
for (byte k = 0; k < 3; k++) colorStep_2[k] = ((long)nextColor_2[k] - color_2[k]) / 255;
byte cycle = 0;
while (cycle < 120)
{
if (millis() - PastMill >= interval)
{
setRGBpoint(0, (color_1[0] + colorStep_1[0]*lights[cycle]) >> 8,
(color_1[1] + colorStep_1[1]*lights[cycle]) >> 8,
(color_1[2] + colorStep_1[2]*lights[cycle]) >> 8);
setRGBpoint(1, (color_2[0] + colorStep_2[0]*lights[cycle]) >> 8,
(color_2[1] + colorStep_2[1]*lights[cycle]) >> 8,
(color_2[2] + colorStep_2[2]*lights[cycle]) >> 8);

PastMill = millis();
cycle++;
}
}
for (byte k = 0; k < 3; k++)
{
color_1[k] = nextColor_1[k];
color_2[k] = nextColor_2[k];
setRGBpoint(0, color_1[0] >> 8, color_1[1] >> 8, color_1[2] >> 8);
setRGBpoint(1, color_2[0] >> 8, color_2[1] >> 8, color_2[2] >> 8);
}
count++;
count %= 3;
}

On a general note, the LEDs behave nicely but I notice that now and then the colors of the two groups meet... I feel that this can somehow be avoided by tweaking setNextColorSine(), but I am not sure how. Could you spare a couple of minutes to help me out? Thanks in advance :)

And BTW, I am sure I have common ANODE LEDs (with "+" being common) and still I had to remove the "255-" bits to have them function normally.

Hello good day, is there is something I need to change to make it work on WS2812B?

I tried the rainbow using the sine method you did, but when the value of the loop is around 270 it will go back to color red to yellow to slight green, before resetting the value to zero and turn red again.

Thanks.

Hi. It seems it resets at 256, check your loop variable (make sure it's integer, not byte).

Thanks for this. Would it be possible for you to post a sketch for a rainbow sequence for one LED using the sine fade - I've got a bit lost in the code with all the different methods!

Sure. Get the lights[360] array from step 2, then just:

for (int k=0; k<360; k++)

{

analogWrite(3, lights[(k+120)%360]);

analogWrite(5, lights[k]);

analogWrite(6, lights[(k+240)%360]);

}

// change the pin numbers if needed

Oh, the code above is for a common cathode LED.

very nice approach. I'll try tomorrow