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.

HARDWAREButtonsSensorsSpeakerSOFTWARESnake gameLauncher menuTone widgetEvent bus(the middleman)

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.

Event buspublish · subscribeJoystickpublishes pressesSensorpublishes readingsSnakepublishes tonesLauncherlistens for buttonsLED gridrenders framesSpeakerplays tonesbtn/up/press

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.

Event bussys/sys/boot (version)btn/btn/up/press (when)switch/switch/slide/state (0 or 1)analog/analog/dial/value (0…1)imu/imu/accel (x, y, z)tone/tone/play (hz, ms)display/display/out/frame (frame data)Wildcardsbtn/+/press→ any buttonimu/#→ every IMU topic

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.

INPUTButtonreal GPIO pinButton driverreads pin, debouncesbtn/up/pressAppif pressed: jumpsame pattern, reversed for outputOUTPUTAppplay victory tonetone/playSpeaker driverdrives the piezoSpeakerreal GPIO pin

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.

Retainedanalog/dial/valueThe bus remembers the last value. Late subscribers get it instantly.0.120.340.420.500.550.62new subscriber gets 0.50Transientbtn/up/pressThe bus doesn't remember. Late subscribers miss past events.late subscriber missed those three

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.

DeviceMicroPythonbusLumenLinkbrowser hostbusSimulatorTypeScriptbusUSB serialpostMessageSame topic strings. Same payload shapes.btn/up/press · tone/play · display/out/frame
Want to see it in motion? Open the simulator — every button press, tone, and frame on the display is a topic message you can subscribe to.