I recently built a device that displays MUNI bus and train arrival predictions in real-time. I live in a rare pocket of MUNI route richness in San Francisco, the Lower Haight, and am lucky to be within short walking distance of nine MUNI lines. When a line is delayed, has three buses backed up in a row, or I simply need to go downtown, I can pick from a number of options. The NextBus website is easy to use, and iPhone transit apps abound, but it’s still nice to just glance up to a screen as I’m running out the door and see which bus or train I should take.
The display shows up to three upcoming times for inbound and outbound routes I selected. Each route and its predictions are shown for three seconds before the display shows the next route. At the end of the route list, the display returns to the beginning of the list and starts over. A single button lets the user cycle quickly to a route of interest instead of waiting through each route in the list. The prediction times are updated about once per minute.
The core is an Arduino Uno Ethernet, which is very similar to the Arduino Uno with an Ethernet shield, except the Ethernet interface is integrated into the same board as the Uno. A protoboard shield holds a few peripheral components, such as a potentiometer for setting the LCD contrast and hardware button de-bouncing. The LCD is a 16×2 character amber-on-black display to keep in style with SFMTA’s bus shelter indicators. The single button is an SPST momentary on-on switch.
The NextBus API is reasonably documented and gives not only MUNI predictions, but also info for transit agencies in a few other cities. The API can give route details, such as a list of all the stops in a route, or even the list of all the routes in a transit agency, from which one could systematically build a list of all the stops for all the routes. All that’s needed for this project is to call the API for a particular route and stop ID pair, and handle the response from the server. For the curious, I’ve made the Arduino code available on Github.
The API response for a single route and stop, which can hold several prediction times, is often in the range of 800 bytes. Since the Uno has only 1 kB of RAM, it’s a bit of a challenge to accept and manipulate the response to find the times of interest. I started using Arduino String objects, but wasn’t able to get it to work reliably with the long string responses returned by the NextBus API. I eventually used character buffers and C
string.h substring matching and copying instead. Rather than holding the entire response in memory, the device captures single line responses from the server and extracts prediction times from each line, if available. After the third route time is extracted, the rest of the route API response is discarded.
The list of route and stop pairs is hard-coded into the Arduino sketch. The Nextbus API format is also hard-coded into the UNO, but I assume this changes very infrequently.
The enclosure is made of layers of laser-cut 0.5mm thickness wood from Ponoko (timber core with a timber veneer on each side). I designed each layer in Illustrator, stacked them on top of each other, and glued them together. This is how the laser-cut wood came shipped from Ponoko:
Prototype showing the layers before they were glued together:
After gluing the layers together, I added wood fill to smooth the gaps between the layers, then sanded it down to smooth it further, and stained it to give it a darker look.
I’m considering adding a few things now that the display works reliably and is installed in the hallway:
- A great enhancement would be to add a software-based route and stop selector / menu that updates the UNO’s sketch so users can add or delete routes and stops.
- Over-the-wire firmware updates would allow for tweaks, such as updating routes and stops, without needing to connect a computer directly to the device.
- Moving from wired to wireless: this certainly isn’t necessary, but would allow for installation in many more locations and without requiring connecting the device with an Ethernet cable.