Echtzeitdaten: React Native mit BLE-Herzmonitor verbinden
Lernen Sie, eine React-Native-App mit einem BLE-Herzmonitor zu verbinden: Geräte suchen, Rechte verwalten, Merkmale abonnieren und Daten auswerten.
Auf dieser Seite
In der rasant wachsenden Welt von IoT und Wearable-Technologie ist Bluetooth Low Energy (BLE) zum bevorzugten Protokoll für Kommunikation mit kurzer Reichweite und geringem Energieverbrauch geworden. Für mobile Entwicklerinnen und Entwickler eröffnet das vielfältige Möglichkeiten, von Fitness-Apps, die Ihre Herzfrequenz erfassen, bis hin zu Steuerzentralen für das Smart Home. Wenn Sie sich jemals gefragt haben, wie Ihre Lieblings-Fitness-App wie von selbst mit Ihrem Herzfrequenzmesser synchronisiert, sind Sie hier richtig.
In diesem Tutorial erstellen wir eine React-Native-Anwendung, die sich mit einem BLE-Herzfrequenzmesser verbindet, seinen Datenstrom abonniert und Ihre Herzfrequenz in Echtzeit anzeigt. Wir entmystifizieren die Komplexität von BLE, vom Umgang mit Berechtigungen bis zum Parsen binärer Datenpakete.
Dieser Leitfaden richtet sich an Entwicklerinnen und Entwickler mit grundlegenden Kenntnissen in React Native, die in die spannende Welt der Hardware-Integration eintauchen möchten. Für die praktischen Schritte benötigen Sie einen physischen BLE-Herzfrequenzmesser.
Das Problem verstehen
Die Verbindung mit BLE-Geräten kann knifflig sein. Anders als bei einfachen API-Aufrufen handelt es sich um einen mehrstufigen, asynchronen Prozess: Scannen, Verbinden, Dienste entdecken und Datenströme (Characteristics) abonnieren. Jeder Schritt bringt eigene Herausforderungen mit sich, insbesondere bei den Feinheiten der Berechtigungen unter Android und iOS.
Die Daten von Sensoren wie Herzfrequenzmessern werden häufig nicht in einem menschenlesbaren Format wie JSON gesendet. Stattdessen handelt es sich um einen Bytestrom, der gemäß den offiziellen Bluetooth-GATT-Spezifikationen (Generic Attribute Profile) geparst werden muss. Die standardisierte Characteristic für die Herzfrequenzmessung (Heart Rate Measurement) packt beispielsweise mehrere Informationen in nur wenige Bytes, und wir müssen wissen, wie wir sie entpacken.
Voraussetzungen
- Node.js und npm/yarn sind installiert.
- Eine React-Native-Entwicklungsumgebung ist für iOS und Android eingerichtet.
- Ein physischer BLE-Herzfrequenzmesser (die meisten modernen Fitness-Tracker funktionieren).
- Vertrautheit mit React Hooks.
Wir verwenden die Bibliothek react-native-ble-plx, ein beliebtes und leistungsfähiges Werkzeug für die Interaktion mit BLE-Geräten in React Native.
Schritt 1: Projekt und Berechtigungen einrichten
Zuerst erstellen wir ein neues React-Native-Projekt und installieren die erforderlichen Abhängigkeiten.
Was wir tun
Wir initialisieren ein neues React-Native-Projekt und fügen die Bibliothek react-native-ble-plx hinzu. Anschließend konfigurieren wir die erforderlichen Berechtigungen für Android und iOS, damit unsere App Bluetooth nutzen darf.
Umsetzung
npx react-native init BLEHeartRateApp
cd BLEHeartRateApp
npm install react-native-ble-plx
Für iOS:
Wechseln Sie in Ihr Verzeichnis ios und führen Sie pod install aus. Öffnen Sie anschließend Info.plist und fügen Sie die folgenden Schlüssel hinzu, um Bluetooth-Berechtigungen anzufordern:
<!-- ios/BLEHeartRateApp/Info.plist -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Unsere App verwendet Bluetooth, um sich mit Herzfrequenzmessern zu verbinden.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Unsere App verwendet Bluetooth, um sich mit Herzfrequenzmessern zu verbinden.</string>
Für Android:
Öffnen Sie android/app/src/main/AndroidManifest.xml und fügen Sie die folgenden Berechtigungen hinzu:
<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Unter Android müssen wir diese Berechtigungen außerdem zur Laufzeit anfordern. Das erledigen wir in unserer Anwendungslogik.
So funktioniert es
Diese Berechtigungen verlangen die Betriebssysteme, damit Apps nicht ohne Zustimmung der Nutzerinnen und Nutzer auf Bluetooth-Funktionen zugreifen. react-native-ble-plx verwendet diese zugrunde liegenden nativen Berechtigungen, um mit dem Bluetooth-Modul des Geräts zu interagieren.
Schritt 2: Nach BLE-Geräten suchen
Nachdem unser Projekt eingerichtet ist, beginnen wir mit der Suche nach BLE-Geräten in der Nähe.
Was wir tun
Wir erstellen eine einfache Benutzeroberfläche mit einer Schaltfläche, um die Suche nach BLE-Geräten zu starten. Mit react-native-ble-plx starten wir den Scan und zeigen eine Liste der gefundenen Geräte an. Wir suchen gezielt nach Geräten, die die standardisierte UUID des Herzfrequenzdienstes (0x180D) bekannt geben.
Umsetzung
Hier ist eine einfache Komponente für den Scan:
// src/components/DeviceScanner.js
import React, 'useState', useEffect } from 'react';
import { View, Text, Button, FlatList, PermissionsAndroid, Platform } from 'react-native';
import { BleManager } from 'react-native-ble-plx';
const manager = new BleManager();
// Standard-Bluetooth-Dienst-UUID für die Herzfrequenz
const HEART_RATE_SERVICE_UUID = '0000180d-0000-1000-8000-00805f9b34fb';
const DeviceScanner = () => {
const [devices, setDevices] = useState([]);
const [isScanning, setIsScanning] = useState(false);
const requestBluetoothPermission = async () => {
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Bluetooth-Berechtigung',
message: 'Diese App benötigt Zugriff auf Ihren Standort, um nach BLE-Geräten zu suchen.',
buttonNeutral: 'Später fragen',
buttonNegative: 'Abbrechen',
buttonPositive: 'OK',
},
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
};
const startScan = async () => {
const hasPermission = await requestBluetoothPermission();
if (!hasPermission) {
console.log('Permission denied');
return;
}
setIsScanning(true);
setDevices([]);
manager.startDeviceScan([HEART_RATE_SERVICE_UUID], null, (error, device) => {
if (error) {
console.error(error);
setIsScanning(false);
return;
}
if (device) {
setDevices(prevDevices => {
if (!prevDevices.some(d => d.id === device.id)) {
return [...prevDevices, device];
}
return prevDevices;
});
}
});
// Scan nach 10 Sekunden beenden
setTimeout(() => {
manager.stopDeviceScan();
setIsScanning(false);
}, 10000);
};
return (
<View>
<Button title={isScanning ? 'Suche läuft...' : 'Nach Geräten suchen'} onPress={startScan} disabled={isScanning} />
<FlatList
data={devices}
keyExtractor={item => item.id}
renderItem={({ item }) => <Text>{item.name || 'Unbekanntes Gerät'} ({item.id})</Text>}
/>
</View>
);
};
export default DeviceScanner;
So funktioniert es
Wenn die Schaltfläche „Nach Geräten suchen“ gedrückt wird, fordern wir zuerst die erforderliche Standortberechtigung für Android an. Anschließend beginnt manager.startDeviceScan() mit der Suche nach Geräten in der Nähe. Wir haben HEART_RATE_SERVICE_UUID übergeben, um den Scan zu filtern und nur Geräte zu finden, die genau diesen Dienst bekannt geben. Gefundene Geräte werden unserem Zustand devices hinzugefügt und in einer FlatList angezeigt.
Schritt 3: Verbinden und Daten abonnieren
Sobald wir unseren Herzfrequenzmesser gefunden haben, besteht der nächste Schritt darin, eine Verbindung herzustellen und die Characteristic für die Herzfrequenzmessung zu abonnieren.
Was wir tun
Wir erweitern unsere Komponente, damit sie sich mit einem ausgewählten Gerät verbinden kann. Nach der Verbindung entdecken wir seine Dienste und Characteristics. Anschließend suchen wir die Characteristic für die Herzfrequenzmessung (0x2A37) und abonnieren ihre Benachrichtigungen, um Echtzeitdaten zu empfangen.
Umsetzung
Fügen wir Logik für Verbindung und Abonnement hinzu:
// ... (Importe und vorhandener Code)
// Standard-Bluetooth-Characteristic-UUID für die Herzfrequenzmessung
const HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = '00002a37-0000-1000-8000-00805f9b34fb';
const HeartRateMonitor = () => {
// ... (vorhandener Zustand)
const [connectedDevice, setConnectedDevice] = useState(null);
const [heartRate, setHeartRate] = useState(0);
// ... (Logik für Berechtigung und Scan)
const connectToDevice = async (device) => {
try {
manager.stopDeviceScan();
const connected = await device.connect();
setConnectedDevice(connected);
await connected.discoverAllServicesAndCharacteristics();
// Herzfrequenz-Benachrichtigungen abonnieren
monitorHeartRate(connected);
} catch (error) {
console.error('Connection error:', error);
}
};
const monitorHeartRate = (device) => {
device.monitorCharacteristicForService(
HEART_RATE_SERVICE_UUID,
HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID,
(error, characteristic) => {
if (error) {
console.error('Monitoring error:', error);
return;
}
if (characteristic?.value) {
const buffer = Buffer.from(characteristic.value, 'base64');
const heartRateValue = parseHeartRate(buffer);
setHeartRate(heartRateValue);
}
}
);
};
// ... (return-Anweisung mit FlatList und Button)
// FlatList so ändern, dass die Einträge antippbar sind
renderItem={({ item }) => (
<Button title={`Mit ${item.name || 'Unbekannt'} verbinden`} onPress={() => connectToDevice(item)} />
)}
// Herzfrequenz anzeigen
{connectedDevice && <Text>Herzfrequenz: {heartRate} BPM</Text>}
};
So funktioniert es
Wenn ein Gerät aus der Liste angetippt wird, wird connectToDevice aufgerufen. Die Funktion beendet zuerst den Scan und verbindet sich anschließend über device.connect() mit dem Gerät. Nach erfolgreicher Verbindung ruft discoverAllServicesAndCharacteristics() alle Dienste und Characteristics ab, die das Gerät anbietet. Schließlich richtet monitorCharacteristicForService einen Listener für die Characteristic der Herzfrequenzmessung ein. Immer wenn der Herzfrequenzmesser einen neuen Wert hat, wird dieser Listener mit den neuen Daten ausgelöst.
Schritt 4: Herzfrequenzdaten parsen
Die Daten, die wir von der Characteristic erhalten, liegen in einem rohen Binärformat vor. Wir müssen sie parsen, um den tatsächlichen Herzfrequenzwert zu extrahieren.
Was wir tun
Wir erstellen eine Funktion, die das vom Herzfrequenzmesser empfangene Byte-Array gemäß der Bluetooth-Spezifikation interpretiert. Das erste Byte des Datenpakets enthält Flags, die uns das Format des Herzfrequenzwerts mitteilen.
Umsetzung
Hier ist die Funktion zum Parsen der Daten:
import { Buffer } from 'buffer'; // buffer muss importiert werden
const parseHeartRate = (buffer) => {
const flags = buffer.readUInt8(0);
// Prüfen, ob die Herzfrequenz im 16-Bit-Format vorliegt (Bit 0 der Flags)
const is16Bit = (flags & 0x01) !== 0;
if (is16Bit) {
return buffer.readUInt16LE(1);
} else {
return buffer.readUInt8(1);
}
};
So funktioniert es
Gemäß der offiziellen Bluetooth-Spezifikation für die Characteristic der Herzfrequenzmessung ist das erste Byte ein Feld „Flags“.
- Bit 0: Ist dieses Bit 0, ist der Herzfrequenzwert ein einzelnes Byte (UINT8). Ist es 1, umfasst der Wert zwei Bytes (UINT16).
- Weitere Bits: Zeigen weitere Daten an, etwa den Sensorkontaktstatus und den Energieverbrauch, die wir in diesem Tutorial ignorieren.
Unsere Funktion parseHeartRate liest das erste Byte, um dieses Flag zu prüfen, und liest anschließend entsprechend das oder die folgenden Bytes, um den Herzfrequenzwert zu erhalten.
Alles zusammenfügen
Hier ist die vollständige Datei App.js mit allen integrierten Komponenten:
// App.js
import React, { useState, useEffect } from 'react';
import {
SafeAreaView,
View,
Text,
Button,
FlatList,
PermissionsAndroid,
Platform,
StyleSheet,
} from 'react-native';
import { BleManager } from 'react-native-ble-plx';
import { Buffer } from 'buffer';
const manager = new BleManager();
const HEART_RATE_SERVICE_UUID = '0000180d-0000-1000-8000-00805f9b34fb';
const HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = '00002a37-0000-1000-8000-00805f9b34fb';
const App = () => {
const [devices, setDevices] = useState([]);
const [isScanning, setIsScanning] = useState(false);
const [connectedDevice, setConnectedDevice] = useState(null);
const [heartRate, setHeartRate] = useState(0);
const requestBluetoothPermission = async () => {
// Die Berechtigungslogik bleibt unverändert...
};
const startScan = async () => {
// Die Scan-Logik bleibt unverändert...
};
const connectToDevice = async (device) => {
try {
manager.stopDeviceScan();
const connected = await device.connect();
setConnectedDevice(connected);
await connected.discoverAllServicesAndCharacteristics();
monitorHeartRate(connected);
} catch (error) {
console.error('Connection error:', error);
}
};
const parseHeartRate = (buffer) => {
const flags = buffer.readUInt8(0);
const is16Bit = (flags & 0x01) !== 0;
return is16Bit ? buffer.readUInt16LE(1) : buffer.readUInt8(1);
};
const monitorHeartRate = (device) => {
device.monitorCharacteristicForService(
HEART_RATE_SERVICE_UUID,
HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID,
(error, characteristic) => {
if (error) {
console.error('Monitoring error:', error);
return;
}
if (characteristic?.value) {
const buffer = Buffer.from(characteristic.value, 'base64');
const heartRateValue = parseHeartRate(buffer);
setHeartRate(heartRateValue);
}
},
);
};
const disconnectDevice = () => {
if (connectedDevice) {
connectedDevice.cancelConnection();
setConnectedDevice(null);
setHeartRate(0);
}
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>BLE-Herzfrequenzmonitor</Text>
</View>
{connectedDevice ? (
<View style={styles.heartRateContainer}>
<Text style={styles.heartRateText}>{heartRate} BPM</Text>
<Button title="Trennen" onPress={disconnectDevice} />
</View>
) : (
<>
<Button title={isScanning ? 'Suche läuft...' : 'Nach Geräten suchen'} onPress={startScan} disabled={isScanning} />
<FlatList
data={devices}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View style={styles.deviceItem}>
<Text>{item.name || 'Unbekanntes Gerät'}</Text>
<Button title="Verbinden" onPress={() => connectToDevice(item)} />
</View>
)}
/>
</>
)}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: { flex: 1, margin: 20 },
header: { marginBottom: 20 },
title: { fontSize: 24, fontWeight: 'bold' },
heartRateContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
heartRateText: { fontSize: 48, fontWeight: 'bold', marginBottom: 20 },
deviceItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 10, borderBottomWidth: 1, borderBottomColor: '#ccc' },
});
export default App;
Best Practices für die Sicherheit
- Fordern Sie Berechtigungen stets zum passenden Zeitpunkt an. Fragen Sie nicht im Moment des App-Starts nach Bluetooth-Berechtigungen; warten Sie, bis die Nutzerin oder der Nutzer eine Funktion verwenden möchte, die sie erfordert.
- Behandeln Sie Verbindungsfehler elegant. BLE-Verbindungen können instabil sein. Implementieren Sie Logik für unerwartete Verbindungsabbrüche und geben Sie den Nutzerinnen und Nutzern klares Feedback.
- Validieren Sie eingehende Daten. Auch wenn wir dem Herzfrequenzmesser vertrauen, sollten Sie in anderen Anwendungen die von einem BLE-Peripheriegerät empfangenen Daten stets validieren, um Abstürze oder unerwartetes Verhalten zu vermeiden.
Fazit
Sie haben nun eine voll funktionsfähige React-Native-App erstellt, die sich mit einem BLE-Herzfrequenzmesser verbinden und Echtzeitdaten anzeigen kann. Sie haben einige der anspruchsvollsten Aspekte der BLE-Entwicklung gemeistert, darunter Berechtigungen, Scannen, Verbinden und das Parsen binärer Daten. Diese Grundlage öffnet die Tür zu unzähligen IoT-Projekten, von Fitness-Trackern bis zu Smart-Home-Steuerungen.
Als Nächstes könnten Sie Funktionen hinzufügen, etwa die Herzfrequenz im Zeitverlauf grafisch darzustellen, Sitzungen zu speichern oder sich mit anderen Arten von BLE-Sensoren zu verbinden.
Ressourcen
Kurzreferenz
| Aspekt | Wichtige Informationen |
|---|---|
| Thema | Entwicklung |
| Relevanz | Patientenaufklärung und Bewusstsein |
| Maßnahme | Besprechen Sie dies mit Ihrer Ärztin oder Ihrem Arzt |
| Häufigkeit | Wie von Ihrem Behandlungsteam empfohlen |
| Evidenz | Basierend auf aktuellen klinischen Leitlinien |
Häufig gestellte Fragen
F: Wie weiß ich, ob das auf mich zutrifft? A: Besprechen Sie Ihre persönliche und familiäre Krankengeschichte mit Ihrer Ärztin oder Ihrem Arzt, um Ihre individuellen Risikofaktoren zu beurteilen und geeignete nächste Schritte festzulegen.
F: Was sollte ich meine Ärztin oder meinen Arzt fragen? A: Gute Fragen sind: „Was bedeuten meine Ergebnisse?“, „Welche Möglichkeiten habe ich?“, „Welche Lebensstiländerungen würden helfen?“ und „Wann sollte ich zur Nachkontrolle kommen?“
F: Gibt es Lebensstiländerungen, die helfen können? A: Die meisten Erkrankungen profitieren von grundlegenden Maßnahmen: ausgewogene Ernährung, regelmäßige körperliche Aktivität (150+ Minuten/Woche), erholsamer Schlaf (7–9 Stunden), Stressbewältigung und Verzicht auf Tabak.
Medizinischer Haftungsausschluss
Dieser Artikel dient ausschließlich der Aufklärung und stellt keine medizinische Beratung dar. Konsultieren Sie stets eine qualifizierte medizinische Fachperson, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Versorgung treffen. Individuelle Umstände unterscheiden sich, und was für eine Person angemessen ist, muss für eine andere nicht angemessen sein.
Wenn Sie einen medizinischen Notfall erleben, rufen Sie sofort 911 (oder Ihre örtliche Notrufnummer) an.