A Raspberry Pi 5 powered 128×128 RGB LED matrix that runs one of several modes:
- Wallart mode — plays animated GIFs from
wallart-gifsin random order, one per minute - Christmas mode — same, from the
xmas-gifsdirectory - Butterfly mode — loops a single butterfly GIF continuously
- Game mode — runs PICO-8 in splore via a virtual X display mirrored to the matrix
A controller daemon manages the mode processes. Right now the panel is driven entirely over systemd from the command line — no physical buttons are wired yet (Current state). Once the button board is built, the same daemon switches modes on GPIO presses, with systemd still available as an override (State once buttons installed).
| Component | Notes |
|---|---|
| Raspberry Pi 5 | Tested on Pi 5; Pi 4 would need adafruit-blinka-raspberry-pi5-piomatter equivalent |
| 128×128 RGB LED matrix | Built from two 64×64 HUB75 panels |
| Adafruit Matrix Bonnet | HUB75 driver HAT |
| Momentary push buttons | One per mode, wired to GPIO — not yet wired; see State once buttons installed |
Each button connects a GPIO pin to GND. The controller enables internal pull-up resistors, so no external resistors are needed.
GPIO pin ──┤ button ├── GND
Default pin assignments (configurable in .env):
| Button | GPIO (BCM) | Mode |
|---|---|---|
| Button 1 | 17 | Wallart |
| Button 2 | 27 | Game |
Christmas, butterflies, and butterfly buttons are disabled by default (pin 0); set their pin env vars in .env to enable them.
For a dedicated power-off button, add this to /boot/firmware/config.txt and reboot:
dtoverlay=gpio-shutdown,gpio_pin=22
This is handled by the kernel directly and is more reliable than software shutdown.
git clone <repo-url> ~/arcade
cd ~/arcade
cp .env.example .env
nano .env # set paths and GPIO pins for your setupThe project uses the blinka_venv virtual environment. Install into it:
source ~/.venvs/blinka_venv/bin/activate
pip install -r requirements.txtbash scripts/install.shThis renders systemd/arcade.service and the systemd/arcade@.service template with your install path and user, copies both to /etc/systemd/system/, then enables and starts the button controller.
Check that it's running:
sudo systemctl status arcade
journalctl -u arcade -f # live logsNo buttons are wired, so ARCADE_BTN_*_PIN are all 0 in .env. The controller (arcade.service) skips GPIO entirely and just boots into ARCADE_DEFAULT_MODE (default: wallart). You switch modes from the command line.
Only one process may drive the matrix at a time, so starting a arcade@<mode> template instance automatically stops whatever was running (via Conflicts=):
sudo systemctl start arcade@wallart # all wallart-gifs, random rotation
sudo systemctl start arcade@christmas # all xmas-gifs, random rotation
sudo systemctl start arcade@butterfly # single butterfly.gif, looped forever
sudo systemctl start arcade@game # PICO-8 in splore
sudo systemctl start arcade@butterflies # (the whole butterfly-gifs directory)To return to the boot behaviour — the plain controller, which without buttons just sits in ARCADE_DEFAULT_MODE:
sudo systemctl start arcadeWhen switching directly between two forced modes, use the helper instead of two start commands — it stops every instance first so two processes never fight over the panel:
sudo scripts/arcade-mode.sh butterflySee what's active and follow logs:
systemctl status 'arcade*'
journalctl -u 'arcade@*' -fReboot behaviour: arcade@<mode> instances are not enabled, so they don't survive a reboot — on boot the panel always returns to arcade.service running ARCADE_DEFAULT_MODE. To change the boot mode, set ARCADE_DEFAULT_MODE in .env and sudo systemctl restart arcade.
When the button board is built, each mode gets a physical button and the controller switches modes on GPIO presses. The systemd commands above still work as an override. None of this needs a reinstall — buttons are pure .env config picked up on restart.
-
Wire each button. Connect one momentary push button per mode between its GPIO pin and GND. The controller enables internal pull-ups, so no external resistors are needed:
GPIO pin ──┤ button ├── GND -
Pick a pin per mode and set the matching env vars in
.env. Every mode has a button slot;0means disabled. Suggested assignments:.envvariableMode Suggested pin (BCM) ARCADE_BTN_WALLART_PINwallart 17 ARCADE_BTN_GAME_PINgame 27 ARCADE_BTN_CHRISTMAS_PINchristmas (choose) ARCADE_BTN_BUTTERFLIES_PINbutterflies (choose) ARCADE_BTN_BUTTERFLY_PINbutterfly (choose) Give every enabled button a unique pin. Verify pin numbering with
pinoutin a terminal. (Pi 5 requires thelgpiopin factory, already set viaGPIOZERO_PIN_FACTORY=lgpio.) -
Apply it:
sudo systemctl restart arcade
-
Verify.
journalctl -u arcade -fshould log aButton on GPIO <pin> → <mode> modeline for each enabled button at startup. Press each button and confirm the panel switches. -
(Optional) power-off button — see Power button above: add
dtoverlay=gpio-shutdown,gpio_pin=22to/boot/firmware/config.txt(the Bonnet's J2 header can also handle graceful shutdown natively).
Gotchas:
- Buttons only work while the button controller (
arcade.service) is the active owner. If you've forced a mode withsudo systemctl start arcade@<mode>, the controller is stopped and buttons do nothing until you runsudo systemctl start arcadeagain. - Remember to change the pin values away from
0— leaving a pin at0keeps that button disabled even if it's physically wired.
arcade/
├── controller.py # mode manager daemon — watches GPIO, manages subprocesses
├── virtualdisplay.py # mirrors a virtual X session to the LED matrix (game mode)
├── modes/
│ └── art.py # GIF player: --dir (wallart/christmas) or --file (butterfly)
├── systemd/
│ ├── arcade.service # button controller unit (rendered by install.sh)
│ └── arcade@.service # per-mode template unit: arcade@<mode> (rendered by install.sh)
├── scripts/
│ ├── run.sh # activates venv, launches controller (or one mode)
│ ├── arcade-mode.sh # force a single mode via systemd
│ └── install.sh # one-shot install helper
├── .env.example # all configuration options with defaults
└── requirements.txt
GIF files are not tracked in git — store them in the directory pointed to by ARCADE_GIF_DIR_WALLART (default: ~/wallart/wallart-gifs/).
All options live in .env (copied from .env.example). Key settings:
| Variable | Default | Description |
|---|---|---|
ARCADE_GIF_DIR_WALLART |
~/wallart/wallart-gifs |
Directory of .gif files for wallart mode |
ARCADE_GIF_DIR_CHRISTMAS |
~/wallart/xmas-gifs |
Directory of .gif files for christmas mode |
ARCADE_GIF_DIR_BUTTERFLIES |
~/wallart/butterfly-gifs |
Directory of .gif files for butterflies mode |
ARCADE_BUTTERFLY_GIF |
~/wallart/butterfly-gifs/butterfly.gif |
Single GIF looped by butterfly mode |
ARCADE_LOG_DIR |
~/logs |
Where log files are written |
ARCADE_PICO8_BIN |
~/pico-8/pico8_64 |
Path to PICO-8 binary |
ARCADE_VENV |
~/.venvs/blinka_venv |
Python venv used by scripts/run.sh |
ARCADE_BRIGHTNESS |
0.5 |
LED brightness (0.1 – 1.0) |
ARCADE_LOOP_LENGTH |
60 |
Seconds per GIF in directory (wallart/christmas) modes |
ARCADE_FRAME_DELAY |
0.2 |
Seconds between GIF frames |
ARCADE_DEFAULT_MODE |
wallart |
Mode to start in on boot |
ARCADE_BTN_WALLART_PIN |
17 |
GPIO BCM pin for wallart button (0 = disabled) |
ARCADE_BTN_GAME_PIN |
27 |
GPIO BCM pin for game button (0 = disabled) |
ARCADE_BTN_CHRISTMAS_PIN |
0 |
GPIO BCM pin for christmas button (0 = disabled) |
ARCADE_BTN_BUTTERFLIES_PIN |
0 |
GPIO BCM pin for butterflies button (0 = disabled) |
ARCADE_BTN_BUTTERFLY_PIN |
0 |
GPIO BCM pin for butterfly button (0 = disabled) |
- Create
modes/mymode.py— any script that runs until killed viaSIGTERM. - Add an entry to
MODESincontroller.py:"mymode": [PYTHON, str(HERE / "modes" / "mymode.py")],
- Add a button mapping in
controller.pyand the matching pin env var in.env.example.
For a seasonal GIF set, reuse modes/art.py with a different --dir (see the wallart/christmas entries in MODES), or --file to loop a single GIF (see butterfly). Every mode name is also reachable via arcade@<mode> with no extra work.
| File | Contents |
|---|---|
logs/controller.log |
Mode switches, GPIO events, subprocess crashes |
logs/art.log |
Which GIF is playing, errors |
Matrix doesn't light up on boot — check journalctl -u arcade and confirm the venv path in .env is correct.
Buttons do nothing — verify GPIO pin numbers with pinout (run in terminal) and check controller.log for GPIO errors. Pi 5 requires lgpio; install with pip install lgpio.
PICO-8 game mode blank — confirm the ARCADE_PICO8_BIN path is correct and that xvfb is installed (sudo apt install xvfb).