8.9 KiB
Buzzer-Kommunikationsprotokoll (vNext Entwurf)
Diese Datei beschreibt den geplanten Neuentwurf des Protokolls. Ziel ist ein klar strukturiertes Binärprotokoll mit stabilen Typen, expliziter Endianness und sauberem Streaming für große Daten.
1) Grundregeln
- Transport: USB CDC (Byte-Stream)
- Multi-Byte-Integer: Little Endian
- Integer-Typen im Protokoll: nur feste Breiten (
uint8_t,uint16_t,uint32_t,uint64_t) bool: alsuint8_t(0= false,1= true)- Strings: immer
len + bytes, ohne implizites\0 unsigned longwird im Protokoll nicht verwendet (plattformabhängig)
Hinweis: Obwohl nRF52 und STM32G0 beide Little Endian sind, bleibt Endianness explizit Teil der Spezifikation.
2) Frame-Struktur
Alle Requests und Responses nutzen denselben Header.
2.1 Header
| Feld | Typ | Beschreibung |
|---|---|---|
sync |
4 Byte | Immer BUZZ (0x42 0x55 0x5A 0x5A) |
frame_type |
uint8_t |
Typ des Frames (siehe unten) |
command_id |
uint8_t |
Kommando-ID |
sequence |
uint16_t |
Anfrage-/Antwort-Korrelation |
payload_len |
uint32_t |
Länge von payload in Bytes |
header_crc16 |
uint16_t |
CRC16 über Header ohne sync und ohne header_crc16 |
Danach folgen:
| Feld | Typ | Beschreibung |
|---|---|---|
payload |
byte[payload_len] |
Kommandoabhängige Nutzdaten |
payload_crc32 |
uint32_t |
CRC32 über payload |
2.2 frame_type
| Wert | Name | Bedeutung |
|---|---|---|
0x01 |
REQ |
Host → Device Anfrage |
0x10 |
RESP_ACK |
Erfolgreich, ohne Nutzdaten |
0x11 |
RESP_DATA |
Erfolgreich, mit Nutzdaten |
0x12 |
RESP_STREAM_START |
Start eines Datenstroms |
0x13 |
RESP_STREAM_CHUNK |
Chunk eines Datenstroms |
0x14 |
RESP_STREAM_END |
Ende eines Datenstroms |
0x7F |
RESP_ERROR |
Fehlerantwort |
3) Antwortmodell (ACK vs DATA)
Ja, wir unterscheiden explizit:
RESP_ACK: „OK ohne Payload“ (Host muss nur Erfolg verbuchen)RESP_DATA: „OK mit Payload“RESP_ERROR: Fehlercode + optionaler Detailtext
Damit ist Parsing eindeutig und ohne Sonderfälle wie „OK aber vielleicht mit Daten“.
4) Fehlercodes
Fehlercodes bleiben wie bisher inhaltlich erhalten (P_ERR_*), werden aber als Binärwert in RESP_ERROR übertragen.
4.1 Fehler-Payload (RESP_ERROR)
| Feld | Typ | Beschreibung |
|---|---|---|
error_code |
uint8_t |
Protokollfehlercode |
detail_len |
uint8_t |
Länge von detail |
detail |
byte[detail_len] |
Optionaler Kurztext (ASCII/UTF-8) |
5) Kommandos (erste Aufteilung)
5.1 0x00 GET_PROTOCOL_VERSION
Request-Payload: keine
Response: RESP_DATA
| Feld | Typ | Beschreibung |
|---|---|---|
protocol_version |
uint16_t |
Versionsnummer des Protokolls |
5.2 0x01 GET_FIRMWARE_STATUS
Request-Payload: keine
Response: RESP_DATA
| Feld | Typ | Beschreibung |
|---|---|---|
status |
uint8_t |
0 = pending, 1 = confirmed |
version_len |
uint8_t |
Länge des Versionsstrings |
version |
byte[version_len] |
Firmware-Version |
5.3 0x02 GET_FLASH_STATUS
Request-Payload: keine
Response: RESP_DATA
| Feld | Typ | Beschreibung |
|---|---|---|
block_size |
uint32_t |
Bytes pro Block |
total_blocks |
uint32_t |
Gesamtblöcke |
free_blocks |
uint32_t |
Freie Blöcke |
path_max_len |
uint32_t |
Maximale erlaubte Pfadlänge in Bytes |
5.4 0x10 LIST_DIR
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
path_len |
uint8_t |
Länge von path |
path |
byte[path_len] |
Zielverzeichnis, z. B. / oder /lfs/a |
path_len darf den in GET_FLASH_STATUS.path_max_len gemeldeten Wert nicht überschreiten.
Response: RESP_DATA
LIST_DIR liefert Einträge als Stream:
RESP_STREAM_STARTRESP_STREAM_CHUNKpro VerzeichniseintragRESP_STREAM_END
RESP_STREAM_START Payload
| Feld | Typ | Beschreibung |
|---|---|---|
entry_count |
uint32_t |
Anzahl Einträge, 0xFFFFFFFF = unbekannt |
RESP_STREAM_CHUNK Payload (ein Eintrag)
| Feld | Typ | Beschreibung |
|---|---|---|
entry_type |
uint8_t |
0 = Datei, 1 = Verzeichnis |
name_len |
uint8_t |
Länge von name |
size |
uint32_t |
Dateigröße in Bytes (0 für Verzeichnisse) |
name |
byte[name_len] |
Name ohne führenden Pfad |
RESP_STREAM_END Payload
- leer
Hinweis: Rekursion bleibt Host-seitig. Der Host setzt aus angefragtem Basispfad + name den vollständigen Pfad zusammen.
5.5 0x11 CHECK_FILE_CRC
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
path_len |
uint8_t |
Länge von path |
path |
byte[path_len] |
Pfad zur Datei |
path_len darf den in GET_FLASH_STATUS.path_max_len gemeldeten Wert nicht überschreiten.
Response: RESP_DATA
| Feld | Typ | Beschreibung |
|---|---|---|
crc32 |
uint32_t |
CRC32 (Little Endian) über den Audio-Inhalt |
Hinweis: Die CRC-Berechnung folgt der aktuellen Firmware-Logik für Audio-Dateien (ohne angehängte Tag-Daten).
5.6 0x12 MKDIR
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
path_len |
uint8_t |
Länge von path |
path |
byte[path_len] |
Zielpfad für neues Verzeichnis |
path_len darf den in GET_FLASH_STATUS.path_max_len gemeldeten Wert nicht überschreiten.
Response: RESP_ACK
Bei Erfolg wird eine leere ACK-Antwort gesendet. Bei Fehlern RESP_ERROR mit passendem P_ERR_*.
5.7 0x13 RM
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
path_len |
uint8_t |
Länge von path |
path |
byte[path_len] |
Pfad zu Datei oder leerem Verzeichnis |
path_len darf den in GET_FLASH_STATUS.path_max_len gemeldeten Wert nicht überschreiten.
Response: RESP_ACK
Bei Erfolg wird eine leere ACK-Antwort gesendet. Bei Fehlern RESP_ERROR mit passendem P_ERR_*.
Hinweis: Rekursives Löschen bleibt Host-seitig (mehrere LIST_DIR + RM Aufrufe).
5.8 0x20 GET_TAG_BLOB
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
path_len |
uint8_t |
Länge von path |
path |
byte[path_len] |
Pfad zur Datei |
path_len darf den in GET_FLASH_STATUS.path_max_len gemeldeten Wert nicht überschreiten.
Response: Stream
RESP_STREAM_STARTmittotal_len:uint32_tRESP_STREAM_CHUNKmit rohen Tag-Blob-BytesRESP_STREAM_END(leer)
Der Blob-Inhalt ist die TLV-Metadatenstruktur aus Tags.md (ohne Footer version+len+TAG!).
5.9 0x21 SET_TAG_BLOB_START
Startet eine Tag-Blob-Übertragung.
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
path_len |
uint8_t |
Länge von path |
path |
byte[path_len] |
Pfad zur Datei |
total_len |
uint16_t |
Gesamtlänge des folgenden Blobs |
Response: RESP_ACK
5.10 0x22 SET_TAG_BLOB_CHUNK
Sendet einen weiteren Chunk des Blobs.
Request-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
data |
byte[] |
Chunk-Daten |
Response: RESP_ACK
5.11 0x23 SET_TAG_BLOB_END
Schließt den Upload ab und schreibt den Blob in die Datei (ersetzt bestehende Tags).
Request-Payload: leer
Response: RESP_ACK
Hinweis: Ein leerer Blob (total_len=0) ist erlaubt und entspricht "Tags leeren".
6) Große Daten (Dateien, Audio, Firmware)
Für mehrere MB nicht als einzelnes RESP_DATA senden, sondern streamen.
6.1 Warum nicht ein riesiges payload_len?
- Höheres Risiko bei Paketverlust/Timeout
- Mehr RAM-/Pufferdruck auf MCU
- Schlechtere Wiederanlaufstrategie bei Fehlern
6.2 Streaming-Ablauf
RESP_STREAM_STARTmit MetadatenRESP_STREAM_CHUNK(mehrfach)RESP_STREAM_ENDmit Gesamt-CRC/Abschlussstatus
6.3 Stream-Metadaten (RESP_STREAM_START)
| Feld | Typ | Beschreibung |
|---|---|---|
stream_id |
uint16_t |
ID des Streams |
total_len |
uint32_t |
Gesamtlänge in Bytes |
chunk_size |
uint16_t |
Ziel-Chunkgröße |
6.4 Chunk-Payload (RESP_STREAM_CHUNK)
| Feld | Typ | Beschreibung |
|---|---|---|
stream_id |
uint16_t |
Zuordnung zum Stream |
offset |
uint32_t |
Byte-Offset im Gesamtstrom |
data_len |
uint16_t |
Länge von data |
data |
byte[data_len] |
Nutzdaten |
total_len ist uint32_t, damit sind bis 4 GiB abbildbar. Für euer Gerät ist das mehr als ausreichend und trotzdem standardisiert.
7) Offene Punkte für die Implementierung
- Fixe maximale
chunk_size(z. B. 256/512/1024 Bytes) - ACK/NACK auf Chunk-Ebene nötig oder „best effort + Retry auf Kommando-Ebene“?
- Timeout-/Retry-Policy pro Kommando
- Welche Kommandos zuerst in
protocol.cmigriert werden
8) Kurzfazit für den nächsten Schritt
- Ja, getrennte Kommandos sind sinnvoll.
- Ja, Endianness muss explizit definiert sein (Little Endian).
- Ja,
ACKundDATAsollten als unterschiedliche Frame-Typen geführt werden. - Für große Dateien:
uint32_t total_len+ Chunk-Streaming statt Einmal-Payload.