Traffic Light
Contents
Background
When I was a kid, I once convinced my parents to buy me a traffic light shaped lamp that I saw at a store. One problem with it though was that it wasn’t really a traffic light, which was what I had wanted. It just had three bulbs in: one for all of the reds, one for all of the yellows, and one for all of the greens, and the bulbs all just blinked randomly (they were just basic thermally activated flashing bulbs), so you would just get random combinations of colors. Very disappointing if you want an actual traffic light to play with.
So, now that I have some basic electrical skills, I decided to rectify this problem.
Teardown
The original traffic light was really dead simple. The main body was just a shell with colored windows, and the back panel just had a set of 3 light bulb sockets mouled into it, which were wired directly to 120V AC wall power through a simple switch in the cord:
All I had to do was clip the wires to feed them back out of the stand, remove the bulbs, and then cut the original lightbulb sockets off the back with a hacksaw.
This left me with an empty shell to put whatever I wanted in.
Light Partitioning
So, the traffic light was now just an empty shell, and I wanted to put in individually controllable LEDs for every window. For that I would need something to actually put the LEDs on. Additionally, I wanted something to block light from passing between windows so that lighting up one window wouldn’t bleed over into its neighbors.
I did this whole project before I got a 3D printer, so the way I decided to solve this problem was by using a relatively light but sturdy cardboard and constructing a frame that could sit inside the shell to hold the lights and block light from bleeding between windows.
It took a lot of careful measurement and cutting and hand-fitting, but I was able to assemble somethig that actually fit quite well.
Electronics
For the brain of the traffic light, I chose to use a Particle Photon which I won at one of the hackathons I did. Particle provides IoT services that make it really easy to connect the devices to wifi and then send commands to them over the internet. However, its I/O pins aren’t really all that good for supplying the kind of current that bright LEDs need, so to run the lights I needed something else.
What I ended up choosing was a pair of Texas Instrumets TLC5916IN constant-current LED drivers. These are devices that act like a shift-register from the perspective of the microcontroller driving them, but sit between the LED and ground and select between either on at a constant current or off. The actual current supplied is chosen by an external resistor; the current through the driven LEDs is proportional to the current through the external resistor, so if you know the current you want on the LEDs you just choose a resistor that gets you that current setting.
I needed two LED drivers because each one has only 8 outputs but my traffic light has 3 faces with 3 lights each, so I had to chain the two drivers together into a 16 bit shift register to control all 9 lights independently.
I initially put this together on a breadboard to test that everything was working, then installed the lights in the traffic light while still connecting them to the breadboard.
I then assembled the LED drivers into a prototype board. However, I kept the Photon separate from this, partily because I only had one of them and partly because these prototype boards are hard to be space-efficient with. This caused problems to crop up which didn’t happen on the breadboard. Specifially, the length of wire between the Photon, which was outside the trafficlight, and the prototype board inside was sufficient to cause signaling issues that meant the shift registers became unreliable and sometimes wouldn’t shift the correct number of times.
To fix this, I ended up learning how to use KiCad, and designed a single circuit board that could hold the Photon and shift registers and provide a place to soldier on the leads from all of the LEDs. It’s a pretty simple board, but it is the first board I ever designed and had manufactured.
One mistake I made was not making the holes for the pins connecting the Photon large enough. Fortunately it was close enough that a bit of very careful tapping with a hammer could make it fit, and I didn’t have to order a whole new board. Once it was all assembled onto a single board, the issues with the shift registers being unreliable completely disappeared.
For the power supply, I just bought a basic USB power supply and spliced the cord in order to fit it through the narrow stem of the traffic light base. It then just plugs into the USB port on the Photon.
Software
The software on the Particle is relatively straightforward. It is divided into two different modes. The default mode is traffic light mode, where it sets green on two sides and red on the other for about 30 seconds, then switches to yellow and all red before going to green on the one side, and repeatedly cycles through that pattern. The other mode is manual mode, where a client gives explicit state change commands over the Particle web service, which can command individual lights on or off or set to specific states. There was originally intended to be a third mode as well called ‘indicator’ which would be similar to manual, but have additional options such as the ability to set lights into different blink patterns without having to explicitly toggle them on and off constantly through the web API.
One complicaton for this software was that the Particle web service doesn’t allow delivering arbitrary bytes, only some subset of ASCII or maybe Unicode, I’m not even really sure. The thing is, I generally prefer to avoid working with text-based encodings on microcontrollers, so I didn’t want to do something like JSON or some other text format. I had already come up with a binary encoding scheme to use for sending commands and I wanted to use that.
One way to send binary data over a connection that only allows text is to encode it to Base64. While a variety of libraries were available for working with Base64 in C++ that I might have been able to adapt to run on the Photon, many of them did a lot of string allocation, or at least made an allocation to store the output, which I felt would kind of defeat the purpose of my attempts to avoid excessive text manipulation.
To resolve this, I ended up creating my own Base64 decoder which works on
absl::string_view
to avoid unnecessary copies and decodes known-size
messages into either a stack allocated buffer or directly into a series of variables on
the stack, provided it knows how to convert their type to the correct endianness. For
example, this code decodes two uint8_t
and a uint16_t
from a network_data
string:
uint8_t major_code;
uint8_t minor_code;
uint16_t argument;
if (base64::Decode(network_data, &major_code, &minor_code, &argument)
!= base64::DecodeError::kNone) {
// handle error
}
This all works by doing a substantial portion of the decoding in C++ templates, which trades binary size for precompiling a faster exact-match function for the data length being decoded. Neither speed nor space were particularly problematic in this project, but I thought it was interesting to pick this side of that tradeoff.
On the other side, I had two ways of controlling the traffic light. One was a Python library I build wrapped around the Particle API, with a CLI available as well, and the other is a React web app that lets you (well, me; the controls for my traffic light aren’t open to the public, sorry) select the mode for the traffic light, view its current state, and switch lights on and off. In this screenshot, the trafficlight in the upper right corner shows the state of all three faces (currently all off) and lower section is a set of buttons that let you turn on, turn off, or toggle individual lamps or whole rows or columns.
Assembly Video
Here is a timelapsed video of me assembling the traffic light.