PROJECT: Build my own Christmas tree lights

Discussion on Electronics

PROJECT: Build my own Christmas tree lights

Postby Bracken » Tue Dec 04, 2012 12:34 am

Incandescent lamps are bad, we know this to be true. Yet all the LED Christmas tree lights I've ever seen are terrible for one reason or another:
- Only one colour.
- Only one channel.
- Noticeable flicker on fade.
I always wanted to make a 4 channel, 4 colour set with a few patterns.

A friend at work recently pointed out this string of RGB LEDs: http://www.ebay.co.uk/itm/230873716903 (String of 50 WS2801 based RGB LED Pixels).
He's using them to make the adafruit ambilight clone using an Arduino, but it's essentially a set of individually addressable RGB LEDs that can be daisy chained. This is way better than my requirements as I can have as many colours as I want and move them too!

The parts I'm planning to use are:
- 1 or 2 50-LED strings.
- TI MSP430 value line uC (launchpad) - also available are Microchip PIC16/PICaxe or Atmel ATMega644/Sanguino if the TI chip is too weedy.
- vim/mspgcc/mspdebug toolchain, could also have used the TI software but I like the free compiler more.

--------

The stuff has actually arrived already, here's a photo showing Red-Orangre-Yellow-Green-Blue-Indigo-Violet down the first 7 LEDs, sadly one of my strings has a dead pixel.

Image

I got the Hex codes off the 7 Wikipedia articles for the colours, they aren't great on these LEDs, but they are better than the photo makes them look. I need to fix my msp430-gdb as it just segfaults, then I can try out colours very quickly (no need to compile over and over).

Another problem is that I can't keep an array with all the LED values, each RGB colour needs 3 bytes, the MSP430 I'm using has 128B of RAM, though Benjie has lent me a 256B model, and I know he even has some 512B chips. For now I'm trying to code around that limitation.

A big plus is that driving the string is handled ENTIRELY by the chip's USI, leaving me two whole timers to use for my code. I found another person using the MSP430 but bit-banging the string, I don't know why...?
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am




Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Tue Dec 04, 2012 12:43 am

So choosing 56K pull-ups at random was a spot of luck, it's over spec for the uC but under spec for the string. :-S 68K probably won't work, or have SI issues, 47K will probably damage the first LED Pixel. My logic high voltage is also under spec for the string, probably only works because I'm driving at 1MHz rather than 25.

The launchpad really shouldn't be used for this, so let's continue anyway.

Image

This is my new less fragile prototype, using my 1/4W metal film resistors rather than the 1/2W cold war russian ones. And a better 5V PSU thanks to Chris Smith. Now I can get on with some coding, next job is to get Timer A doing something useful.
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Re: PROJECT: Build my own Christmas tree lights

Postby XRobots » Tue Dec 04, 2012 9:54 am

Looks good - I'd be interested to see a video when it's operational - hopefully in time for Christmas ;-)
-James
XRobots.co.uk
User avatar
XRobots
Site Admin
 
Posts: 237
Joined: Wed Nov 07, 2012 6:13 pm

Re: PROJECT: Build my own Christmas tree lights

Postby PropBuilder » Tue Dec 04, 2012 10:20 am

things like this always interest me, but its beyond my level of knowledge! lol i wish i had the brain power to understand these things so i could make my own things like this :)
View my full build progress at http://www.starkswarehouse.com
and my build videos and updates at http://www.youtube.com/starkswarehouse
PropBuilder
 
Posts: 10
Joined: Fri Nov 30, 2012 9:45 am

Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Wed Dec 05, 2012 6:33 pm

So I started messing around with different colour formats and way's to fade LEDs gracefully yesterday. I've not set up the timer yet so I just used a loop with a big counter in it for now. :lol:

Be warned, this post is about code, if it's too dry for you just skip to the video.

Each LED wants to hear three bytes of 8 bits, one for red, one green, and the last blue.
Up till now I've been storing each colour as an "unsigned long", this is a four byte type, thus I waste a whole byte each time I store a colour. Believe me you feel this on a device with 128B (yes, bytes) of RAM and 2KB of program words. Incidentally these simple test programs use 0 bytes of RAM, the C compiler is being clever and optimising it to be all instructions.

I had a play with structures and custom types like:
Code: Select all
typdef struct {
  unsigned char r;
  unsigned char g;
  unsigned char b;
} rgb;

Note that a character is also a very small integer.

This is great because (in theory) it only uses three bytes of memory, it's easy to pass into and return from functions. It's different when you're defining a colour (constant rather than #define). Sadly this used exactly as many program words as the unsigned long did, DOH!

So some people at work suggested I use less resolution for the colour, technically I've been using 24-bit "True Colour" so far, how about 16-bit "High Colour". This is a bit strange, from MSB to LSB it uses; 5 bits for red, 6 bits for green then 5 bits for blue. This is because our eyes are more sensitive to green light. I did try 15-bit colour leaving 1 bit unused, I could get substantially less close to the colours I wanted than I could with 16-bit. Now this format will fit into an "unsigned int" only two bytes, and it actually led to a substantial saving of program words.

I'm not exactly using High Colour as I map the 16-bit value to the 14-bit value the LEDs want like this:
Code: Select all
16-bit           15              7             0
           MSB [ R R R R R G G G G G G B B B B B ] LSB
                 |       | \         \ \       \
                 |       |  \         \ \       \
                 |       |   \         \ \       \
                 |       |    \         \ \       \
                 |       |     \         \ \       \
                 |       |      \         \ \       \
                 |       |       |         | \       \
                 |       |       |         |  \       \
                 |       |       |         |   \       \
                 |       |       |         |    \       \                   
24-bit           |       | 0 0 0 |         | 0 0 |       | 0 0 0
           MSB [ R R R R R R R R G G G G G G G G B B B B B B B B ] LSB
                 23              15              7             0

This may seem foolish, but there is a reason:

Dividing or multiplying with floating point values is strictly off limits! Do it only once and you immediately crash past the 2,000 program words limit.


This limitation brings me onto the next topic: Fading.

I need to take these defined colours and make them dimmer. I immediately found that there is not enough resolution to do this in 16-bit land, so I worked out a way to dim the 24-bit value before transmission. You can't just subtract 1 from each sub-pixel, this changes the colour as it fades unless you are fading pure red, green or blue. Eventually I found that you could define another number which is, or is close to the lowest common denominator for that 24-bit colour (eg: 0xFF0000 needs 0x010000, 0xFF8000 is 0x020100). Yet still this is not good for anything other than primary colours. :-(

What about timing:
* I know I want 25FPS, I do not want flicker.
* I know I might want to do something in the sequences every 1 second, maybe 2, maybe 0.25?
* I know I might want 100 LEDs in a string.
* I know I'm currently clocking the LED string and uC at 1Mhz, and I could move one or both up to 8MhZ, or even 16Mhz with an external crystal.
* I know the LEDs need 300us (microseconds) of silence for them to commit their colour to the LED via the PWM drivers.

So I need to have a timer interrupt every 0.04 seconds, this calls a service routine that every 25 times can call "do_sequence_actions" that can do things like change from fading down to fading up. And on every pass it will call "do_frame_actions" which can move an LED to the next faded value then cause a frame to be displayed. Easy, I sill have a spare timer too, we may be able to have a watchdog (I'll explain what that is in a later post).

Now, can we actually display a frame in less than 1/25th of a second (0.04 seconds)? 100 LEDs needs 2,400 bits of data to be sent. At the current 1Mbaud this takes 0.0024 seconds, plus the 300us delay after transmission is 0.0027 seconds. We have ample room, and there is no need to worry about how long the code takes to execute because the transmission will be handled in hardware while we work out what to draw in the next frame (there is a small interrupt driven bit of code, but it's very small).

Here's a video of fading down red, and fading up and down RGB:
[youtube]s2x8GbdKIq0[/youtube]
http://www.youtube.com/watch?v=s2x8GbdKIq0

It does not work well at all for non-primary colours. You'll also notice that the fading is not linear, this is the LEDs, they are more sensitive at lower duty cycles, I need to have something where I skip several values at high duty cycles and go one by one at low duty cycles. So all the code I wrote last night will be going in the bin, I need to attack this from a different angle.

I noticed something cool about my colours back when they were 24-bit values:
Code: Select all
#define RED     0xFF0000
#define ORANGE  0xFF2000
#define YELLOW  0xFF7000
#define GREEN   0x00FF00
#define BLUE    0x0000FF
#define INDIGO  0x5000FF
#define WHITE   0xFFFFFF

I only have 4 distinct sub-pixels!

This means I can solve the no division problem the way every uC developer eventually does, with a lookup table. I can have 4 100-element arrays of unsigned char, implemented as C constants these will eat 400 out of the 2,000 program words, I can live with this. Each array will run from 00 to the sub-pixel value logarithmically, this way I can sweep over the arrays to fade any colour I like in a liner fashion. It's perfect!
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Fri Dec 07, 2012 11:18 pm

Less stuff this time. I've had a stab at getting the fading sorted but I'm not there. Instead I did a bit more work on getting the interrupt driven code in place, and it's working quite well now, I've managed to get a precession mode running, in this video:

[youtube]azu4H1Ni40Y[/youtube]
http://youtu.be/azu4H1Ni40Y

Next I need to move it out into a header file, write a configuration.h file, and get it running multiple profiles with a button to switch. I'll just use the one profile with different speeds for that.

I've also been thinking about the modes I should have, such as: Static, Precession, Wave, Twinkle, Candle simulation (they would flicker). This is where I'd love a bit of audience participation, what should my tree do?
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Sat Dec 08, 2012 11:47 pm

I got a lot of work done on the lights today. I finally implemented the lookup table fading, which is gracefully working on all colours, not just primary colours. It turns out the hardest part of this was making the lookup tables. I did it with perl and the ** operator (power) in the end, it's quick and easy.

I also sorted out the flow of the code that does the pattern and the code that diaplays it, which involved writing this state machine:
Image

The benefit (other than being clean, quick, concise and well documented) is that the code is never spinning waiting for something, just driving the hardware and moving on, or sleeping.

Here's a video of all the colours fading:
[youtube]YjhcKK_LazE[/youtube]
http://www.youtube.com/watch?v=YjhcKK_LazE

How is the size looking after the lookup tables? Well the tables are 400 bytes in total, the entire code is currently using 1,258 bytes out of 2,048 of program space, and 34 bytes out of 128 of RAM. The only thing left to add (other than a bit of reorganising) is the patterns. So it looks like my weediest chip may just cut it.
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Thu Dec 13, 2012 1:38 am

I'm excited about this update, aside from a few bugs I'll fix later, the groundwork is all done, all I have left to do is write the patterns. But first things first, a video, I talk in this one and everything!

[youtube]JJp8H9tR00c[/youtube]
http://www.youtube.com/watch?v=JJp8H9tR00c

As you can see I've got a static mode, as many patterns as the user has written (they're easy to write and I'll show you how when I write the next one), and then a cycle mode that runs through all the defined patterns. The timing, the order and the number of patterns and which patterns is all user configurable in one light configuration.h file.

I did however blow past the 2K program space limit at 3.4K, yet still only using 42B of RAM. I'm using a bigger MSP430 now with 4K program space and 256B of RAM, but looking at the size the patterns turn out to be I'll need an 8K/512B model by about tomorrow. I did all I could to limit the code size, and I am compiling with "-Os", but it's just not possible to have more than one pattern on the littler chip (I encourage anyone to prove me wrong).

Bugs? There is a noticeable flicker at 25Hz, I might try 50Hz frame rate, though this does give me only half as many clock cycles to generate the next frame in my patterns. I'm already running the uC as fast as possible (~25Mhz) and the LED string as fast as it actually works (~4Mhz, the spec totally lied).

I've started thinking of features for release 2 as well:
I'd like to have a rand() function to use in my patterns, I might use a floating GPIO for this, or the ADC maybe, though the latter would limit the number of different chips from the range you could run it on. It'll be fun to find out what works, and will be more like the posts near the start of this project, experiment mode.
Watchdog timer, though I did promise to make a post all about that so it may come for release 1.
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Wed Dec 19, 2012 4:30 am

Start with a video again, I've written more patterns, it looks like Christmas lights do now and is ready for a tree.

[youtube]EP_6cUj5zko[/youtube]
http://www.youtube.com/watch?v=EP_6cUj5zko

It turns out I can bash out patterns really quickly, no one has taken longer than half an hour at this point. I hope I've made it easy enough for others to write their own, and I'll explain that now. So go on, fork me on github.

Here is the code for the first pattern, each LED remains the same colour all the time, but all the LEDs fade up and down together.
Code: Select all
/* Global definitions for this pattern */
unsigned char fade_pat_brt;
_Bool fade_pat_dir;
unsigned long fade_buffer[6];

/* Private functions for this pattern, if you need any.
Make the names unique. */

/* init function for this pattern */
void fade_init(void) {
  fade_pat_brt = 0;
  fade_pat_dir = 1;
  return;
}

/* frame function for this pattern */
void fade_frame(void) {
  fade_buffer[0] = colour(RED,fade_pat_brt);
  fade_buffer[1] = colour(ORANGE,fade_pat_brt);
  fade_buffer[2] = colour(YELLOW,fade_pat_brt);
  fade_buffer[3] = colour(GREEN,fade_pat_brt);
  fade_buffer[4] = colour(BLUE,fade_pat_brt);
  fade_buffer[5] = colour(INDIGO,fade_pat_brt);
  if (fade_pat_dir) {
    if (fade_pat_brt >= 99) {
      fade_pat_dir = 0;
    } else {
      fade_pat_brt++;
    }
  } else {
    if (fade_pat_brt <= 0) {
      fade_pat_dir = 1;
    } else {
      fade_pat_brt--;
    }
  }
  return;
}

/* getled function for this pattern */
unsigned long fade_getled(unsigned char led) {
  return fade_buffer[led%6];
}


First are some global variables for the pattern, never initialise them here because your pattern will be started multiple times without the program re-starting. It's also fine to add any functions you want. Anything in this space needs unique names, so prefix with the pattern name.

Next is the "init" function, this sets up the initial conditions before the first frame is worked out, now you can initialise variables, or just return if you don't need to.

Then we have the "frame" function, this will be called 25 times per second and by the end, your code should be ready to answer this question: "What colour should this LED be?" You can use an array or any other way you like to remember what each LED should be as long as it fits on the microcontroller.

The fade pattern uses a 6 element array, which is wrapped up however many pixels you have, and today you should handle up to 255. It just puts the faded value for each colour into each element of the array and increments or decrements the fade value. Your frame function obviously needs to execute in less than a frame's time, plus the time it takes for the program to transmit to every pixel, but if you take too long the red LED on the launchpad will light and the display will probably mess up.

colour() is a useful function you can call with a colour defined in colours.h and a brightness value between 0 and 99, it will return a 24-bit colour value compatible with the final function.

Finally we have the "getled" function, this will be called with the index of an LED (0 to 254) and you must answer with the colour of that pixel. It will be called a great many times in no particular order and may be called many times for the same LED. The fade pattern just takes the modulus 6 of the led number and uses that as an index to read from the array.

Just look how small this file is, once you've written that you would "#include patterns/mypattern.h" in the configuration.h file and add these three functions to the switches toward the bottom of configuration.h in the order you would like, then amend NUM_PATTERNS if you need to.

Why didn't I just use one global array of pixels rather than this getled() nonsense? Simple, no value line MSP430 chip has enough RAM to store a value for all 255 LEDs. This way each pattern can handle the low RAM situation in a suitable way, like keeping a small array and wrapping it.

Randomness
You'll notice the last pattern in the video uses randomness, this was quite hard. I thought about several methods:
* Copy and paste someone's pseudo-random number generator - they take a lot of code space and need several iterations, making them unsuitable for use in frame generating code. :-(
* Use a floating GPIO - this might actually work, I didn't try, my experience of floating GPIOs is that they don't tend to float consistently. If I used this method with the ADC (Analogue to Digital Converter) I would be adding another line to the minimum requirements, not all the MSP430s have this.
* Use the thermal sensor - I didn't try this either, even though I know that the thermal sensor has some lovely white noise on it. It uses the ADC, which not all MSP430s have.
* Pre-compute the randomness - Yes, lookup tables again! This is the method I used, I have 32 randomly generated numbers in an array and I add each successive number to the current LED that is to be toggled. I made another perl script to generate my white noise. Interestingly my hand crafted random numbers were not good enough, the pattern came out looking awful, I tried this many times and all I could get was a clearly cyclic pattern. The perl generated randomness looked excellent the first time, there's a thing.

This is actually the same way I solved the fading without dividing problem. It seems all things microcontrollers can't do can be solved with a lookup table.
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Re: PROJECT: Build my own Christmas tree lights

Postby Bracken » Mon Dec 24, 2012 7:39 pm

I've made some reasonably permanent hardware now, first my schematic:
Image
Kickad files are on github.

The connector on the right is the LED string. The T2 and T3 connectors allow me to use the launchpad to program the microcontroller on this board. U2, an LM317 is a variable regulator, set to 3.3V by R1 and R2, this powers the MSP430 and the clock and data lines up the string.

Here's the circuit on a breadboard:
Image

My set is now prototyped on veroboard (copper strip board):
Image

This was the second attempt at a veroboard, the first time I tried to get the LED string connector together on one edge of the board but this put a lot of power supply noise on the LED string's clock. RevB puts those wires right next to the uC, convenience will have to wait for a real printed circuit board.

And video:
[youtube]NXn8obhYZuw[/youtube]
http://www.youtube.com/watch?v=NXn8obhYZuw

The next job is a 3D printed case...
User avatar
Bracken
 
Posts: 10
Joined: Mon Nov 19, 2012 12:59 am

Next

Return to Electronics

Who is online

Users browsing this forum: No registered users and 1 guest




cron