Public transit arrivals on a 128×32 HUB75 LED display, driven by a Raspberry Pi. This is the Python-side renderer that calls the arrivals-kmp CLI for data.
There's more detail in this blog post.
Common setup:
- Adafruit RGB Matrix Bonnet
- 2× 64×32 HUB75 RGB LED panels (2.5mm pitch), chained horizontally → 128×32
- 5V power supply (10A recommended to power both panels)
Supported Pi boards:
- Raspberry Pi Zero 2 W — uses hzeller's rpi-rgb-led-matrix driver via the Adafruit install script
- Raspberry Pi 5 — uses the Piomatter driver (PIO-based)
- The Bonnet's HUB75 socket feeds panel 1's IN
- Run a HUB75 ribbon from panel 1's OUT to panel 2's IN
- Power both panels from the same 5V rail
- The library treats the pair as a single 128×32 display via
Geometry(width=128, height=32, ...)
If panel 2 appears flipped or mirrored, either flip it physically or change Orientation.Normal to Orientation.R180 in led_matrix.py.
Follow the Adafruit RGB Matrix Bonnet guide and run the install script to compile and install the rgbmatrix Python library.
Follow the Adafruit Pi 5 RGB Matrix Panel guide first to install the Piomatter library system deps and udev rules (so /dev/pio0 is accessible without sudo).
Clone this repo on the Pi and run:
./install.shThe script detects your Pi model and installs the correct driver. On Pi Zero 2 it builds the rgbmatrix Python bindings from ~/rpi-rgb-led-matrix into the venv; on Pi 5 it installs Piomatter via pip.
The Python script calls the arrivals binary from jdamcd/arrivals-kmp. Cross-compile the native CLI for ARM Linux from a macOS or x86 Linux machine:
./gradlew :cli:linkReleaseExecutableLinuxArm64Then copy the binary to the Pi and put it on your PATH:
scp cli/build/bin/linuxArm64/releaseExecutable/cli.kexe <user>@<host>:/tmp/arrivals
ssh <user>@<host> 'sudo mv /tmp/arrivals /usr/local/bin/arrivals'Verify on the Pi:
arrivals --json tfl --station 910GSHRDHST --platform 2You should get a JSON object with station and arrivals fields.
source venv/bin/activate
python arrivals.py "arrivals --json tfl --station 910GSHRDHST --platform 2"The rgbmatrix driver needs root for GPIO access, so on the Pi Zero 2:
sudo venv/bin/python arrivals.py "arrivals --json tfl --station 910GSHRDHST --platform 2"There's a template in systemd/arrivals-led-root.service. To install:
# Edit the ExecStart line to point at your preferred station and user home
sudo cp systemd/arrivals-led-root.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now arrivals-led-rootThere's a template in systemd/arrivals-led.service. To install:
# Edit the ExecStart line to point at your preferred station
mkdir -p ~/.config/systemd/user
cp systemd/arrivals-led.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now arrivals-led
loginctl enable-linger $USER # So it runs when you're not logged inI've included a couple of models for 3D-printed parts that might be useful:
- Bracket to connect the 2x LED panels horizontally (with M3 screws)
- Riser to attach the Raspberry Pi Zero 2 & bonnet (with M2.5 & M3 screws)
/dev/pio0: permission denied(Pi 5): The Adafruit udev rule isn't in place. Check the Pi 5 guide's udev section.- Flicker during data refresh: The LED refresh thread can be preempted by the Linux scheduler, especially when a subprocess is running. Isolating a CPU core helps on both boards:
- Append
isolcpus=3to the existing line in/boot/firmware/cmdline.txt, then reboot. - Pi 5 only: Install the patched Piomatter from this branch which pins the blit thread to the isolated core:
See Piomatter PR #79 for details. The rgbmatrix driver picks up the isolated core automatically.
pip install git+https://github.com/lehni/Adafruit_Blinka_Raspberry_Pi5_Piomatter.git@pin-blit-thread-to-isolated-cpu
- Append
- Colours look wrong: The panels are assumed to be wired in RBG order. If your panels use standard RGB wiring, change
RGB_SEQUENCE = "RBG"to"RGB"inarrivals.py. - Power: If you have any power issues, try powering the Pi separately via its standard USB adapter.
The bundled bitmap font was generated based on London Underground Dot Matrix Regular.