Creating a live UK train-map

Wyatt Sell
5 min readJan 25, 2022

I created a live updating trainmap of the UK, complete with a laser-etched map, and blinking LEDs that illuminate on a trains arrival at a certain station. The project was spawned out of a requirement from my engineering course to show aptitude in certain skills (laser cutting and woodworking), but after completing the fairly lax requirements, I decided I wanted to see the project to completion. Here’s how I did it.

Designing and planning

In all honesty, I’m not much of a planner when it comes to projects like these — I prefer to just go for it, and see what happens.

However, there were some parts which needed to be carefully designed, as I didn’t want to be continually laser cutting material until I got it right (inevitably, this did happen once, likely due to tolerance stack up). Unfortunately, I can’t take credit for making the case design — it was generated by Makercase, an excellent site for generating laser cutting plans

The base shape of the map (

I then further modified the case to overlay an editted UK train map (source), making sure to etch and cut in appropriate places.

I might point out that some modifications were required to bend the edges to form a complete corner — the spacing needed to be increased and they needed to be wet, but eventually I managed to form them.

The final product (left), and the laser cut panels (right)


It took me a while to find the right components for the project (which admittedly I did forget about for a few months), but here they are:

  • 19 3mm Red LEDS
  • 19 330Ω resistors
  • 39 jumper wires (19 female to female) (20 female to male)
  • Random breadboard
  • Raspberry Pi 3A

The jumper wires were the most important, as it was difficult to find wires that could directly connect to LEDs with a clean fit — there doesn’t really seem to be a jumper wire standard, so when I got a kit as a birthday present, I thought I would see if the jumper wires would work. Lo and behold they fit perfectly.

I added a dot of hot glue on the LED’s, and then connected them up to the jumper wires, making sure to take note of the positive and negative ends.

The ground wires were connected to the breadboard, along with a 330Ω resistor, all connected to a lane wired to the ground pin on the RPi. The positive wires were connected to controllable GPIO pins on the RPi.

Completed wiring (left) and close-up RPi GPIO wiring (left)

Some masking tape and bodging later…

And yes, I did just jam the back panel on and hope for the best. Yes, I know. And yes, it all still worked.


This was easily the most frustrating, complex and time-consuming part of the project. Thankfully, I had this repo to help me, but it was still an absolute nightmare.

After reading through more NetworkRail API Wiki entries than anyone should ever have to endure, I eventually got an understanding of how I was going to approach the problem. Here’s the massively oversimplified rundown:

  1. Subscribe to the Darwin Push Port which provides a live data feed of basically every single train and station update in the UK
  2. Parse and filter the XML feed items to only get status updates to trains arriving at the stations
  3. Start a timer for each station, which calls a function to illuminate the corresponding LED

Of course, it wouldn’t be proper programming without a myriad of bugs and issues cropping up. Some of the more frustrating issues, with the more complex solutions were as follows:

  1. Creating a timer for every train arrival and depature is not very performant — each timer creates a thread, and the threads will very quickly add up, until you are out of memory

To allievate this, I made modifications to my scheduling process to schedule the next station LED routine, after they get called — essentially daisy chaining the scheduling. I also include a few statements for catching occasional outlier trains (i.e. trains which are scheduled to arrive earlier than the next scheduled LED).

2. The connection to the Darwin Push Port has a tendency to crash fairly regularly, meaning the timer and station data stored in memory will be removed when the app auto-restarts

This was a significantly simpler challenge to fix — I used a wonderful little caching library for Python which would be loaded into memory every time the app restarted.

I threw the entire script into a Linux service, set it to start on boot, and always restart, and… It worked!

The end result

If you’re curious, here’s my code for the map. Beware, there aren’t many comments, and it’ll likely be confusing, as I didn’t initially plan to share it: