RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-30中級

Rork アプリで Bluetooth Low Energy(BLE)通信を実装する — react-native-ble-plx で IoT デバイスと連携

Rork で作ったアプリから心拍計・スマートロック・センサーといった BLE デバイスと通信するための実装ガイドです。権限・スキャン・接続・データ送受信・切断処理までを実装例とともに整理します。

Rork515Bluetooth Low EnergyBLEIoTReact Native209

「Rork で作ったアプリから心拍計の値を取りたい」「自作の M5Stack 基板にコマンドを送りたい」— BLE(Bluetooth Low Energy)が絡む相談を受けるたびに、最初の壁になるのは権限まわりとスキャン処理だと感じます。Web の解説記事の多くは Native iOS / Android が前提で、Expo・React Native・Rork の組み合わせで何をどこまで触れるのかが意外と整理されていません。

ライブラリは react-native-ble-plx 一択でいいか

React Native + Expo の世界で BLE を扱うとき、現実的な選択肢は次の二つです。

  • react-native-ble-plx: メンテナンスが活発で、Expo の prebuild と相性が良いです。iOS / Android の双方を一つの API で扱える設計になっています
  • react-native-ble-manager: 古くから使われていますが、Expo SDK のバージョンアップに追随しないことがあります

特別な理由がない限り react-native-ble-plx を選んでおくと、Expo SDK のメジャー更新時に詰む確率が下がります。Rork のビルド構成(Expo prebuild ベース)にもそのまま乗りますので、追加の native コード書き換えはほぼ発生しません。

権限の落とし穴 — Android 12 以降は別物

iOS と Android で必要な権限が大きく異なる点が、最大の落とし穴です。

  • iOS: NSBluetoothAlwaysUsageDescription を Info.plist に必ず記述します。記述がないとアプリ起動直後にクラッシュします
  • Android 12(API 31)以降: BLUETOOTH_SCANBLUETOOTH_CONNECT がランタイム権限化されました。組み合わせによっては ACCESS_FINE_LOCATION も必要です
  • Android 11 以前: BLE スキャンに ACCESS_FINE_LOCATION が必須です

つまり Android では、SDK バージョンで挙動が変わる前提で実装します。Platform.Version で分岐するか、ライブラリ側のヘルパーで権限要求を行うのが安全です。

import { PermissionsAndroid, Platform } from "react-native";
 
// Android 12 以降と 11 以前で必要な権限が変わる
async function requestBlePermissions(): Promise<boolean> {
  if (Platform.OS !== "android") return true;
 
  const apiLevel = Platform.Version as number;
  const perms =
    apiLevel >= 31
      ? [
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
        ]
      : [PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION];
 
  const results = await PermissionsAndroid.requestMultiple(perms);
  return Object.values(results).every(
    (r) => r === PermissionsAndroid.RESULTS.GRANTED,
  );
}
// 期待動作: 権限が全て許可されたとき true、ひとつでも拒否されたとき false を返す

「Android 12 で ACCESS_FINE_LOCATION を要求すると Google Play 審査で弾かれるのでは」と心配されるかもしれませんが、BLE 用途であれば location 権限の利用は許容されています。ストア説明文に用途を明記すれば問題なく審査を通せます。

デバイスをスキャンして接続する流れ

スキャン・接続・解放までの基本フローを一つの hook にまとめると、UI 側の実装がきれいになります。

import { useEffect, useRef, useState } from "react";
import { BleManager, Device } from "react-native-ble-plx";
 
export function useBleScanner(targetName: string) {
  const managerRef = useRef<BleManager | null>(null);
  const [device, setDevice] = useState<Device | null>(null);
  const [error, setError] = useState<string | null>(null);
 
  useEffect(() => {
    const manager = new BleManager();
    managerRef.current = manager;
 
    const start = async () => {
      const ok = await requestBlePermissions();
      if (!ok) {
        setError("Bluetooth/Location 権限が許可されていません");
        return;
      }
 
      manager.startDeviceScan(null, null, (err, scanned) => {
        if (err) {
          setError(err.message);
          return;
        }
        if (scanned?.name === targetName) {
          manager.stopDeviceScan();
          scanned
            .connect()
            .then((d) => d.discoverAllServicesAndCharacteristics())
            .then(setDevice)
            .catch((e) => setError(String(e)));
        }
      });
    };
 
    start();
 
    return () => {
      manager.stopDeviceScan();
      manager.destroy();
    };
  }, [targetName]);
 
  return { device, error };
}
// 期待動作: targetName と一致する BLE デバイスを発見したら接続し、device を state にセットする

ここで一番大事なのは discoverAllServicesAndCharacteristics() を必ず呼ぶことです。これを忘れると Service / Characteristic UUID の検索ができず、後段の readCharacteristicForDeviceService not found エラーが出ます。私はこれで2時間溶かしました。

バイト列と Base64 — データの読み書き

react-native-ble-plx は Characteristic の値を Base64 でやり取りします。生のバイト列ではないので、自前のエンコード/デコードが必要です。

import { Buffer } from "buffer";
 
// 受信: Base64 → バイト配列
const value = await device.readCharacteristicForService(
  serviceUUID,
  characteristicUUID,
);
const bytes = Buffer.from(value.value ?? "", "base64");
console.log("Heart rate:", bytes[1]); // 例: HRP プロファイルの 2 バイト目
 
// 送信: バイト配列 → Base64
const payload = Buffer.from([0x01, 0x02, 0x03]).toString("base64");
await device.writeCharacteristicWithResponseForService(
  serviceUUID,
  characteristicUUID,
  payload,
);
// 期待動作: 受信側 console に Heart rate の数値が出力され、送信は writeCharacteristicWithResponseForService の Promise が resolve する

組込側の仕様書がリトルエンディアンかビッグエンディアンか、何バイト目に何が入るかは、必ず先に確認しておきましょう。私の経験では、ここの読み違いがバグの 7 割を占めます。

切断と再接続の設計

BLE は Wi-Fi よりずっと不安定です。電車や地下に入った瞬間に切れる前提で組まないと、ユーザー体験が壊れます。

  • device.onDisconnected() で切断を検知し、UI に状態を反映します
  • アプリをバックグラウンドに送っても接続を維持したい場合は、iOS では Background Modes の bluetooth-central を有効にします
  • 自動再接続を実装するなら、指数バックオフで最大 30 秒程度に上限を設けて諦める設計が無難です

エラーハンドリングの基本的な考え方は Rork アプリのエラーハンドリングとクラッシュ対策入門 で書いた原則と同じで、「BLE は失敗するもの」として扱うのが結局いちばん安定します。

実機での検証 — シミュレータでは確認できない

BLE は Xcode のシミュレータでも Android の Emulator でも動きません。実機での確認が必須です。

  • スキャンに 10 秒以上かかる場合は、Service UUID をスキャンフィルタに渡して絞り込むとバッテリー消費も抑えられます
  • 端末側のフォーム入力と組み合わせる場合は、Rork アプリのフォームバリデーション完全ガイド で扱った zod スキーマと組み合わせると、ペアリング前に入力ミスを弾けます
  • 画面の縦横切替で接続が切れる挙動が出る場合は Rork で作ったアプリの画面回転・オリエンテーション制御が効かないときの対処法 と合わせて、Configuration 変更時の再接続ロジックを見直すと安定します

ハードウェア絡みのデバッグは「アプリ側 / ファーム側 / Bluetooth スタック」の三層で原因を切り分けると、特定の速度が一段上がります。

全体を振り返って — まずは「読み」だけ動かす

BLE 実装は「権限 → スキャン → 接続 → 読み書き → 切断」と段階が多いので、初手でいきなり全部つなげようとすると挫折しがちです。今日できる最小の一歩としては、心拍計や万歩計のような既存の BLE デバイスを一台用意して、device.readCharacteristicForService で値を1回だけ読み取るところまで書き切ってみてください。それが動けば、書き込みも通知(notify)も同じ枠組みで足し算するだけです。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-07-15
「、」が行頭に落ちた — Rork が組んだ名言カードと日本語の禁則処理
React Native の Text は日本語の行頭禁則を保証しません。実測した違反率、WORD JOINER による事前結合の実装、onTextLayout での自動監査、Rork Max(SwiftUI)との挙動差までまとめました。
開発ツール2026-07-15
Rorkの癒しアプリにスリープタイマーを付けたら、フェードアウトが実際の音とズレた話
Rorkが生成するスリープタイマーは、壁時計のsetIntervalで音量を下げます。数値上は30分でも、実際の音はもっと早く消えたり不自然に途切れたりします。フェードを再生位置で駆動し直す実装と、対数カーブ・停止処理・画面ロック時の限界までを実運用の視点でまとめました。
開発ツール2026-07-14
AI再生成に壊されない設計 — Rork の生成コードと手書きロジックを分ける境界の引き方
Rork へ追加指示を出すたびに手書きのロジックが消える。プロンプトで守るのは対症療法です。生成コードと自分の所有コードを層で分け、再生成が届かない境界を設計で引く方法を、Zustand と service 層の実装例つきでお伝えします。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →