One press lights a beacon, starts a scan, and changes what the story knows.
Build it with Otherwhere OS. We’ll go from loose parts to a complete scenario, with the configuration beside the thing it controls.
01 Raspberry Pi with a 40-pin header, power supply, and storage
02 Two momentary buttons: action and push-to-talk
03 An LED, a 1 kΩ resistor, breadboard, and jumper wires
04 A supported microphone and powered speaker
This guide uses the reference board, orion.console@3. The wiring is shared by both adventures. Changing the physical layout means defining your own board.
GPIO signals are 3.3 V. Keep 5 V away from these connections, and keep the resistor in series with the LED. Check your Pi’s header orientation ↗
Speaker, microphone, and power
Use a supported powered speaker or amplifier and microphone. The board expects ALSA devices named speaker and microphone; these aliases must match your Pi’s audio setup. The GPIO header does not drive the speaker directly.
The board owns the pins. The scenario asks for a button, a light, a speaker, and a way to talk. That is what lets the same wiring become a different machine.
A button has no permanent sound or story role. Adding another supported button usually means adding a component and binding. New hardware primitives may need a driver.
Write who needs help and what the player can choose. Turning on a beacon is a fact. Someone receiving it is another part of the story.
characters:
orion:
name: Commander Orion
personality: Calm, curious, and encouraging under pressure.
prompt: You coordinate a friendly space rescue. Let the child choose who to help
and how to explore.
voice: orion_voice
initial_presence: rescue_bridge
scenario.yaml · root excerpts
Start from the complete download, then change the character, rules, and starting facts together. Give your version its own scenario identity. Published versions are immutable.
The button turns the lamp on, starts a named scan, and sends beacon_requested. A seven-second guard keeps repeat presses from starting another sequence.
The sounds are included in the download: verified prototype cues from the existing inventory. An asset alias chooses audio; it does not generate it.
Use your own sound
Choose or create a WAV, measure its SHA-256, byte count, and PCM duration, and add it to assets/catalog.json. Its file path is relative to that catalog. Point the scenario’s asset alias to that hash, then rerun the local exercise. The helper checks the actual bytes.
The server handles beacon_requested with two explicit effects: remember that the beacon is active, and publish its lamp state. The scan’s completion plays the confirmation chirp.
The light reacts locally right away. The server’s accepted effect makes the fact durable. Beacon active does not mean explorers rescued.
What if the story is already speaking?
A non-record input can be accepted as busy_recorded during an active turn. It is recorded without queuing a surprise story action for later. Device observations remain diagnostics; only declared story inputs take this path.
On reconnect, restore the saved beacon value with an absolute light command. Repeating it is harmless. Reopen temporary controls without replaying the scan or confirmation.
Every playable map keeps press and release capture bindings unconditional. Talking clears the temporary cooldown while preserving the saved beacon state.
Local cues yield to narration and capture. Keep the completion, timeout, reconnect, and talk routes together when you adapt the example.
Download the whole build: board, complete scenario, audio, catalog, and interaction trace. Extract the builds folder into your supplied pi-exp checkout.
Use Bun 1.4.0, Python 3.12, and uv 0.12.0. The prototype runtime comes with the supplied checkout; this download contains the build files. Run these commands from the checkout root.
bun --no-env-file tools/install-typescript-dependencies.ts
uv sync --project apps/device-agent --frozen --all-groups
terminal · trace tab shows the opening inputs
The exercise builds the package, verifies the audio, and runs the real reactor with fake hardware. Read each step’s commands, local state, and server intents. Completing a fake sound is explicit; advancing time only drives timers.
With your Pi enrolled against the matching board, upload the audio, publish the scenario, and build its package. Otherwhere OS downloads and verifies the package before activating it.
This prototype uses the supplied installation and enrollment workflow, plus a configured server and authorized admin CLI. Set up and enroll your Pi → Keep credentials in your local runtime file.
Use the actual package UUID returned by the build for PACKAGE_ID, and your enrolled Pi’s UUID for DEVICE_ID. Confirm orion.console@3 is published and matches that device. If you changed the layout or story, publish the new board/scenario identity and use those references instead.
How do I know it is running?
Inspect the package’s exact board, scenario, assets, and hash. After selecting it, inspect the device again and wait for desired and active package IDs to agree. An existing story session can retain its current scenario; preserve it or end it deliberately before switching.
Then try the real button, lamp, speaker, and push-to-talk. The local exercise does not prove physical wiring, microphone capture, server effects, or a heard story response. Package and device reference →