リアルタイムヘルスデータ: React NativeとBLE心拍モニターの接続
React NativeアプリとBLE心拍モニターを接続する高度なチュートリアル。デバイスのスキャン、権限の処理、特性のサブスクライブ、生バイナリデータの解析を学び、リアルタイムのフィットネストラッキングを実現します。
このページの内容
はじめに
IoTとウェアラブル技術の急速な成長に伴い、Bluetooth Low Energy(BLE)は低消費電力の近距離通信における主要なプロトコルとなりました。モバイル開発者にとって、心拍数を記録するフィットネスアプリからスマートホームコントロールセンターまで、無限の可能性が広がっています。お気に入りのフィットネスアプリが心拍数モニターとシームレスに同期する仕組みに興味をお持ちの方は、適切な場所にたどり着きました。
本チュートリアルでは、BLE心拍数モニターに接続し、データストリームを購読し、リアルタイムで心拍数を表示するReact Nativeアプリケーションを構築します。パーミッション処理からバイナリデータパケットの解析まで、BLEの複雑さを紐解いていきます。
本ガイドは、React Nativeの基本的な理解を持ち、ハードウェア統合の興味深い世界に飛び込みたい開発者向けです。実践的な手順に沿って進めるには、物理的なBLE心拍数モニターが必要です。
問題の理解
BLEデバイスへの接続は厄介です。単純なAPI呼び出しとは異なり、スキャン、接続、サービス検出、データストリーム( характеристики)への購読というマルチステップで非同期なプロセスを経ます。各ステップには、特にAndroidとiOSのパーミッションの詳細に対処する場合、独自の課題が伴います。
心拍数モニターのようなセンサーからのデータは、JSONのような人間が読める形式で送信されないことがよくあります。代わりに、Bluetoothの公式GATT(Generic Attribute Profile)仕様に従って解析する必要があるバイトストリームです。たとえば、标准的な心拍数測定 характеристикиはわずか数バイトに複数の情報をパックしており、その解开方法を知る必要があります。
前提条件
- Node.jsとnpm/yarnがインストール済みであること。
- iOSとAndroidの両方でReact Native開発環境がセットアップ済みであること。
- 物理的なBLE心拍数モニター(最新のフィットネストラッカーのほとんどが作動します)。
- Reactフックへの精通していること。
本チュートリアルでは、React NativeでBLEデバイスと対話するための人気かつ強力なツールであるreact-native-ble-plxライブラリを使用します。
ステップ1:プロジェクトとパーミッションのセットアップ
まず、新しいReact Nativeプロジェクトを作成し、必要な依存関係をインストールしましょう。
何をしているのか
新しいReact Nativeプロジェクトを初期化し、react-native-ble-plxライブラリを追加します。次に、アプリがBluetoothを使用できるようにするために、iOSとAndroidの両方で必要なパーミッションを構成します。
実装
npx react-native init BLEHeartRateApp
cd BLEHeartRateApp
npm install react-native-ble-plx
iOSの場合:
iosディレクトリに移動し、pod installを実行します。次に、Info.plistを開き、Bluetoothパーミッションを要求するために以下のキーを追加します:
<!-- ios/BLEHeartRateApp/Info.plist -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>このアプリはまだ使用していません</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>このアプリはまだ使用していません</string>
Androidの場合:
android/app/src/main/AndroidManifest.xmlを開き、以下のパーミッションを追加します:
<!-- 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" />
Androidでは、実行時にこれらのパーミッションを要求する必要もあります。アプリロジック内でこれを処理します。
動作の仕組み
これらのパーミッションは、アプリがユーザーの同意なしにユーザーのBluetooth機能にアクセス하지 않도록するために、オペレーティングシステムによって要求されます。react-native-ble-plxは、デバイスのBluetoothモジュールと対話するためにこれらの基盤となるネイティブパーミッションを使用します。
ステップ2:BLEデバイスのスキャン
プロジェクトのセットアップが完了したので、近隣のBLEデバイスのスキャンを開始しましょう。
何をしているのか
BLEデバイスをスキャンするボタンを備えたシンプルなUIを作成します。react-native-ble-plxを使用してスキャンを開始し、検出されたデバイスのリストを表示します。特に、心拍数サービスUUID(0x180D)をアドバタイズしているデバイスを検索します。
実装
スキャンを処理する基本的なコンポーネントを以下に示します:
// 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();
// 心拍数の標準BluetoothサービスUUID
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 Permission',
message: 'This app needs access to your location to scan for BLE devices.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
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;
});
}
});
// 10秒後にスキャンを停止
setTimeout(() => {
manager.stopDeviceScan();
setIsScanning(false);
}, 10000);
};
return (
<View>
<Button title={isScanning ? 'Scanning...' : 'Scan for Devices'} onPress={startScan} disabled={isScanning} />
<FlatList
data={devices}
keyExtractor={item => item.id}
renderItem={({ item }) => <Text>{item.name || 'Unknown Device'} ({item.id})</Text>}
/>
</View>
);
};
export default DeviceScanner;
動作の仕組み
「Scan for Devices」ボタンが押されると、まずAndroidの位置情報パーミッションを要求します。次に、manager.startDeviceScan()が近隣のデバイスの検索を開始します。HEART_RATE_SERVICE_UUIDを提供してスキャンをフィルタリングし、この特定のサービスをアドバタイズしているデバイスのみを検出するようにしました。検出されたデバイスはdevicesステートに追加され、FlatListに表示されます。
ステップ3:接続とデータ購読
心拍数モニターを見つけたら、次のステップはそれに接続し、心拍数測定 характеристикиを購読することです。
何をしているのか
選択したデバイスへの接続を処理するようにコンポーネントを拡張します。接続後、そのサービスと特性を検出します。最後に、心拍数測定 характеристики(0x2A37)を見つけ、リアルタイムデータを受け取るためにその通知を購読します。
実装
接続と購読ロジックを追加しましょう:
// ... (インポートと既存のコード)
// 心拍数測定の標準Bluetooth характеристикиUUID
const HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = '00002a37-0000-1000-8000-00805f9b34fb';
const HeartRateMonitor = () => {
// ... (既存のステート)
const [connectedDevice, setConnectedDevice] = useState(null);
const [heartRate, setHeartRate] = useState(0);
// ... (パーミッションとスキャンロジック)
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 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);
}
}
);
};
// ... (FlatListとボタンを備えた返り値ステートメント)
// FlatListを押し下げ可能に変更
renderItem={({ item }) => (
<Button title={`Connect to ${item.name || 'Unknown'}`} onPress={() => connectToDevice(item)} />
)}
// 心拍数を表示
{connectedDevice && <Text>Heart Rate: {heartRate} BPM</Text>}
};
動作の仕組み
リスト内のデバイスがタップされると、connectToDeviceが呼び出されます。まずスキャンを停止し、device.connect()を使用してデバイスに接続します。接続が成功すると、discoverAllServicesAndCharacteristics()がデバイスが提供するすべてのサービスと特性を取得します。最後に、monitorCharacteristicForServiceが心拍数測定 характеристикиのリスナーを設定します。心拍数モニターが新しい測定値を持つたびに、このリスナーは新しいデータとともに起動されます。
ステップ4:心拍数データの解析
характеристикиから受け取るデータは、生のバイナリ形式です。実際の心拍数値を抽出するために解析する必要があります。
何をしているのか
Bluetooth仕様に従って、心拍数モニターから受け取ったバイト配列を解釈する関数を作成します。データパケットの最初のバイトには、心拍数値の形式を示すフラグが含まれています。
実装
データ解析関数を以下に示します:
import { Buffer } from 'buffer'; // bufferのインポートが必要
const parseHeartRate = (buffer) => {
const flags = buffer.readUInt8(0);
// 心拍数が16ビット形式かどうかを確認(フラグのビット0)
const is16Bit = (flags & 0x01) !== 0;
if (is16Bit) {
return buffer.readUInt16LE(1);
} else {
return buffer.readUInt8(1);
}
};
動作の仕組み
心拍数測定 характеристикиの公式Bluetooth仕様によると、最初のバイトは「フラグ」フィールドです。
- ビット0: このビットが0の場合、心拍数値は単一バイト(UINT8)です。1の場合、値は2バイト(UINT16)です。
- その他のビット: センサー接触ステータスや消費エネルギーなどのその他のデータを示しますが、本チュートリアルでは無視しています。
parseHeartRate関数は最初のバイトを読んでこのフラグを確認し、心拍数値を取得するために後続のバイトをそれに応じて読み取ります。
すべてをまとめる
すべてのコンポーネントが統合された完全なApp.jsを以下に示します:
// 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 () => {
// パーミッションロジックはそのまま...
};
const startScan = async () => {
// スキャンロジックはそのまま...
};
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 Heart Rate Monitor</Text>
</View>
{connectedDevice ? (
<View style={styles.heartRateContainer}>
<Text style={styles.heartRateText}>{heartRate} BPM</Text>
<Button title="Disconnect" onPress={disconnectDevice} />
</View>
) : (
<>
<Button title={isScanning ? 'Scanning...' : 'Scan for Devices'} onPress={startScan} disabled={isScanning} />
<FlatList
data={devices}
keyExtractor={item => item.id}
renderItem={({ item }) => (
<View style={styles.deviceItem}>
<Text>{item.name || 'Unknown Device'}</Text>
<Button title="Connect" 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;
セキュリティのベストプラクティス
- 適切なタイミングで常にパーミッションを要求してください。 アプリが起動した瞬間にBluetoothパーミッションを要求しないでください。ユーザーがそれが必要な機能のいずれかを使用しようとするまで待ちましょう。
- 接続エラーを適切に処理してください。 BLE接続は不安定になることがあります。予期しない切断を処理し、ユーザーに明確なフィードバックを提供するロジックを実装してください。
- 入力データを検証してください。 心拍数モニターを信頼していますが、他のアプリケーションでは、BLEペリフェラルから受け取ったデータを常に検証して、クラッシュや予期しない動作を防ぐべきです。
結論
BLE心拍数モニターに接続し、リアルタイムデータを表示できる完全な機能を備えたReact Nativeアプリを構築しました。パーミッション、スキャン、接続、バイナリデータの解析など、BLE開発における最もchallengingな側面に取り組みました。この基盤は、フィットネストラッカーからスマートホームコントローラーまで、無数のIoTプロジェクトへの扉を開きます。
次のステップとして、心拍数を時間的にグラフ化する、セッションを保存する、または他の種類のBLEセンサーに接続するなどの機能を追加することが考えられます。
参考資料
クイックリファレンス
| 項目 | 重要な情報 |
|---|---|
| トピック | 開発 |
| 関連性 | 患者教育と意識向上 |
| 行動 | 医療チームと相談してください |
| 頻度 | ケアチーム推奨通り |
| 証拠 | 現在の臨床ガイドラインに基づく |
よくある質問
Q: これが自分に当てはまるかどうかはどうすればわかりますか? A: 個人的な病史と家族歴について医療提供者にご相談ください。個々のリスク因子を評価し、適切な次のステップを決定帮助你。
Q: 医師に何を聞くべきですか? A: 良い質問例としては、「私の結果はどういう意味ですか?」「私のオプションは何ですか?」「どのような生活習慣の改善が役立ちますか?」「いつフォローアップすべきですか?」があります。
Q: 生活を改善するための変化はありますか? A: ほとんどの健康状態は、基本的な対策から恩恵を受けます:バランスの取れた栄養、定期的な運動(週150分以上)、質の良い睡眠(7〜9時間)、ストレス管理、タバコの回避です。
医療免責事項
この記事は教育目的のみを目的としており、医学的なアドバイスではありません。健康、治療、またはケアに関する決定を行う前は、常に資格のある医療提供者と相談してください。個人の状況是不同的であり、ある人适当的并不意味着他の人也适当異なります。
如果您遇到医疗紧急情况,请立即拨打911(或您当地的紧急电话号码)。