React Native BLE PLXでBLE接続・通信の安定運用を実現する即実践アドバイス
- BleManagerインスタンス生成をアプリ起動直後に1回だけ行う複数生成によるメモリ浪費や不具合発生率が10%以上減少 
- スキャン前にBluetoothと位置情報の有効状態を毎回チェック無効時は接続失敗・探索漏れが7割超えるためユーザー体験向上 
- 権限リクエスト時、Android/iOS両対応で必要最小限のみ許可取得余分な権限要求を避けて審査落ちや警告表示リスク10%未満に抑制 
- [通知]機能利用時はデータ受信間隔を3秒以上空ける設計にする(高頻度通知)によるバッテリー消費増加やクラッシュ発生率低減 
React Native BLE PLXとは何か
現代の相互接続された世界では、Bluetooth Low Energy(BLE)デバイスが医療からスマートホームシステムに至るまで、さまざまなアプリケーションで重要な役割を果たしています。React Nativeはクロスプラットフォームのモバイルアプリケーションを構築するための人気フレームワークであり、BLE機能をシームレスに統合する柔軟性を開発者に提供します。このガイドでは、React Native BLE PLXという強力なライブラリを使用してBLEデバイスと接続する方法について探ります。
**React Native BLE PLXの紹介**
React Native BLE PLXは、React Nativeアプリケーション内でBLE周辺機器とやり取りするプロセスを簡素化する包括的なライブラリです。このライブラリは、BLEデバイスのスキャンや接続、通信に関する堅牢なAPIセットを提供しており、多様なBluetooth対応デバイスの能力を活かした魅力的なアプリケーションの構築が可能です。
**React Native BLE PLXの紹介**
React Native BLE PLXは、React Nativeアプリケーション内でBLE周辺機器とやり取りするプロセスを簡素化する包括的なライブラリです。このライブラリは、BLEデバイスのスキャンや接続、通信に関する堅牢なAPIセットを提供しており、多様なBluetooth対応デバイスの能力を活かした魅力的なアプリケーションの構築が可能です。
プロジェクトへのReact Native BLE PLXの設定方法
**React Native BLE PLXのプロジェクトへの設定方法**  
まず、npmまたはyarnを使用してReact Native BLE PLXをインストールします:
または
セットアップに関する問題が発生した場合は、以下のnpmパッケージリンクを参照してください。
[https://www.npmjs.com/package/react-native-ble-plx]
**必要な権限**
React NativeアプリケーションからBLEデバイスを検出するために、以下の権限がリクエストされているか確認してください。
**Android:**
- `PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION`
- `PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT`(API 31以上で必要)
AndroidManifest.xmlにはBluetoothの権限を追加し、
まず、npmまたはyarnを使用してReact Native BLE PLXをインストールします:
npm i react-native-ble-plx  
または
yarn add react-native-ble-plx  
セットアップに関する問題が発生した場合は、以下のnpmパッケージリンクを参照してください。
[https://www.npmjs.com/package/react-native-ble-plx]
**必要な権限**
React NativeアプリケーションからBLEデバイスを検出するために、以下の権限がリクエストされているか確認してください。
**Android:**
- `PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION`
- `PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT`(API 31以上で必要)
AndroidManifest.xmlにはBluetoothの権限を追加し、
<manifest xmlns:android="http://schemas.android.com/apk/res/android">     
    ...     
    <!-- Android >= 12 -->    
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />    
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> 
    <!-- Android < 12 -->    
    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />    
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />    
    <!-- 共通 -->    
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />     
    <!-- アプリケーションが常にBLEを必要とする場合は、この行を追加します。詳細については、        
        https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#permissions      
        を参照してください。-->   
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>      
    ...   
必要な権限の確認と設定
iOSでは、Info.plistファイルにNSBluetoothAlwaysUsageDescriptionを追加する必要があります。これはiOS 13以降の要件です。また、Androidでは位置情報やBluetooth、近接共有の権限を確認することも重要です。BLEマネージャーを起動する前に、位置情報とBluetoothが有効になっているかどうかをチェックしてください。
次に、BLEマネージャーのインスタンスを作成します。このインスタンスはすべての利用可能なAPIへの入り口となりますので、アプリケーションが実行される際に作成するようにしましょう。例えば、静的な参照として保持することができます:
BleManagerは一度だけインスタンス化できるため、一つのインスタンスで全ての操作を行う必要があります。もしBLE機能が不要になった場合は、manager.destroy()メソッドを呼び出してインスタンスを破棄できます。
注意点として、新しいコンポーネントで再レンダリングごとに新しいBleManagerのインスタンスを作成すると、不具合が発生することがあります。同じBleManager上で関数を呼び出し続けないようにしましょう。
次に、BLEマネージャーのインスタンスを作成します。このインスタンスはすべての利用可能なAPIへの入り口となりますので、アプリケーションが実行される際に作成するようにしましょう。例えば、静的な参照として保持することができます:
import { BleManager } from 'react-native-ble-plx';
export const manager = new BleManager();
BleManagerは一度だけインスタンス化できるため、一つのインスタンスで全ての操作を行う必要があります。もしBLE機能が不要になった場合は、manager.destroy()メソッドを呼び出してインスタンスを破棄できます。
注意点として、新しいコンポーネントで再レンダリングごとに新しいBleManagerのインスタンスを作成すると、不具合が発生することがあります。同じBleManager上で関数を呼び出し続けないようにしましょう。
BLEマネージャーの作成方法
アプリケーションが起動すると、BLEスタックはすぐには利用できないため、その状態を確認する必要があります。現在のBluetoothの状態を検出し、Bluetoothの状態変化を追跡するために、`onStateChange()`関数を使用します。以下のように実装できます:
次に、位置情報の状態変化を確認します。こちらも同様に`useEffect`フックを使って実装可能です:
このコードでは、Bluetoothがオンになったときや位置情報サービスが有効になったときに、それぞれ適切な処理を行うことができます。このようにしてデバイス間で円滑な通信を実現し、安全性やパフォーマンスにも配慮した設計となっています。
useEffect(() => {
    const stateChangeListener = manager.onStateChange(state => {
        console.log('onStateChange: ', state);
        if (state === State.PoweredOn) {
            scan();
        }
    });
    return () => {
        stateChangeListener?.remove();
    };
}, [manager]);
次に、位置情報の状態変化を確認します。こちらも同様に`useEffect`フックを使って実装可能です:
useEffect(() => {
    let locationSubscription: EmitterSubscription | null = null;
    
    const addLocationListener = async () => {
        try {
            const subscription = await SystemSetting.addLocationListener(data => {
                console.log('Location: ', data);
                setIsLocationOn(data);
            });
            locationSubscription = subscription;
        } catch (error) {
            console.error('Error adding location listener:', error);
        }
    };
    
    addLocationListener();
}, []);
このコードでは、Bluetoothがオンになったときや位置情報サービスが有効になったときに、それぞれ適切な処理を行うことができます。このようにしてデバイス間で円滑な通信を実現し、安全性やパフォーマンスにも配慮した設計となっています。
Bluetoothと位置情報の状態確認方法
BLEデバイスをスキャンするためには、まずプロジェクトの設定が完了している必要があります。周囲のBLEデバイスを検出するために、スキャンを開始しましょう。接続する前に、デバイスをスキャンしなければなりません。以下は、検出されたデバイスを処理するために一つのコールバック関数のみを登録できるシンプルな関数です。
ここで注意すべき点は、スキャン中に同じデバイスが複数回通知されることがあるということです。ただし、一度接続すると、そのデバイスはもうブロードキャストされず、中央から切断しない限り再びスキャンできません。また、特定の時間経過後には必ずスキャンを停止させる必要があります。そのためには`setTimeout()`関数などを利用してスキャン停止プロセスを管理すると良いでしょう。
次に、BLEデバイスへの接続とサービス及び特性の探索についてですが、一旦デバイスが見つかっても、それはまだ切断状態です。そこで、そのデバイスに接続し、その中に含まれる全てのサービスと特性を発見する必要があります。サービスとは、それぞれ意味ごとに特性がグループ化されたコンテナとして理解できます。
function scan() {
  manager.startDeviceScan(null, null, (error, device) => {
    if (error) {
      // エラーが発生した場合は、スキャンが自動的に停止します。
      return;
    }
    
    // 広告データや他の基準に基づいて探しているデバイスかどうか確認します。
    if (device.name === 'TI BLE Sensor Tag' || device.name === 'SensorTag') {
      // 一つのデバイスだけを探しているので、スキャンを停止します。
      connect();
      manager.stopDeviceScan();
      
      // 接続処理へ進みます。
    }
  });
}
ここで注意すべき点は、スキャン中に同じデバイスが複数回通知されることがあるということです。ただし、一度接続すると、そのデバイスはもうブロードキャストされず、中央から切断しない限り再びスキャンできません。また、特定の時間経過後には必ずスキャンを停止させる必要があります。そのためには`setTimeout()`関数などを利用してスキャン停止プロセスを管理すると良いでしょう。
次に、BLEデバイスへの接続とサービス及び特性の探索についてですが、一旦デバイスが見つかっても、それはまだ切断状態です。そこで、そのデバイスに接続し、その中に含まれる全てのサービスと特性を発見する必要があります。サービスとは、それぞれ意味ごとに特性がグループ化されたコンテナとして理解できます。
BLEデバイスのスキャン方法
特性とは、値を保持するためのコンテナであり、利用可能な機能に基づいて読み取り、書き込み、および監視ができます。たとえば、接続は次のようになります:
connect = async () => {  try {  await manager.connectToDevice(deviceId).then(device=>{console.log('デバイスに接続しました:', device.name);  // 接続されたデバイスを扱うためのロジックを追加します。return device.discoverAllServicesAndCharacteristics();}).catch(error => {    // エラー処理  })} catch (error) {console.error('デバイスへの接続中にエラーが発生しました:', error);}}; BLEデバイスへの接続とサービス・キャラクタリスティックの発見
接続が確立された後、BLEデバイスとのコミュニケーションを行うためには、その特性に対して読み取りや書き込みを実施する必要があります。場合によっては、接続中にデバイスのサービスやキャラクタリスティックセットが変更されることが非常に稀にあります。このような場合、追加のサービス探索が求められることがあります。接続したBLEデバイスと通信する際には、まずそのデバイスのサービスIDおよびキャラクタリスティックIDを確認することが重要です。
以下は特定のサービスUUIDとキャラクタリスティックUUIDを用いて値を読み取る例です。
このように、GATTプロトコルなど基本的なBLE通信の原理について理解しつつ、ペアリング方法や特定のUUIDによるサービス・キャラクタリスティックの識別についても把握しておくと良いでしょう。プラットフォームごとの具体的な実装例やエラーハンドリングも知識として加えることで、一層有益な情報となります。
以下は特定のサービスUUIDとキャラクタリスティックUUIDを用いて値を読み取る例です。
// 特定の読み取りサービスUUIDとキャラクタリスティックUUIDを設定します。
const serviceUUID = 'YOUR_SERVICE_UUID';
const characteristicUUID = 'YOUR_CHARACTERISTIC_UUID';
// キャラクタリスティックから値を読み取る関数
const readCharacteristic = async () => {
    const readData = await manager.ReadCharacteristicForDevice(deviceID, serviceUUID, characteristicUUID)
        .then(readData => {
            console.log("BLEデバイスから読み取ったデータ:", readData);
        })
        .catch(error => {
            console.log("BLEデバイスからのデータ読み取り中にエラーが発生しました:", error);
        });
};
このように、GATTプロトコルなど基本的なBLE通信の原理について理解しつつ、ペアリング方法や特定のUUIDによるサービス・キャラクタリスティックの識別についても把握しておくと良いでしょう。プラットフォームごとの具体的な実装例やエラーハンドリングも知識として加えることで、一層有益な情報となります。
BLEデバイスとの通信方法
デバイスから受信したデータはBase64形式ですので、必要に応じてデータを変換してください。**注意:** BLEデバイスのserviceUUIDおよびcharacteristicUUIDについて理解することが重要です。読み取るための特定の特性がない場合、そのデバイスからデータを読み取ることはできません。**例として、デバイスに値を書き込む方法:** 現在サポートされている2つの方法があります:- WriteWithResponse - WriteWithoutResponse // 具体的な書き込み用サービスUUIDと特性UUIDを仮定します const serviceUUID = 'YOUR_SERVICE_UUID'; const characteristicUUID = 'YOUR_CHARACTERISTIC_UUID'; // 応答ありで特性を書き込む関数```javascriptconst writeCharacteristic = async () => {await manager.writeCharacteristicWithResponseForDevice(deviceID, serviceUUID, characteristicUUID, valueBase64).then (value =>{console.log('書き込み成功: ', value);}).catch(error=>{console.log('書き込み失敗: ', error);})}]
通知機能を利用したデバイスからの応答取得法
デバイスに値を書き込む際、アプリケーションはペリフェラルからデータが正常に受信されたかの確認を期待します。ここで紹介する`writeWithoutResponse`関数は、応答なしで特性に書き込むためのものです。
応答なしで値を書くことは通常より早く行えますが、ペリフェラルがデータを受信した保証はありませんので注意が必要です。**注記:** BLEデバイスに書き込むデータは必ずbase64形式である必要があります。このため、返答するデータもbase64フォーマットに変換してください。react-native-ble-plxではbase64形式のみをサポートしているためです。また、BLEデバイスのserviceUUIDとcharacteristicUUIDについて理解しておくことも重要です。もし書き込む特性が存在しない場合、そのデバイスにはデータを書き込むことができません。
さらに、BLEデバイスからの通知機能を活用すれば、通信の安定性や耐障害性について深い理解を得ることができます。このような情報を通じて、実装時の考慮点や具体的なプロファイルに基づいた応答取得方法も明確になるでしょう。
const writeWithoutResponse = async () => {
    await manager.writeCharacteristicWithoutResponseForDevice(deviceID, serviceUUID, characteristicUUID, valueBase64)
        .then(value => {
            console.log('書き込み成功: ', value);
        })
        .catch(error => {
            console.log('書き込み失敗: ', error);
        });
};
応答なしで値を書くことは通常より早く行えますが、ペリフェラルがデータを受信した保証はありませんので注意が必要です。**注記:** BLEデバイスに書き込むデータは必ずbase64形式である必要があります。このため、返答するデータもbase64フォーマットに変換してください。react-native-ble-plxではbase64形式のみをサポートしているためです。また、BLEデバイスのserviceUUIDとcharacteristicUUIDについて理解しておくことも重要です。もし書き込む特性が存在しない場合、そのデバイスにはデータを書き込むことができません。
さらに、BLEデバイスからの通知機能を活用すれば、通信の安定性や耐障害性について深い理解を得ることができます。このような情報を通じて、実装時の考慮点や具体的なプロファイルに基づいた応答取得方法も明確になるでしょう。
BLEデバイスからの切断手順
初めに、BLEデバイスと接続すると、そのデバイスがデータを送信することがあります。このデータを通知または監視するためには、`monitorCharacteristicForDevice`関数を使用します。**デバイスからの値を監視する例:**
                                // 特定の通知サービスUUIDおよび特性UUIDを仮定const serviceUUID = 'YOUR_SERVICE_UUID';const characteristicUUID = 'YOUR_CHARACTERISTIC_UUID';// デバイスからのデータを監視する関数const monitorCharateristicData= async () => {await manager.monitorCharacteristicForDevice(deviceID, serviceUUID, characteristicUUID).then(value =>{console.log('モニタリング成功: ', value);}).catch(error=>{console.log('モニタリング失敗: ', error);})}// デバイスから切断する関数const disconnectFromDevice = async () => {await manager.cancelDeviceConnection(deviceID).then(device=>{console.log('切断成功: ', device);}).catch(error=>{console.log('切断失敗: ', error);})} 
                             
												 
                                            