Files
buzzer/firmware/Protokoll.md
2026-03-02 00:25:40 +01:00

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: als uint8_t (0 = false, 1 = true)
  • Strings: immer len + bytes, ohne implizites \0
  • unsigned long wird 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:

  1. RESP_STREAM_START
  2. RESP_STREAM_CHUNK pro Verzeichniseintrag
  3. RESP_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

  1. RESP_STREAM_START mit total_len:uint32_t
  2. RESP_STREAM_CHUNK mit rohen Tag-Blob-Bytes
  3. RESP_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

  1. RESP_STREAM_START mit Metadaten
  2. RESP_STREAM_CHUNK (mehrfach)
  3. RESP_STREAM_END mit 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.c migriert werden

8) Kurzfazit für den nächsten Schritt

  • Ja, getrennte Kommandos sind sinnvoll.
  • Ja, Endianness muss explizit definiert sein (Little Endian).
  • Ja, ACK und DATA sollten als unterschiedliche Frame-Typen geführt werden.
  • Für große Dateien: uint32_t total_len + Chunk-Streaming statt Einmal-Payload.