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.

Guides · 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