Files
buzzer_2/protocol.md
2026-03-19 07:39:25 +01:00

11 KiB

Buzzer Protocol (Wire Specification)

Stand: 2026-03-18
Quelle: aktueller Implementierungsstand aus Firmware (buzz_proto, fs_mgmt, ble_mgmt) und Web-Client (transport, parser).

1. Ziel und Scope

Das Buzzer Protocol ist ein binäres Frame-Protokoll für Host <-> Device Kommunikation.

Abgedeckte Funktionen:

  • Capability-Abfrage (PROTO_INFO)
  • Dateisystem-Info (FS_INFO)
  • Verzeichnisliste als Stream (LS)
  • Datei-/Tag-Download (FILE_GET, TAGS_GET)
  • Datei-/Tag-Upload (FILE_PUT, TAGS_PUT)
  • Datei löschen / umbenennen (RM_FILE, RENAME_FILE)

Nicht produktiv implementiert:

  • DEVICE_INFO (0x02)
  • Firmware-Update (FW_*, FW_UPDATE)

2. Transport und Grundregeln

  • Alle Integer-Felder sind Little Endian.
  • Jedes Frame hat einen 3-Byte Header.
  • payload_length enthält nur die Payload-Länge (ohne Header).
  • Aktiver Produktiv-Transport ist BLE GATT (RX Write Without Response, TX Notify).
  • Es darf genau ein Stream gleichzeitig aktiv sein (LS, FILE_GET, FILE_PUT, TAGS_*).

BLE Service UUIDs:

  • Service: e517d988-bab5-4574-8479-97c6cb115ca0
  • RX: e517d988-bab5-4574-8479-97c6cb115ca1
  • TX: e517d988-bab5-4574-8479-97c6cb115ca2

3. Frame-Format

3.1 Header

uint8_t  frame_type;
uint16_t payload_length; // LE

3.2 Paketstruktur

---
title: "Basic Packet Structure"
---
packet
+8: "Frame type"
+16: "Payload length (LE)"
+40: "Payload (variable length)"

3.3 Maximalgröße

Firmware-Buffer ist slab-basiert (CONFIG_BUZZ_PROTO_SLAB_SIZE).
Der effektive Chunk für Transfers wird zusätzlich durch den Transport limitiert. Bei Bluetooth sind das zum Beispiel 3 Bytes:

PROTO_INFO.max_chunk_size wird dynamisch berechnet als:

min(slab_size - 3, transport_max_payload - 3)

Das Protokoll ist so ausgelegt, dass es mit mindestens 100 Bytes auskommen sollte.

4. Frame-Typen

Wert Name Richtung Bedeutung
0x00 REQUEST Host -> Device API-Aufruf per data_type
0x10 RESPONSE Device -> Host Antwort auf REQUEST
0x11 ACK Host <-> Device Credit-basierte Flusskontrolle
0x12 ERROR Device -> Host Fehler mit errno-Code
0x13 SUCCESS Device -> Host Erfolgreicher Abschluss
0x20 FILE_START Device -> Host Start Download-Stream
0x21 FILE_CHUNK Host <-> Device Datenblock (Upload/Download)
0x22 FILE_END Host <-> Device Streamende mit CRC32
0x30 FW_START reserviert nicht aktiv
0x31 FW_CHUNK reserviert aktuell ENOSYS
0x32 FW_END reserviert nicht aktiv
0x40 LS_START Device -> Host Start Verzeichnis-Stream
0x41 LS_ENTRY Device -> Host Verzeichniseintrag
0x42 LS_END Device -> Host Ende Verzeichnis-Stream

5. Data-Typen (REQUEST)

Wert Name Status Beschreibung
0x01 PROTO_INFO aktiv Protokollversion + max Chunkgröße
0x02 DEVICE_INFO reserviert aktuell nicht bedient
0x03 FS_INFO aktiv Dateisystem- und Pfadinfos
0x20 FILE_GET aktiv Datei vom Device streamen
0x21 FILE_PUT aktiv Datei zum Device hochladen
0x22 TAGS_GET aktiv nur Tag-Bereich streamen
0x23 TAGS_PUT aktiv nur Tag-Bereich schreiben
0x24 RM_FILE aktiv Datei löschen
0x25 RENAME_FILE aktiv Datei umbenennen
0x30 FW_UPDATE reserviert aktuell nicht bedient
0x40 LS aktiv Verzeichnisliste starten

6. Request/Response-Formate

6.1 Generischer Request

uint8_t data_type;
// optional daten_typspezifische Parameter

Wire:

[0x00][payload_len LE][data_type][optional...]
---
title: "Generic Requst Structure"
---
packet
+8: "Frame type REQUEST: 0x00"
+16: "Payload length (LE)"
+8: "Data type"
+32: "Optional payload (variable length)"

6.2 PROTO_INFO (0x01)

Request: keine Zusatzdaten.

Response-Payload:

uint8_t  data_type;       // 0x01
uint16_t version;         // LE
uint16_t max_chunk_size;  // LE
---
title: "PROTO_INFO response structure"
---
packet
+8: "Frame type RESPONSE: 0x10"
+16: "Payload length (LE): 5"
+8: "Data type PROTO_INFO: 0x01"
+16: "Protocol Version (LE)"
+16: "Max Chunk Size (LE)"

6.3 FS_INFO (0x03)

Request: keine Zusatzdaten.

Response-Payload:

uint8_t  data_type;         // 0x03
uint32_t total_size;        // LE
uint32_t free_size;         // LE
uint8_t  max_path_length;
uint8_t  sys_path_length;
uint8_t  audio_path_length;
uint8_t  data[];            // sys_path + audio_path (beide nicht nullterminiert)

Im data folgen sich System- und Audiopfad ohne Abstand, und ohne 0-Terminierung (\0). Beispiel für Systempfad /lfs/sysund Audiopfad /lfs/a:

/lfs/sys/lfs/a

sys_path_len ist in diesem Beispiel 8 und audio_path_len ist 6.

---
title: "FS_INFO response structure"
---
packet
+8: "Frame type RESPONSE: 0x10"
+16: "Payload length (LE): variable, 12 + sys path length + audio path length"
+8: "Data type FS_INFO: 0x03"
+32: "Total size (LE)"
+32: "Free size (LE)"
+8: "Max path length"
+8: "Sys path length"
+8: "Audio path length"
+40: "Sys path + Audio Path (variable)"

Beispielpaket mit 8 MiB Flash, wovon 7 MiB frei sind und den Pfaden /lfs/sys und /lfs/a. Die maximale Pfadlänge sind 32 Zeichen:

---
title: "FS_INFO response example"
---
packet
+8: "Frame type RESPONSE: 0x10"
+16: "Payload length (LE): 26 (0x1A 0x00)"
+8: "Data type FS_INFO: 0x03"
+32: "Total size (LE): 8388608 (0x00 0x00 0x80 0x00)"
+32: "Free size (LE): 7340032 (0x00 0x00 0x70 0x00)"
+8: "Max path length: 32"
+8: "Sys path length: 8"
+8: "Audio path length: 6"
+112: "data: '/lfs/sys/lfs/a'"

Das Beispiel schaut in HEX so aus:

0x10 0x1A 0x00 0x03 0x00 0x00 0x80 0x00 0x00 0x00 0x70 0x00 0x20 0x08 0x06
0x2F 0x6C 0x66 0x73 0x2F 0x73 0x79 0x73 0x2F 0x6C 0x66 0x73 0x2F 0x61

6.4 LS (0x40)

Request-Payload:

uint8_t data_type; // 0x40
char    path[];    // ohne Nullterminierung

6.5 FILE_GET (0x20) und TAGS_GET (0x22)

Request-Payload:

uint8_t data_type; // 0x20 oder 0x22
char    path[];    // ohne Nullterminierung

Antwort ist ein Stream aus FILE_START -> FILE_CHUNK* -> FILE_END.

6.6 FILE_PUT (0x21) und TAGS_PUT (0x23)

Request-Payload:

uint8_t  data_type;   // 0x21 oder 0x23
uint32_t total_size;  // LE
char     path[];      // ohne Nullterminierung

Danach sendet der Host:

  • FILE_CHUNK Frames
  • abschließend FILE_END mit CRC32

6.7 RM_FILE (0x24)

Request-Payload:

uint8_t data_type;    // 0x24
uint8_t path_length;
char    path[];       // ohne Nullterminierung

6.8 RENAME_FILE (0x25)

Request-Payload:

uint8_t data_type;       // 0x25
uint8_t old_path_length;
uint8_t new_path_length;
char    paths[];         // old_path + new_path (jeweils ohne Nullterminierung)

7. ACK / ERROR / SUCCESS

7.1 ACK (0x11)

Payload:

uint16_t credits; // LE

Wichtig: Es gibt zwei Semantiken je nach Richtung.

  • Download (LS, FILE_GET, TAGS_GET):
    Host -> Device, Credits werden im Device als absoluter neuer Stand gesetzt.
  • Upload (FILE_PUT, TAGS_PUT):
    Device -> Host, Credits sind zusätzlich gewährte Tokens (Host addiert sie).

7.2 ERROR (0x12)

Payload:

uint16_t error_code; // positiver errno-Wert, LE

Häufige Codes:

Code Name Bedeutung
1 EPERM fehlende Rechte
2 ENOENT Datei/Ordner nicht gefunden
5 EIO Flash-I/O-Fehler
16 EBUSY Stream/Ressource belegt
22 EINVAL ungültige Nutzdaten
36 ENAMETOOLONG Pfad zu lang
71 EPROTO unzulässiger Frame-Typ
74 EBADMSG ungültiger Stream-Frame/CRC Fehler
88 ENOSYS nicht implementiert
90 EMSGSIZE max Payload ungültig
116 ETIMEDOUT Credit-/Stream-Timeout
134 ENOTSUP nicht unterstützt

7.3 SUCCESS (0x13)

Payload:

uint8_t data_type; // erfolgreich abgeschlossener Befehl

Wird derzeit u.a. für FILE_PUT, TAGS_PUT, RM_FILE, RENAME_FILE genutzt.

8. Stream-Sequenzen (Mermaid)

8.1 Verzeichnisliste (LS)

sequenceDiagram
	participant Host
	participant Device

	Host->>Device: REQUEST(LS, path)
	Device-->>Host: LS_START
	Host->>Device: ACK(credits=64)

	loop solange credits > 0
		Device-->>Host: LS_ENTRY
	end

	alt Verzeichnis vollständig
		Device-->>Host: LS_END(total_entries)
	else keine Credits/Timeout
		Device-->>Host: ERROR(ETIMEDOUT)
	end

Hinweis zum aktuellen Web-Client (März 2026): Für LS wird initial ACK(64) gesendet, ein dynamisches Nachfüllen ist noch nicht implementiert. Große Verzeichnisse können deshalb in ETIMEDOUT laufen.

8.2 Datei-/Tag-Download (FILE_GET, TAGS_GET)

sequenceDiagram
	participant Host
	participant Device

	Host->>Device: REQUEST(FILE_GET|TAGS_GET, path)
	Device-->>Host: FILE_START(total_size)
	Host->>Device: ACK(credits=128)

	loop chunks
		Device-->>Host: FILE_CHUNK(data)
		Note over Host: Credits dekrementieren
		alt Credits <= 64
			Host->>Device: ACK(credits=128)
		end
	end

	Device-->>Host: FILE_END(crc32)
	Note over Host: CRC prüfen

8.3 Datei-/Tag-Upload (FILE_PUT, TAGS_PUT)

sequenceDiagram
	participant Host
	participant Device

	Host->>Device: REQUEST(FILE_PUT|TAGS_PUT, total_size, path)
	Device-->>Host: ACK(initial credits)

	loop solange Host-Credits > 0
		Host->>Device: FILE_CHUNK(data)
		Note over Device: schreibt Flash, zählt unacked_chunks
		alt ACK_WATERMARK erreicht
			Device-->>Host: ACK(additional credits)
		end
	end

	Host->>Device: FILE_END(crc32)
	alt CRC korrekt
		Device-->>Host: SUCCESS(FILE_PUT|TAGS_PUT)
	else CRC/Write Fehler
		Device-->>Host: ERROR(EBADMSG/EIO/...)
	end

9. Payload-Frames im Detail

9.1 LS_ENTRY (0x41)

uint8_t  type;         // 0x00 file, 0x01 dir
uint32_t size;         // LE
uint8_t  name_length;
char     name[];       // ohne Nullterminierung

9.2 LS_END (0x42)

uint32_t total_entries; // LE

9.3 FILE_START (0x20)

uint32_t total_size; // LE

FILE_GET: komplette Dateigröße.
TAGS_GET: nur Tag-Teil der Datei.

9.4 FILE_CHUNK (0x21)

Payload sind rohe Nutzdatenbytes.

9.5 FILE_END (0x22)

uint32_t crc32; // LE, IEEE CRC32

10. Beispiel-Frames (Hex)

10.1 PROTO_INFO Request/Response

Request:

00 01 00 01

Beispiel-Response:

10 05 00 01 01 00 FD 00

Interpretation:

  • 10: RESPONSE
  • 05 00: Payload 5 Byte
  • 01: PROTO_INFO
  • 01 00: Version 1
  • FD 00: max_chunk_size = 253

10.2 LS Request für /a

00 03 00 40 2F 61

Interpretation:

  • 00: REQUEST
  • 03 00: Payload 3 Byte
  • 40: data_type LS
  • 2F 61: /a

11. Implementierungsnotizen

  • Unknown REQUEST.data_type wird aktuell mit ERROR(EINVAL) beantwortet.
  • Unbekannte/unerwartete frame_type im aktiven Protokollthread führen zu ERROR(EPROTO).
  • Stream-Timeout in Firmware erzeugt aktiv ERROR(ETIMEDOUT).
  • Upload-Timeout im FS-Thread (2 s Inaktivität) bricht intern ab; die Host-Seite sollte eigene Watchdogs haben.