# 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 | \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](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:`, wenn sie fertig sind — oder mit `DONE::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:` | Symbol anzeigen (bleibt stehen) | | `FACE:RAW:z0:z1:z2:z3:z4` | eigenes 5×5-Bild, je Zeile eine Bitmaske 0–31 | | `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:[:ms]` | 440, 400 | einzelner Ton (50–5000 Hz, 10–5000 ms) | | `MELODY:` | — | 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 `` 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+:PWR=<0\|1>` | ## `WAIT:` — 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+` | 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:` | `DANCE`/`STAMP`/`SHAKE`/`TEXT`/`TONE`/`MELODY` fertig | | `DONE::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:` | 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::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 ```