Stackchan Dev
Your robot from your own programs and AI agents: show text, ask a question, light the LEDs, play a sound, hear what people do at it. One key per robot, made here; then a few lines of curl, Python, JavaScript or Go, or an AI agent over MCP.
Your robots: try every action by hand, make keys, see what people do at the robot.
Start here
- Give your robot Dev as one of its apps (on m.i41.eu, or your home manager). Signed in there, Your robots shows it at once (Sign in at the top); or switch the robot to Dev (swipe up, QR, Next, Connect) and scan its QR code.
- New key on the robot's card in Your robots: it shows the three variables for your shell,
SDEV_URL,SDEV_ROBOT,SDEV_KEY. - Run your first program below: here with Run, then on your computer, in the language you like.
The whole way, with the robot's setup over USB: Getting started.
Your first program
a card, green LEDs, a nod, a chime: the same in every language. In a clone of the repository, with the three variables set:
it on your robot from this page
sh examples/curl/hello.shDownload#!/bin/sh
# hello: a card on the robot's screen, its LEDs, a nod and a chime.
# export SDEV_URL=… SDEV_ROBOT=… SDEV_KEY=… (Dev's page shows them with a new key)
# sh hello.sh
set -eu
act() { # act <action> <JSON>: one action on the robot
curl -sS --fail-with-body -X POST "$SDEV_URL/v1/robots/$SDEV_ROBOT/$1" \
-H "Authorization: Bearer $SDEV_KEY" -H "Content-Type: application/json" -d "$2"
echo
}
act show '{"title":"Hello","text":"from curl","note":"q41eu-dev examples","color":"green"}'
act leds '{"color":"green"}'
act move '{"gesture":"nod"}'
act sound '{"chime":"tada"}'
python3 examples/python/hello.pyDownload#!/usr/bin/env python3
"""hello: a card on the robot's screen, its LEDs, a nod and a chime.
export SDEV_URL=… SDEV_ROBOT=… SDEV_KEY=… (Dev's page shows them with a new key)
python3 hello.py
Only the standard library.
"""
import json, os, urllib.request
URL, ROBOT, KEY = os.environ["SDEV_URL"].rstrip("/"), os.environ["SDEV_ROBOT"], os.environ["SDEV_KEY"]
def act(action, **args):
"""One action on the robot: POST /v1/robots/<robot>/<action> with the arguments as JSON."""
req = urllib.request.Request(f"{URL}/v1/robots/{ROBOT}/{action}", data=json.dumps(args).encode(),
headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=180) as res:
return json.load(res)
act("show", title="Hello", text="from Python", note="q41eu-dev examples", color="green")
act("leds", color="green")
act("move", gesture="nod")
act("sound", chime="tada")
print("done: look at the robot")
node examples/js/hello.mjsDownload#!/usr/bin/env node
// hello: a card on the robot's screen, its LEDs, a nod and a chime.
//
// export SDEV_URL=… SDEV_ROBOT=… SDEV_KEY=… (Dev's page shows them with a new key)
// node hello.mjs
// Node 18 or newer, nothing to install: only fetch.
const { SDEV_URL, SDEV_ROBOT, SDEV_KEY } = process.env;
if (!SDEV_URL || !SDEV_ROBOT || !SDEV_KEY) {
console.error("set SDEV_URL, SDEV_ROBOT and SDEV_KEY (Dev's page shows them with a new key)");
process.exit(2);
}
// act: one action on the robot: POST /v1/robots/<robot>/<action> with the arguments as JSON.
async function act(action, args = {}) {
const res = await fetch(`${SDEV_URL.replace(/\/$/, "")}/v1/robots/${SDEV_ROBOT}/${action}`, {
method: "POST",
headers: { Authorization: `Bearer ${SDEV_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify(args),
});
const out = await res.json().catch(() => ({ ok: false, error: res.statusText }));
if (!res.ok || out.ok === false) throw new Error(`${action}: ${out.error || res.status}`);
return out;
}
await act("show", { title: "Hello", text: "from JavaScript", note: "q41eu-dev examples", color: "green" });
await act("leds", { color: "green" });
await act("move", { gesture: "nod" });
await act("sound", { chime: "tada" });
console.log("done: look at the robot");
go run ./examples/go/helloDownload// hello: a card on the robot's screen, its LEDs, a nod and a chime.
//
// export SDEV_URL=… SDEV_ROBOT=… SDEV_KEY=… (Dev's page shows them with a new key)
// go run ./examples/go/hello
package main
import (
"context"
"log"
"github.com/i41-eu/q41eu-dev/client"
)
func main() {
ctx := context.Background()
robot, err := client.FromEnv()
if err != nil {
log.Fatal(err)
}
must(robot.Show(ctx, client.Card{Title: "Hello", Text: "from Go", Note: "q41eu-dev examples", Color: "green"}))
must(robot.LEDs(ctx, "green"))
must(robot.Gesture(ctx, "nod"))
must(robot.Chime(ctx, "tada"))
log.Println("done: look at the robot")
}
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
hello: a card on the robot's screen, its LEDs, a nod and a chime.
Ask your agent (Dev added as an MCP server: getting started, step 5):
Say hello on the robot: a green card "Hello", green LEDs, a nod and the tada chime.
The tool calls it makes:
robot_show {"title": "Hello", "text": "from an agent", "note": "q41eu-dev examples", "color": "green"}
robot_leds {"color": "green"}
robot_move {"gesture": "nod"}
robot_sound {"chime": "tada"}
More
- The API by part: every action by the part of the robot it uses (screen, head, LEDs, speaker), its arguments, a call in each language; the events people make at the robot.
- Examples: whole programs (a question with buttons, a menu, a picture to tap, a build status, what people do), each in every language.
- Getting started: from a robot on your desk to your program driving it, about ten minutes.
- OpenAPI 3.1: the whole API, for API tools and client generators.
- Case studies: Focus and Pet, two apps built on the Go kit: what they use of the kit and of the robot, their key code, what they teach.
- The Go kit: for apps the robot connects to itself, as Focus and Pet do.
- Lower level: the robot's device protocol, every sensor raw (the Raw data app).