====== Bluetooth mit Lyx ======
Die Lyx Bluetooth-Bibliothek ist in zwei Ebenen aufgeteilt: **Control-Plane** (D-Bus / BlueZ: Discovery, Pairing, Adapter) und **Data-Plane** (AF_BLUETOOTH-Sockets: L2CAP, RFCOMM, GATT). Alle 9 Units sind reine Lyx-Implementierungen ohne libusb oder libbluetooth.
→ [[lyx_-_programmiersprache:guides|Guides]] · [[lyx_-_programmiersprache:units:hardware:bluetooth|std.hardware.bluetooth Referenz]]
**Voraussetzung:** Linux mit BlueZ-Daemon + ''AF_BLUETOOTH''-Kernel-Support (x86_64 und ARM64).
----
===== Architektur =====
bluetooth — AF_BLUETOOTH-Konstanten, Socket-Primitiven, Protokollkonstanten
bluetooth_types — BDAddr (int64), sockaddr_rc/l2, Hilfsfunktionen
├─ bluetooth_rfcomm — Classic BT: RFCOMM (serial, SPP)
├─ bluetooth_l2cap — BLE: L2CAP + ATT-Protokoll
│ └─ bluetooth_gattc — BLE GATT-Client (Services, Characteristics, Notifications)
├─ bluetooth_gatts — BLE GATT-Server (eigene BLE-Services anbieten)
├─ bluetooth_dbus — Control-Plane: Discovery, Pairing via BlueZ/D-Bus
├─ bluetooth_ext — BLE Scanner, Advertising, Mesh
└─ bluetooth_ai — Typsichere Charakteristik-Wrapper (WP-8)
^ Protokoll ^ Anwendungsfall ^ Unit ^
| RFCOMM | Klassisches BT, serielle Schnittstelle, GPS, SPP | ''bluetooth_rfcomm'' |
| BLE GATT Client | Sensoren, Wearables, Fitness-Geräte | ''bluetooth_gattc'' |
| BLE GATT Server | Eigene BLE-Peripherie (Sensor-Node) | ''bluetooth_gatts'' |
| Discovery/Pairing | Adapter-Steuerung, Gerätesuche | ''bluetooth_dbus'' |
| BLE Scanner/Advertising | Passive Geräterkennung, Beacons | ''bluetooth_ext'' |
----
===== Bluetooth-Adressen =====
BDAddr wird als ''int64'' gespeichert (48-Bit-MAC in Bits [47:0], Little-Endian).
import std.hardware.bluetooth_types;
// Adresse aus String parsen ("B5:B4:B3:B2:B1:B0"):
var addr: int64 := BTStrToAddr("00:11:22:33:44:55"c);
// Adresse aus Einzelbytes aufbauen:
var addr2: int64 := BTAddrFromBytes(0x55, 0x44, 0x33, 0x22, 0x11, 0x00);
// Adresse in lesbaren String umwandeln:
var outBuf: pchar := StrNew(18);
BTAddrToStr(addr, outBuf as int64);
----
===== Classic Bluetooth: RFCOMM =====
RFCOMM emuliert eine serielle Schnittstelle. Typisch: GPS-Mäuse, Drucker, SPP-Profile, serielle BT-Adapter.
==== Client (Verbindung aufbauen) ====
import std.hardware.bluetooth_rfcomm;
import std.alloc;
fn main(): int64 {
var addr: int64 := BTStrToAddr("00:11:22:33:44:55"c);
// Verbinden auf Kanal 1:
var fd: int64 := RFCommConnect(addr, 1);
if fd < 0 { return -1; }
// Senden:
var msg: pchar := "AT+VERSION\r\n"c;
RFCommSend(fd, msg as int64, 12);
// Empfangen:
var buf: int64 := alloc(256);
var n: int64 := RFCommRecv(fd, buf, 256);
free(buf, 256);
RFCommClose(fd);
return 0;
}
==== Server (auf Verbindungen warten) ====
import std.hardware.bluetooth_rfcomm;
import std.alloc;
var listenFd: int64 := RFCommListen(1, 5); // Kanal 1, Backlog 5
var remoteAddr: int64 := alloc(10); // sockaddr_rc = 10 Bytes
var clientFd: int64 := RFCommAccept(listenFd, remoteAddr);
// clientFd ist jetzt eine Verbindung zum Remote-Gerät
// Sicherheits-Modus setzen (optional):
RFCommSetSecurity(clientFd, BT_LM_ENCRYPT | BT_LM_AUTH);
var buf: int64 := alloc(256);
RFCommSend(clientFd, buf, n);
RFCommClose(clientFd);
RFCommClose(listenFd);
free(remoteAddr, 10);
free(buf, 256);
----
===== BLE: GATT-Client =====
Für BLE-Sensoren und Wearables: Herzfrequenzmesser, Temperatursensoren, Fitness-Tracker.
==== Verbinden und Services entdecken ====
import std.hardware.bluetooth_l2cap;
import std.hardware.bluetooth_gattc;
import std.alloc;
fn main(): int64 {
var addr: int64 := BTStrToAddr("AA:BB:CC:DD:EE:FF"c);
// BLE L2CAP-Verbindung (ATT-Protokoll, PSM 0x1F):
var fd: int64 := L2CapConnect(addr, BT_PSM_ATT, BDADDR_LE_PUBLIC);
if fd < 0 { return -1; }
// MTU verhandeln (517 = BLE 5.0 max):
AttExchangeMtu(fd, 517);
// Alle Services entdecken:
var svcBuf: int64 := alloc(GATT_SVC_RECORD_SIZE * 32);
var svcCount: int64 := GattDiscoverServices(fd, svcBuf, 32);
// Heart-Rate-Service suchen:
var hrIdx: int64 := GattFindServiceByUUID(svcBuf, svcCount, GATT_SVC_HEART_RATE);
var startH: int64 := GattSvcStart(svcBuf, hrIdx);
var endH: int64 := GattSvcEnd(svcBuf, hrIdx);
// Characteristics entdecken:
var charBuf: int64 := alloc(GATT_CHAR_RECORD_SIZE * 64);
var charCount: int64 := GattDiscoverChars(fd, startH, endH, charBuf, 64);
// HR-Characteristic finden:
var hrCharIdx: int64 := GattFindCharByUUID(charBuf, charCount, GATT_UUID_HR_MEASUREMENT);
var valueHandle: int64 := GattCharValue(charBuf, hrCharIdx);
free(svcBuf, GATT_SVC_RECORD_SIZE * 32);
free(charBuf, GATT_CHAR_RECORD_SIZE * 64);
L2CapClose(fd);
return 0;
}
==== Notifications abonnieren ====
import std.hardware.bluetooth_gattc;
import std.alloc;
// CCCD = Client Characteristic Configuration Descriptor
// Typischerweise value_handle + 1 (Konvention, nicht garantiert):
var cccdHandle: int64 := valueHandle + 1;
GattEnableNotification(fd, cccdHandle);
// Notification-Empfangsschleife:
var dataBuf: int64 := alloc(32);
var handleOut: int64 := alloc(8);
var i: int64 := 0;
while i < 10 {
var n: int64 := GattRecvNotification(fd, handleOut, dataBuf, 32);
if n > 0 {
// dataBuf[0] = Flags, dataBuf[1] = BPM (bei HR-Profil)
}
i := i + 1;
}
GattDisableNotification(fd, cccdHandle);
free(dataBuf, 32);
free(handleOut, 8);
==== Typsichere Charakteristik-Wrapper (bluetooth_ai) ====
''bluetooth_ai'' bietet Wrapper-Typen die Richtungsfehler zur Compilezeit verhindern:
import std.hardware.bluetooth_ai;
import std.alloc;
// Characteristic-Handles aus Discovery (charBuf, charCount von oben):
var hrChar: ReadableChar := MakeReadableChar(charBuf, hrCharIdx);
var ctrlChar: WritableChar := MakeWritableChar(charBuf, ctrlCharIdx);
var tempChar: NotifiableChar := MakeNotifiableChar(charBuf, tempIdx, cccdHandle);
// Lesen:
var buf: int64 := alloc(32);
ReadableCharRead(hrChar, fd, buf, 32);
// Schreiben:
var cmd: pchar := "\x00"c;
WritableCharWrite(ctrlChar, fd, cmd as int64, 1);
// Notification abonnieren:
NotifiableCharSubscribe(tempChar, fd);
var notifBuf: int64 := alloc(32);
NotifiableCharRecv(tempChar, fd, notifBuf, 32);
NotifiableCharUnsubscribe(tempChar, fd);
free(buf, 32);
free(notifBuf, 32);
^ Wrapper-Typ ^ Erlaubte Operationen ^
| ''ReadableChar'' | ''ReadableCharRead()'' |
| ''WritableChar'' | ''WritableCharWrite()'', ''WritableCharCmd()'' |
| ''NotifiableChar'' | ''NotifiableCharSubscribe/Unsubscribe/Recv()'' |
| ''RWChar'' | Read + Write |
| ''RWNChar'' | Read + Write + Notify |
==== Fertige Profile: Herzfrequenz und Batterie ====
import std.hardware.bluetooth_ai;
import std.alloc;
// Sensor-Objekte (wrappen alle benötigten Handles):
var hrSensor: HeartRateSensor := HeartRateSensorCreate(fd, hrHandle, ctrlHandle);
var battery: BatteryMonitor := BatteryMonitorCreate(fd, battHandle);
// Herzfrequenz lesen (synchron):
var flags: int64 := alloc(8);
var bpm: int64 := alloc(8);
HeartRateSensorRead(hrSensor, flags, bpm);
// Batteriestand lesen:
var level: int64 := BatteryMonitorRead(battery); // 0–100 %
// Notifications über BleEventStream:
var stream: BleEventStream := HeartRateSensorSubscribe(hrSensor);
BleEventStreamOpen(stream);
var data: int64 := alloc(32);
BleEventStreamNext(stream, data, 32);
BleEventStreamClose(stream);
free(flags, 8); free(bpm, 8); free(data, 32);
----
===== BLE GATT-Server =====
Eigene BLE-Services anbieten (z. B. Sensor-Node, die andere Geräte auslesen können). Registrierung erfolgt vollständig über BlueZ/D-Bus.
import std.hardware.bluetooth_gatts;
import std.hardware.bluetooth_dbus;
// D-Bus-Verbindung zu BlueZ aufbauen:
var dbusFd: int64 := BlueZOpenConnection();
// Service + Characteristics registrieren:
var charDefs: int64 := alloc(GATTS_CHAR_DEF_SIZE * 2);
GattsCharDefUUID(charDefs, 0); // UUID setzen
GattsCharDefProps(charDefs, 0); // Eigenschaften
GattsCharDefSetValue(charDefs, 0, sensorData, dataLen); // Initiale Daten
GattServerRegister(dbusFd, svcUUID, charDefs, 2);
// Eingehende Read/Write-Requests behandeln:
var handleBuf: int64 := alloc(128);
GattServerHandleNext(fd, svcUUID, handleBuf, 128);
// Notification an verbundene Clients senden:
GattServerSendNotification(fd, valueHandle, sensorData, dataLen);
BlueZClose(dbusFd);
----
===== Discovery und Pairing (bluetooth_dbus) =====
import std.hardware.bluetooth_dbus;
import std.alloc;
var fd: int64 := BlueZOpenConnection();
// Discovery starten:
var adapterPath: pchar := "/org/bluez/hci0"c;
BlueZStartDiscovery(fd, adapterPath as int64, StrLen(adapterPath));
// Gefundene Geräte abfragen:
var outBuf: int64 := alloc(4096);
var len: int64 := BlueZGetManagedObjects(fd, outBuf, 4096);
// Einzelnes Gerät verbinden:
var devicePath: pchar := "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF"c;
BlueZConnectDevice(fd, devicePath as int64, StrLen(devicePath));
// Gerät pairen:
BlueZPairDevice(fd, devicePath as int64, StrLen(devicePath));
BlueZStopDiscovery(fd, adapterPath as int64, StrLen(adapterPath));
BlueZClose(fd);
free(outBuf, 4096);
----
===== BLE Scanner und Advertising (bluetooth_ext) =====
import std.hardware.bluetooth_ext;
import std.alloc;
// BLE-Scanner starten und erste Ergebnisse lesen:
var fd: int64 := BlueZOpenConnection();
var adapterPath: pchar := "/org/bluez/hci0"c;
BleScannerStart(fd, adapterPath as int64, StrLen(adapterPath));
var result: int64 := alloc(BLE_SCAN_RESULT_SIZE);
var rc: int64 := BleScannerReadNext(fd, result);
if rc == 0 {
var addrBuf: int64 := BleScanResultAddr(result, 0);
var rssi: int64 := BleScanResultRSSI(result, 0);
}
BleScannerStop(fd, adapterPath as int64, StrLen(adapterPath));
BlueZClose(fd);
free(result, BLE_SCAN_RESULT_SIZE);
----
===== Wichtige Konstanten =====
**Protokolle:**
^ Konstante ^ Wert ^ Bedeutung ^
| ''BTPROTO_RFCOMM'' | 3 | RFCOMM (Classic BT) |
| ''BTPROTO_L2CAP'' | 0 | L2CAP (BLE) |
| ''BT_PSM_ATT'' | 0x1F | ATT-Protokoll-Multiplexer |
| ''BDADDR_BREDR'' | 0 | Klassisches Bluetooth |
| ''BDADDR_LE_PUBLIC'' | 1 | BLE öffentliche Adresse |
| ''BDADDR_LE_RANDOM'' | 2 | BLE zufällige Adresse |
**Standard GATT-UUIDs:**
^ Konstante ^ UUID ^ Bedeutung ^
| ''GATT_SVC_HEART_RATE'' | 0x180D | Heart Rate Service |
| ''GATT_SVC_BATTERY'' | 0x180F | Battery Service |
| ''GATT_UUID_HR_MEASUREMENT'' | 0x2A37 | HR Measurement Characteristic |
| ''GATT_UUID_BATTERY_LEVEL'' | 0x2A19 | Battery Level Characteristic |
| ''GATT_UUID_CCCD'' | 0x2902 | Client Char Config (Notifications) |
----
===== Welche Unit wann? =====
^ Szenario ^ Unit ^
| Klassisches BT zu GPS-Maus, Drucker, seriellem Adapter | ''bluetooth_rfcomm'' |
| BLE-Sensor auslesen (Herzfrequenz, Temperatur) | ''bluetooth_l2cap'' + ''bluetooth_gattc'' |
| Typsichere Charakteristiken (keine Richtungsfehler) | ''bluetooth_ai'' |
| Eigene BLE-Peripherie anbieten | ''bluetooth_gatts'' + ''bluetooth_dbus'' |
| BLE-Geräte in der Umgebung scannen | ''bluetooth_ext'' |
| Adapter-Steuerung, Pairing | ''bluetooth_dbus'' |
Letzte Aktualisierung: 2026-06-08