Files
buzzer_2/protocol.md
2026-03-18 15:05:45 +01:00

12 KiB
Raw Blame History

Buzzer Protocol (Wire Specification)

1. Zweck und Geltungsbereich

Das Buzzer Protocol definiert ein transportunabhaengiges, binaeres Frame-Format fuer die Kommunikation zwischen Host und Device. Unterstuetzte Transporte sind aktuell BLE und USB CDC ACM/UART.

Das Protokoll spezifiziert:

  • Frame-Struktur (Header + Payload)
  • Frametypen
  • Datentypen fuer Request/Response
  • Semantik fuer Stream-Transfers (Verzeichnisliste, Datei, Firmware)

2. Transport- und Codierungsregeln

  • Alle ganzzahligen Felder werden in Little Endian uebertragen.
  • Die im Header angegebene payload_length bezieht sich ausschliesslich auf die Nutzdaten ohne Header.
  • Bei UART kann optional eine Synchronisationssequenz BUZZ (0x42 0x55 0x5A 0x5A) vor einem Frame verwendet werden, um Framing nach Leitungsstoerungen zu resynchronisieren.

3. Frame-Format

3.1 Header (3 Byte)

uint8_t  frame_type
uint16_t payload_length   // Little Endian

3.2 Gesamtframe

+------------------+-------------------------+
| Header (3 Byte)  | Payload (optional)      |
| frame_type (1B)  | payload_length Byte     |
| payload_len (2B) |                         |
+------------------+-------------------------+

4. Frametypen

4.1 Steuer- und Anfrageframes

Wert Name Richtung Beschreibung
0x00 REQUEST Host → Device Abfrage eines Datentyps
0x10 RESPONSE Device → Host Antwort auf REQUEST
0x11 ACK Host ↔ Device Flusskontrolle bei Stream-Transfers
0x12 ERROR Device → Host Fehlerantwort mit Fehlercode
0x13 SUCCESS Device → Host Bestaetigung einer Operation

4.2 Datei-Transfer

Wert Name Richtung Beschreibung
0x20 FILE_START Host ↔ Device Beginn eines Dateitransfers
0x21 FILE_CHUNK Host ↔ Device Ein Datenblock des Dateitransfers
0x22 FILE_END Host ↔ Device Abschluss des Dateitransfers inkl. CRC32

4.3 Firmware-Transfer (reserviert, noch nicht implementiert)

Wert Name
0x30 FW_START
0x31 FW_CHUNK
0x32 FW_END

4.4 Verzeichnisliste

Wert Name Richtung Beschreibung
0x40 LS_START Device → Host Beginn des Listing-Streams
0x41 LS_ENTRY Device → Host Ein Verzeichniseintrag
0x42 LS_END Device → Host Ende des Listing-Streams

5. Request/Response-Schema

5.1 Request (frame_type = 0x00)

Payload-Mindestformat:

uint8_t data_type   // Nutzt enum buzz_data_type
// optional: datentypspezifische Parameter

Wire-Format:

[0x00][payload_length LE][data_type][optional parameters]

5.2 Response (frame_type = 0x10)

Payload-Mindestformat:

uint8_t data_type   // Echo des angefragten data_type
// danach: datentypspezifische Response-Daten

Wire-Format:

[0x10][payload_length LE][data_type][response payload]

6. Datentypen (Request/Response)

Definierte data_type-Werte:

Wert Name Beschreibung
0x01 PROTO_INFO Protokollversion und Chunk-Groesse
0x02 DEVICE_INFO Geraeteinformationen (TBD)
0x03 FS_INFO Dateisystem-Statistik und Pfadnamen
0x20 FILE_GET Datei vom Device anfordern
0x21 FILE_PUT Datei auf das Device hochladen
0x22 TAGS_GET Metadaten-Tags anfordern
0x23 TAGS_PUT Metadaten-Tags schreiben
0x24 RM_FILE Datei loeschen
0x25 RENAME_FILE Datei umbenennen
0x30 FW_UPDATE Firmware-Update starten
0x40 LS Verzeichnisliste starten

6.1 PROTO_INFO (0x01)

Request-Parameter: keine

Response-Payload:

uint8_t  data_type;       // 0x01
uint16_t version;         // Protokollversion (LE)
uint16_t max_chunk_size;  // max. Nutzdaten pro Frame ohne Header (LE)

Hinweis: max_chunk_size ergibt sich aus der internen Slab-Konfiguration (CONFIG_BUZZ_PROTO_SLAB_SIZE - 3).

6.2 DEVICE_INFO (0x02)

TBD

6.3 FS_INFO (0x03)

Request-Parameter: keine

Response-Payload:

uint8_t  data_type;          // 0x03
uint32_t total_size;         // Gesamtgroesse Flash in Bytes (LE)
uint32_t free_size;          // Freier Speicher in Bytes (LE)
uint8_t  max_path_length;    // Maximal erlaubte Pfadlaenge
uint8_t  sys_path_length;    // Laenge des System-Pfades (ohne 0-Terminator)
uint8_t  audio_path_length;  // Laenge des Audio-Pfades (ohne 0-Terminator)
uint8_t  data[];             // sys_path gefolgt von audio_path, nicht nullterminiert

6.4 LS (0x40) — Verzeichnisliste anfordern

Startet einen LS-Stream fuer den angegebenen Pfad.

Request-Payload:

uint8_t  data_type;   // 0x40
char     path[];      // Pfad ohne 0-Terminator, Laenge ergibt sich aus payload_length - 1

Wire-Format (Beispiel fuer Pfad /a):

[0x00][0x03 0x00][0x40][0x2F 0x61]

Das Device antwortet mit dem LS-Stream (siehe Abschnitt 8).

6.5 RM_FILE (0x24) — Datei loeschen

Request-Payload:

uint8_t  data_type;     // 0x24
uint8_t  path_length;   // Laenge des Pfads
char     path[];        // Pfad ohne 0-Terminator

6.6 RENAME_FILE (0x25) — Datei umbenennen

Request-Payload:

uint8_t  data_type;       // 0x25
uint8_t  old_path_length; // Laenge des alten Pfads
uint8_t  new_path_length; // Laenge des neuen Pfads
char     paths[];         // Alter Pfad, direkt gefolgt vom neuen Pfad (beide ohne 0-Terminator)

6.7 FILE_PUT (0x21) / TAGS_PUT (0x23) — Upload initiieren

Request-Payload:

uint8_t  data_type;   // 0x21 (Datei) oder 0x23 (Tags)
uint32_t total_size;  // Dateigroesse in Bytes (LE)
char     path[];      // Zielpfad ohne 0-Terminator

7. ACK-, ERROR- und SUCCESS-Frames

7.1 ACK (frame_type = 0x11) — Host ↔ Device

Wird waehrend eines laufenden Stream-Transfers gesendet, um der sendenden Seite Credits (Sendeerlaubnisse) zu erteilen. Bei einem Download (LS oder FILE_GET) sendet der Host das ACK. Bei einem Upload (FILE_PUT oder TAGS_PUT) sendet das Device das ACK.

Format:

// Header:
uint8_t  frame_type;      // 0x11
uint16_t payload_length;  // 0x0002

// Payload:
uint16_t credits;         // Anzahl der Entries, die das Device senden darf (LE)

Wire-Format (Beispiel: 64 Credits):

[0x11][0x02 0x00][0x40 0x00]

Semantik:

  • Der Host sendet nach Empfang von LS_START initial Credits (typisch 64).
  • Das Device dekrementiert seinen internen Credit-Zaehler mit jeder gesendeten LS_ENTRY.
  • Bei 0 Credits wartet das Device auf ein weiteres ACK (Timeout: 5 × 500 ms, danach Abbruch).
  • Der Host soll bei Bedarf weitere Credits nachsenden, bevor die bisherigen aufgebraucht sind.

7.2 ERROR (frame_type = 0x12) — Device → Host

Format:

// Header:
uint8_t  frame_type;      // 0x12
uint16_t payload_length;  // 0x0002

// Payload:
uint16_t error_code;      // Positiver Zephyr-errno-Wert (LE)

Wire-Format (Beispiel: ENOENT = 2):

[0x12][0x02 0x00][0x02 0x00]

ERROR kann jederzeit als Antwort auf einen REQUEST oder waehrend eines Streams gesendet werden. Ein ERROR-Frame waehrend eines aktiven LS-Streams beendet diesen implizit.

Fehlercode-Tabelle (Zephyr errno, positiver Wert):

Code Zephyr-Name Bedeutung
1 EPERM Fehlende Berechtigung
2 ENOENT Datei oder Verzeichnis nicht gefunden
5 EIO Ein-/Ausgabefehler auf dem Flash
12 ENOMEM Nicht genuegend Speicher frei
16 EBUSY Geraet oder Ressource belegt
22 EINVAL Ungültiges Argument oder Parameter
24 EMFILE Zu viele offene Dateien
28 ENOSPC Kein freier Speicherplatz mehr
36 ENAMETOOLONG Dateiname oder Pfad zu lang
88 ENOSYS Funktion nicht implementiert
134 ENOTSUP Operation nicht unterstuetzt

7.3 SUCCESS (frame_type = 0x13) — Device → Host

Bestaetigt den erfolgreichen Abschluss einer Operation, z. B. nach Beendigung eines Uploads oder einer Dateioperation (Loeschen, Umbenennen).

Format:

// Header:
uint8_t  frame_type;      // 0x13
uint16_t payload_length;  // 0x0001

// Payload:
uint8_t  data_type;       // Der Befehl, der erfolgreich war (z.B. 0x21 fuer FILE_PUT)

Wire-Format (Beispiel: Erfolg bei RM_FILE):

[0x13][0x01 0x00][0x24]

8. LS-Stream (Verzeichnisliste)

Der LS-Stream wird durch einen REQUEST mit data_type = 0x40 ausgeloest und laeuft wie folgt ab:

Host                              Device
 |                                   |
 |-- REQUEST (data_type=LS, path) -->|
 |                                   |  (oeffnet Verzeichnis)
 |<--------- LS_START (leer) --------|
 |                                   |
 |------ ACK (credits=64) ---------->|
 |                                   |
 |<-- LS_ENTRY (entry 1) ------------|
 |<-- LS_ENTRY (entry 2) ------------|
 |         ...                       |
 |<-- LS_ENTRY (entry 64) -----------|  (credits = 0, Device wartet)
 |                                   |
 |------ ACK (credits=64) ---------->|
 |                                   |
 |<-- LS_ENTRY (entry 65) -----------|
 |         ...                       |
 |<--------- LS_END -----------------|

8.1 LS_START (0x40) — Device → Host

Signalisiert den Beginn des Streams. Keine Payload.

[0x40][0x00 0x00]

8.2 LS_ENTRY (0x41) — Device → Host

Ein Eintrag pro Verzeichniselement.

Payload:

uint8_t  type;         // 0x00 = Datei, 0x01 = Verzeichnis (buzz_fs_entry_type)
uint32_t size;         // Dateigroesse in Bytes (LE); bei Verzeichnissen 0
uint8_t  name_length;  // Laenge des Namens (ohne 0-Terminator)
char     name[];       // Datei-/Verzeichnisname, nicht nullterminiert

type-Werte:

Wert Bedeutung
0x00 Datei (FILE)
0x01 Verzeichnis (DIR)

8.3 LS_END (0x42) — Device → Host

Signalisiert das Ende des Streams.

Payload:

uint32_t total_entries;  // Gesamtzahl gesendeter Eintraege (LE)

Der Host kann total_entries mit der empfangenen Anzahl von LS_ENTRY-Frames vergleichen, um Vollstaendigkeit zu pruefen.

8.4 Fehler- und Timeoutbehandlung

  • Tritt ein Fehler beim Lesen auf, sendet das Device einen ERROR-Frame und beendet den Stream.
  • Empfaengt das Device 5 Mal in Folge keine Credits innerhalb von je 500 ms (2,5 s gesamt), bricht es den Stream intern ab (kein ERROR-Frame, Stream wird still verworfen).
  • Der Host sollte einen eigenen Watchdog implementieren; empfohlener Timeout: 3 s ohne empfangenen Frame.

9. Beispiele

9.1 PROTO_INFO abfragen

Request:

00 01 00 01
  • 00: REQUEST
  • 01 00: payload_length = 1
  • 01: data_type = PROTO_INFO

Response (Beispielwerte):

10 05 00 01 01 00 FD 00
  • 10: RESPONSE
  • 05 00: payload_length = 5
  • 01: data_type = PROTO_INFO
  • 01 00: version = 1
  • FD 00: max_chunk_size = 253

9.2 Verzeichnisliste /a anfordern

Request:

00 03 00 40 2F 61
  • 00: REQUEST
  • 03 00: payload_length = 3
  • 40: data_type = LS
  • 2F 61: Pfad /a

Antwort (Sequenz):

40 00 00                          // LS_START, keine Payload
// Host sendet ACK mit Credits
11 02 00 40 00                    // ACK, 64 Credits
// Device sendet Eintraege
41 0A 00 00 00 00 00 00 06 73 6F 75 6E 64 31  // LS_ENTRY: FILE, size=0, name="sound1" (gekuerzt)
// ... weitere Eintraege ...
42 04 00 01 00 00 00              // LS_END, total_entries = 1