Let's talk about the client side. As mentioned in earlier posts, I'm using an Arduino Uno with a CC3000 WiFi shield. The clock lighting is a ring of 60 NeoPixel LEDs.
The full source of the Arduino sketch is available here https://github.com/jimnelson2/WeatherClock-Client. I'll walk through it and then finish up by noting what work remains to be done, improvements could be made, and I how'd do this project differently now that I've done it once.
The sketch starts by defining a numerous constants - hardware references, constants, and globals used primarily for timing.
In the setup() function, we clear the NeoPixels to make sure they are off, initialize the CC3300 shield, and get the IP address of the server hosting our API described in the prior post.
In the loop() function, we first handle the "heartbeat" indicator. Since most of the time there is no weather info to display, I want an indication that the unit is functioning. I do this by lighting a single pixel every time we loop, which is every second. The LED to light is determined by simply mod-ing the sketch time by 60. This gives us, the appearance of a somewhat randomly selected pixel. It's nice.
After the heartbeat is handled, we check if it's been 2 minutes since our last call to the API. If so...we make the call. The API request is to the static API URL, and we include in our request header the calculated one-time password value needed for authentication. We make the call and assume success (more on that later). Our 60 characters of response to display on the ring are demarcated by the " character, so once we encounter or first " we use the next 60 characters to map into the array of 16 colors defined at the top of the sketch, setting each NeoPixel in turn to the mapped color. When all 60 LEDs have been set, we tell the NeoPixel ring to update and end the loop.
The remainder of the sketch code is composed of supporting functions such as OTP calculation, wifi shield functions, etc.
I mounted the NeoPixel ring and the Arduino inside a wall clock from Target that was only four bucks [http://www.target.com/p/room-essentials-9-wall-clock/-/A-50330028#prodSlot=medium_1_1&term=wall+clock]. The clock diameter was perfect for the ring, leaving about an extra inch all around and the depth of the clock left just enough room to mount the Arduino. I did all the mounting simply by drilling holes through the clock faceplate and securing the ring and board with twisted wires. I used a flexible cutting board (also from Target) as my diffuser. It was easy to cut with kitchen shears to the same dimension as the clock's cover and fits snuggly underneath.
Getting the diffuser to fit in the clock frame
LED ring mounted in frame
All electronics mounted. I am clearly NOT good at routing wires, but this works.
Among the work that remains to be done before I can really call this finished is:
- My API call assumes success. When the call doesn't succeed for whatever reason, there is either no indication of the failure OR the NeoPixel ring ends up displaying some odd lighting combinations as a result of parsing HTTP error text rather than the expected colors indicators. The code needs to check for HTTP 200 and take action when a non-200 is received. I'm not sure what that action might be. Perhaps I could change the color of the heartbeat LED, or light a particular pixel based on the failure. e.g. If pixel 1 is a solid white, that could mean the API was unreachable, etc.
- DNS lookup of the API occurs only during setup, and never again. I should be doing that lookup at some interval in case the mapping changes
- Sometimes, the client will take upwards of 10 minutes before making a successful API call after powering up. I don't know why.
Improvements:
- The heartbeat is a decent indicator of a functional client, but I'd like something "nicer". Maybe it could display an actual clock?
- It might be nice to have the clock display an indication of weather alerts, e.g. thunderstorm warnings, etc.
- The clock is quite bright in a darkened room, even with the LEDs set to very low power. I think two things can be done here. One, the diffusion panel can simply be thicker to block more light. Two, a photoresistor could be added to the circuit to automatically brighten/dim the LEDs based upon ambient light.
- All of the useful settings - Location, one-time-password key, access point name/password -- are hard-coded. A "real" product would require a way to enter this information without having to compile and upload a sketch
What I'd do differently:
- The CC3000 shield certainly works, but I found it challenging to use with respect to stability - my hardware would at times simply "lock up". By making my sketch size in RAM as small as possible, and doing all I could think of / find online to ensure the shield was working/connected the stability problem appears to be gone. But, this took a lot of time to figure out - probably more than any other part of the code. While I learned a lot in the process of making it work, I'd choose different hardware.
- I might try a raspberry pi or other board. I never really exhausted the 2k of RAM or 32k of code space...but I got close. Given the improvements I'd like to make I'm not sure I can fit it all onto an Uno. Apparently it is possible to drive the NeoPixels from a pi...[link here]
- Get more equipment. Some of those "third-hand" setups to hold wires while soldering would've been nice, as well as a big magnifying glass to see better, and a better soldering setup.
- Beyond that...not much.
I'm pretty happy with how this has turned out. I don't know how long I'll keep this hanging on my wall but I'm enjoying it now. A couple more in-use pictures are below. All the pictures I've taken show it in a dark room, but that's just because it's the only way I could get a decent picture - my camera phone just washes out the images otherwise. The display is plenty bright even in a well-lit room.
Light rain starting in 15 minutes, continuing for at least 45
Medium/heavy rain for next 15 minutes, followed by light rain