‹ Projects

Unicorn-Pi

A collection of LED animations without the constant reflashing.

I built Unicorn-Pi so one Pico and Pimoroni Unicorn Pack could hold a whole collection of effects. I can switch between them with the buttons, and it remembers the last one after a reboot. Much better than reflashing the board every time I want a different animation.

Unicorn-Pi running on its LED matrix
A small LED matrix with a launcher handling the boring parts.

Each effect gets its own file

The repository has clocks, digital rain, a DVD-style bounce, fire, fireworks, snowfall, waves, and other views. Some are my own, and others are adapted from existing examples. I've kept the original credits with the project where they belong.

The launcher handles the shared behavior, and each effect keeps its drawing loop in its own module. I can work on one animation without digging through the entire collection.

Each view has an async run() entry point. The launcher keeps the ordered list, starts the selected task, and swaps tasks when I press a button. The individual animations don't need to carry a miniature framework around with them.

Unicorn-Pi digital clock animation
The clock is one view in the same launcher.

Switching without losing the selection

The entry point creates the graphics objects, loads the selected view, and starts its task. A separate listener watches the buttons. When I change views, the manager cancels the old task, clears the matrix, saves the new key, and starts the replacement.

The A and X buttons move through the collection. Saving the key in current_view.json means a power cycle doesn't drop me back at the first effect. It's a small detail, but it makes the board feel like a finished device instead of a demo.

Canceling tasks and clearing the display happen in that shared code. If every effect handled its own cleanup, one forgotten task would eventually keep running behind the new view. Async code always finds a way to remind you it exists.

Installing it on the board

The board needs Pimoroni's MicroPython build for the Unicorn Pack. Its graphics and hardware helpers aren't normal desktop Python modules. Follow the firmware instructions in the repository, then connect with Thonny.

Copy the contents of scripts/ to the device root so main.py runs at startup. Restart the board, change views with the buttons, and restart it again to make sure the selection was saved.

The desktop tooling formats and lints the source. Passing those checks doesn't prove that an animation looks right or responds properly on the hardware. For that, I still need the actual board.

Adding an effect

I usually start with an existing view that does something similar. Put the new module in scripts/views/, give it the same async entry point, and register it with the launcher. Its drawing and animation state should stay inside that module.

Several views can run on their own from Thonny. That's the quickest way to adjust colors or frame timing before adding a view to the full launcher.

The project stays small because the shared code handles the repetitive work and then gets out of the way. If you have a Unicorn Pack, the source and setup guide are the place to start. The larger Galactic Unicorn project builds on a similar idea with sound and optional network views.