# Serial-Protokoll (CDC USB) Dateien: - `hal/usb_serial.h` - `hal/usb_serial.cpp` - `CMainController.cpp` ## Paketformat Alle Pakete sind exakt 8 Byte lang: ```text Byte 0: command / event id Byte 1: key_id oder chunk-index oder chunk-count Byte 2..7: kommandospezifische Daten ``` LED-Kommandos verwenden Byte 2..4 für RGB. Config- und Makro-DATA-Pakete verwenden alle sechs Bytes 2..7 als Nutzlast. Einfache Events aus `usb_serial_send()` nutzen höchstens Byte 2..3 und füllen den Rest mit null. Es gibt kein Framing, keinen Längenheader und keine Prüfsumme auf Paketebene. ## Richtungen | Richtung | IDs | Verarbeitung | |---|---|---| | PC -> Board | `0x01..0x7F` | `poll_vendor()` | | Board -> PC | `0x81..0xFF` | `usb_serial_send()` | ## Commands | ID | Name | Zweck | |---|---|---| | `0x01` | `SET_LED_OVERRIDE` | temporaere LED-Override setzen | | `0x02` | `CLEAR_LED_OVERRIDE` | Override entfernen | | `0x03` | `SET_LED_BASE` | Base-Farbe im RAM setzen | | `0x05` | `PING` | Antwort: `PONG` | | `0x10` | `CONFIG_BEGIN` | Config-Transfer starten | | `0x11` | `CONFIG_DATA` | 6 Byte Config-Nutzdaten | | `0x12` | `CONFIG_COMMIT` | Config pruefen und speichern | | `0x13` | `CONFIG_READ` | Config-Dump an Host senden | | `0x20` | `MACRO_BEGIN` | Makro-Transfer starten | | `0x21` | `MACRO_DATA` | 6 Byte Makro-Nutzdaten | | `0x22` | `MACRO_COMMIT` | Makros speichern | | `0x23` | `MACRO_READ` | Makro-Dump an Host senden | ## Events | ID | Name | Zweck | |---|---|---| | `0x81` | `KEY_DOWN` | Host-Button gedrückt | | `0x82` | `KEY_UP` | Host-Button losgelassen | | `0x83` | `ENC_CW` | Encoder-Host-Action im Uhrzeigersinn | | `0x84` | `ENC_CCW` | Encoder-Host-Action gegen Uhrzeigersinn | | `0x85` | `PONG` | Antwort auf Ping | | `0x90` | `CONFIG_ACK` | Config erfolgreich gespeichert | | `0x91` | `CONFIG_NACK` | Config ungueltig oder NVM-Timeout | | `0x92` | `CONFIG_BEGIN` | Config-Dump beginnt | | `0x93` | `CONFIG_DATA` | 6 Byte Config-Dump | | `0x94` | `CONFIG_END` | Config-Dump fertig | | `0x95` | `MACRO_ACK` | Makros erfolgreich gespeichert | | `0x96` | `MACRO_BEGIN` | Makro-Dump beginnt | | `0x97` | `MACRO_DATA` | 6 Byte Makro-Dump | | `0x98` | `MACRO_END` | Makro-Dump fertig | | `0x99` | `MACRO_NACK` | Makro-Speichern fehlgeschlagen | Bei `ActionType::HOST_COMMAND` enthält Byte 1 die Matrix-Key-ID oder Encoder-ID. Die 16-Bit-Command-ID aus `SAction.data` steht little-endian in Byte 2 und 3. Encoder-Actions verwenden die richtungsspezifischen IDs `0x83/0x84`. ## Chunk-Zahlen Aktuelle Blob-Groessen: - Config: `740` Byte - Makros: `512` Byte Bei 6 Nutzbytes pro Paket ergibt das: ```text Config: ceil(740 / 6) = 124 Chunks Makros: ceil(512 / 6) = 86 Chunks ``` Wichtig fuer Implementierungen: Der Byte-Offset eines Chunks muss mindestens 16 Bit breit sein. Bei der Config liegt der Offset ab Chunk 43 ueber 255 Byte; ein 8-Bit-Offset wuerde ueberlaufen und spaetere Profilbereiche falsch dumpen. ## Transferablauf ### PC -> Board ```text BEGIN(chunk_count) DATA 0 DATA 1 ... COMMIT ``` ### Board -> PC ```text BEGIN(chunk_count) DATA 0 DATA 1 ... END ``` ## Validierung `CONFIG_COMMIT` prueft: - Magic - Version - CRC Nur bei erfolgreicher Pruefung wird in NVM geschrieben. `MACRO_COMMIT` schreibt ohne CRC direkt nach NVM und signalisiert nur Erfolg oder Fehler. Beide Empfangspfade erwarten exakt die berechnete Chunkzahl, markieren jeden Index einmalig und akzeptieren `COMMIT` nur nach einem vollständigen Transfer. Doppelte oder außerhalb des Bereichs liegende Chunks machen den Transfer ungültig und führen beim Commit zu NACK. Config-Commit prüft zusätzlich Magic, Version, CRC, Profilindex, Actiontypen/-daten, LED-Enums und kritische Animationsperioden. Makro-Commit prüft die Vollständigkeit und alle HID-Keycodes. Die Makrotabelle besitzt weiterhin keine eigene persistente CRC. ## Praktische Hinweise fuer die GUI - nach `CONFIG_COMMIT` auf `CONFIG_ACK` oder `CONFIG_NACK` warten - danach erst `MACRO_*` senden - Dumps besser sequenziell lesen: zuerst Config, danach Makros - `DtrEnable` muss aktiv sein, sonst verwirft das Board CDC-Ausgaben - ausschließlich vollständige 8-Byte-Pakete schreiben; schon ein verlorenes Byte verschiebt die Paketgrenzen für alle folgenden Daten - Host-Command-ID little-endian aus Byte 2/3 lesen - Config- und Makro-Dumps ebenfalls auf Chunkzahl, eindeutige Indizes und Vollständigkeit prüfen ## Implementierungsdetails - RX-Ringbuffer: 256 Byte = 32 volle Pakete; er ist ein laufender Zwischenpuffer und fasst keinen kompletten Configtransfer - feste 8-Byte-Pakete vereinfachen Firmware und GUI - nach einem reinen SWD-Reflash kann ein physischer USB-Reconnect noetig sein