Web-Bluetooth-Herzfrequenzmonitor mit React
Erstellen Sie mit Web Bluetooth und React einen Echtzeit-Herzfrequenzmonitor. Verbinden Sie sich mit BLE-Geräten, analysieren Sie Binärdaten und visualisieren Sie den Puls – ohne Backend.
Auf dieser Seite
- Wir werden eine React-App erstellen, die sich direkt mit einem Bluetooth-Herzfrequenz-Brustgurt verbindet.
- Kein Backend erforderlich: Wir verwenden die Web Bluetooth API des Browsers.
- Sie erfahren, wie Sie binäre Datenströme mit
DataViewparsen. - Wir werden Verbindungszustände und Berechtigungen elegant behandeln.
Einführung
Jahrelang erforderte das Verbinden von Hardware-Geräten mit Webanwendungen komplexe native Wrapper, spezialisierte Plugins oder einen Backend-Server als Brücke. Hier kommt die Web Bluetooth API ins Spiel.
Sie ermöglicht es Ihrem Browser (speziell Chrome, Edge und Opera), direkt mit Bluetooth Low Energy (BLE)-Geräten zu kommunizieren. Dies eröffnet eine Welt voller Möglichkeiten für HealthTech, IoT und Bildungswerkzeuge, die vollständig auf der Client-Seite laufen.
In diesem Tutorial werden wir einen Echtzeit-Herzfrequenzmonitor erstellen. Wir verbinden einen standardmäßigen BLE-Herzfrequenz-Brustgurt (wie einen Polar H10 oder einen generischen Monitor) mit einer React-Anwendung, dekodieren die binären Daten und visualisieren Ihren Puls.
Das Problem verstehen
Wie BLE funktioniert (Die 30-Sekunden-Version)
Bluetooth Low Energy-Geräte organisieren Daten in Dienste und Merkmale (diese Struktur wird als GATT bezeichnet).
| GATT-Ebene | Zweck | Beispiel-UUID |
|---|---|---|
| Dienst | Datenkategoriegruppierung | Herzfrequenz: 0x180D |
| Merkmal | Spezifischer Datenpunkt | HR-Messung: 0x2A37 |
- Server: Das Gerät (Herzfrequenz-Brustgurt).
- Dienst: Eine Sammlung von Daten (z.B. Herzfrequenzdienst: UUID
0x180D). - Merkmal: Der tatsächliche Datenpunkt (z.B. Herzfrequenzmessung: UUID
0x2A37).
Die Herausforderung: Die vom Gerät kommenden Daten sind kein JSON. Es ist ein roher Binärdatenstrom. Wir müssen Berechtigungen anfordern, verbinden, auf Änderungen lauschen und die Bytes bit-weise maskieren, um menschenlesbare Zahlen zu erhalten.
Web-Bluetooth-Herzfrequenz-Pipeline
Voraussetzungen
- Ein grundlegendes Verständnis von React (Hooks).
- Einen Bluetooth Low Energy-Herzfrequenzmonitor (oder eine Emulator-App wie „nRF Connect" auf Ihrem Telefon).
- Einen Computer mit Bluetooth-Unterstützung.
- Chrome- oder Edge-Browser (Firefox- und Safari-Support ist derzeit eingeschränkt).
Schritt 1: React-Projekt mit Vite initialisieren
Lassen Sie uns ein einfaches React-Projekt initialisieren. Wir verwenden Vite wegen der Geschwindigkeit, aber CRA funktioniert auch.
npm create vite@latest heart-rate-monitor -- --template react
cd heart-rate-monitor
npm install
npm run dev
Wir brauchen keine externen Bibliotheken für Bluetooth – es ist nativ im Browser! Wir werden jedoch standardmäßige React-Hooks verwenden, um unsere Oberfläche zu verwalten.
Input: Leeres Vite React-Projekt Output:
- Entwicklungsserver läuft
- Bereit für Web-Bluetooth-Integration
Schritt 2: BLE-Geräteverbindung implementieren
Sicherheit beinhaltet eine „Benutzergeste". Sie können nicht automatisch beim Laden der Seite nach Bluetooth-Geräten suchen; der Benutzer muss auf eine Schaltfläche klicken.
Was wir tun werden
Wir erstellen eine Komponente, die den vollständigen BLE-Verbindungsfluss behandelt: Geräteerkennung, GATT-Serververbindung, Dienst-/Merkmalszugriff und Benachrichtigungsabonnement.
Input: Benutzer klickt auf „Nach Gerät suchen"-Schaltfläche (erforderliche Geste) Output: Verbundenes BLE-Gerät, das Herzfrequenzdaten streamt
Erstellen Sie eine Datei src/components/HeartRateMonitor.jsx:
import React, { useState, useRef } from 'react';
const HeartRateMonitor = () => {
const [heartRate, setHeartRate] = useState(null);
const [device, setDevice] = useState(null);
const [status, setStatus] = useState('Getrennt');
const [error, setError] = useState('');
// Wir behalten eine Referenz auf das Gerät, um die Trennung zu bereinigen
const deviceRef = useRef(null);
const connectToDevice = async () => {
try {
setStatus('Gerät anfordern...');
// 1. Gerät anfordern
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['heart_rate'] }], // Standard-HR-Dienst-UUID
optionalServices: ['battery_service'] // Falls wir später den Akku wollen
});
deviceRef.current = device;
setDevice(device);
// 2. Mit dem GATT-Server verbinden
setStatus('Mit GATT-Server verbinden...');
const server = await device.gatt.connect();
// 3. Den Herzfrequenz-Dienst abrufen
const service = await server.getPrimaryService('heart_rate');
// 4. Das Merkmal abrufen
const characteristic = await service.getCharacteristic('heart_rate_measurement');
// 5. Benachrichtigungen abonnieren
await characteristic.startNotifications();
characteristic.addEventListener('characteristicvaluechanged', handleHeartRateChange);
// Trennung behandeln
device.addEventListener('gattserverdisconnected', onDisconnected);
setStatus('Verbunden');
setError('');
} catch (err) {
console.error(err);
setError(err.message);
setStatus('Fehler');
}
};
const onDisconnected = () => {
setStatus('Getrennt');
setHeartRate(null);
setDevice(null);
};
const disconnectFromDevice = () => {
if (deviceRef.current) {
deviceRef.current.gatt.disconnect();
}
};
// Platzhalter für Parsing-Logik (Schritt 3)
const handleHeartRateChange = (event) => {
console.log("Daten empfangen", event.target.value);
};
return (
<div className="monitor-container">
<h1>Web Bluetooth HR-Monitor</h1>
{error && <p className="error">{error}</p>}
<p>Status: {status}</p>
{!device ? (
<button onClick={connectToDevice}>Mit Herzfrequenzmonitor verbinden</button>
) : (
<button onClick={disconnectFromDevice}>Trennen</button>
)}
{heartRate && <div className="heart-rate">{heartRate} BPM</div>}
</div>
);
};
export default HeartRateMonitor;
Wie es funktioniert
navigator.bluetooth.requestDevice: Öffnet das native Popup des Browsers. Wir filtern nachheart_rate, weil wir nur relevante Geräte wollen.gatt.connect(): Stellt die Verbindung her.startNotifications(): Teilt dem Gerät mit: „Hey, sende mir Daten, wann immer sich die Herzfrequenz ändert" (normalerweise einmal pro Sekunde).
Schritt 3: Binäre Herzfrequenzdaten parsen
Dies ist der kritischste Teil. Der Bluetooth-Standard für Herzfrequenz (UUID 0x2A37) sendet einen Byte-Strom. Wir müssen sie parsen.
Was wir tun werden
Wir implementieren die binäre Parsing-Logik, um die Herzfrequenz aus der DataView zu extrahieren. Das erste Byte ist das Flags-Byte, das das Datenformat bestimmt.
Input: DataView vom characteristicvaluechanged-Event
Output: Geparste Herzfrequenz als Ganzzahl (BPM)
Aktualisieren Sie die handleHeartRateChange-Funktion in Ihrer Komponente:
const handleHeartRateChange = (event) => {
const value = event.target.value;
// value ist ein DataView-Objekt
// 1. Die Flags parsen (erstes Byte)
const flags = value.getUint8(0);
// 2. Format basierend auf Bit 0 bestimmen
// (flags & 1) prüft, ob das erste Bit 1 ist
const rate16Bits = (flags & 0x1) === 1;
let heartRateMeasurement;
if (rate16Bits) {
// 16-Bit-Herzfrequenz (Offset 1)
heartRateMeasurement = value.getUint16(1, true /* littleEndian */);
} else {
// 8-Bit-Herzfrequenz (Offset 1)
heartRateMeasurement = value.getUint8(1);
}
setHeartRate(heartRateMeasurement);
};
Warum brauchen wir das?
Die meisten Verbraucher-Brustgurte verwenden 8-Bit (uint8). Medizinprodukte oder spezialisierte Geräte könnten jedoch 16-Bit verwenden. Wenn Sie einfach das Byte bei Index 1 lesen, ohne das Flag zu prüfen, wird Ihre App bei High-End-Geräten fehlerhaft.
Schritt 4: Visuelle Verfeinerung hinzufügen
Lassen Sie es wie eine echte Gesundheits-App aussehen. Fügen Sie einen CSS-Puls-Effekt basierend auf dem Verbindungsstatus hinzu.
App.css:
.monitor-container {
display: flex;
flex-direction: column;
align-items: center;
font-family: sans-serif;
margin-top: 50px;
}
.heart-rate {
font-size: 6rem;
font-weight: bold;
color: #e74c3c;
margin: 20px 0;
animation: pulse 1s infinite;
}
button {
padding: 10px 20px;
font-size: 1rem;
background-color: #3498db;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #2980b9;
}
.error {
color: red;
background: #fee;
padding: 10px;
border-radius: 4px;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
Alles zusammenfügen
Hier ist die vollständige, integrierte Komponente.
// src/components/HeartRateMonitor.jsx
import React, { useState, useRef, useEffect } from 'react';
import './App.css'; // Angenommen, Sie haben die Styles hier
const HeartRateMonitor = () => {
const [heartRate, setHeartRate] = useState(null);
const [device, setDevice] = useState(null);
const [status, setStatus] = useState('Getrennt');
const [error, setError] = useState('');
const deviceRef = useRef(null);
// Bei unmount aufräumen
useEffect(() => {
return () => {
if (deviceRef.current) {
deviceRef.current.gatt.disconnect();
}
};
}, []);
const connectToDevice = async () => {
try {
setError('');
setStatus('Suche läuft...');
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: ['heart_rate'] }]
});
deviceRef.current = device;
setDevice(device);
device.addEventListener('gattserverdisconnected', onDisconnected);
setStatus('Verbinde...');
const server = await device.gatt.connect();
const service = await server.getPrimaryService('heart_rate');
const characteristic = await service.getCharacteristic('heart_rate_measurement');
await characteristic.startNotifications();
characteristic.addEventListener('characteristicvaluechanged', handleHeartRateChange);
setStatus('Verbunden');
} catch (err) {
console.error(err);
setStatus('Getrennt');
// „Benutzer abgebrochen"-Fehler ignorieren
if (err.name !== 'NotFoundError') {
setError('Verbindung fehlgeschlagen: ' + err.message);
}
}
};
const onDisconnected = () => {
setStatus('Getrennt');
setHeartRate(null);
setDevice(null);
// Optional: Auto-Reconnect-Logik könnte hier stehen
};
const handleHeartRateChange = (event) => {
const value = event.target.value;
const flags = value.getUint8(0);
// Niedrigstwertiges Bit auf Format prüfen
const rate16Bits = (flags & 0x1) === 1;
let heartRateMeasurement;
if (rate16Bits) {
heartRateMeasurement = value.getUint16(1, true);
} else {
heartRateMeasurement = value.getUint8(1);
}
setHeartRate(heartRateMeasurement);
};
const disconnect = () => {
if (deviceRef.current) {
deviceRef.current.gatt.disconnect();
}
};
return (
<div className="monitor-container">
<h1>❤️ React HR-Monitor</h1>
<div className="status-indicator">
Status: <strong>{status}</strong>
</div>
{error && <div className="error">{error}</div>}
{heartRate !== null && (
<div className="heart-rate">
{heartRate} <span style={{fontSize: '2rem'}}>BPM</span>
</div>
)}
<div className="controls">
{!device ? (
<button onClick={connectToDevice}>Nach Gerät suchen</button>
) : (
<button onClick={disconnect} style={{backgroundColor: '#e74c3c'}}>
Trennen
</button>
)}
</div>
{!navigator.bluetooth && (
<p className="warning">
⚠️ Ihr Browser unterstützt kein Web Bluetooth. Versuchen Sie Chrome oder Edge.
</p>
)}
</div>
);
};
export default HeartRateMonitor;
Häufige Fallstricke & Sicherheit
- HTTPS ist Pflicht: Web Bluetooth funktioniert nur in sicheren Kontexten (HTTPS) oder
localhost. Sie können dies nicht auf einer HTTP-Site bereitstellen. - Benutzergeste: Sie können
requestDevicenicht in einemuseEffectbeim Laden aufrufen. Es muss durch ein Klick-Event ausgelöst werden. - Browser-Unterstützung: Derzeit funktioniert dies am besten in Chrome, Edge und Android (Chrome). iOS Safari unterstützt Web Bluetooth nicht.
- Workaround für iOS: Verwenden Sie die „Bluefy" Web BLE-Browser-App, um Ihre Website auf einem iPhone zu testen.
- Gerätekompatibilität: Stellen Sie sicher, dass Ihr BLE-Gerät nicht gerade über das native Systemmenü Ihres Telefons verbunden ist. BLE-Geräte können normalerweise nur eine Verbindung gleichzeitig aufrechterhalten.
Fazit
Sie haben gerade eine Brücke zwischen der physischen Welt (Ihrem Herzen) und der digitalen Welt (React) gebaut, ohne eine einzige Zeile Backend-Code.
Auswirkungen von Web Bluetooth: Browserbasierte Bluetooth-Konnektivität eliminiert native App-Entwicklungskosten bei gleichzeitiger Aufrechterhaltung der Echtzeit-Datengenauigkeit. Web Bluetooth-APIs ermöglichen direkte Gerät-zu-Browser-Kommunikation mit Latenzzeiten unter einer Sekunde. Herzfrequenz-Monitoring-Apps verzeichnen eine dreifach höhere Benutzerbindung im Vergleich zur manuellen Protokollierung. Kein Backend bedeutet 50% Infrastrukturkostensenkung und schnellere Markteinführung für HealthTech-Prototypen.
Dieses Muster kann erweitert werden, um Lichter zu steuern, Thermometer auszulesen oder mit Bildungsrobotern zu interagieren. Die Web Bluetooth API verwandelt den Browser in ein leistungsstarkes Hardware-Schnittstellenwerkzeug.
Für mehr zum Thema Echtzeit-Gesundheitsdatenvisualisierung erkunden Sie Umgang mit hochfrequenten Sensordaten mit RxJS oder Erstellung von Herzfrequenz-Dashboards mit Echtzeit-Updates.
Nächste Schritte für Sie:
- Versuchen Sie, ein Echtzeit-Diagramm mit
rechartshinzuzufügen, um die Herzfrequenz über die Zeit zu visualisieren. - Lesen Sie das „Energieverbrauch"-Feld aus dem Bluetooth-Paket (es ist oft im gleichen Datenstrom!).
- Experimentieren Sie mit dem Battery Service, um den Akkustand des Geräts anzuzeigen.
Ressourcen
- MDN Web Bluetooth API-Dokumentation
- Bluetooth GATT-Spezifikationen
- Google Chrome Web Bluetooth-Beispiele
- Verwandte Artikel:
- React + RxJS: Hochfrequente Sensordaten verarbeiten - Echtzeit-Datenverarbeitung optimieren
- Schlaf-Hypnogramme mit React & Recharts erstellen - Herzfrequenz über die Zeit visualisieren
FAQ
F: Warum funktioniert Web Bluetooth nicht in Safari oder Firefox?
A: Web Bluetooth wird derzeit hauptsächlich in Chromium-basierten Browsern (Chrome, Edge, Opera) auf Android, ChromeOS und Desktop unterstützt. Firefox hat es hinter einem Flag versteckt, und Safari (iOS) hat keine Unterstützung. Für iOS-Benutzer empfehlen wir die Bluefy-Browser-App oder den Bau einer nativen App.
F: Ist Web Bluetooth sicher? Kann jede Website eine Verbindung zu meinen Geräten herstellen?
A: Web Bluetooth erfordert eine Benutzergeste (Klick auf eine Schaltfläche), um die Suche zu starten, und Benutzer müssen explizit auswählen, mit welchem Gerät sie sich verbinden möchten. Browser zeigen auch eine Verbindungsanzeige. Verwenden Sie jedoch nur HTTPS in der Produktion.
F: Welche anderen Daten kann ich von einem Herzfrequenzmonitor lesen?
A: Viele BLE-Herzfrequenzmonitore übertragen auch:
- Akkustand (Battery Service, UUID 0x180F)
- Energieverbrauch (in Kilojoule, Teil der Herzfrequenzmessung)
- RR-Intervalle (Zeit zwischen Herzschlägen, für HRV-Analyse)
F: Wie gehe ich mit automatischer Wiederverbindung um?
A: Speichern Sie die Geräte-ID nach der ersten Verbindung in localStorage. Versuchen Sie beim Laden der Seite, eine Wiederverbindung mit navigator.bluetooth.getDevices() herzustellen (erfordert Berechtigung und Benutzergeste), gefolgt von gatt.connect(). Behandeln Sie gattserverdisconnected-Events, um Wiederverbindungsversuche auszulösen.
F: Kann ich gleichzeitig mehrere Geräte verbinden?
A: Ja! Erstellen Sie separate Verbindungshandler für jedes Gerät. Browser-Limits variieren, aber 5-10 gleichzeitige BLE-Verbindungen sind typischerweise machbar. Berücksichtigen Sie die Verbindungsstabilität und den Akkuverbrauch beim Entwerfen von Multi-Geräte-Apps.
Diskussionsfragen
- Haben Sie jemals die Web Bluetooth API für ein Projekt verwendet? Was war der schwierigste Teil?
- Welche Sicherheitsbedenken haben Sie bezüglich Websites, die sich mit lokaler Hardware verbinden?
- Wenn Sie ein beliebiges Gerät mit einer Webseite verbinden könnten, was würden Sie bauen?
Haftungsausschluss
Dieser Artikel dient ausschließlich zu Bildungszwecken und stellt keine medizinische Beratung dar. Konsultieren Sie immer einen qualifizierten Gesundheitsdienstleister, bevor Sie Entscheidungen über Ihre Gesundheit, Behandlung oder Pflege treffen. Individuelle Umstände variieren, und was für eine Person angemessen ist, ist möglicherweise nicht für eine andere geeignet.
Wenn Sie einen medizinischen Notfall erleben, rufen Sie sofort 112 (oder Ihre lokale Notrufnummer) an.