====== 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