Raspberry Pi Controlled Christmas Tree

Phase 1: Planning

This a tricky chicken and egg problem. I know what the results I want are: I want a layout of 100 individually laid out RGB LEDs that are all controlled with a programmable micro-controller. I wasn't committed to the raspberry pi when I started the purchasing decisions for the project, nor had I figured out what sort of LEDs I wanted. After much research I chose ws2811 LEDs due to them being available in a form factor that worked well for this application. I chose some 50 LED per 8m strand odd China made strands.

The next step was deciding how to control them. These suffer from an 'interesting' control protocol that you can't efficiently output directly from a Raspberry Pi. People have done it, but for this project, I'm not going to mess with it. I opted to use a FadeCandy USB driver that uses an AtTiny to interface with USB and directly drive up to 8 strands of 64 LEDs. This isn't without its own problem for this project. This will require the LEDs to be laid out in 2 50 LED segments. I had originally hoped to be able to directly run the lights in one linear string. I could still pseudo do that with a long control wire for the second strand, but I have bigger plans for the future (and another article), so I'm going to layout the LEDs in 2 parallel strands.

Now that I've settled on driving ws2811s with a FadeCandy, I'm opting for a Raspberry Pi B+. I bought one of the million start kits on Amazon.

The final 'big' piece is powering all of this. The LEDs are rated for 60mA each. That's 6A at 5V. The raspberry pi isn't real power intensive (we'll call it 2A at 5V). That means 8A at 5V is our MINIMUM. I happen to have a dependable 10A 5V benchtop power supply for setting this up, but plan on getting a 15A or so 5V power source for this later. This is the only part that makes me nervous. Power supply selection is the most overlooked step in most electronics builds, and I can't stress it enough that a good quality power supply will be uneventful, and a bad quality one can kill your electronics, you, or burn down your house.

Phase 2: The Design

Part 1: The Layout

I had already laid out the LEDs like this:

OneStrand.png

Expecting to build the tree with one continuous strand. I re-did the layout like this:

TwoStrand.png

For two strands. In these drawings, Each dot represents a 'fixed position' LED along the individual chains. In the 'trunk' portion of the tree, I plan on making all the LEDs brown, and along the 'tree' portion, I plan on making these 'fixed' LEDs the blinking ones. In between each dot, there are 2 floating LEDs, that will be positioned based on their individual best look. In the trunk, they'll be brown; in the tree, they'll be green.

If you count the dots, and do the math, you'll see I have 46 LEDs accounted for in each strand(16 dots, 15 * 3 + 1(at the end)). This leaves 8 LEDs at the top that can be used for a 'topper' design or to light some other topper. I haven't decided yet.

But then the WAF was considered. The wife (who has power of veto over all things aesthetic for a very good reason), liked the idea of the horizontal lines being straight across rather than my wavy design. So we're putting a pin in the layout for now.

The plan is to layout the LEDs in between 2 sheets of plexiglass, cut in a shape similar to that of a tree. This will be stuck into a standard Christmas tree holder, which will hold all of the electronics, probably covered with felt or some other tacky material.

Part 2: The Electrical

The plan is to have a single 5v power supply running everything. This will prevent ground loop issues that could affect the signal down the LED strands.

Phase 3: The Order

The various bits were all ordered through Amazon. I took my time in selecting what I would use to get the total hardware to come out to less than $100 before the plexiglass. Parts started showing up 2 days later.

TODO: Link What I'm using

I started with...

Phase 4: The Raspberry Pi

First, I had to replace the Noobs image on the SD Card with raspbian. I don't have an extra USB Keyboard/Mouse to use the Noobs installer, so use whatever raspbian install method you prefer.

I SSH'd in, expanded the filesystem, twiddled the settings a little, and rebooted it. Next, I setup wpa_supplicant (I just copy/pasted the entry for my wifi network from my main Linux box). Rebooted and unplugged the wire. Now I had a Raspberry Pi that automatically powered up and connected to the wifi, onto configuring the Pi.

attachment:pi.jpg

Next on the list was the FadeCandy. First up is plugging it in to see if it's detected:

[ 1124.047209] usb 1-1.3: new full-speed USB device number 5 using dwc_otg
[ 1124.150596] usb 1-1.3: New USB device found, idVendor=1d50, idProduct=607a
[ 1124.150633] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1124.150650] usb 1-1.3: Product: Fadecandy
[ 1124.150663] usb 1-1.3: Manufacturer: scanlime
[ 1124.150677] usb 1-1.3: SerialNumber: ZBUEBZRNHPATURWX

And there it is. Next up, installing the fadecandy server.

pi@raspberrypi ~ $ cd /opt
pi@raspberrypi /opt $ sudo chmod a+rw .
pi@raspberrypi /opt $ git clone https://github.com/scanlime/fadecandy.git
Cloning into 'fadecandy'...
remote: Counting objects: 4326, done.
remote: Total 4326 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (4326/4326), 10.20 MiB | 341 KiB/s, done.
Resolving deltas: 100% (2232/2232), done.

I opted to give the 'pi' user write permissions to /opt instead of requiring sudo for all future work (or having to change the owner on /opt/fadecandy). I chose /opt for where to place it because it's my traditional spot to stuff binaries that aren't installed by the host distro.

Testing fadecandy server:

pi@raspberrypi /opt $ cd fadecandy/bin/
pi@raspberrypi /opt/fadecandy/bin $ sudo ./fcserver-rpi 
[1418847051:8469] NOTICE: Server listening on 127.0.0.1:7890
USB device Fadecandy (Serial# ZBUEBZRNHPATURWX, Version 1.07) attached.

So far, so good. Now I want to test remote access... so I have to add a config file. I created /etc/fadecandy.json with:

{
    "listen": [null, 7890],
    "verbose": true,

    "color": {
        "gamma": 2.5,
        "whitepoint": [1.0, 1.0, 1.0]
    },

    "devices": [
        {
            "type": "fadecandy",
            "map": [
                [ 0, 0, 0, 512 ]
            ]
        }
    ]
}

I launched fadecandy with that config:

pi@raspberrypi /opt/fadecandy/bin $ sudo ./fcserver-rpi /etc/fadecandy.json 
[1418847708:4504] NOTICE: Server listening on *:7890
USB device Fadecandy (Serial# ZBUEBZRNHPATURWX, Version 1.07) attached.

and launched the webui:

fc-web.png

Next, I updated the config file to 'know' about my specific FadeCandy and know that Port 1 has 50 LEDs, and Port 2 has 50 LEDs:

{
    "listen": [ null, 7890 ],
    "verbose": true,
    "color": { "gamma": 2.5, "whitepoint": [ 1, 1, 1 ] },
    "devices": [
        {
            "type": "fadecandy",
            "serial": "ZBUEBZRNHPATURWX",
            "map": [ [ 0, 0, 0, 50 ], [ 0, 50, 0, 50 ] ]
        }
    ]
}

The initial raspberry Pi config is done, now onto the...

Phase 5: LEDs

The LED strands I bought were conveniently designed to be chained together. With careful violent chopping, I can use the 'far' end connectors to build a nice little harness to power it. I made sure to trim the wires to different lengths to easily isolate them from shorting. On the input side, the connectors were female. I soldered the connectors directly to the FadeCandy.

Signal Wires To the LEDs:

attachment:signal.jpg

Fade Candy Side:

attachment:fade-candy-tails.jpg

Ghetto Short Term Power Harness:

attachment:ghetto-wires.jpg

Rats Nest of Unlit LEDs:

attachment:ratsnest.jpg

And all that's left is to power it up (the 'smoke' test). I plugged the LED strands into the BlinkCandy, plugged the BlinkCandy into the Raspberry Pi, then plugged the power into the LED strands. Finally I used the FCServer webui to do a 50% brightness test pattern.

attachment:nosmoke.jpg

Success!

Phase 6: More Software

Now that I have 100 LEDs lit up, I need to setup a better control system than Full on, Half on, off that's provided by FCServer. I'm planning a http frontend, so I installed lighttpd (I've always preferred this to apache, but either should work).

sudo apt-get install lighttpd

I pulled up the test page, verified that lighttpd was working, and then transfered the example html control program from the git code checkout. I edited the mouse.html example, set leds to 100, set the server to the lan IP (instead of localhost), and pulled it up in my laptop webbrowser. It turns out only LEDs on the first strand worked from the webpage. I pulled up the built in webui to the server and it still lit them all. I tinkered with the fcserver settings file again and came up with:

{
    "listen": [null, 7890],
    "verbose": true,

    "color": {
        "gamma": 2.5,
        "whitepoint": [1.0, 1.0, 1.0]
    },

    "devices": [
        {
            "type": "fadecandy",
            "serial": "ZBUEBZRNHPATURWX",
            "map": [
                [ 0, 0, 0, 50, "grb" ],
                [ 0, 50, 64, 50, "grb" ]
            ]
        }
    ]
}

You'll find 2 major differences from earlier:

1. parameter 3 in the second strand definition changed to 64 (0-63 are the LEDs on the first port, of which 50-63 aren't populated).

2. I added "grb". I noticed that red was showing up as green, so I swapped red and green.

Next up, I wanted to create a basic script that simulates a Christmas tree light pattern. There are 3 types of LEDs in it: 'B' is Brown, 'G' is Green, 'S' is sparkling/blinking. The code is basically completely undocumented. It's a simple program so it shouldn't be too hard to follow.

use strict;
use OPC;
use Time::HiRes qw/usleep/;

my $num_leds = 100;
my $client = new OPC('192.168.1.9:7890');
$client->can_connect();

my $pixels = [];
#             0   1   2   3   4   5   6   7   8   9
my $bulbs = ["B","B","B","B","B","B","G","S","G","G",  #0
             "G","G","S","G","G","G","G","S","G","G",  #1
             "G","G","S","G","G","G","G","S","G","G",  #2
             "G","G","S","G","G","G","G","S","G","G",  #3
             "G","G","S","G","G","G","G","S","G","G",  #4
             "B","B","B","B","B","B","G","S","G","G",  #5
             "G","G","S","G","G","G","G","S","G","G",  #6
             "G","G","S","G","G","G","G","S","G","G",  #7
             "G","G","S","G","G","G","G","S","G","G",  #8
             "G","G","S","G","G","G","G","S","G","G" ];#9

my $green = [0, 0x4f, 0];
my $brown = [0x40,0x20,0x00];

my $led = 0;
for($led = 0; $led < 100; $led++) {
  @$pixels[$led] = [@$green[0], @$green[1] + int(rand(5)), @$green[2] + int(rand(5))];
}       
while(1) {
  for($led = 0; $led < 100; $led++) {
    if(@$bulbs[$led] eq "G") { @$pixels[$led] = [@$green[0], @$green[1] + int(rand(5)), @$green[2] + int(rand(5))]; }
    elsif(@$bulbs[$led] eq "B") { @$pixels[$led] = $brown; }
    elsif(@$bulbs[$led] eq "S" && int(rand(10)) == 5) {
      @$pixels[$led] = [int(rand(128)),int(rand(128)),int(rand(128))];
    }
  }
  $client->put_pixels(0,$pixels);
  usleep(300000);
}

And the results:

attachment:LightsOn.jpg

And the animated gif:

attachment:animation.gif

Other/RaspberryPi-ws2811-XMasTree (last edited 2014-12-18 22:10:11 by PatErley)