BLE入門

BLEを使う製品の開発にかかわることになったものの、プロトコルスタックの全体像がつかめないまま手を動かしている。FW開発者であれば見覚えのある状況でしょう。

この記事では、Classic Bluetoothとの違いからGATT・GAP層の設計、接続パラメータの調整まで、現場でまず押さえておきたい範囲を整理しました。

BLEとは|Classic Bluetoothとの違いとFW開発者が押さえるべき前提

BLEは、Bluetooth 4.0で導入された低消費電力の通信方式です。Classic Bluetoothが音声ストリーミング(A2DP)やファイル転送のために常時接続を保つのに対し、BLEは必要なときだけ起きて数ミリ秒で通信を終えます。

簡単にいえば、呼ばれたら出てきて、用が済んだらすぐ寝る設計と考えてよいでしょう。RFチャンネル数もClassicの79に対して40と少なく、この割り切りが省電力の土台になっています。

また、FW実装ではプロトコルスタックの層構造を避けて通れません。

プロトコルスタックは、下からPHY・リンク層が積み上がり、HCIを越えた先にL2CAP・ATT・GATT・GAPと続きます。ただ、開発者の手が実際に届くのは一番上のGATTとGAPの2層がほとんどです。

GAP(Generic Access Profile)|接続確立までの制御

BLE通信の第一歩は相手を見つけて接続することで、その制御を引き受けるのがGAPです。Peripheralがアドバタイズを飛ばし、Centralがそれを拾って接続要求を返すという流れが動かないと先の実装は全部止まります。

PeripheralとCentralの役割

Peripheralはアドバタイズを出して接続を待つ側、Centralはそれをスキャンして接続を開始する側です。温湿度センサやウェアラブル機器がPeripheral、データを集約するスマートフォンやゲートウェイがCentralに該当します。

FW開発ではPeripheral側を組む場面のほうが多く、アドバタイズ間隔を何ミリ秒にするかで最初に手が止まりがちです。

アドバタイズ設計のポイント

アドバタイズパケットにはAD Typeという構造があり、デバイス名やサービスUUID、TxPowerなどを載せられます。

ただしペイロードは31バイトしかなく、入りきらない情報はScan Responseで追加の31バイトを補う設計が一般的です。

もう一つ判断がいるのがアドバタイズ間隔です。100ミリ秒で約120μA、1秒に伸ばすと約15μAまで消費電流が落ちるといった具合で、発見しやすさと電池寿命が正面からぶつかります。電源設計に直結するだけに、見積もっておくと安心です。

GATT(Generic Attribute Profile)|データ構造の設計

接続が確立した後のデータのやり取りを定義するのがGATTで、FW開発者が最も長く向き合う層です。

GAPが「相手を見つけて手をつなぐ」までの担当だとすれば、GATTは「つないだ手で何を渡すか」を決める役割に当たります。

Service・Characteristic・Descriptorの階層

GATTのデータ構造はService・Characteristic・Descriptorの3層です。Serviceは心拍計やバッテリーといった機能単位のまとまりで、その下にCharacteristicがぶら下がり、個々のデータ項目を受け持っています。

CharacteristicにはRead・Write・Notify・Indicateなどの属性があり、Centralから何ができるかはここで決まります。補足情報を添えるのがDescriptorで、代表格のCCCDはNotify/Indicateの有効化をCentral側が書き込む仕組みです。

この書き込みを拾い損ねてNotifyが飛ばないのは、FW実装で地味に踏みやすい落とし穴です。

UUID設計:標準UUIDとカスタムUUID

UUIDには、Bluetooth SIGが定めた16bit標準UUIDと、ベンダー独自の128bitカスタムUUIDがあります。標準UUIDは心拍計なら0x180Dのように短く、異なるメーカー間でも互換性を保てるのが利点です。

一方、独自機能にはカスタムUUIDを使うことになりますが、128bit分ATTテーブルを消費するため、定義数が増えるとメモリを圧迫します。

BLE SoCのRAMに余裕がない環境では、標準UUIDで代替できないか先に洗い出しておくと、後で詰まる場面が減るはずです。

Notify/Indicate|データ送信方式の選択

GATTで定義したデータをPeripheral側から送り出す方法は、NotifyとIndicateの2種類です。Notifyは送りっぱなし、IndicateはACKを待ちます。

センサ値を連続送信するならNotify、設定変更の確認を取りたい場面ではIndicateと、用途で使い分けるのが基本です。

Notify:ACKなしの高速データ送信

Notifyは、PeripheralからCentralへACKなしでデータを送る方式です。応答を待たない分スループットが出やすく、センサ値の定期送信に向いています。

ただ、Notifyはデフォルトでは無効になっており、Central側がCCCDに0x0001を書き込んで初めて動き出す仕組みです。そのため、この手順を飛ばすと相手には何も届きません。

Indicate:ACK付きの信頼性重視データ送信

Indicateは、CentralからConfirmationが返ってくるまで次のデータを送らない方式です。1回ごとにConfirmationの往復が入るため、Notifyよりもスループットは落ちますが、その分データの到達が保証されます。

ファームウェアアップデートのステータス通知のように、1パケットでも抜けると処理が壊れる場面ではIndicateを選ぶのが安全です。どちらを使うか迷ったら、「届かなかったとき困るかどうか」で切り分ければ判断がぶれにくくなるでしょう。

接続パラメータと省電力設計

BLEの消費電力は接続パラメータの設定一つで桁が変わります。アドバタイズ間隔を前のセクションで扱いましたが、接続後もConnection IntervalやSlave Latencyといったパラメータが電池寿命を左右し続けます。

パラメータをどう調整するかが、省電力設計の勝負どころです。

Connection Interval・Slave Latency・Supervision Timeout

Connection Intervalはコネクションイベントの間隔で、7.5ミリ秒から4秒の範囲を取れます。40ミリ秒で約200μA、1秒なら約15μAと、応答性と電池寿命のどちらを取るかが問われるところです。

Slave Latencyはイベントをスキップできる回数で、送るデータがない間は無線をオフにしておけるため、省電力に直結します。ただし、Centralからの受信は遅れるため、制御コマンドの即応がいる機器では注意が必要です。

Supervision Timeoutは相手からの応答が途絶えてから切断と判定するまでの猶予です。Slave Latencyを大きくしたのにこの値が短いままだと、正常なスキップ中に「切断された」と誤判定する事故が起きます。

パラメータ調整のハマりどころ

iOSとAndroidでConnection Intervalの許容範囲が違う点は最初に踏みやすい罠で、iOSは15ミリ秒以上が推奨です。また、Slave Latencyを上げすぎると、Notifyの応答が体感で遅れて「押しても反応しない」印象になりかねません。

また、Supervision Timeoutは(1+Slave Latency)×Connection Interval×2を超えていなければならず、3つのパラメータはセットで整合を取らないと思わぬ切断やもたつきにつながります。

BLE開発でよくあるトラブルと対策

ペアリングとボンディングの混同は最初に踏みやすいトラブルです。ペアリングは暗号化キーを交換するだけで、ボンディングはその保存まで含みます。保存する手順を省くと再接続が毎回失敗し、「さっきつながっていたのに」という事態になってしまうでしょう。

MTUも見落とされがちで、デフォルト23バイトのままネゴシエーションを省くとデータが細切れになります。

もう一つがスタックベンダーのAPI差異です。スタックごとに、イベント通知の順序やエラーコード体系が異なり、あるSoCで動いたコードが別環境で通らない場面は珍しくありません。そのため、移植前にイベント発火順序を突きあわせておくのが定石です。

組み込みソフトの世界 トップへ戻る