mit IoT-Geräten zuverlässig koppeln
Eine React-Native-App, die mit einem IoT-Gerät über Bluetooth Low Energy spricht, muss Scanning, Kopplung, Berechtigungen und Verbindungsabbrüche robust handhaben. Dieser Artikel zeigt die vollständige Bluetooth-Integration mit react-native-ble-plx, von der GATT-Hierarchie bis zur Reconnect-Logik im Produktivbetrieb.
Inhaltsverzeichnis
- 1. Warum Bluetooth Low Energy die Grundlage für IoT-Kopplung ist
- 2. Bibliothekswahl: react-native-ble-plx vs. react-native-ble-manager
- 3. Berechtigungen: iOS Info.plist und Android-Laufzeitrechte
- 4. Geräte scannen: Filter, Duplikate und Batterieverbrauch
- 5. Verbindungsaufbau: die GATT-Hierarchie verstehen
- 6. Charakteristiken lesen, schreiben und abonnieren
- 7. Verbindungsstatus: Reconnect-Strategien und MTU
- 8. Bluetooth im Hintergrund: iOS vs. Android
- 9. Fehlerbehandlung und Debugging in der Praxis
- 10. Zusammenfassung
- 11. FAQ
1. Warum Bluetooth Low Energy die Grundlage für IoT-Kopplung ist
Jede React Native Bluetooth-Integration mit IoT-Hardware beginnt mit einer Entscheidung, die selten hinterfragt wird: Bluetooth Low Energy (BLE) statt klassisches Bluetooth. BLE wurde gezielt für Geräte mit kleinen Batterien entworfen. Ein Herzfrequenzsensor, ein Smart-Lock oder ein Temperaturfühler senden ihre Daten in kurzen, energieeffizienten Bursts statt einer permanenten Verbindung mit hoher Bandbreite. Für die Bluetooth-Integration in einer mobilen App bedeutet das: Man spricht nicht mit einem Stream, sondern mit einem strukturierten Datenmodell aus Services und Charakteristiken.
Das GATT-Profil (Generic Attribute Profile) ist das Herzstück jeder BLE-Kommunikation. Jedes Peripheriegerät bewirbt einen oder mehrere Services, jeder Service enthält Charakteristiken mit klar definierten UUIDs. Eine Bluetooth-Integration, die dieses Modell ignoriert und versucht, BLE wie eine klassische Socket-Verbindung zu behandeln, scheitert regelmäßig an Timing-Problemen und inkonsistenten Zuständen.
Für React Native gibt es keine native BLE-API im Core, jede React Native Bluetooth-Lösung läuft über eine Community-Bibliothek, die eine native Bridge zu Core Bluetooth (iOS) beziehungsweise der Android BluetoothLeScanner-API bereitstellt. Das macht die Wahl der richtigen Bibliothek zum ersten kritischen Schritt jeder Implementierung.
2. Bibliothekswahl: react-native-ble-plx vs. react-native-ble-manager
Für eine produktionsreife Bluetooth-Integration stehen zwei etablierte Bibliotheken zur Wahl. react-native-ble-plx baut auf RxJS-ähnlichen Observables auf und bietet eine konsistente Promise-basierte API für Scan, Verbindung und Charakteristik-Operationen. react-native-ble-manager arbeitet dagegen mit Events über einen NativeEventEmitter, was mehr manuellen Verdrahtungsaufwand bedeutet, aber etwas näher an der nativen API bleibt.
In der Praxis überzeugt react-native-ble-plx durch bessere Wartbarkeit: Verbindungsstatus, Fehler und Charakteristik-Updates lassen sich als Streams abonnieren, was Reconnect-Logik und Zustandsverwaltung deutlich vereinfacht. react-native-ble-manager punktet dagegen bei sehr alten React-Native-Versionen oder wenn man aus einem bestehenden nativen Bluetooth-Stack migriert und die Event-basierte Denkweise bereits etabliert ist.
Beide Bibliotheken erfordern natives Linking und einen Rebuild der iOS- und Android-Projekte, ein reiner Expo-Go-Workflow ohne Custom Dev Client funktioniert nicht. Für jede neue React Native Bluetooth-App empfiehlt sich daher von Anfang an ein Development Build statt Expo Go.
// hooks/useBleDevice.js
import { useEffect, useRef, useState } from 'react';
import { BleManager } from 'react-native-ble-plx';
const manager = new BleManager();
const IOT_SERVICE_UUID = '0000181a-0000-1000-8000-00805f9b34fb';
export function useBleScan() {
const [devices, setDevices] = useState([]);
const subscription = useRef(null);
useEffect(() => {
// Start scanning, filtered by our IoT service UUID to reduce noise
manager.startDeviceScan([IOT_SERVICE_UUID], { allowDuplicates: false }, (error, device) => {
if (error) {
console.warn('Scan error:', error.message);
return;
}
if (device) {
setDevices((prev) => {
const exists = prev.find((d) => d.id === device.id);
return exists ? prev : [...prev, device];
});
}
});
return () => manager.stopDeviceScan();
}, []);
return devices;
}
3. Berechtigungen: iOS Info.plist und Android-Laufzeitrechte
Ohne korrekte Berechtigungen startet keine Bluetooth-Integration. Auf iOS muss die Info.plist den Schlüssel NSBluetoothAlwaysUsageDescription mit einem klaren, nutzerverständlichen Text enthalten. Fehlt dieser Eintrag, stürzt die App beim ersten Scan-Aufruf kommentarlos ab, ein Verhalten, das viele Entwickler beim ersten Mal überrascht.
Auf Android hat sich das Berechtigungsmodell mit Android 12 fundamental geändert. Vor Android 12 benötigte Scanning die Berechtigung ACCESS_FINE_LOCATION, weil BLE-Beacons theoretisch zur Standortbestimmung genutzt werden können. Seit Android 12 gibt es die granularen Berechtigungen BLUETOOTH_SCAN und BLUETOOTH_CONNECT, die explizit keine Standortdaten implizieren, sofern das Flag neverForLocation gesetzt wird. Eine App, die beide Berechtigungsmodelle bedienen muss, prüft die API-Version zur Laufzeit und fordert die passenden Rechte an.
Die Laufzeitanfrage selbst muss vor jedem Scan-Start erfolgen, nicht nur beim App-Start. Nutzer können Berechtigungen jederzeit in den Systemeinstellungen widerrufen, und eine robuste React Native Bluetooth-Implementierung prüft den aktuellen Berechtigungsstatus, bevor sie startDeviceScan aufruft, statt sich auf einen einmaligen Check beim Onboarding zu verlassen.
# Android 12+ manifest permissions for BLE scanning and connecting
# app/src/main/AndroidManifest.xml additions (shown here as setup commands/comments)
# neverForLocation: scan does not derive location, no location permission needed
# <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
# android:usesPermissionFlags="neverForLocation" />
# <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
# Legacy support for API < 31
# <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
# android:maxSdkVersion="30" />
# Rebuild native projects after adding react-native-ble-plx
npx pod-install ios
cd android && ./gradlew clean && cd ..
npx react-native run-android
4. Geräte scannen: Filter, Duplikate und Batterieverbrauch
Ein ungefilterter Scan liefert in dicht besiedelten Umgebungen dutzende fremde BLE-Geräte, von Kopfhörern bis zu Smart-TVs. Eine gezielte Bluetooth-Integration filtert deshalb bereits beim startDeviceScan-Aufruf nach der Service-UUID des eigenen IoT-Geräts, statt clientseitig nachträglich zu filtern. Das reduziert sowohl die Menge der zu verarbeitenden Advertisement-Pakete als auch den Energieverbrauch des Scans selbst.
Das Flag allowDuplicates steuert, ob wiederholte Advertisement-Pakete desselben Geräts erneut gemeldet werden. Für eine einfache Geräteliste sollte es deaktiviert bleiben, für Anwendungsfälle wie eine Signalstärke-Anzeige (RSSI) in Echtzeit ist es dagegen notwendig, aber kostet spürbar mehr Batterie. Ein Scan-Timeout von 10 bis 15 Sekunden ist in der Praxis ein guter Kompromiss zwischen Trefferquote und Akkuschonung.
Dauerhaftes Scannen im Hintergrund ist auf beiden Plattformen restriktiv geregelt und sollte vermieden werden, wo es geht. Eine gute React Native Bluetooth-App scannt gezielt bei Nutzerinteraktion, etwa beim Öffnen eines Kopplungs-Screens, und stoppt den Scan sofort nach dem Fund des Zielgeräts oder Ablauf des Timeouts.
5. Verbindungsaufbau: die GATT-Hierarchie verstehen
Nach dem Fund eines Geräts folgt der eigentliche Verbindungsaufbau, und hier zeigt sich, wie zentral die GATT-Hierarchie für jede Bluetooth-Integration ist. Ein Peripheriegerät bietet einen oder mehrere Services an, jeder Service gruppiert thematisch zusammengehörige Charakteristiken. Eine Smart-Lock-Hardware bietet zum Beispiel einen "Lock Service" mit einer "Lock State"-Charakteristik und einer "Unlock Command"-Charakteristik.
Der Ablauf ist immer derselbe: connectToDevice, dann discoverAllServicesAndCharacteristics. Erst nach diesem Discovery-Schritt kennt die App die konkreten UUIDs, über die später gelesen und geschrieben wird. Viele Bugs in React Native Bluetooth-Apps entstehen, weil Discovery und der eigentliche Lese-/Schreibzugriff nicht sauber sequenziert werden, etwa weil eine Charakteristik-Operation gestartet wird, bevor Discovery abgeschlossen ist.
UUIDs sollten als Konstanten zentral gepflegt werden, idealerweise passend zur Firmware-Dokumentation des Herstellers. Bei proprietärer IoT-Hardware weichen die UUIDs oft vom Bluetooth-SIG-Standard ab, weshalb die genaue Spezifikation des Geräteherstellers immer die verlässlichste Quelle ist.
6. Charakteristiken lesen, schreiben und abonnieren
Drei Operationen decken den Großteil jeder Bluetooth-Integration ab: Lesen (readCharacteristicForDevice), Schreiben (writeCharacteristicWithResponseForDevice) und Abonnieren von Benachrichtigungen (monitorCharacteristicForDevice). Alle Payloads werden über die native Bridge als Base64-kodierte Strings übertragen, was in JavaScript eine Konvertierung zu Byte-Arrays erfordert, bevor die eigentlichen Nutzdaten interpretierbar sind.
Notify- und Indicate-Charakteristiken sind für IoT-Sensordaten der wichtigste Mechanismus: Statt aktiv zu pollen, abonniert die App eine Charakteristik einmalig und erhält danach automatisch jedes Update, das die Firmware sendet. Das reduziert sowohl Funkverkehr als auch Latenz gegenüber periodischem Polling erheblich und ist der bevorzugte Pattern für Sensoren wie Temperatur oder Batteriestand.
Schreiboperationen mit Response bestätigen den Erhalt durch das Peripheriegerät und sollten für kritische Befehle wie "Tür entriegeln" immer verwendet werden, während Write-Without-Response für hochfrequente, unkritische Daten wie kontinuierliche Steuersignale genutzt wird. Die Wahl der richtigen Schreibart ist ein Detail, das in vielen React Native Bluetooth-Tutorials übergangen wird, aber direkten Einfluss auf die wahrgenommene Zuverlässigkeit der App hat.
{
"device": "SmartLock-IoT-4471",
"services": [
{
"uuid": "0000181a-0000-1000-8000-00805f9b34fb",
"name": "Lock Service",
"characteristics": [
{ "uuid": "00002a6e-0000-1000-8000-00805f9b34fb", "name": "Lock State", "properties": ["read", "notify"] },
{ "uuid": "00002a6f-0000-1000-8000-00805f9b34fb", "name": "Unlock Command", "properties": ["writeWithResponse"] }
]
}
],
"mtu": 185
}
7. Verbindungsstatus: Reconnect-Strategien und MTU
BLE-Verbindungen brechen ab, das ist keine Ausnahme, sondern der Normalfall bei mobiler Nutzung: Der Nutzer entfernt sich vom Gerät, wechselt die App, oder das iOS-System beendet die Verbindung wegen Ressourcenmanagement. Eine belastbare Bluetooth-Integration abonniert deshalb den Verbindungsstatus über onDeviceDisconnected und implementiert eine automatische Wiederverbindung mit exponentiellem Backoff statt sofortiger, endloser Reconnect-Versuche.
Die Standard-MTU (Maximum Transmission Unit) von BLE liegt bei 23 Byte, wovon nur 20 Byte Nutzdaten sind, ein enges Limit für größere Datenpakete wie Firmware-Updates oder Konfigurationsobjekte. Eine MTU-Verhandlung über requestMTU kann dieses Limit auf bis zu 517 Byte anheben, wobei die tatsächlich erreichbare Größe vom Chipsatz des Peripheriegeräts abhängt und immer als Verhandlungsergebnis behandelt werden muss, nicht als garantierter Wert.
Reconnect-Logik sollte den letzten bekannten Geräte-Identifier persistieren, sodass die App bei erneutem Start direkt versucht, sich mit dem zuletzt gekoppelten Gerät zu verbinden, statt einen kompletten neuen Scan zu starten. Das verkürzt die wahrgenommene Wartezeit für Nutzer erheblich und ist ein zentrales Qualitätsmerkmal einer ausgereiften React Native Bluetooth-App.
8. Bluetooth im Hintergrund: iOS vs. Android
Sobald die App in den Hintergrund wechselt, unterscheiden sich iOS und Android fundamental in ihrem Umgang mit bestehenden BLE-Verbindungen. iOS erlaubt das Fortbestehen einer bereits aufgebauten Verbindung im Hintergrund, wenn der Background Mode "bluetooth-central" in den Capabilities aktiviert ist, jedoch werden neue Scans im Hintergrund stark gedrosselt und liefern nur noch gefilterte Advertisements in größeren Zeitabständen.
Android verlangt für dauerhafte BLE-Aktivität im Hintergrund einen Foreground Service mit sichtbarer Benachrichtigung, sobald das Betriebssystem in den Doze-Modus wechselt. Eine Bluetooth-Integration, die versucht, ohne Foreground Service dauerhaft im Hintergrund zu scannen oder zu pollen, wird nach kurzer Zeit vom System gedrosselt oder komplett pausiert.
Für die meisten IoT-Anwendungsfälle ist die pragmatischste Lösung, Bluetooth-Operationen bewusst auf den Vordergrund zu beschränken und beim Zurückkehren in den Vordergrund aktiv zu prüfen, ob die Verbindung noch besteht. Das vermeidet komplexe plattformspezifische Hintergrund-Workarounds und hält die React Native Bluetooth-Logik wartbar.
9. Fehlerbehandlung und Debugging in der Praxis
Android-Gerätehersteller implementieren den Bluetooth-Stack unterschiedlich, und genau dort entstehen die hartnäckigsten Bugs jeder Bluetooth-Integration. Manche OEMs cachen die GATT-Discovery aggressiv, was dazu führt, dass Charakteristik-Änderungen der Firmware nicht erkannt werden, bis die App die Verbindung explizit trennt und neu aufbaut. Ein expliziter refreshDeviceCache-Aufruf vor dem erneuten Discovery-Schritt behebt dieses Problem bei den meisten betroffenen Geräten.
Auf iOS bietet Core Bluetooth einen State-Restoration-Mechanismus, der es der App erlaubt, nach einem vom System erzwungenen Beenden den Verbindungszustand wiederherzustellen. react-native-ble-plx exponiert diesen Mechanismus nur eingeschränkt, weshalb für hochverfügbare Anwendungsfälle oft ein dünner nativer Wrapper notwendig ist, der die restoreIdentifier-Konfiguration direkt gegen CBCentralManager setzt.
| Kriterium | react-native-ble-plx | react-native-ble-manager |
|---|---|---|
| API-Stil | Observable/Promise-basiert | Event-Emitter-basiert |
| Reconnect-Logik | Einfach über Streams | Manuelles Event-Wiring nötig |
| MTU-Verhandlung | Eingebaut (requestMTU) | Eingebaut, weniger dokumentiert |
| Wartungsstand | Aktiv, gute Community | Aktiv, kleinere Community |
| Empfehlung | Neue Projekte | Migration von Legacy-Code |
// Simplified Core Bluetooth state restoration handling (native wrapper)
// Used to recover connection state after the OS terminates the app in background
func centralManager(_ central: CBCentralManager,
willRestoreState dict: [String: Any]) {
// Restore previously connected peripherals from the restoration dictionary
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] {
for peripheral in peripherals {
peripheral.delegate = self
// Re-discover services once restoration completes
peripheral.discoverServices(nil)
}
}
}
// Refreshing the GATT cache on Android to pick up firmware characteristic changes
// Some OEM Bluetooth stacks cache service discovery aggressively
fun refreshGattCache(gatt: BluetoothGatt): Boolean {
return try {
val method = gatt.javaClass.getMethod("refresh")
method.invoke(gatt) as Boolean
} catch (e: Exception) {
Log.w("BleIntegration", "GATT cache refresh failed", e)
false
}
}
Zum Debuggen einer React Native Bluetooth-Verbindung ist ein Werkzeug wie nRF Connect unverzichtbar: Es zeigt die vollständige GATT-Struktur eines Geräts unabhängig von der eigenen App und macht sichtbar, ob ein Problem an der App-Logik oder an der Firmware liegt. Wer diese Referenzimplementierung nicht kennt, sucht Fehler oft an der falschen Stelle im eigenen Code.
Mironsoft
React Native Entwicklung für IoT- und Hardware-Anbindung
Bluetooth-Integration, die im Feld zuverlässig funktioniert?
Wir bauen React-Native-Apps, die stabil mit BLE-Hardware koppeln, inklusive Berechtigungsmodell, Reconnect-Strategien und plattformspezifischem Hintergrundverhalten.
BLE-Architektur
GATT-Modellierung, Bibliothekswahl und native Bridge-Konzeption
Berechtigungs-Audit
iOS- und Android-12-Berechtigungsmodell korrekt implementiert
Reconnect-Logik
Backoff-Strategien und State-Restoration für hohe Verfügbarkeit
10. Zusammenfassung
Eine belastbare React Native Bluetooth-Integration steht und fällt mit dem Verständnis des GATT-Modells: Services, Charakteristiken und deren Eigenschaften bestimmen, wie Lesen, Schreiben und Abonnieren korrekt orchestriert werden. react-native-ble-plx bietet dafür die konsistenteste API, insbesondere für Reconnect-Logik und MTU-Verhandlung. Berechtigungen müssen für iOS und für das granulare Android-12-Modell separat behandelt werden, und Hintergrundverhalten unterscheidet sich fundamental zwischen den Plattformen.
Der größte Hebel für Stabilität liegt in der konsequenten Behandlung von Verbindungsabbrüchen als Normalfall, nicht als Ausnahme. Eine Bluetooth-Integration, die Reconnects mit Backoff, GATT-Cache-Refresh bei problematischen Android-Geräten und State-Restoration auf iOS von Anfang an einplant, liefert im Feld eine spürbar zuverlässigere Nutzererfahrung als eine Implementierung, die BLE wie eine immer verfügbare Verbindung behandelt.
React Native Bluetooth-Integration: Das Wichtigste auf einen Blick
Bibliothek
react-native-ble-plx für neue Projekte, Observable-basierte API vereinfacht Reconnect-Logik erheblich.
Berechtigungen
iOS NSBluetoothAlwaysUsageDescription, Android BLUETOOTH_SCAN/CONNECT ab API 31, ACCESS_FINE_LOCATION davor.
GATT-Hierarchie
Service → Charakteristik → Descriptor. Discovery immer vor Lese-/Schreibzugriff abschließen.
Robustheit
Reconnect mit Backoff, GATT-Cache-Refresh bei Android-OEM-Quirks, State Restoration auf iOS.