Docs / Event Bus
The Event Bus
Three explainers, getting more technical as you scroll. Read just the first one for a quick mental model; keep going for the details.
The quick version
A middleman between software and hardware
Think of the event bus as a middleman. The hardware (buttons, sensors, the LED grid, the speaker) and the software (the apps you run, the launcher, the tools in your browser) never talk to each other directly. They both talk to the middleman.
When the joystick is pressed, the hardware tells the middleman. Whichever app cares — the snake game, the launcher menu, a tone player — hears it through the middleman and reacts. Add a new sensor? It tells the middleman, and any app that's interested starts hearing about it. Nothing else changes.
That's really it. The rest of this page is the same idea, with more detail.
Slightly more technical
Publishers, subscribers, and topics
The middleman is a single shared channel that any piece of code can send messages to or listen on. Sending is called publishing; listening is called subscribing. Every message is tagged with a topic — a short name that says what kind of message this is — and the middleman uses the topic to route messages from publishers to whoever subscribed to it.
Topics are organized like file paths — a prefix groups related messages, the rest names the specific one. Buttons publish tobtn/…, the speaker listens for tone/…, the IMU sensor publishes to imu/…, and so on. A subscriber can listen to one exact topic, or to a whole family at once.
The pieces that actually connect to hardware — reading a pin, driving an LED — are tiny adapters called drivers. A driver sits between a real device and the bus, translating physical events into topic messages and incoming messages back into physical effects. Want to swap a piezo for a real speaker? Replace one driver; every app keeps working.
Technical concept
MQTT-style pub/sub, in-process, cross-runtime
Under the hood the bus is a synchronous pub/sub router with MQTT-style topic matching. Topics are slash-separated strings; subscriptions accept two wildcards — + matches a single segment (btn/+/press covers every button), #matches the tail (imu/#covers every IMU topic). Publishing is synchronous: callbacks fire on the publisher's call stack, before publish()returns. Exceptions in one callback don't affect the others.
Topics come in two flavours. Retainedtopics carry state — the current slide position, the latest analog reading, the IMU orientation. The bus stores the last value, and new subscribers receive it immediately on subscribe. They're for things you can ask about at any time: “is the switch up?” Transient topics are events — a button was just tapped, a tone should play. They fire once, and if nobody was listening, the moment is gone.
The same bus runs in three places: on the device (in MicroPython), inside the browser-based simulator (in TypeScript), and inside LumenLink (the tool that connects to a running device). The three runtimes have separate in-process buses, bridged by a serial cable or by browser-to-iframe messaging — but the topic strings and payload shapes are identical across all of them. An app that publishes tone/play on the simulator works without modification on hardware; a widget subscribed to display/out/framerenders the same frames whether it's looking at a simulated or a flashed display.