Demo
Real screen captures, shown on the device. The firmware draws the same 135 × 240 screen in the desktop simulator and on hardware, so what you see here is exactly what ships.
Configuration
A brand is one config.yaml under brands/<id>/, in three groups: brand (identity), device (what ships in firmware), and hub (metrics and rules). Anything you leave out is simply off. A brand only bakes in the packs, games, and rules it lists, keeping the image small.
config builderwrites the file for you, pick identity, packs and rules, then flash or download→
config referenceevery section explained in depth, with live examples to poke at→
Mascots don't have to come from the built-in packs. Any transparent PNG works as a custom mascot, and if you want to design one, the mixer builds it from parts.
- Six expressions. One pick per slot, recolored live, previewed in every device mood.
- Avatar or mascot. Download one frame or all six as one image.
- Config. Save the image next to
config.yaml, list it undermascot.custom.
Build & flash
The config gets onto a device three ways. Flash it in the browser, use the macOS app, or self-host the console with Docker. Same firmware, same result.
The config builder is one page with the whole flow. Build the config, plug the device in over USB, and flash it in one go. Nothing to install.
- Live yaml. Every pick updates the
config.yamlnext to the form. - Flash over USB. The
gooshiimage plus your selections as a config partition, custom theme palettes included. Chrome or Edge on desktop. - Or download. Drop the file into
brands/<id>/for the other paths.
The flashed device still needs a hub feeding it metrics. Install it once on the machine that holds your API keys and it pairs over BLE. One pairing carries hub metrics and Claude together.
pip install -e hub/backend # once, Python 3.10+ make hub # pairs with your device over BLE
Or skip the bare install. The Docker tab runs the same hub and serves the full console in the browser.
One download bundles the hub and its console. It lives in the menu bar and pairs over BLE. Sources, secrets, brand edits, and flashing all happen in the app.
- Menu bar, not dock. The hub runs in the background and the console opens in its own window.
- BLE pairing. Scan, pick your stick, done. No broker, no always-on machine.
- Flash from Devices. Pick a board and a brand. The app writes the image and config over USB.
One command runs a broker plus the hub and serves the same console the macOS app wraps, on any OS. Open it in the browser and everything is there: sources, secrets, brand editing, and flashing over the hub's USB port.
make up # broker + hub, console on http://localhost:8000 TAMA_PORT=9090 make up # port 8000 taken? serve it elsewhere
Development
For working on the project itself. Nothing here is needed for the three setups above.
The firmware is a PlatformIO project under firmware/. The desktop simulator draws the same screen as hardware, so most work needs no board at all.
make sim BRAND=demo # simulator, offline make sim-live BRAND=demo # simulator against the Docker stack's broker
Build a lean image
The flashed image carries everything and picks what to show at runtime. For the smallest possible image, build only what a brand lists and skip the config partition entirely.
make brand BRAND=<id> # generate headers into firmware/.gen/current cd firmware && pio run -e m5stickc-plus -t upload # build + flash only that brand (pick your board's env)
A lean build compiles just its listed content, so nothing is filtered at runtime. Pass DEV=<id> to bake in a developer name.
Build-time environment
| Variable | Purpose |
|---|---|
TAMA_BRAND | Which brand config to build (default gooshi). |
TAMA_DEV | Developer name/persona baked into the build. |
TAMA_TRANSPORTS | The set of link:protocol channels compiled into the firmware, e.g. ble:gatt or ble:gatt,wifi:mqtt. Overrides the brand's device.transports. |
The hub is a Python package under hub/backend, with the console it serves in hub/console. The wire format is defined once per side, in hub/backend/src/wire and firmware/lib/wire.
pip install -e hub/backend # once, Python 3.10+ make hub # run it bare, pairs over BLE make logs # tail the Docker stack's hub logs make hub-test # hub unit tests
Runtime environment
| Variable | Purpose |
|---|---|
TAMA_TRANSPORT | The single link:protocol channel the hub uses to reach the device, e.g. ble:gatt or wifi:mqtt. |
TAMA_BROKER | MQTT broker address (default localhost:1883). |
TAMA_DEVICE_ID | Device identity for MQTT routing. |
DATADOG_API_KEY / DATADOG_APP_KEY | Credentials for a datadog source. |
POSTHOG_API_KEY | Personal API key for a posthog source. |