TruckTree!
This project is for a Christmas tree that I put in the back of my truck each year around that time. It uses a strand (or 2) of individually addressable RGB LEDs. The LEDs have a WS2811 driver IC which communicates using 1 wire serial. This is NOT to be confused with the WS2801 which is a 2 wire serial IC, and quite frankly a lot easier to work with. Luckily though, people like Alan Burlison have already done the hard work for everyone and have been nice enough to share. Alan has written a very good AVR driver for the nasty WS2811 chip which covers about 95% of the work. His article on it can be seen here.
My project uses his WS2811.h and would not be possible at all without it. Thanks very much Alan!
Operation
The idea here is to have a nice little tree in your truck that fades slowly randomly R G and B and everything in-between. But when you apply the brakes it will cause the whole tree to fade to red. When the brake are released it will go back to its normal random color stuff.
The brake lights are hooked up to an opto isolator on the board which tell the micro when the brake is applied.
There is a DC to DC converter on the board that will provide power for everything. Be mindful about the current limitations of it. It should be fine for at least 100 LEDs.
These strands of lights are found on ebay. Just search for WS2811. THE MANUFACTURING QUALITY OF THESE LIGHTS IS NOT GREAT. You will most likely have to fix some bad solder joints and/or other things like that. Everything is "potted" so it makes it that much more difficult. Just be ready to do this kind of work.
In hindsight… I have no idea why I chose to use the beefy TI switching regulator. Maybe I had a good reason when I drafted the original schematic some months ago but I can’t remember what it is now. Maybe I thought it would use more current than it actually does? Yea that’s it… we’ll go with that. The reality is 2 strings does not use that much current and you could probably get away with using a linear regulator with some good heat dissipation.
Video and Pics
http://www.youtube.com/watch?v=dCNIRQI3RAA
Board and schematic
BOM
Qty Value Part Num Package Parts 3 * FE08 PORTB, PORTC, PORTD 1 609-3218-ND MA03-2 ISP 4 .1uF 311-1361-1-ND C0805K C4, C6, C7, C8 1 1.4K RHM1.40KBDCT-ND R1210 R1 1 2.2uF 490-1733-1-ND C0805K C3 1 4N25SM 4N25SM-ND DIL6-SMD OK1 1 16MHz 535-10226-1-ND HC49UP Q1 2 18pF 399-9179-1-ND C0805K C1, C2 1 21k P21.0KCCT-ND M0805 R2 1 330uF 338-1790-1-ND 153CLV-0810 C5 1 ATMEGA88PA ATMEGA88PA-AU-ND TQFP32-08 IC1 1 Brake Signal WM4201-ND 22-23-2031 BRAKE 1 PTN78020WAH 296-20515-ND PTH DC 1 Power WM4201-ND 22-23-2031 POWER 1 TPPAD1-13Y * P1-13Y INHIB 1 Tree WM4201-ND 22-23-2031 TREE 3 WM2012-ND 9 WM1114CT-ND
Code
I am only posting my edited example file from Alan. You will need to grab the WS2811.h from Alan's page.
/*
* Copyright 2012 Alan Burlison, alan@bleaklow.com. All rights reserved.
* Use is subject to license terms.
*/
/* Fuse settings
BOOTSZ = 1024W_0C00
BOOTRST = [ ]
RSTDISBL = [ ]
DWEN = [ ]
SPIEN = [X]
WDTON = [ ]
EESAVE = [ ]
BODLEVEL = DISABLED
CKDIV8 = [ ]
CKOUT = [X]
SUT_CKSEL = EXTXOSC_8MHZ_XX_258CK_14CK_4MS1
EXTENDED = 0xF9 (valid)
HIGH = 0xDF (valid)
LOW = 0x8E (valid)
*/
#include <avr/io.h>
#include <util/delay.h>
#include "WS2811.h"
#include <stdlib.h>
#define BIT(B) (0x01 << (uint8_t)(B))
#define SET_BIT_HI(V, B) (V) |= (uint8_t)BIT(B)
#define SET_BIT_LO(V, B) (V) &= (uint8_t)~BIT(B)
#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))
#define PAUSE 500 // msec
#define DELAY 10 // msec
#define CHASE_SPEED 10 // msec
#define FADE_TO_RED_SPEED 1 // msec
#define RAND_MAX 0xFF
#define NUMBER_OF_NODES 100
#define COLOR_CHANGE_SPEED 1000
volatile uint8_t direction=0, LED_num=0;
// Define the output function, using pin 0 on port b.
DEFINE_WS2811_FN(WS2811RGB, PORTC, 0)
RGB_t rgb[NUMBER_OF_NODES];
volatile uint8_t colorchange=0;
volatile uint8_t desired_color[NUMBER_OF_NODES][3];
void Blank (void);
void All_Red (void);
void Random (void);
void Chase (void);
void Random_fader(void);
void Update_Colors(void);
void main(void){
// Configure pin for output.
SET_BIT_HI(DDRC, 0);
SET_BIT_LO(PORTC, 0);
SET_BIT_LO(DDRC, 1); //an input
SET_BIT_HI(PORTC, 1); //enable pull up
Blank();
for (;;) {
WS2811RGB(rgb, ARRAYLEN(rgb)); //call the LED driver
_delay_ms(DELAY);
if(bit_get(PINC, BIT(1)) == 0 ){ //if 0 then pressed
_delay_ms(FADE_TO_RED_SPEED);
Fade_to_red();
}
else{
_delay_ms(FADE_TO_RED_SPEED);
Update_Colors();
Random_fader();
}
//Chase();
}
}
void Blank (void){ //blanks the color of all the LEDs
for(int i=0; i<NUMBER_OF_NODES ; i++){
rgb[i].r = 0;
rgb[i].g = 0;
rgb[i].b = 0;
}
}
void All_Red (void){ //blanks the color of all the LEDs
for(int i=0; i<NUMBER_OF_NODES ; i++){
rgb[i].r = 255;
rgb[i].g = 0;
rgb[i].b = 0;
}
}
void Fade_to_red(void){
for(int i=0; i<NUMBER_OF_NODES ; i++){
if (rgb[i].r < 255 ){
rgb[i].r++;
}
if (rgb[i].g > 0 ){
rgb[i].g--;
}
if (rgb[i].b > 0 ){
rgb[i].b--;
}
}
}
void Update_Colors(void){
static dominant_color;
if (colorchange++ == 255){ //its time to change the color
for(int i=0; i<NUMBER_OF_NODES ; i++){
dominant_color=random() % 3;
switch (dominant_color){
case 0: //red is dominant color
desired_color[i][0] = random();
desired_color[i][1] = random() % 95;
desired_color[i][2] = random() % 95;
break;
case 1: //green is dominant color
desired_color[i][0] = random() % 95;
desired_color[i][1] = random();
desired_color[i][2] = random() % 95;
break;
case 2: //blue is dominant color
desired_color[i][0] = random() % 95;
desired_color[i][1] = random() % 95;
desired_color[i][2] = random();
break;
default: //white
desired_color[i][0] = random();
desired_color[i][1] = random();
desired_color[i][2] = random();
break;
}
}
colorchange=0; //reset colorchange timer
}
}
void Random_fader(void){
//adjust the driven LED color to get close to the desired color
for(int i=0; i<NUMBER_OF_NODES ; i++){
if (rgb[i].r > desired_color[i][0]){ //if driven color is greater than desired color
rgb[i].r--; //decrease driven color
}
else if(rgb[i].r < desired_color[i][0]){ //if driven color is less than desired color
rgb[i].r++; //increase driven color
}
if (rgb[i].g > desired_color[i][1]){ //if driven color is greater than desired color
rgb[i].g--; //decrease driven color
}
else if(rgb[i].g < desired_color[i][1]){ //if driven color is less than desired color
rgb[i].g++; //increase driven color
}
if (rgb[i].b > desired_color[i][2]){ //if driven color is greater than desired color
rgb[i].b--; //decrease driven color
}
else if(rgb[i].g < desired_color[i][2]){ //if driven color is less than desired color
rgb[i].b++; //increase driven color
}
}
}
void Random (void){
_delay_ms(PAUSE);
static dominant_color;
for(int i=0; i<NUMBER_OF_NODES ; i++){
dominant_color=random() % 3;
switch (dominant_color){
case 0: //red is dominant color
rgb[i].r = random();
rgb[i].g = random() % 125;
rgb[i].b = random() % 125;
break;
case 1: //green is dominant color
rgb[i].r = random() % 125;
rgb[i].g = random();
rgb[i].b = random() % 125;
break;
case 2: //blue is dominant color
rgb[i].r = random() % 125;
rgb[i].g = random() % 125;
rgb[i].b = random();
break;
default: //white
rgb[i].r = random();
rgb[i].g = random();
rgb[i].b = random();
break;
}
}
}
void Chase (void){
_delay_ms(CHASE_SPEED);
static int rand=89;
if (direction == 0){ // value of 0 for direction means its increasing
if (LED_num > 0){
rgb[LED_num-1].r = 0;
rgb[LED_num-1].g = 0;
rgb[LED_num-1].b = 0;
}
rgb[LED_num].r =random();
rgb[LED_num].g =random();
rgb[LED_num].b =random();
LED_num++;
if (LED_num == NUMBER_OF_NODES) {
direction = 1; //switch directions
}
}
else{ //else value if direction means decreasing
if (LED_num < NUMBER_OF_NODES){
rgb[LED_num+1].r = 0;
rgb[LED_num+1].g = 0;
rgb[LED_num+1].b = 0;
}
rgb[LED_num].r =random();
rgb[LED_num].g =random();
rgb[LED_num].b = random();
LED_num--;
if (LED_num == 0) {
direction = 0; //switch directions
}
}
}