Controlling LED strips with chipKIT

A few weeks ago, we found ourselves in the Digilent Makerspace tinkering with some fantastic LED strips, the WS2811 / WS2812. If you haven’t played with these yet, you really ought to. I grew up loving colored lights. Some kids threw the baseball, while I made amusing shapes on a Lite-Brite and begged to put Christmas lights in my window in mid-September. That being said, you can see how giving a weirdo like me a strip of individually-controlled LEDs, each supporting 24-bit color, would be like giving matches to a madman.

A few sections of LED strip. These have adhesive backing and can be cut with scissors in between the elements.
A few sections of LED strip. These have adhesive backing and can be cut with scissors in between the elements.

In fact, this ended up being the first time in the 10+ years I’ve worked for Digilent where I’ve spent serious time programming microcontrollers, which has forced me to dust off some very old C++ programming experience. The result? It’s easy, it’s rewarding, and if you have flashing LEDs, it’s quite colorful.

 

That afternoon in the Makerspace, we used our chipKIT Max32 boards along with a pre-written library and a simple sketch. Together, these set up a table of colors, pass the colors to the LED strip, then cycles the colors down the LED strip for a wave effect. Before we knew it, the room was lit up like a disco on New Year’s. We had fun tinkering with the color tables and flash rates, and a few of us just had to dig deeper.

Here we all were, soldering away.
Here we all were, soldering away.

If you are interested in the project & sketch we started with, please visit the project on Instructables.

 

Being a graphic designer, the first thing I wanted to improve upon was the way color was handled in the first place. The simple version is that each element on the strip has its own tiny microcontroller embedded inside, allowing for individual control over the red, green, and blue LEDs. Each will accept an 8-bit value, which gives us (roughly) 24-bit color overall. However, dealing with each element’s color as separate RGB values has its drawbacks. For example, say we’ve created a really nice blue [15,150,230] and we want to darken it down a bit. Working with RGB values, we’d simply subtract off a percentage from each value. But, what happens as the values get smaller? The precision is less and before you know it you’ve darkened down to [0,0,1]. What happens when you brighten things back up? Our specific shade of blue is gone.

 

A better model for this is Hue, Saturation, and Value (HSV). If you’ve ever played with an art or drawing program, you’ve probably seen these. Sometimes you’ll see it as HSB (Brightness) or HSL (Lightness), but they all describe the same thing. Hue is represented from 0 to 360 degrees, just as on a color wheel. Saturation describes how rich the color is from pale white, through pastel shades, all the way up to full color. Value describes how bright the color is, from almost black to fully bright. So, you might think of it as describing a color by its shade, its white-ness, and its black-ness.

 

Now, back to blinking lights. My first goal was to add a simple function that takes HSV vaues and converts them to RGB on the fly. This meant that I could use the existing library which, for the moment, only accepts RGB. As a bonus, why not add a speed control to go from relaxing to frenetic? Time to dig in!

 

First, we need to wire up a potentiometer to control the speed. Here we can see the Max32 prepared for the basic demo. The LED strip is connected to 3.3V and GND for power, as well as digital pin 43 for data.

The Max32 set up for the basic LED demo.
The Max32 set up for the basic LED demo.

To this, we’ll add new connections to 5V, another GND, and the analog input pin A0.

HSV-RGB-3
3 new wires added for the potentiometer.

On the other end, grab a bit of breadboard and plug in a 10kΩ resistor. I’m using the one that comes in the chipKIT Starter Kit which has nice vertical pins and a thumbwheel. Wire GND to pin 1 on the potentiometer, A0 to pin 2, and finally the 5V power supply to pin 3.

HSV-RGB-4
Our new speed control!

Now we’re ready to tweak the code. You can find the modified sketch [here]. Let’s step through the additions. First, let’s change the declaration of the array that holds the color values. In the original example, the values were assigned right away:

 

WS2812::GRB rgGRB[CDEVICES] =
{
    {0, 0, 255},
    {15, 0, 235},
    {25, 0, 215},
...
    {25, 0, 215},
    {15, 0, 235},
    {0, 0, 255}
}; 

 

We don’t actually need that anymore, so I’ve left it as a simple declaration, and for clarity’s sake, I also renamed the array to ‘ColorArray’.

WS2812::GRB ColorArray[CDEVICES];

 

Next, I declared a variable to hold the analog value read from the potentiometer:

int SpeedPotVal = 0;

 

Next, in the setup() function, we need to tell the Max32 to listen to our potentiometer by setting up the analog input pin A0:

pinMode(A0, INPUT);

 

Next, in the loop() function, we need to read in the value of the potentiometer and map those values to ones we can use:

SpeedPotVal = analogRead(A0);
SpeedPotVal = map(SpeedPotVal, 0, 1023, 30, 800);    //map the value from 0-1023 to 30-800  

 

Now, just below you’ll see a switch statement. In the WAIT case, tell the if statement to compare against our potentiometer value:

if(millis() - tWaitShift >= SpeedPotVal)

 

Now we need to add a few new functions. The most important, the reason we’re here in the first place, is a function that converts from HSV to RGB. You can find algorithms for this all over the net. Here, I’ve slightly adapted one of them, but the cleverness was someone else’s.

void SetHSV( float h, float s, float v, int index )
{
    int i;
    float r, g, b = 0;
    float f, p, q, t;

    if( s == 0 ) {
	// achromatic (grey)
	r = g = b = v;
	return;
    }

    h /= 60;			// sector 0 to 5
    i = floor( h );
    f = h - i;			// factorial part of h
    p = v * ( 1 - s );
    q = v * ( 1 - s * f );
    t = v * ( 1 - s * ( 1 - f ) );

    switch( i ) {
	case 0:	 r = v, g = t, b = p; break;
	case 1:	 r = q, g = v, b = p; break;
	case 2:	 r = p, g = v, b = t; break;
	case 3:	 r = p, g = q, b = v; break;
	case 4:	 r = t, g = p, b = v; break;
	default: r = v,	g = p, b = q; break;
    }
    
    r *= 255;
    g *= 255;
    b *= 255;
    
    ColorArray[index] = {(uint8_t)b, (uint8_t)r, (uint8_t)g};
}

 

Now, filling the color array with RGB values is a breeze. With one simple function we can create a rainbow simply by incrementing the hue value and moving through the color wheel.

void SetRainbow()
{
    float Hue = 0;
    float HueIncrement = (360 / (CDEVICES - 1));
    for (int i=0; i < CDEVICES; i++){
      SetHSV(Hue, 1, 1, i);
      Hue += HueIncrement;
    }
}

 

Now all we need to do is add a call to our SetRainbow function from the sketch’s setup function:

SetRainbow();

 

Program your board, jiggle the potentiometer a bit to set the speed, and you’ll see a nice, smooth rainbow wave move down the strip. The best part is that we didn’t have to specify a bunch of RGB values to do it!

HSV-RGB-5

The next challenge is to rewrite the sketch to work with HSV values. Then, we’ll be able to do some nice effects, and certain things will be more efficient. Imagine dimming the LEDs by simply seting all their Values to 0 without needing to alter the Hue. Stay tuned!

Be the 1st to vote.

2 Comments on “Controlling LED strips with chipKIT”

  1. Norm, your photos are absolutely gorgeous.
    Thank you for sharing them!

Leave a Reply

Your email address will not be published. Required fields are marked *