I've created this separate page from the main page with the video stream and controls, to keep bandwidth consumption down. That said, this page will cover some of the details behind the build process and the decisions that went into the build.
First of all, be sure you're familiar with Last Year's Display. This one is much like that one, with the addition of 2 more strings, a webcam feed, and internet control of the mode and speed. :)
I took the solid state relay controlled outlets from last year, and used them for this year's setup, as well as making an extra 2 relay controlled outlets.
But that may be jumping ahead already. Let's go back to the beginning of the planning phase for this project. I had a "Nanode" unit, which is much like an "Arduino Ethernet" (came out long before the Arduino Ethernet did). The biggest hurdle I ran into with using that device was that it doesn't have an onboard serial chip, and I don't have an external serial board handy to get around that problem. I pretty much resorted to pulling the CPU out, putting it in my Uno to program it, and then swapping it back. The next major hurdle was that the Nanode uses a different ethernet chipset than the Arduino board does, so just using the standard Arduino ethernet library and example sketches was completely out. Documentation on the Nanode was a bit hard to come by, but I eventually tracked down an "Ethershield" library that seemed to have been recently updated/still being maintained. (For the record, I've since learned there's an "ethercard" library that's much nicer to work with, but I was already done with this at that point) So I downloaded that library and used the example Webserver sketch it came with to test out the Nanode. It took some work to even get it working on my network, but I did eventually get the test page to pull up.
Next up, I needed a faster way to load programs onto this thing. Pulling the CPU out and swapping back and forth definitely wasn't a long term solution. I also wanted more IO because much of the ATMega328's IO on the Nanode was taken up with communications to/from the ethernet chip. I think I figured out that I'd have exactly 10 spare IO pins, which would do what I needed, but they were randomly distributed and I'd have no serial debugging, and I just didn't want to go that route as well as not having an onboard serial port. So I grabbed a Teensy++, it's an awesome little Arduino compatible unit. It's a native USB chip, so it just comes up as a serial port when you plug it into a computer, it's got four times the amount of ram the ATMega328 has, and four times the flash as well. I had to figure out which pins the Nanode used for the SPI interface to the ENC28J60 ethernet chip, and I also had to find the appropriate SPI pins on the Nanode, and run jumper wires between them. Then I had to edit the ethershield library to change the pin assignments in that, it had a spot where it checked to see if you were using a Mega, but hadn't been written to check for a Teensy, so I just hard changed them for my own needs (email me if you want a copy of those changes).
I originally thought I wanted a telnet type server on the Arduino, so I could have a program on my web server connected to that to relay the commands, but after being unable to find a telnet server example for that ether shield library, I decided to skip that plan. I ended up ripping the guts out of the web server example sketch. To do that, I first had to make heads and tails of one of the most convoluted example sketches I've ever come across. Best I could figure was that it had been ported over from some other microcontroller, one that had none of the elegance of the Arduino. It had a goto statement in it, which I will admit, there is a time and place for them, and I'm not just being a hater. This goto statement was 100% unnecessary, it didn't help ANYTHING. There was also a command I'd never seen before, "continue", which pretty much skips the rest of the commands in a while loop and goes back to the start of the while loop. Speaking of which there was a while(1) loop as the only command executed by the Arduino's main loop.. WTF, why not use the main loop? So I first had to learn the programming techniques that had been used to write that sketch, then reconcile it with the way things normally work on an Arduino, and translate that. All that before I could actually start ripping the guts out of the web server to get it to simple receive the request, and give absolutely no response back.
Now that I had the web server sketch neutered, I had a simple way of passing commands to the Arduino. I wrote a quick test routine that would take the received command, and toggle an LED hooked to one of the output pins on and off. It worked like this, in a web browser, I'd type http://192.168.1.1/m3 that 192.168.1.1 is the address of the Nanode on my LAN, m would be a mode change, s is a speed change, the number after that selects the mode or speed, in the test, I just had 3 LED's and 1-3 would toggle one of those on or off. It worked perfectly. This was now stage one proof of concept down. I knew from last year that I could do an arduino controlled Christmas light display. Now I also knew that I could receive commands from an http request.
The next step would be creating a web page that could receive commands from the user, and then somehow relay them on my LAN to the Arduino. This presented some problems. First was that I'm far from being a web developer. Lacking fancy apps like iWeb or Sandvox on my Mac, I would be creating startlingly ugly Web1.0 pages. I first got some great help from my wife, pointing me in the right direction. I will say I did know that I needed to do something with a CGI script. I just had no idea what they were or how to create one. Google search to the rescue, and I started with trying to write one in Python. Got stuck a couple times with commands that were supposed to work, so I went to the Python channel on the Freenode IRC network. They suggested I not use Python after I requested some help. Okie dokie. I decided to just do a bash script. I found some examples of CGI bash scripts, and went with one of them. I created a test page with some radio buttons that would call the CGI script, the CGI script would parse out the command to decide if it was a speed or mode request, and pass along the appropriate command to the Unix program "wget". WGet is usually used to download a file from an HTTP server from the command line. I simply used to to relay the command to my Arduino. It worked great once I told it to only try once. It gets no response back, so it'd just try and try and try assuming it failed.
The problem with my first attempt was when you'd click submit on the radio button, it'd load the cgi as a new web page. Totally navigating away from the site. So that's where another friend came in. He suggested putting the forms into a frame. That way only the frame gets redrawn. This still led me to the problem of the forms going away, until I found another cgi example where the CGI drew the webpage if it didn't receive a command, so I went with that style. The form calls the CGI, if it gets a command, it passes it along, and then either way it draws the forms (in a table, to keep them side by side - suggested by my wife). This did the trick nicely.
At this point I needed to write the "modes" the patterns that the controller would be displaying. I took my laptop (the lovely 11" MacBook Air </brag>) to work and during my breaks during the work week, I got the 8 modes written. For most of them, I could do a mode inside of a 15 minute break. I'd come home with two new modes each evening, load them up onto the Teensy, and then pull up my test webpage to change to them and try them out. Most did not quite work the first try, but were easy to nail down from there. It took awhile to think up all the different modes. Some of them are completely different from last year. I had a lot of redundant modes last year that were simply variations of another mode. One of the best parts of the sketch, in my opinion would be the way the main loop is structured. I have a small loop that pauses the program for the appropriate amount of time based on the current speed selected, while it's looping waiting for the time to go by, it's checking the ethernet chip to see if any commands have come in. If it's a mode change command, it breaks out of that delay immediately so it can call the new mode, if it's a speed change, the target of this delay loop was changed and it automatically exits sooner or later than it otherwise would have. From there it calls the appropriate mode's subroutine. Each mode subroutine moves it's pattern one step along, increments it's counter, and returns to the main loop. I'm quite happy with the simplicity and elegance of that design.
Now I had the website that could control the Arduino, and I had a functional sketch on the Arduino to control the lights. Next up was setting up a webcam for the video feed. I had the perfect computer for this, it's a Nettop computer with a dual core Atom chip in it that I picked up off Newegg really cheap a couple years ago. I didn't have a webcam before this project, so I had to run to Staples and pick one up. I got the cheapest one that I though could do the job, it's a Microsoft brand, as much as I loathe that company, at least I know they wouldn't lie about their hardware specs, unlike some of the no-name webcams. As for keeping this machine dry, I had that idea almost immediately. I'd stick it inside of an upside down fish tank. After a quick trip to the local Goodwill store, I had a nice used fish tank for $6.
The first thing I wanted to do with the fish tank was to black out all the glass that I wasn't using. I masked off what would become the front of the fish tank, and then painted the rest of it flat black with some spray paint.
Not the greatest picture, but it shows the fish tank after the paint job. The fish tank was about 12" deep, I wanted to put the computer inside of it at least 6" up, so that it'd be safe from any moisture that tried to wick up the cords or otherwise get into the box. So I grabbed some old wood and created a structure to fit neatly into the tank.
I had done some experiments with the camera out in the yard, and figured out about where the camera needed to be, and how high it needed to be. The problem was, it needed to be about 6' up in the air, and there was nothing anywhere near there to put it on. I thought about building some kind of structure but in the end settled with a nice and simple solution. I had some 30 gallon barrels in the garage, so I stacked two of them up. That got me the height I was looking for. I put about 10 gallons of water into each to help stabilize the stack, and then I added some support ropes going down to some stakes in the ground to keep the whole thing from blowing over. I seem to have misplaced the pictures of that process, but here it is in completed form.
You can kind of tell that I was running out of yard, it pretty much HAD to go right there.
The next trick was getting a live webcam feed from that camera up on the webpage, in a way that wouldn't overwhelm my home server's bandwidth. I looked into a couple different streaming services, and eventually just settle with UStream because they were the only ones that actually worked when I tried. Their crazed overwhelming overload of ads and commercials is annoying, but at least they're reliable. I did a test run for a couple days to confirm the thing actually worked reliably, showing the front of my house live to the internet. I also did a test run in the house with the Teensy/Nanode setup with 10 LED's and the webcam pointing at it, I got several people to log into the stream at the same time and they all tried changing modes and speeds. They weren't exactly trying to do it at the same time, but they were jostling for control and it handled it quite well.
Next up was putting the lights up on the roof, nothing really mind boggling there except that I tried my best to avoid using extension cords, because they're expensive. I bought a 1000' roll of speaker wire and calculated how far it was from each string's location on at the peak of the roof down to my back door. I cut each plug off the end of the string of lights, and put the appropriate length of speaker wire in between the plug and the rest of the string. For the most part, I got it the lengths just right, but 4 of the cords were too short for some reason. So I ended up using 4 very short extension cords. Not perfect, but better than having to buy 10 extension cords, some of which would have had to have been 50 footers.
Final problem to solve was putting the Arduino and it's outlets in a dry place. Last year I had them on the front porch, but that left the front of the house looking pretty bad with extension cords and wires strung all over the place. This year as you can see, I ran them down the back side of the roof. This definitely made things tidier, but I no longer had a nice dry porch to put everything under.
So I put it under the awning of the house. This turned out to be a lot easier than I had originally feared. I peered up under there and I saw vents to let air circulate through there, and the vents had metal screens over them to keep birds out. So a couple zipties later, I had the whole thing hanging from the metal screen. It's far enough up under there that I think it'll be safe from the majority of the rain, time will tell if it's not good enough, though.
The above is the sketch I've got running on this controller. Feel free to use it for your own projects, portions of it, whatever. Or simply marvel at the simplicity of this program. :)
Here's the CGI Script I had processing the requests on the server:
As I mentioned earlier, I had planned to write the script in Python, but ended up settling on just writing it in bash. I'd never done a bash script before, and I found a couple good examples online which I bent to do my bidding.
And finally, a video tour of this whole contraption. Maybe you'll like it better than the pictures and text posted above.