LINK / BAUUNTERLAGEN

HID & HB1-Protokoll

UART-Frames, USB-Reports und Koordinatenübertragung.

Die Raspberry Pi Pico 2 ist ausschließlich USB-Device am Zielrechner. Sie präsentiert drei HID-Schnittstellen: Boot-Tastatur, absolute Maus und relative Boot-Maus. Der Controller kommuniziert ausschließlich über UART0 mit der Pico. Es gibt weder USB-CDC noch eine Netzwerkschnittstelle auf der Pico. Die Firmware führt keine Textkonvertierung und keine gespeicherten Makros aus.

Verdrahtung und lokale Freigabe

Pico-Pin Funktion
GP0, physisch Pin 1 UART0 TX → RX des Controllers
GP1, physisch Pin 2 UART0 RX ← TX des Controllers
GND, beispielsweise Pin 3 Gemeinsame UART-Masse
GP14, physisch Pin 19 Lokaler Freigabetaster, normalerweise offen, beim Drücken nach GND
GP15, physisch Pin 20 Not-Aus-Kontakt, normalerweise geschlossen nach GND
USB-Buchse der Pico 2 Ausschließlich zum autorisierten Zielrechner

UART: 115200 Baud, 8 Datenbits, keine Parität, 1 Stoppbit, keine Flusssteuerung; 3,3-V-Logik. Kein RS-232 und keine 5-V-Signale an GPIO. Die Tabelle beschreibt die direkte Laborverbindung mit gekreuztem TX/RX und gemeinsamer Masse. Für die isolierte Box gemäß HARDWARE.md liegen Controller- und Pico-Masse auf getrennten Seiten des digitalen Isolators: keine GND-Brücke über die Isolation. Der Zielrechner versorgt die Pico über USB; keine zusätzliche Versorgung über den 3,3-V- oder 5-V-Pin des Controllers.

GP14 und GP15 verwenden interne Pull-ups. GP15 HIGH beziehungsweise ein geöffneter Kontakt oder eine unterbrochene Leitung setzt den Not-Aus-Latch sofort über GPIO-Interrupt und zusätzlich durch zyklisches Abfragen. Der Kontakt wird nicht entprellt: Schon eine erkannte steigende Flanke löst aus. Ein beim Start offener Kontakt löst ebenfalls aus.

Ein frischer, mindestens 30 ms entprellter Druck auf GP14 gibt im disarmed-Zustand ein einmaliges Zeitfenster von 10 Sekunden für ARM. Beim Einschalten gedrückt gehaltenes GP14 gibt keine Freigabe; zuerst loslassen und erneut drücken. Ein erfolgreicher ARM verbraucht das Fenster. GP14 hebt den Not-Aus-Latch nicht auf. Nach Not-Aus muss der Kontakt geschlossen und die Pico bewusst neu gestartet werden; danach bleibt sie disarmed und benötigt erneut die lokale Freigabe. Auch ein Hardware-Watchdog-Neustart setzt nur in den disarmed-Startzustand zurück.

Die Pico-LED leuchtet bei ARM dauerhaft, blinkt langsam während des Freigabefensters und schnell bei gelatchtem Not-Aus. Im normalen disarmed-Zustand ist sie aus.

Framing

HB1 <seq> <OP> [dezimal_argumente] *<CRC16HEX>\n

Antworten verwenden dasselbe Framing:

HB1 <seq> OK [dezimal_argumente] *<CRC16HEX>\n
HB1 <seq> ERR <FEHLERCODE> *<CRC16HEX>\n
  • Ausschließlich druckbares ASCII und abschließend genau LF (0x0A); CRLF wird verworfen.
  • Genau ein Leerzeichen zwischen Tokens. Keine führenden oder abschließenden Leerzeichen, Tabs oder zusätzlichen Tokens.
  • Maximal 159 Bytes ohne LF, also 160 Bytes auf der Leitung. Eine unvollständige Zeile verfällt nach 250 ms ab ihrem ersten Byte; danach werden Bytes bis zum nächsten LF verworfen.
  • <seq> ist eine vorzeichenlose dezimale 32-Bit-Zahl von 0 bis 4294967295. Argumente sind dezimale, vorzeichenbehaftete 32-Bit-Zahlen; ein führendes Minus ist erlaubt, ein Plus nicht. Führende Nullen sind zulässig, ändern aber die Byteidentität eines Requests.
  • Operationsnamen und Fehlercodes sind Großbuchstaben beziehungsweise Großbuchstaben mit Unterstrichen. CRC-Eingaben dürfen Groß- oder Kleinbuchstaben enthalten; Antworten verwenden Großbuchstaben.
  • CRC-16/CCITT-FALSE: Polynom 0x1021, Initialwert 0xFFFF, kein Reflektieren, kein abschließendes XOR. Prüfsummen-Testvektor: ASCII 123456789 → 29B1.
  • Die CRC umfasst exakt die Bytes von H in HB1 bis zum letzten Zeichen vor *. Das Leerzeichen vor dem Stern, der Stern, die vier Hexziffern und LF sind ausgeschlossen.

CRC erkennt beschädigte Übertragungen; sie ist keine Authentifizierung. UART-Zugriff ist physisch auf den vertrauenswürdigen Controller zu begrenzen.

Die UART-Verbindung arbeitet als Stop-and-wait: Der Controller sendet genau einen Request und wartet auf dessen Antwort, bevor er den nächsten sendet. Auch der Heartbeat benutzt denselben seriellen Zugriff. Die Pico verwendet feste Puffer ohne Heap. UART-Framing-, Paritäts-, Break- oder FIFO-Overrun-Fehler verwerfen die ganze betroffene Zeile. Ungültige oder beschädigte Frames lösen keine HID-Aktion aus.

Sequenzen und Antworten

Normalerweise muss jede neue gültige Anfrage eine größere Sequenznummer als die zuletzt konsumierte Anfrage haben. Lücken sind erlaubt. Kein Wrap-around: Bei Erreichen des Maximums zuerst disarmen, dann mit HELLO neu beginnen.

Ein syntaktisch gültiger, CRC-geprüfter Request mit neuer Sequenz wird konsumiert, auch wenn die Operation mit einem Zustands-, Argument- oder Wertefehler endet. Die Firmware speichert die letzte vollständige Request-Zeile und deren Antwort:

  1. Eine unmittelbar wiederholte, byteidentische letzte Anfrage bekommt die gespeicherte Antwort. Die Aktion wird nicht erneut ausgeführt. Ein wiederholtes PING aktualisiert den Watchdog nicht.
  2. Dieselbe Sequenz mit anderen Bytes erzeugt ERR SEQ_CONFLICT; eine ältere Sequenz erzeugt ERR SEQ_OLD. Beide ändern den Cache nicht.
  3. Neustart-Ausnahme: Ein argumentloses, gültiges HELLO darf bei disarmed eine beliebige Sequenz verwenden und setzt die Reihenfolge neu. Auch eine zuvor anders verwendete gleiche Sequenz ist dann erlaubt. Eine exakte Wiederholung des letzten Requests wird weiterhin zuerst aus dem Cache beantwortet. Bei armed ist HELLO an die normale Reihenfolge gebunden.
  4. Parser-, CRC- und Sequenzfehler konsumieren keine Sequenznummer. Wenn der Parser die Sequenz nicht zuverlässig lesen kann, meldet er Fehler mit Sequenz 0; bei einer lesbaren Sequenz verwendet er diese.

Eine gecachte ARM-Antwort beschreibt die ursprüngliche Anfrage. Sie schaltet nach Watchdog oder Not-Aus nicht erneut frei. Der Controller soll eine ausbleibende Aktionsantwort als unklaren Ausgang behandeln, den Ablauf stoppen und eine neue, gültige STOP-Anfrage senden, statt die Aktion als neuen Request zu wiederholen. Bereits ausgeführte Eingaben lassen sich nicht rückgängig machen.

OK bei ABS, REL und KEY bedeutet, dass TinyUSB den USB-Transfer erfolgreich abgeschlossen hat. Es bestätigt keine Wirkung in der Anwendung des Zielrechners. STOP OK bestätigt die lokale Deaktivierung und die vorgemerkten Release-Reports; es wartet nicht auf einen möglicherweise abgetrennten USB-Host. RELEASE OK bestätigt abgeschlossene Release-Transfers.

Operationen

Operation Argumente Wirkung und Voraussetzung
HELLO keine OK 1: Protokollversion 1; keine Freigabe und kein Heartbeat. Bei disarmed ist ein Sequenz-Neustart erlaubt.
ARM keine Nur mit geschlossenem, nicht gelatchtem Interlock, gültigem lokalen Freigabefenster und betriebsbereitem USB. Wartet auf ausstehende Release-Reports. Startet den 750-ms-Heartbeat-Watchdog und verbraucht die lokale Freigabe.
PING keine Nur bei armed; erneuert ausschließlich den Heartbeat. Ein neues PING kann niemals selbst armen.
STOP keine In jedem Zustand zulässig: disarmt, verwirft das lokale Freigabefenster und merkt Release aller Tasten und Maustasten vor.
RELEASE keine Lässt Tasten und beide Maus-Schnittstellen los. Bleibt bei zuvor armed normalerweise armed; ein USB-Timeout oder ein Sicherheitsereignis disarmt. Auch bei disarmed zulässig.
ABS x y buttons X/Y jeweils 0…32767; Buttonmaske 0…7. Setzt absolute Position und gehaltene Buttons. Nur bei armed.
REL dx dy wheel pan buttons Deltas und Scrollwerte jeweils −127…127; Buttonmaske 0…7. Bewegungen und Scrollwerte sind einmalige Ereignisse, Buttons sind gehaltene Zustände. Nur bei armed.
KEY mods k1 k2 k3 k4 k5 k6 Modifiermaske und alle sechs HID-Usages jeweils 0…255. Ersetzt den gehaltenen Tastaturzustand. 0 bedeutet leeren Key-Slot. Nur bei armed.

Buttonmaske: Bit 0 = links, Bit 1 = rechts, Bit 2 = Mitte. Ein Klick besteht aus einem Report mit gedrücktem Button und einem nachfolgenden Report mit Buttonmaske 0 oder RELEASE. Bei Wechsel zwischen absoluter und relativer Maus werden zunächst eventuell gehaltene Buttons der anderen Maus-Schnittstelle losgelassen.

Modifiermaske: 1 linker Ctrl, 2 linker Shift, 4 linker Alt, 8 linker GUI, 16 rechter Ctrl, 32 rechter Shift, 64 rechter Alt, 128 rechter GUI. Werte lassen sich bitweise kombinieren. Die Firmware interpretiert Usages roh und akzeptiert technisch den ganzen 8-Bit-Bereich. Der Controller muss sinnvolle Usages, erlaubte Tastenkombinationen und die passende Tastaturbelegung wählen.

KEY enthält keinen Text und keine Unicode-Codepoints. Sprachlayout, Shift/AltGr und Textumsetzung gehören in den Controller; das aktive Layout des Zielrechners muss dazu passen. Die Tastatur ist ein echtes Boot-Keyboard mit 8-Byte-Report: Modifier, reserviertes Nullbyte und sechs Key-Slots.

USB- und Sicherheitsverhalten

  • Der Zielrechner sieht drei unabhängige HID-IN-Interfaces, ohne Report-IDs: Keyboard (Endpoint 0x81, 8 Byte), absolute Generic-Desktop-Mouse (Endpoint 0x82, 5 Byte) und relative Maus (Endpoint 0x83, 5 Byte im Report-Modus, 3 Byte im Boot-Modus). Poll-Intervall jeweils 1 ms.
  • Die absolute Maus überträgt Buttons sowie X/Y als zwei Little-Endian-16-Bit-Werte. Beim Release bleiben die zuletzt gesendeten Koordinaten erhalten. Vor dem ersten ABS sendet die Firmware keine unaufgeforderten absoluten Positionsreports.
  • Die relative Maus benutzt im Report-Modus Buttons, signed 8-Bit X/Y, vertikales Wheel und Consumer AC Pan. Im Boot-Modus sind ausschließlich Buttons und X/Y verfügbar. Nicht-null Wheel oder Pan erzeugt dann ERR BOOT_SCROLL_UNAVAILABLE, statt Scrollen stillschweigend zu bestätigen.
  • Boot-Tastatur und relative Boot-Maus können grundsätzlich ohne installierten OS-Agenten verwendet werden. Die tatsächliche BIOS/UEFI-, KVM-, Betriebssystem- und Mehrschirm-Kompatibilität muss am Ziel geprüft werden. Absolute HID-Koordinaten garantieren keine Pixel- oder Mehrschirm-Zuordnung.
  • Nur neue gültige PING-Requests verlängern den Watchdog. ARM startet ihn einmal. Nach 750 ms ohne solches PING disarmt die Firmware und merkt Release vor. Der mitgelieferte Controller verwendet ein Heartbeat-Intervall von 100 ms und maximal 350 ms Wartezeit auf eine UART-Antwort; keine Anfrage-Pipeline.
  • USB-Trennung, Suspend, Resume, neue Enumeration oder ein Wechsel des HID-Protokolls disarmen. Nach Wiederverbindung werden Nullzustände geliefert; jede erneute Freigabe benötigt wieder GP14 und ARM.
  • Warten auf USB-Bereitschaft, Release oder Report-Abschluss ist pro Operation insgesamt auf 100 ms begrenzt; beim Senden teilen sich vorherige Releases und der eigentliche Report dieselbe Frist. Währenddessen laufen TinyUSB, GPIO-Sicherheitsprüfung, Heartbeat-Prüfung und UART-Ausgabe weiter. Empfangene UART-Requests werden anschließend verarbeitet; ein UART-STOP kann deshalb hinter einem laufenden Report-Warten bis zu 100 ms warten. Der physische Not-Aus wird auch währenddessen geprüft.
  • Ein zusätzlicher RP2350-Hardware-Watchdog startet die MCU bei einem blockierten Hauptablauf nach ungefähr 1 Sekunde neu. Der Startzustand ist immer disarmed.
  • Alle Sicherheitsstopps setzen den gespeicherten Tastatur- und Buttonzustand auf null und senden Release-Reports, sobald USB sie transportieren kann. Bei einem abgetrennten, suspendierten oder nicht reagierenden Ziel ist die Übertragung nicht garantierbar. Bereits gesendete oder gerade laufende USB-Reports sind nicht rückholbar. Der GPIO-Interlock ist eine Software-Sicherheitsfunktion, kein zertifizierter physischer USB-/Stromtrenner.

Fehlercodes

Code Bedeutung
BAD_FRAME Syntax, Länge, Nicht-ASCII, RX-Timeout oder UART-Übertragungsfehler
CRC Prüfsumme falsch; keinerlei HID-Aktion
SEQ_OLD / SEQ_CONFLICT Veraltete Sequenz beziehungsweise andere Anfrage mit letzter Sequenz
UNKNOWN_OP Unbekannte Operation
ARG_COUNT / RANGE Falsche Anzahl beziehungsweise Werte außerhalb des Operationsbereichs
LOCAL_ARM_REQUIRED Kein gültiges lokales GP14-Freigabefenster
ALREADY_ARMED / NOT_ARMED Operation im falschen Freigabezustand
ESTOP Not-Aus ist gelatcht
WATCHDOG Heartbeat lief während der USB-Operation ab
USB_NOT_READY USB nicht verbunden, suspendiert oder nicht nutzbar
USB_TIMEOUT / USB_FAILED USB-Operation überschritt die Frist beziehungsweise TinyUSB meldete einen fehlgeschlagenen Transfer
BOOT_SCROLL_UNAVAILABLE Boot-Maus kann Wheel/Pan nicht transportieren

Prüfsummenbeispiele

Jede folgende Zeile ist mit LF abzuschließen. Vor ARM ist die lokale Freigabe notwendig; während eines längeren Ablaufs müssen zusätzlich neue PING-Anfragen mit steigender Sequenz eingefügt werden.

→ HB1 1 HELLO *76B3
← HB1 1 OK 1 *3A0D
→ HB1 2 ARM *67CC
← HB1 2 OK *A5B9
→ HB1 3 ABS 16384 16384 0 *8651
→ HB1 4 KEY 2 4 0 0 0 0 0 *03AF
→ HB1 5 RELEASE *C666
→ HB1 6 PING *6511
→ HB1 7 STOP *EBB8

Firmware bauen und prüfen

Benötigt werden CMake, ein Arm-GCC-Toolchain mit arm-none-eabi-gcc und ein Raspberry Pi Pico SDK ab 2.1 mit initialisiertem lib/tinyusb-Submodul. Aus dem Verzeichnis hardware-box:

cmake -S pico -B pico/build -DPICO_BOARD=pico2 -DPICO_SDK_PATH=/absoluter/pfad/pico-sdk
cmake --build pico/build --parallel

Das erwartete Ergebnis ist pico/build/hardware_box_pico.uf2. BOOTSEL beim Anstecken gedrückt halten und die UF2-Datei auf das RP2350-Laufwerk kopieren. Der USB-Vendor/Product-Wert CAFE:B001 ist ausschließlich ein Entwicklungswert; für eine verteilte Hardware sind eigene zugeteilte IDs erforderlich. Diese Konstanten stehen in pico/usb_descriptors.c.

Die nativen Protokoll- und Firmware-Simulationstests benötigen keinen Pico SDK:

./pico/tests/run_native.sh

Das Skript benutzt Clang beziehungsweise einen vorhandenen C-Compiler, aktiviert bei Verfügbarkeit AddressSanitizer/UndefinedBehaviorSanitizer und meldet einen Fallback ohne Sanitizer ausdrücklich. Mit HB_SANITIZE=1 werden verfügbare Sanitizer zwingend verlangt, mit HB_SANITIZE=0 deaktiviert. Alternativ mit CMake:

cmake -S pico -B pico/build-native -DHB_NATIVE_TESTS=ON
cmake --build pico/build-native
ctest --test-dir pico/build-native --output-on-failure

Ohne CMake lassen sie sich beispielsweise mit Clang einschließlich AddressSanitizer/UndefinedBehaviorSanitizer bauen. Aus hardware-box/pico:

clang -std=c11 -Wall -Wextra -Werror -fsanitize=address,undefined -g -I . protocol.c tests/protocol_test.c -o /tmp/hb-protocol-test
/tmp/hb-protocol-test
clang -std=c11 -Wall -Wextra -Werror -fsanitize=address,undefined -g -I tests/fake_pico -I . protocol.c tests/firmware_test.c -o /tmp/hb-firmware-test
/tmp/hb-firmware-test

Die Tests prüfen CRC, begrenztes Parsing, 20.000 deterministische Fuzz-Inputs, Sequenz-/Duplikatregeln sowie den echten Firmware-Kontrollpfad gegen simulierte GPIO-, UART- und USB-Funktionen: lokale Freigabe, Release, Boot-Reports, Watchdog, Not-Aus während USB-Warten, USB-Timeout, Reconnect/Suspend und beschädigte beziehungsweise zu lange UART-Zeilen. Sie ersetzen weder einen vollständigen RP2350-Crossbuild noch einen Hardwaretest am Zielrechner.

Optional prüft HB_TINYUSB_PATH=/pfad/zum/pico-sdk/lib/tinyusb ./pico/tests/run_native.sh die echten USB-Descriptor-Makros gegen die Header dieses TinyUSB-Checkouts, einschließlich Interface-Protokollen, Endpoints, Reportlängen und Seriennummern. Diese Integration wurde mit TinyUSB 0.18.0 sowie dem exakten SDK-Submodul geprüft. Zusätzlich wurden ein vollständiger RP2350-Crossbuild und die UF2-Erzeugung erfolgreich ausgeführt: Pico SDK 2.1.1, Arm GNU Toolchain 14.2.Rel1 / GCC 14.2.1 und CMake 3.31.6. Details stehen in BUILD_VALIDATION.md. Flashen, physische USB-/UART-Tests und Zielrechner-Kompatibilität bleiben mangels angeschlossener Hardware unbestätigt.

Offizielle Referenzen: Raspberry Pi C/C++ SDK, Pico SDK Hardware APIs, TinyUSB HID Device API, USB HID Specification.