Firmware, Bluetooth-Brücke und Agent-Skill für den KOSMOS-Proxi: einmal flashen, danach hört der Roboter über Bluetooth LE auf Textbefehle. Was die Befehle schickt, ist ihm egal — ein Skript, ein curl, oder ein KI-Agent, der aus "mach mal was Lustiges" eine Choreografie baut. firmware/ MakeCode-Projekt (TypeScript) und fertige .hex für V1 und V2 bridge/ BLE-Brücke und HTTP-API, einzige Abhängigkeit ist bleak agent/ Skill-Definition: Alltagssprache -> Befehle tools/ Quelltext aus .hex extrahieren, Musik-Tabellen prüfen docs/ Protokoll, Einrichtung, Hardware, Lizenzen Zwei Dinge, die sonst nirgends stehen und in docs/HARDWARE.md dokumentiert sind: die Pin-Belegung des Roboters ist nicht öffentlich, sie wurde aus den mitgelieferten .hex-Dateien rekonstruiert (tools/hex_source.py holt sie heraus). Und der Ton braucht zwingend eine ganzzahlige Frequenz sowie Pin P0 — sonst kracht der Lautsprecher, der Aufruf hängt und die Bluetooth- Verbindung stirbt. Am echten Roboter durchgetestet: Verbindung, Sensoren, Display, 41 Sekunden Musik am Stück, alle Bewegungsrichtungen, Musik und Fahren gleichzeitig, Tanzfiguren, Not-Stopp mitten in der Bewegung, Autostart nach Neustart. MIT-Lizenz. Die Hardware-Extension stammt von kaku111 (TobbieII) und die Notendaten aus pxt-microbit, beide ebenfalls MIT — siehe THIRD-PARTY-NOTICES.
7.5 KiB
Proxi Command Protocol 1.0
Textbefehle über Bluetooth-LE-UART. Ein Befehl pro Zeile, eine Antwortzeile zurück.
BEFEHL:PARAM:PARAM\n -> OK:...\n | ERR:...\n | <Wert>\n
- Befehlsname und Schlüsselwörter sind nicht groß-/kleinschreibungsabhängig
- Parameter trennt
: - Jede Zeile endet mit
\n - Fehlende Parameter fallen auf den Default zurück, unlesbare ebenso
- Es gibt keinen Speed-Parameter — die Motoren können nur an oder aus, siehe HARDWARE.md
Dauer-Parameter
Alle Bewegungsbefehle nehmen als letzten Parameter eine Dauer in Millisekunden.
| Angabe | Wirkung |
|---|---|
| weggelassen | fährt los und stoppt nach 10 s von selbst (Sicherheitsnetz) |
1..60000 |
fährt genau so lange und stoppt |
0 |
fährt weiter, bis ein Stopp-Befehl kommt |
Die Firmware blockiert während der Fahrt nicht: der Befehl wird sofort mit OK
quittiert, ein Watchdog schaltet die Motoren zur Deadline ab. STOP kommt
deshalb auch mitten in einer laufenden Bewegung durch.
Bewegung
| Befehl | Wirkung |
|---|---|
MOVE:FWD[:ms] |
vorwärts laufen |
MOVE:BWD[:ms] |
rückwärts laufen |
MOVE:STOP |
Laufmotor aus |
TURN:LEFT[:ms] |
Oberkörper nach links drehen |
TURN:RIGHT[:ms] |
nach rechts drehen |
TURN:STOP |
Drehmotor aus |
STOP |
alles aus: beide Motoren, Ton, Display, laufende Jobs |
Lauf- und Drehmotor sind unabhängig. Beide gleichzeitig ergibt eine Kurve:
MOVE:FWD:0
TURN:LEFT:800
Bewegungsfiguren
Laufen im Hintergrund und melden sich mit DONE:<name>, wenn sie fertig sind —
oder mit DONE:<name>:aborted, wenn STOP oder Taste A dazwischenkam.
| Befehl | Default | Wirkung |
|---|---|---|
DANCE[:n] |
2 | n Takte tanzen (vor/zurück + links/rechts) |
STAMP[:n] |
3 | n mal aufstampfen |
SHAKE[:n] |
3 | n mal den Kopf schütteln |
n wird auf 1..20 begrenzt.
Sie laufen nicht parallel zur Musik. DANCE, STAMP, SHAKE, TEXT,
TONE und MELODY teilen sich einen Arbeiter und werden nacheinander
abgearbeitet — eine Figur startet also erst, wenn die Melodie durch ist.
MOVE und TURN laufen dagegen sofort und damit echt gleichzeitig zur Musik.
Für eine Choreografie zur Melodie deshalb MOVE/TURN verwenden; ein
Beispiel liegt in bridge/nyan-dance.txt.
Display
| Befehl | Wirkung |
|---|---|
FACE:<name> |
Symbol anzeigen (bleibt stehen) |
FACE:RAW:z0:z1:z2:z3:z4 |
eigenes 5×5-Bild, je Zeile eine Bitmaske 0–31 |
TEXT:<text> |
Text durchlaufen lassen, danach Display leer |
CLEAR |
Display löschen |
Namen für FACE:
HAPPY SAD ANGRY CONFUSED ASLEEP SURPRISED SILLY FABULOUS MEH
YES NO HEART SMALLHEART SKULL GHOST DUCK HOUSE GIRAFFE COW
RABBIT SNAKE BUTTERFLY TORTOISE STICKFIGURE TARGET DIAMOND
SQUARE TRIANGLE UMBRELLA SWORD NORTH EAST SOUTH WEST
Bei FACE:RAW ist in jeder Zeilen-Bitmaske Bit 0 die linke LED:
FACE:RAW:0:10:0:17:14 # zwei Augen, lächelnder Mund
TEXT darf : enthalten — alles ab dem ersten Parameter gilt als Text.
Ton
| Befehl | Default | Wirkung |
|---|---|---|
TONE:<hz>[:ms] |
440, 400 | einzelner Ton (50–5000 Hz, 10–5000 ms) |
MELODY:<name> |
— | eingebaute Melodie, spielt im Hintergrund |
Namen für MELODY:
DADADUM ENTERTAINER PRELUDE ODE NYAN RINGTONE FUNK BLUES
BIRTHDAY WEDDING FUNERAL PUNCHLINE BADDY CHASE BADING WAWAWAWAA
JUMPUP JUMPDOWN POWERUP POWERDOWN
Laufzeiten (für Choreografien):
| ~0,5–1 s | BADING JUMPUP JUMPDOWN POWERUP POWERDOWN |
| ~2,5–3,5 s | WAWAWAWAA PUNCHLINE RINGTONE BADDY ENTERTAINER FUNERAL |
| ~4 s | DADADUM FUNK |
| ~8 s | CHASE WEDDING PRELUDE |
| ~12 s | BIRTHDAY BLUES |
| ~16 s | ODE NYAN |
Mehrere MELODY hintereinander reihen sich in der Warteschlange und spielen
nahtlos nacheinander.
Sensoren
| Befehl | Antwort |
|---|---|
SENSOR:ALL |
SENSOR:ALL:IR_L=..,IR_R=..,TEMP=..,PWR=.. |
SENSOR:IR_L |
SENSOR:IR_L:<0..1023> — höher = Hindernis näher |
SENSOR:IR_R |
SENSOR:IR_R:<0..1023> |
SENSOR:TEMP |
SENSOR:TEMP:<°C> (Prozessortemperatur, träge) |
SENSOR:PWR |
SENSOR:PWR:<0|1> — Zustand von P8, siehe HARDWARE.md |
Ohne Parameter verhält sich SENSOR wie SENSOR:ALL.
IR-Werte, am Gerät gemessen:
| Situation | Wert |
|---|---|
| freie Sicht | 5–15 |
| Hand in ~20 cm | 30–90 |
| Hand in ~10 cm | 200–600 |
| Hand direkt davor | bis ~980 |
Als Hindernis-Schwelle taugen die 400–600, die auch Kosmos' eigene Programme verwenden — das entspricht etwa einer Handbreit Abstand. Beide Sensoren erreichen den vollen Bereich; ein dauerhaft niedriger Wert auf einer Seite ist also ein Defekt und keine Eigenart.
System
<build> ist eine siebenstellige Kennung des Quellstands, die build.sh aus
main.ts, proxi.ts und pxt.json errechnet. Geflasht wird von Hand — daran sieht
man, welcher Stand tatsächlich auf dem Roboter liegt. Gleicher Code ergibt
immer dieselbe Kennung.
| Befehl | Antwort |
|---|---|
PING |
PONG |
STATUS |
STATUS:proxi-ai/1.0+<build>:PWR=<0|1> |
WAIT:<ms> — Pause in der Brücke
WAIT ist kein Roboter-Befehl. Die Firmware kennt ihn nicht;
proxi_bridge.py fängt ihn ab und legt die angegebene Pause ein, bevor der
nächste Befehl rausgeht (maximal 60000 ms).
Er wird gebraucht, weil Bewegungsbefehle sofort mit OK quittieren und dann im
Hintergrund weiterlaufen. Ohne Pause dazwischen überschreibt der nächste
Befehl die noch laufende Bewegung — Proxi zuckt nur kurz:
MOVE:FWD:1200 # läuft 1,2 s ...
WAIT:1400 # ... so lange warten
TURN:LEFT:700
WAIT:900
STOP
Faustregel: WAIT etwas größer wählen als die Dauer davor.
Unaufgeforderte Meldungen
Diese Zeilen kommen ohne passenden Befehl und sind keine Antwort:
| Zeile | Bedeutung |
|---|---|
READY:proxi-ai/1.0+<build> |
BLE verbunden, Firmware bereit — kommt oft nicht an, weil der Client die Indications meist erst kurz nach dem Verbindungsaufbau aktiviert. Verlass dich nicht darauf, nimm PING |
DONE:<job> |
DANCE/STAMP/SHAKE/TEXT/TONE/MELODY fertig |
DONE:<job>:aborted |
derselbe Job, aber vorzeitig gestoppt |
EVENT:ESTOP |
Taste A gedrückt — Not-Stopp |
proxi_bridge.py hält sie von den Befehlsantworten getrennt.
Fehler
| Antwort | Bedeutung |
|---|---|
ERR:unknown:<CMD> |
Befehl gibt es nicht |
ERR:MOVE:bad_dir |
erwartet FWD, BWD oder STOP |
ERR:TURN:bad_dir |
erwartet LEFT, RIGHT oder STOP |
ERR:FACE:unknown |
Symbolname unbekannt |
ERR:MELODY:unknown |
Melodiename unbekannt |
ERR:SENSOR:bad_type |
Sensorname unbekannt |
ERR:TEXT:empty |
kein Text angegeben |
ERR:<CMD>:busy |
Warteschlange voll (8 Aufträge). Betrifft TEXT, TONE, MELODY, DANCE, STAMP, SHAKE — mit WAIT entzerren |
Tasten am Roboter
Funktionieren auch ohne Verbindung.
| Taste | Wirkung |
|---|---|
| A | Not-Stopp — alles aus, sendet EVENT:ESTOP |
| B | Status senden |
Bricht die BLE-Verbindung ab, stoppen die Motoren sofort von selbst.
Beispiel
PING -> PONG
FACE:HAPPY -> OK:FACE:HAPPY
MOVE:FWD:1500 -> OK:MOVE:FWD:1500
SENSOR:IR_L -> SENSOR:IR_L:612
TURN:RIGHT:700 -> OK:TURN:RIGHT:700
MELODY:POWERUP -> OK:MELODY:POWERUP
DANCE:3 -> OK:DANCE:3
-> DONE:DANCE (später, unaufgefordert)
STOP -> OK:STOP