The “T” Shirt — A Wearable, Real Time Public Transit Tracker

Guy Dupont
10 min readMar 7, 2020

Recently, on a trip to New York, I noticed that the “Departures” board at Union Station (New Haven) had been upgraded. Travelers can now see every train’s destination, track assignment, and status, broadcast in full color on a high-definition digital display above the station’s main doors. It’s bright, crisp, and presumably easy to update. But if you — like me — grew up watching the clicky, clacky, whirring old electromechanical board, you may understand why I sulked all the way to Grand Central that day.

A Solari board in Frankfurt, not New Haven (but you get the idea). Photo by Ratopi — Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=1311688

Union Station’s old board was a large format split-flap display, made at some point in the middle of the 20th century by an Italian company called Solari di Udine. If you’ve never seen (or more importantly, heard) one of these in action, you’re missing out.

I found myself thinking about the retired display after I returned home to Boston. More broadly, I thought about how the engineering/design considerations in the world of information display have changed over the last 100 years. Solari produced this insanely complex machine with hundreds of moving parts, and, at the end of the day, it could never aspire to be anything more or less than a “Departures” board. It housed a finite number of characters and was firmly assembled to display its data in a very specific way. It had more in common with a primitive, human-driven display (like the scoreboard at Fenway Park) than it did with more modern, pixel-based monitors. Still, in spite of the high ratio of complexity to functionality, it feels unfair to dismiss it as “over-engineered” without context. The Solari board provided a great deal of value to Union Station’s operators and customers alike, even after the costs of development, manufacturing, and maintenance have been factored in. I’m not even going to try to quantify any of those things, but it was up for years, right? It performed its one, relatively straightforward job — and did so beautifully, in my opinion — for a very long time. Here I am, writing about it on the internet! I’m not the only one.

As a software developer, I sometimes feel as though I’m just rearranging the same set of abstract building blocks when I bounce from project to project. I rely heavily on pre-built frameworks and libraries developed by my peers. There’s a persistent voice in the back of my head, reminding me to make my code as modular and portable as possible. It makes sense from a time/effort standpoint to not rewrite similar code over and over — I totally get it. But in 2019, after my revelation in New Haven, I couldn’t help but wonder: what would it have been like to design and build a system at a time when compatibility was less of a consideration, simply because there was no existing infrastructure to build on top of? A clean slate, no commonly adopted standards to adhere to. Just a problem to solve, and some kind of technical tool-set to attack it with. I realized that I wanted to try my hand at designing something that would have an explicit, singular purpose. I wanted to build something that wouldn’t (necessarily) be worth stripping for parts, even after it’s time had passed. I decided to build…

a “Departures” board. Kind of.

Early Reveal

For the sake of any readers who have no interest in learning how I did this, I’m going to skip to the part where I show you what I ended up with.

I built a dashboard that uses pulsating LED lights to show the estimated departure time of the next train at each station along the Red Line of the MBTA (a.k.a The T; a.k.a Boston’s public transit system).

I built it into a shirt.

I wore it for Halloween.

Some design notes:

  1. Lights blink faster as a train approaches the station. A solid light means the train is scheduled to leave within 1 minute.
  2. The Red Line has trains moving in both directions along a given route. I’m only showing southbound trains. Locals will point out that the southern-most stations are at the top of the shirt. Oops.

System Design

Now, the how. Let’s back up to the moment I decided to start this project.

The Data

I knew I would have to figure out a way to approximate each running train’s location. I incorrectly assumed that this would be the most challenging part. Turns out, the MBTA has a public “predictions” API. With a single query, you can fetch the list of trains that are closest to each station, along with the associated arrival times. The API is precise, requires no authentication, and is only gently rate-limited. After a few minutes of Googling, I’ve come to the conclusion that these services are quite common in the U.S. — your local metro area probably provides similar data!

The Visuals

Blinky lights! I needed at least 10 lights that I could address individually, attach to fabric, and power from some kind of portable battery pack. I’d like to say I spent time searching for the best option, but I immediately reached for some WS2812 LEDs. If you spend any time on Adafruit, you may also know them as NeoPixels. I cannot recommend these enough if you’re new to hardware — they provide so much visual bang for your buck. They are designed to be daisy-chained, so you can add a good number of lights before you need to add any additional supporting hardware or software. (I ended up using 17). Adafruit even sells them in a form factor that is designed to be sewn on to fabric. No-brainer for this project.

The Driver

I needed some way to tell each of my LEDs to turn on and off at the right times. This job is commonly handled by a microcontroller, which is a small and simple computer designed to perform one job efficiently. A microcontroller generally does not run any kind of operating system —instead, just a single program (a.k.a firmware) written explicitly for it. My program would need to receive some simplified train location data, convert it into a series of light pulses, and push those out the the LEDs. This should happen quickly and repeatedly until the microcontroller is powered off.

Fortunately, the WS2812 LEDs are extremely well supported by the maker community. You can be confident that somebody, somewhere has written a WS2812 library for whichever (reasonably popular) microcontroller you choose for your project. I don’t have any concrete numbers to support that statement, but I feel good about it.

I knew from the outset that I wanted my microcontroller to be responsible for as little work as possible, because:

  1. Hardware/firmware is harder to debug (for me, at least).
  2. Resources are more constrained. I’d have hated to find out down the road that my embedded program was too large or demanding to run on whichever chip I chose.
  3. I’m faster/more comfortable in software-land. I’ve spent significantly more time there.

So, at the end of the day, it didn’t matter which chip I chose. I wouldn’t need it for much. I’ve had good luck running WS2812-based projects off of $1.00 microcontrollers purchased in bulk from AliExpress. Ultimately, I went with the Gemma M0 board from — you guessed it — Adafruit. Like the LEDs (which I already had in my cart), it was designed to be sewn on to a garment. I also liked that it had a physical on/off switch. It’s actually a little overpowered for this project, but still very reasonable at ~$10.

The Glue

I still needed some way to fetch the data from the MBTA, process it, and push it to my microcontroller. I initially thought about consolidating all of this work on a more powerful, WiFi-enabled chip (like the Espressif ESP8266), but I balked at the idea of having to reconfigure the network connection each time I moved to a different location. I eventually realized that — duh — I already have a relatively consistent internet connection in my pocket. The rest of my system’s design suddenly seemed obvious. I could use my Android phone’s USB port to provide both power and data over a single cable. Conveniently, I work professionally as an Android applications developer, so I was confident I could get this done efficiently.

The Build

I plan to open-source all of the code for this project. It’s going to take some work to get it into a presentable state. If you’re reading this after Halloween 2020, and you don’t links to any code, feel free to write me a sternly-worded response.

The Shirt

I used electrically conductive thread to attach and deliver power to my components. I was hoping to use it to carry data for the LEDs as well, but it ended up being significantly faster to run soldered wire for those connections. I personally found the thread to be difficult to work with.

  • I ran one long line of thread to act as a power rail between my microcontroller and LEDs, and another to connect all of their ground pins.
  • I ran another line of thread from the pin on my microcontroller that I had chosen to emit data to the “data in” pin on my first LED.
  • I soldered short lines of solid-gauge wire between subsequent LEDs’ “data out” and “data in” pins.
  • I taped a short USB cable to the inside of the shirt, so that one end could be dropped into my pants pocket (where my phone would sid) and the other could run to the microcontroller that had been sewn to the shirt. I had to cut a small hole in the shirt for the end of the cable, since the microcontroller faced outwards.
  • I used a fabric marker to write each station’s name.
An early shot, before the station names were added. The data line (black wire, up the middle of the LED chain) is much bulkier, but would have taken forever to do with thread.

Android Application

At a high level, here’s how it works: the app performs a task, silently and periodically, until I force-kill it. I found that running the task every 20 seconds was enough to keep my data up-to-date without using excessive battery or bandwidth. The task’s components, in order:

  1. Fetch the Red Line predictions via HTTP from the MBTA’s servers.
  2. Extract the data I care about — estimated departure time (in seconds) for the closest train to each station along the southbound route.
  3. Convert these integers into MIDI Control Change messages.
    Why MIDI? MIDI was designed for the transmission of music-oriented data. However, I’ve discovered that it is one of the simplest ways to get arbitrary data out of the USB port of an Android device and, conveniently, one of the easiest ways to get data into the USB port of my microcontroller. There are APIs for both that help encode/parse your data from MIDI messages. It’s not particularly fast, but it doesn’t need to be in this case.
  4. Send MIDI Control Change messages to any MIDI devices connected via USB. I individually send one message per station. Android handles all of the USB-related heavy lifting here. Thanks, Android.

Gemma M0 Firmware

The microcontroller runs an infinite loop:

  1. Check for a MIDI Control Change message sent via the USB port.
  2. Process and store each message, if available. Control Change messages comprise 3 integer components: a channel, a controller number, and a value. For this project, I am ignoring the channel (it’s hard-coded to 0). I am using the controller number to represent a specific station, and using the the value to represent the next scheduled departure (in seconds) for that station. Each value received is loaded into an array, using the controller number to index. Thus, we always have access to the latest estimate for each station.
  3. Calculate the LED brightness for each station. To give the LEDs a “pulsing” look, I use a simple sine function to calculate a given station light’s brightness at any moment (See the image below for the equation). I loop through the array of departure estimates described in step 2, and derive a new array of brightness levels for each station.
  4. Update the LEDs. Adafruit’s NeoPixel library makes updating individual lights as easy as specifying a bulb index and passing a color. I iterate through the array of brightness levels derived in step 3, and forward each value to the NeoPixel library, using the array index as a bulb index. The library expects a tuple of red, green, and blue components, since WS2812’s can display 24-bit RGB color. Since this project represents the Red Line only, I set the green and blue components to 0. I use the brightness level as the red component. When all positions have been updated, the NeoPixel library writes the LED colors as serial data to the microcontroller pin that was designated as the “data” pin.
ROUGH calculation of brightness of a station’s LED using at any given time. Forgive my handwriting.

Postmortem

I am very pleased with how this project turned out. The “demo gods” smiled brightly upon me; everything generally worked all day. People asked for photos. I had fun building and presenting. I only got shocked once!

It’s hard to feel like I fully accomplished my goal of tackling a technical problem with a “clean slate”. I relied heavily on open-source software and off-the-shelf hardware. I wrote most of the code in the same language that I’ve been using at work for years. Still, I was happy to find myself winging it more than I usually do. I didn’t go diving for opinions or tutorials on how to build an “app” into a shirt. I found some advice on how to sew with conductive thread, but that only lead me to the humbling realization that reading about it wouldn’t magically make me good at it. This may sound obvious to most, but if you spend enough time writing software, you start to believe that there’s always a way to copy and paste your way to success. Not this time. I had to push a little harder than usual, and I think it shows in the final build.

Thank you to those who asked me to write this up, and thank you for reading this far. I’d love to hear about your custom / impractical / silly / over-engineered projects. Please reach out!

--

--