わふうの人が書いてます。

iOSアプリケーション開発、BLEのファームウェアとハードウェア試作のフリーランスエンジニア。

iBeaconの解説

このページの解説はKindle本にまとめました。

iBeacon関連企業リスト

モバイルに近接技術を利用した会社が、iBeacon関連でサービス提供をいろいろと開始しています。iBeacon関連企業のリストです。

参考サイトの調査

ドキュメント

位置情報とマッププログラミングガイド (Appleの日本語ドキュメントpdf) https://developer.apple.com/jp/devcenter/ios/library/documentation/LocationAwarenessPG.pdf

WWDC 2013, What’s New in Core Location

WWDC 2012 Staying on Track with Location Services

Passbook

http://passkit.com/ Passbook生成を提供している。

https://developer.apple.com/passbook/

ブログ

Location and Maps Programming Guide

新たな領域観測サービス iBeacon を使ってみる

Github

RadiusNetworks / android-ibeacon-service
Android4.3のBLEのライブラリで、iBeaconの信号を受信するサンプルコード。

daher-alfawares / iBeacon
iPadで動作するビーコンとその表示アプリみたいです。

nicktoumpelis / HiBeacons
iPhoneで動作する。
ビーコン、出す方と受信する方。表で一覧表示。

スマートフォン勉強会@関西のログ

2013年11月2日、大阪梅田での スマートフォン勉強会@関西 の資料です。

スライド

手書きノート

CoreBluetoothの概要

iOSアプリケーション開発

この章は、iOSアプリケーション開発にBluetooth LEを提供するCore Bluetoothフレームワークを解説します。

Core Bluetoothフレームワークは、iOSアプリケーションにBluetoot LEデバイスの発見、接続と読み書きの操作を提供します。このフレームワークが、2章で述べたBluetooth LEの通信規格や振る舞いなどの技術詳細を隠蔽して抽象化するので、アプリケーション開発者はアプリケーション開発だけに注力できます。

iOS5とiOS6で、Core Bluetothフレームワークが対応する機能が大きく異なります。iOS5は、Bluetooth LEのセントラルという役割を提供します。iOS6は、セントラルに加えてペリフェラルという役割も提供します。iOSのバージョンごとの対応を、iOS5およびiOS6のアイコンを節のタイトルに表示して明示します。

アプリケーション開発のBluetooth LE基礎知識

キーホルダや心拍センサなどの一般に販売されているBluetooth LEデバイスをアプリケーションから使う場合、たいていデバイスに接続して操作するためのライブラリが提供されているので、Core Bluetoothフレームワークを意識することはないかもしれません。また、Core BluetoothフレームワークがBluetooth LEを抽象化していますから、Bluetooth LEの基礎知識がなくてもアプリケーションを開発できます。

しかし、Bluetooth LEを使うオリジナルのハードウェアを企画設計する場合は、Bluetooth LEの基礎知識が不可欠です。iOSとBlutooth LEでなにができるかは企画に必要な知識ですし、試作品のハードウェアが意図した振る舞いをしない時に、その原因を明らかにするには知識が必要です。

2章でBluetooth LEそれ自体の技術詳細を述べました。ここでは、iOSアプリケーション開発者からみたBluetooth LEの基礎技術情報をまとめます。

クラシックBluetoothとBluetooth LE

Bluetooth4.0は、Bluetooth3.0までの近接高速通信技術にBluetooth Low Energyの超低消費電力通信技術を統合したものです。Bluetooth3.0までの技術を、Bluetooth LEと区別するために、ここではクラシックBluetoothと呼びます。

クラシックBluetoothがヘッドフォンやファイル交換などに使う、連続したより高速のデータ通信を目指しているのに対して、Bluetooth Low Energyは、少量のデータを低頻度でやりとりする用途に、ハードウェアの低コスト化と、コイン型電池で1〜2年間の連続動作ができる超低消費電力に特化しています。

クラシックBluetoothのプロファイル

iPhoneは、Human Interface Device Profile (HID)などのクラシックBluetoothのプロファイルをサポートします。BluetoothキーボードをiPhoneに接続しても、アプリケーションは、それがBluetooth接続なのか、あるいは他の接続方式なのかを気にせずに、キーボードとして扱うことができます。クラシックBluetoothでは、周辺機器との接続および制御は全てiOSがおこない、アプリケーションから操作する手段はありません。もしも設計したい周辺機器が、定義されたプロファイルのどれかにあてはまるのであれば、クラシックBluetoothの認証制度に従い設計するだけです。

規格で定義されたプロファイルに当てはまらない機器のために、クラシックBluetoothには、任意の通信を提供するシリアルポートプロファイル(Serial Port Profile, SPP)があります。iOSアプリケーションから、このSPPを使うことはできません。iOSデバイスと接続するクラシックBluetoothデバイスを設計するには、Made For iPhone(MFi)プログラムをApple社と締結して、機器の認証ハードウェアと開発ライブラリの提供をうけなくてはなりません。

シリアルポートプロファイルは、単なる通信路を提供するだけです。例えば、Windowsパソコンに、シリアルポートプロファイルがあるクラシックBluetoothデバイスを接続すると、デバイス・マネージャーは、その接続を仮想シリアルポートとして扱います。Windowsパソコンでは、ユーザが、Bluetoothデバイスの仮想シリアルポートのポート番号をアプリケーションに指定します。

iOSの認証ハードウェアは、これがApple社のライセンスをうけた正規製品であることを示すとともに、
この機器が接続べきiOSアプリケーションもiOSに伝えます。この、本来のシリアルポートプロファイルにはない機能があるため、周辺機器は自動的に対応するiOSアプリケーションと接続できます。

Bluetooth LEのプロファイルとプロトコル

Bluetooth LEは、その規格のもとで任意のプロファイルを勝手に定義することができます。例えば、先ほどの体温計のサービスを利用して、iOSアプリケーションの振る舞いの制御を変えれば、室温を計測して熱中症をアラート音で警告する振る舞いをさせることも、できます。プロトコルと、プロファイルという2つの用語があります。プロファイルはBluetooth LEを使うデバイスの振る舞いを示します。プロトコルは、通信の手順やデータ表現を示します。

Bluetooth LEのデバイスの振る舞いは、ジェネリック・アトリビュート・プロファイル(Generic Attribute Profile, GAP)として定義されています。GAPが定義するBluetooth LEデバイスの4つの役割のうち、Core Bluetoothフレームワークでアプリケーション開発者が扱うのはセントラルとペリフェラルの2つです。ペリフェラルは、そのデバイスを発見してもらうためのアドバタイジングを行なう役割です。セントラルは、アドバタイジングをしているデバイスを発見して、そのデバイスに接続する役割です。

通信機能は、機能毎に階層表示したプロトコル・スタックで表されます。

リンク層は隣接するデバイス都の間に信頼できる1本の通信路を確立します。Bluetoothには、他のデバイスのパケットを他のデバイスに中継するトランスポート層はなく、隣接したデバイスとの直接通信のみをサポートします。このリンク層で通信タイミングなどを制御する中心となる役割をマスターと呼び、そのマスターに接続するデバイスの役割をスレーブと呼びます。1つのマスターに複数のスレーブが接続した小さなネットワークを、ピコネットと呼びます。

L2CAP(Logical Link Control and Adaptation Protocol)は、上層にある複数のプロトコルが、お互いの存在を気にせず、それぞれが通信できる論理的な通信路を提供します。TCP/IPネットワーキング・アプリケーション開発で使う、HTTPやFTPなどのプロトコルには、デフォルトのポート番号を指定して通信します。L2CAPの論理的な通信路は、このポート番号の指定に相当します。

アトリビュート・プロトコル(Attribute Protocol, ATT)とジェネリック・アトリビュート・プロファイル(Generic Attribute Profile, GATT)は、デバイスの持つデータの表現と、読み書きの手順を提供します。このレイヤで、データを持っているデバイスをサーバ、サーバの値を読み書きするものをクライアントと呼びます。例えば、Bluetooth LEに対応した心拍センサにiPhoneが接続した場合を考えます。心拍センサーが、心拍というデータを持っているのでサーバになります。iPhoneは、心拍の値を読みにいくクライアントになります。

GATTの、サーバ/クライアントの役割は、リンク層のマスター/スレーブの役割と関係なく割り当てられます。通常は、セントラルはマスターでクライアント、ペリフェラルにはスレーブでサーバ、の役割が割り当てられます。

サービスとキャラクタリスティクス

GATTは、サーバが持つデータを、キャラクタリスティクスという単位で読み書きする仕組みを提供します。また、サーバには多くのキャラクタリスティクスがありますが、そのキャラクタリスティクスをグループ化する、サービスという仕組みも提供します。

サービスは、その機器のハードウェアとしての機能を表します。例えば、体温計があるとします。体温計のハードウェアとしての機能は、温度の計測と、計測が完了した時のアラート音の出力の2つだとします。この場合、温度計測をするサービスが1つ、そして警告音を出すサービスが1つ、あるでしょう。それぞれのサービスには、温度を読み出すキャラクタリスティクス、警告音のOn/Offの指示を書き込むキャラクタリスティクスがあるでしょう。体温計という振る舞いにではなく、ハードウェアとしての機能に、サービスが割り当てられます。

Bluetooth LEのアプリケーションは、GATTの上に作られます。Bluetooth LEの規格認証の範囲はGATTまでです。サーバがどのようなサービスやキャラクタリスティクスを提供するかは、機器のアプリケーションとして実装されます。ですから、機器の開発者は、Bluetooth LEの規格認証のもとで、任意のサービスおよびキャラクタリスティクスを定義して、それを実装できます。

これはiOSアプリケーションでも同じです。iOSアプリケーションは、Core Bluetoothフレームワークを通して、GATTのサービスとキャラクタリスティクスにアクセスできます。iOSアプリケーションは、接続した機器の複数のサービスを組み合わせて、機器の振る舞いを制御します。Bluetooth LEを使うiOSアプリケーションは、パソコンで言うデバイス・ドライバに相当する部分を、アプリケーション内部に持つことになります。

クラシックBluetoothで任意のアプリケーションを作る場合は、勝手なプロファイルを定義できないため、SPPの汎用通信を使うほかありません。しかし、Bluetooth LEは、その規格のもとで任意のプロファイルを勝手に定義することができます。例えば、先ほどの体温計のサービスを利用して、iOSアプリケーションの振る舞いの制御を変えれば、室温を計測して熱中症をアラート音で警告する振る舞いをさせることも、できます。

Bluetooth SIGは、よく使われるサービスやプロファイルを定義しています。例えば、バッテリー残量やアラートの出力のようなサービスが定義されています。必要なプロファイルに使えるサービスがすでに定義されていれば、それを利用することができます。わざわざ定義や開発をする必要はありません。

また、プロファイルは、開発者が任意に定義できますが、これでは特定のメーカや機種を超えた汎用性が得られません。Bluetooth SIGは、それぞれの業界団体から提出された汎用化したプロファイルを公式のプロファイルとして審査承認して、Bluetooth LEの公式のプロファイルの定義とすることで、これを解決します。

Bluetooth LEの最善の使いかた

電力消費量を最小にする

iOSアプリケーション開発は電池消費量を常に注意します。もしもアプリケーションが、量自体は小さくても、定常的にかつ長時間電力を消費すれば、積分すれば電池消費量は大きくなります。携帯されるiOSデバイスでは、ユーザがすぐそれに気づき、直前にインストールしたアプリケーションが原因だろうと、それを削除するかもしれません。

Bluetooth LEは超低消費電力の無線通信技術です。これは、接続したマスターとスレーブで電波の送受信のタイミングを同期することで、高周波回路を動かす時間を最小にしているからです。Bluetooth LEが使う2.4GHz帯の周波回路の動作電流それ自体は、クラシックBluetoothと違いはありません。その稼働時間を最小限に絞ることで、時間平均では超低消費電力になるのです。

Core Bluetoothフレームワークを使う時は、スキャンの期間を必要最小にすることだけに気をつけます。iOSアプリケーション開発者がほかに注意することは、ありません。通信による電力消費量に大きく影響するリンク層の通信パラメータの設定は、iOSが隠蔽するので、iOSアプリケーション開発者が触れることはできません。通信パラメータの設定は、ペリフェラルのファームウェア開発者の担当領域です。

2.4GHzの高周波回路では、送信する信号が自分でわかる送信回路よりも、むしろ電力値が8桁以上も違う信号を受信する受信回路のほうが、消費電流が大きくなります。スキャンは、おそらく近辺にいるだろうペリフェラルが送信しているかもしれないアドバタイジング・パケットを検出するために、受信回路を長い時間動作させます。このため、消費電力を最小にするために、スキャンの期間が必要最小になるように注意をします。

バックグラウンド状態でのデバイスの発見とアドバタイズメント

Bluetooth LEはコイン型電池で年単位の無線通信をし続けられるのが大きな特徴です。この特徴を活かしたアプリケーションとデバイスの協調動作を実現するために、iOSは、iOSアプリケーションがサスペンド状態のときのBluetooth LEデバイスから通知があればそれをユーザに表示する機能と、バックグラウンド動作を提供しています。バックグラウンド動作がデバイスとの連携に重要です。

iOSアプリケーションは、セントラ・ロールおよびペリフェラル・ロールになります。それぞれに、Information property list (Info.plist) fileに指定するバックグラウンド動作のモードが追加されました。バックグラウンド状態のiOSアプリケーションは、Bluetoothのスイッチのオンオフや、リモートのペリフェラルからの通知発生などの、Bluetooth LEに関係するイベント発生の都度、iOSから10秒間のアプリケーション実行時間を与えられて、アプリケーションのイベントに該当するデリゲートが呼び出されます

iOSアプリケーションのフォアグラウンド状態とバックグラウンド状態で、iOSのBluetooth LEの振る舞いが異なることに、注意が必要です。iOSは電力消費量に注意して設計されています。Bluetoooth LEは電力を消費する無線回路を使います。特にiOSアプリケーションが動作し続けるバックグラウンド状態は、少しの電力消費でも時間積分すると大きな電力消費量になります。このため、開発者が不注意で電力を消費してしまわないように、非接続状態でのBluetooth LEの無線利用に対しては、iOS自体に工夫がなされています。

Bluetooth LEで予期せぬ電力消費を生じる場面は、デバイスの発見およびアドバタイズメントをする期間です。いったんBluetooth LEデバイス間の接続が確立すれば、その無線通信による消費電力量はコイン型電池1つで年単位持つ程度のものになります。

iOSアプリケーションがセントラル・ロールならば、バックグラウンド状態では、アドバタイズメントのスキャン周期が12分程度に1回と長くなります。また、アクティブ・スキャンを行いません。このため、リモートのペリフェラルの発見周期が長くなります。またリモートのペリフェラルが、アクティブ・スキャンでのみサービスのUUIDが取れる設計の場合は、サービスを発見できません。

Bluetooth LEの無線は、他のiOSアプリケーションと共有しています。そのため、他のアプリケーションの振る舞いが、バックグラウンド状態での動作に影響を与えることがあります。例えば、フォアグラウンドのiOSアプリケーションがスキャンを開始すると、その時に発見されたデバイスはバックグラウンド状態のiOSアプリケーションにも通知されます。

iOSアプリケーションがペリフェラル・ロールならば、バックグラウンド状態では、iOSが送信するアドバタイジング・データが変更されます。バックグラウンド状態では、ローカル・ネームが送信されません。したがって、もしもリモートのセントラルが、ローカル・ネームでデバイスを検索していると、このペリフェラルを発見できません。

Core Bluetoothフレームワーク

Core Bluetoothフレームワークは、iOSアプリケーションにBluetooth LEデバイスの発見と接続そして通信を提供するフレームワークです。Bluetooth LEの
通信パラメータ設定などの詳細を隠蔽してくれるので、振る舞いのみに注力して開発ができます。

は、アプリケーションのダンプ・レポートから推測したiOSのBluetooth LEの実装です。Bluetooth LEのホストは、BTServerというデーモンが実装しています。Core BLuetoothフレームワークは、iOSアプリケーションとこのBSServerとの間のプロセス間通信(Inter Process Communication, IPC)を提供します。

UUIDとCBUUIDクラス

UUIDは、サーバのように統合するものがなくとも生成できる一意に特定できる128ビットの識別子のことで、IETFのRFC412に詳細があります。Bluetooth LEの通信はATT層のアトリビュートというデータの単位で通信をします。アトリビュートが表す様々なデータ型の識別子に、このUUID(Universally Unique Identifier)を使います。また個別のサービスやキャラクタリスティクスの識別にもUUIDを使います。

このUUIDを表すのがCBUUIDクラスです。CBUUIDクラスのインスタンスは UUIDWithString:クラスメソッドで文字列から生成できます。

1
CBUUID *anyServiceUUID = [CBUUID UUIDWithString: @"7E20767A-30BB-4EB2-A43E-AC318D9A89A0"];

Bluetooth LEはUUIDの16ビットの短縮表現を決めています。これは128ビットのUUID”0000XXXX-0000-1000-8000-00805F9B34FB”のうち、X部分の16ビットだけを抜き出した表現です。短縮形の16ビットのUUIDをX部分に当てはめて、128ビットの本来のUUIDを復元できます。
例えば、0x180Dが割り振られた心拍を表すサービスの本来のUUIDは、0000180D-0000-1000-8000-00805F9B34FBになります。

UUIDWithString:クラスメソッドに16新表記で16ビットのUUIDを文字列で指定すると、この短縮形のUUIDが生成されます。

1
CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];

16ビットの短縮形UUIDは、無線通信のデータ量の削減が目的です。この16ビットの値は、Bluetooth SIGが割り当てと定義を決めており、ユーザが勝手に利用することはできません。GATTのサーバおよびクライアントは、この16ビットの値を内部で128ビットのUUIDに復元して、UUIDの比較などの処理は、128ビットのUUIDに対して実行されます。

UUIDの生成

任意のサービスまたはキャラクタリスティクスを定義するときには、開発者が128ビットのUUIDを生成して割り当てを決めます。UUIDを生成するネットサービスがありますが、OS Xではuuidgenというコマンドライン・ツールがあります。次のコマンドでターミナルからUUIDを生成できます。

$ uuidgen
71DA3FD1-7E10-41C1-B16F-4430B506CDE7

UUIDはハイフンで区切られたASCII文字列で出力されます。この文字列は、CBUUIDクラスの UUIDWithString:クラスメソッドにそのまま使えます。uuidgenコマンドに引数”-hdr”を指定すると、以下のような、そのままヘッダファイルに貼り付けられるソースコードを出力します。

1
2
3
4
$ uuidgen -hdr
// 5F066077-1DCF-4F13-913A-728584900517
#warning Change the macro name MYUUID below to something useful!
#define MYUUID CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0x5F, 0x06, 0x60, 0x77, 0x1D, 0xCF, 0x4F, 0x13, 0x91, 0x3A, 0x72, 0x85, 0x84, 0x90, 0x05, 0x17)

セントラル・マネージャとペリフェラル・マネージャ

iOSアプリケーションはiOS5およびiOS6ではセントラル・ロールを、iOS6ではペリフェラル・ロールになれます。それぞれ役割を提供するのが、CBCentralManagerクラス、CBPeripheralManagerクラスです。

セントラル・マネージャの役割

iOSアプリケーションがセントラル・ロールのとき、その通信制御は CBCentralManagerクラス で行います。Bluetooth LEを使うiOSアプリケーションの処理手順は:

  • ペリフェラルの発見と接続
  • サービスおよびキャラクタリスティクスの検索
  • キャラクタリスティクスへのデータの読み書き
  • キャラクタリスティクスからの変更通知設定

です。CBCentralManagerクラスの役割は:

  • アドバタイジング・データの取得
  • ペリフェラルの発見と接続
  • ペリフェラルの切断

です。ローカル・デバイスにセントラル・ロールを実装するサンプル・コードで、このこの3つの役割の実装を見ていきます。

セントラル・マネージャのインスタンス生成

セントラル・マネージャに対応するものが、CBCentralManagerクラスです。Bluetooth LEの通信を始める前に、CBCentralMangerクラスのオブジェクトにメモリ領域を割り当て、initWithDelegate:queue:メソッドで初期化します。

myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

initWithDelegate:queue:メソッドの引数は2つあります。最初の引数はセントラル・ロールのイベントを受け取るデリゲートを指定します。ここではselfを指定しています。この引数に指定するインスタンスはCBCentralManagerDelegateを実装します。

2つめのquequeは、通信処理を行なうキューを指定します。nilを指定した場合はメインキューが使われます。もしも、時間がかかるBluetooth LE関連の処理を行なう場合は、その処理がメインキューを止めてしまい、ユーザ・インタフェースを邪魔してしまうため、適当なキューを指定します。

CBCentralManagerクラスのインスタンスはiOSアプリケーションに1つだけ持つようにします。2つ以上を持っても動作するかもしれませんが、おかしな挙動をしたり、またiOSのバージョン更新で動作しなくなるかもしれません。また、アプリケーションがBluetooth LEで通信する間は、CBCentralManagerのインスタンスを保持します。デバイスとの接続が完了したからと、CBCentralManagerを解放しないようにします。

Bluetoothの状態取得

CBCentralManagerのstateプロパティは、Bluetoothの状態を表すCBCentralManagerState列挙型で、その値と意味は次のとおりです:

  • CBCentralManagerStateUnknown = 0,
    • 初期値です。すぐに更新されます。
  • CBCentralManagerStateResetting,
    • システムサービスとの接続が一時的に失なわれました。すぐに更新されます。
  • CBCentralManagerStateUnsupported,
    • Bluetooth Low EnergyのCentral/Clientをサポートしていません。
  • CBCentralManagerStateUnauthorized,
    • このアプリケーションは、Bluetooth Low EnergyのCentral/Client使う認可がありません。
  • CBCentralManagerStatePoweredOff,
    • Bluetoothがオフになっています。
  • CBCentralManagerStatePoweredOn
    • Bluetoothがオンで、かつ、いま利用できます。

CBCentralManagerをインスタンスした直後のstateプロパティは、CBCentralManagerStateUnknown(=0)の初期値です。このstateプロパティが変更されると、CBCentralManagerDelegateプロトコルのcentralManagerDidUpdateStateメソッドが呼び出されます。このメソッドにはCBCentralManagerのインスタンスが渡されます。

CBCentralManagerDelegateプロトコルには、たくさんのメソッドがありますが、必ず実装しなければいけない(required)ものはcentralManagerDidUpdateStateメソッドだけです。その他のメソッドは、オプショナルです。もしもCBCentralManagerDelegateプロトコルのメソッド名をタイプミスしていても、コンパイラは警告を出してくれません。タイプミスは入力補完やドキュメントからのコピーを利用して防止します。

CBCentralManagerのインスタンスを作ると、直ちにstateの値が変更されます。Bluetooth4に対応していないiOSデバイスであれば、stateプロパティはCBCentralManagerStateUnsupportedになります。Bluetooth LEを使うことはできないので、iOSアプリケーションで対処をします。Bluetooth4対応のiOSデバイスであれば、stateプロパティはCBCentralManagerStatePoweredOnまたはCBCentralManagerStatePoweredOffに変更されます。

Bluetoothの電源On/Offは、iOSアプリケーションから操作することはできません。ユーザがiOSの設定アプリケーションから、Bluetoothの電源On/Offができます。ペリフェラルを検索するために、iOSアプリケーションがスキャンを開始したときに、Bluetoothの電源がOffであれば、Bluetoothの電源をOnにする画面が自動で表示されます。設定ボタンを押すと、自分のiOSアプリケーションからiOSの設定アプリケーションに遷移して、Bluetoothの電源設定画面が表示されます。

ペリフェラルのスキャン

セントラルが行なう最初のタスクは、ペリフェラル・デバイスの発見です。ペリフェラル・デバイスは、自分の存在を周囲のセントラル・デバイスに伝えるために、アドバタイジング・パケットを送信しています。CBCentralManagerのscanForPeripheralsWithServices:options:メソッドを呼び出して、セントラルのアドバタイジング・パケットの受信を開始します。

[myCentralManager scanForPeripheralsWithServices:nil options:nil];

scanForPeripheralsWithServices:options:メソッドの引数は2つあります。最初の引数は、ペリフェラルをフィルタリングするためのものです。発見したいサービスのUUIDを、NSArrayで配列にして指定します。サービスはペリフェラルの機能に対応しています。サービスでフィルタリングすることで、セントラルが必要とする機能がある機器のみを、発見できます。この引数にnilを指定すると、フィルタリングせず、発見した全てのペリフェラルが報告されます。Appleは、Core Bluetoothプログラミング・ガイドで、より低消費電力の動作にするために、サービスを指定することを推奨しています。nilを指定することは、推奨していません。

2つめの引数optionsは、スキャンの動作を指定するオプションをNSDictionaryクラスのインスタンスで渡します。NSDictionaryには、オプションの種類をキー値に、そのオプション値を値に設定します。指定できるオプションは、CBCentralManagerScanOptionAllowDuplicatesKey のみで、値にBooleanをとります。この引数がnilのときは、CBCentralManagerScanOptionAllowDuplicatesKey の値をNOに設定したのと同じになります。

CBCentralManagerScanOptionAllowDuplicatesKey の値をYESにすると、ローカルのセントラルデバイスがペリフェラルからのアドバタイジング・パケットを受信するたび、ペリフェラルの発見が通知されます。NOの場合は、初めてアドバタイジング・パケットを受信した時にペリフェラルの発見が通知され、それ以降、同じペリフェラルからアドバタイジング・パケットを受信しても、発見を通知しません。

このオプションは:

  • 非接続で電波強度を知りたいとき
  • ブロードキャスタのデータを取るとき

によく使われます。アドバタイジング・パケットの受信通知では受信信号強度(Received Signal Strength Indicator,RSSI)も通知されます。RSSIから、ペリフェラルとの距離を大雑把に推定できます。ペリフェラルが近くに来た時に初めて接続させたいときなどに、用います。

周囲のBluetooth LEデバイスに非接続で情報を広報するものをブロードキャスターといいます。ブロードキャスタは、位置ビーコンなど、周囲の不特定多数のデバイスに20バイト程度までの少量の同じ情報を伝えます。ブロードキャスタのデータが不変であれば、アドバタイジング・パケットの受信通知は1度だけで済みます。ブロードキャスターまでの位置を大雑把に把握し続けたいとき、またはブロードキャスターのデータが変化するときには、このオプションを使います。

スキャンを一旦開始すると、スキャンし続けます。タイムアウトなど自動的に停止することはありません。スキャンは、iOSアプリケーションが、CBCentralManagerのstopScanメソッドを呼び出して、明示的に停止しなければなりません。

iOSアプリケーションがすでにスキャンを開始しているときに、scanForPeripheralsWithServices:options:メソッドをパラメータを指定して呼び出すと、指定したパラメータで更新されて、動作が切り替わります。

アドバタイジング・パケットの受信

ローカルのセントラルがアドバタイジング・パケットを受信すると、CBCentralManagerDelegateプロトコルのcentralManager:didDiscoverPeripheral:advertisementData:RSSI: メソッドに通知されます。

1
2
3
4
5
6
-(void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
...

centralManager:didDiscoverPeripheral:advertisementData:RSSI: メソッドには4つの引数があります。最初の引数はCBCentralManagerクラスのインスタンスを、次の引数はペリフェラルを表すCBperipheralクラスのインスタンスです。3つ目の引数 advertisementData は、アドバタイジング・パケットのデータをおさめたNSDictionaryクラスのインスタンスです。最後のRSSIは受信信号強度を表します。

受信信号強度の値は、次の式で与えられます:

RSSI = 10 * log I(mW) (dBm)

この式のIは、受信信号電力をミリワット単位で表します。RSSIの値は、送信電力、デバイスとの距離等できまる伝搬損失、そして受信感度で決まります。受信信号電力は桁違いに変化するため、扱いやすくするためにRSSIは対数で表現されます。RSSIは、目安として、-30 dBm から-100 dBm までのマイナスの値を取ります。

scanForPeripheralsWithServices:options:メソッドのオプションに、CBCentralManagerScanOptionAllowDuplicatesKeyにYESの値設定していれば、同じペリフェラルのアドバタイジング・パケットを受診するたび、このcentralManager:didDiscoverPeripheral:advertisementData:RSSI: メソッドが呼び出されます。これは変化するアドバタイジング・データやRSSIのモニタリングに使います。

アドバタイジング・データ

アドバタイジング・パケットのペイロードは、AD structureの配列です。AD structureは、1オクテットのAD typeと、それにつづくAD Dataというバイト・データです

scanForPeripheralsWithServices:options:メソッドの3つ目の引数advertisementDataは、アドバタイジング・パケットから読みだしたデータを収めたNSDictionaryクラスのインスタンスです。キー値にAD typeに対応した文字列が、バリューにはキー値に対応するクラスのインスタンスが入ります。アドバタイジング・パケットにどのようなタイプの情報を入れるかは、ペリフェラル次第です。もしもアドバタイジング・パケットにキー値に対応する値がない場合は、そのキーバリューペアはありません。

次の6つのキー値が CoreBluetooth/CBAdvertisementData.h に定義されています。

  • CBAdvertisementDataLocalNameKey
  • CBAdvertisementDataTxPowerLevelKey
  • CBAdvertisementDataServiceUUIDsKey
  • CBAdvertisementDataOverflowServiceUUIDsKey (iOS6以降)
  • CBAdvertisementDataManufacturerDataKey
  • CBAdvertisementDataServiceDataKey

CBAdverisementDataLocalNameKey のバリューは、ペリフェラルのローカル・ネームを表す、NSStringクラスのオブジェクトです。ローカル・ネームは、特定のペリフェラルを探すのによく使います。機種番号であったり、あるいはシリアル番号を含む何かの識別文字列が割り当てられます。

AD typeには、完全なローカル名を表すタイプと、ローカル名が長すぎてアドバタイジング・パケットに収まりきらない場合に使う短縮形の2つのタイプがあります。AD typeがいずれの値でもCBAdverisementDataLocalNameKey の1つのキーで、ローカル名が返されるので、ここでそれらを区別する方法はありません。

CBAdvertisementDataTxPowerLevelKey のバリューは、ペリフェラルの送信電力を表すNSNumberクラスのインスタンスです。送信電力の単位はdBmです。ペリフェラルとの距離の推定に、送信電力値を使います。

セントラルのスキャンには、パッシブ・スキャンとアクティブ・スキャンがあります。ペリフェラルが送信するアドバタイジング・パケットを受信するだけなのが、パッシブ・スキャンです。セントラルからペリフェラルに、更にアドバタイジング・パケットをリクエストするのが、アクティブ・スキャンです。

CBAdvertisementDataServiceUUIDsKey のバリューは、ペリフェラルのサービスを表すCBUUIDクラスのインスタンスの配列(NSArrayクラスのインスタンス)です。CBAdvertisementDataOverflowServiceUUIDsKey はiOS6移行にあるキー値です。アクティブ・スキャンで得られた、ペリフェラルのサービスを表すCBUUIDクラスのインスタンスの配列(NSArrayクラスのインスタンス)です。

周囲の不特定多数のレシーバに一方向にデータを伝える役割をブロードキャスタと呼びます。位置ビーコンなどに使われます。アドバタイジング・パケットは、ペリフェラルの存在を周囲に知らせる以外に、ブロードキャストに使われます。

CBAdvertisementDataManufacturerDataKey のバリューは、ペリフェラルの設計製造会社が設定できる任意のバイト・データを表すNSDataクラスのインスタンスです。Bluetooth SIGが企業に割り当てた2バイトの識別子に任意のデータが続きます。CBAdvertisementDataServiceDataKeyのバリューは、NSDataクラスのインスタンスで、そのバイト・データは先頭2バイトがサービスを表す16ビットのUUIDが、その後にサービスに関連する任意のバイト・データが続きます。

ブロードキャスティングに、いずれのキー値が使われるかは、ペリフェラルの実装次第です。もしも、その仕様が製造業者が独自に定義したものであれば、CBAdvertisementDataManufacturerDataKey が使われるでしょう。仕様が特定の製造会社に縛られない一般的なもので、Bluetooth SIGが承認したサービスの定義があるならば、CBAdvertisementDataServiceDataKey が使われるでしょう。

ペリフェラルへの接続

ペリフェラルが発見できれば、次にそれが接続したいペリフェラルかを判定して、接続処理を行います。

1
2
3
4
5
6
7
8
9
10
-(void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
...
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
if(localName != nil && [localName isEqualToString:@"devide name"] ) {
_peripheral = peripheral;
[myCentralManager connectPeripheral:peripheral options:nil];
}

ペリフェラルが接続先か否かは、引数advertisementDataの内容で判別します。ローカル・ネームが対応機種名かで判定する実装が、よく使われます。接続するならば、CBCentralManagerクラスのconnectPeripheral:options: メソッドを呼び出します。

CBPeripheralクラスのインスタンスは、そのペリフェラルの利用が終了するまで、必ずiOSアプリケーションで保持します。上のコードでは、引数peripheralをインスタンス変数 __peripheral_で保持しています。CBperipheralクラスのインスタンスを解放すると、CBCentralManagerは、接続開始処理および接続状態を終了させて通信を切断します。CBperipheralManagerは、CBPeripheralクラスのインスタンスを保持しません。

Bluetooth LEの接続処理は、アドバタイジング・パケットを受信した後に、直ちに接続要求を出さねばなりません。したがって、connectPeripheral:options:メソッドを呼び出しても、接続が完了するのは、次のアドバタイジング・パケットを受信したタイミング以降になります。

ペリフェラルとの接続が確立すると、CBCentralManagerDelegateプロトコルのcentralManager:didConnectPeripheral:メソッドが呼び出されます。この引数peripheralは、connectPeripheral:options:メソッドに指定したCBPeripheralクラスのインスタンスと同じものです

1
2
3
4
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
...

接続処理に失敗した場合は、CBCentralManagerDelegateプロトコルのcentralManager:didFailToConnectPeripheral:error:メソッドが呼び出されます。接続失敗の原因は、引数errorで渡されます。

1
2
3
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"Failed to connect to a Peripheral");
...
サービスの検索

ペリフェラルには様々な機能があります。その機能それぞれを表すのが、Bluetooth LEのサービスです。ペリフェラルと接続できたならば、次は必要なサービスを検索します。

1
2
3
4
5
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {
...
peripheral.delegate = self;
[peripheral discoverServices:nil];

ペリフェラルの処理結果は、デリゲートのメソッド呼び出しで非同期に返されます。このコードはCBPeripheralDelegateプロトコルを実装したselfを、引数peripheralのdelegateプロパティに設定します。

CBPeripheralクラスのdiscoverServices:メソッドを呼び出して、サービスの検索を開始します。ペリフェラルの全てのサービスを検索するならば、引数にnilを指定します。検索するサービスを指定するならば、NSArrayクラスのインスタンスで、指定したいサービスのUUIDを設定したCBUUIDクラスのインスタンスを配列にして、引数に渡します。

ペリフェラルを発見した時のアドバタイジング・パケットのサービスUUIDよりも、このdiscoverServices:メソッドで発見できるサービスのほうが、多いかもしれません。これは、128ビットのサービスUUIDはアドバタイジング・パケットに1つしか入らないため、アドバタイジング・パケットですべてのサービスを取得できるとは、限らないからです。

Core Bluetoothフレームワークは、discoverServices:メソッドに、サービスを指定することを推奨しています。サービスの検索には通信時間と処理時間がかかります。検索対象のサービスを事前に指定することで、それらをより短くできます。

サービスを発見すると、そのCBPeripheralのインスタンスのdelegateプロパティに設定したインスタンスの、CBPeripheralDelegateプロトコルのperipheral:didDiscoverServices: メソッドが呼ばれます。

1
2
3
4
5
6
7
8
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
if ([service.UUID.data isEqualToData:_targetServiceUUID.data]) {
[peripheral discoverCharacteristics:nil forService:service];
}
...

引数peripheralのservicesプロパティは、NSArrayクラスのインスタンスで、その内容はCBServiceクラスのインスタンスの配列です。もしもサービス検索でエラーが発生していれば、引数errorにその内容が入ります。

キャラクタリスティクスの検索

使いたいサービスの検索が完了すれば、次はサービスそれぞれの、アクセスしたいキャラクタリスティクスを取得していきます。サービスごとに検索したいキャラクタリスティクスは異なるので、まず、CBPeripheralクラスのインスタンスのservicesプロパティのインスタンスが、どのサービスに対応しているのかを判別しなければなりません。これは、CBServiceクラスのインスタンスのUUIDプロパティの一致で判定できます。

1
2
3
4
5
6
7
8
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
if ([service.UUID.data isEqualToData:_targetServiceUUID.data]) {
[peripheral discoverCharacteristics:nil forService:service];
}
...

CBPeripheralクラスの discoverCharacteristics:forService: メソッドを呼び出してキャラクタリスティクスを検索します。このメソッドには2つの引数があります。最初の引数は、検索したいキャラクタリスティクスのUUIDの配列を指定します。forService:には検索対象のサービスを指定します。

discoverCharacteristics:forService: メソッドの最初の引数にnilを指定すると、そのサービスの全てのキャラクタリスティクスを検索します。iOSアプリケーションが、そのペリフェラルをどう使うかは、設計時点で決まるので、ここでnilを渡すことは、あまりありません。キャラクタリスティクスの検索には、通信時間がかかり電池を消費するため、Apple社のプログラミングガイドは、キャラクタリスティクスのUUIDを指定することを推奨しています。

キャラクタリスティクスが検索できると、CBPeripheralDelegateプロトコルのperipheral:didDiscoverCharacteristicsForService:error: メソッドが呼ばれます。

1
2
3
4
5
6
7
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"Discovered characteristic %@", characteristic);
...
} ...

キャラクタリスティクスは、CBCharacteristicクラスのインスタンスで与えられます。引数serviceのcharacteristicsプロパティは、このCBCharacteristicのインスタンスをNSArrayで配列にしたものです。キャラクタリスティクスの取得時にエラーが発生した場合は、引数errorでエラー情報が渡されます。正常動作時は、引数errorの値はnilです。

キャラクタリスティクスの読み出し

ペリフェラルの値の読み書きは、キャラクタリスティクスを通して行います。例えば、ペリフェラルが心拍計なら、心拍計速のサービスのなかに、心拍数を表すキャラクタリスティクスがあります。

キャラクタリスティクスの値読み出し方法には、直接読み出しと、サブスクリプションの2つがあります。直接読み出しは、セントラルからペリフェラルに読み出し要求を送信して、それを受けたペリフェラルがセントラルにキャラクタリスティクスのデータを返す方法です。サブスクリプションは、ペリフェラルがキャラクタリスティクスの値を変更したときに、ペリフェラルがセントラルに、更新された値を送信する方法です。

Bluetooth LEの通信プロトコルには、キャラクタリスティクスの読み出し方法には、ノーティフィケーションとインディケーションの2つがあります。Core Bluetoothフレームワークのキャラクタリスティクスの読み出しでは、この2つを区別する方法はありません。用語を区別して混同しないように、Core Blutoothフレームワークのペリフェラルからセントラルの変更通知を、ここではサブスクリプションと呼びます。

キャラクタリスティクスの値が固定値ならば、接続時に1度読み出せばいいので、直接読み出しをします。値が変化するキャラクタリスティクスを常にモニタしたいならば、サブスクリプションを使います。セントラルが一定周期で読み出しをするポーリングに比べて、値の変更時に通知してくれるサブスクリプションは、無駄に無線通信することがありません。

キャラクタリスティクスの読み出しには、CBPeripheralクラスのreadValueForCharacteristic:メソッドを使います。引数には、読み出したいキャラクタリスティクスに対応するCBCharacteristicのインスタンスを与えます。

NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];

キャラクタリスティクスの読み出し結果は、CBPeripheralDelegateプロトコルのperipheral:didUpdateValueForCharacteristic:error:メソッドで返されます。

1
2
3
4
5
6
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
NSData *data = characteristic.value;
// parse the data as needed
...

読み出しに成功すれば引数errorはnilです。もしも失敗した場合は、引数errorにはエラー内容を表すNSErrorのインスタンスが与えられます。読み出しに成功していれば、読み出し値は、引数characteristicのvalueプロパティから取り出せます。

読み出しでは、全てのキャラクタリスティクスに読み出し権限があるとは限らないことに注意します。キャラクタリスティクスには、それぞれにパーミションが設定されています。パーミションの、読み出しフラグと、サブスクリプション権限のフラグは、それぞれ独立して設定できます。このため、読み出しはできるがサブスクリプションはできない設定や、逆に、読み出しができないがサブスクリプションはできる設定が、ありえます。読み出し権限がないキャラクタリスティクスに直接読み出しをした場合、あるいはサブスクリプション権限のフラグがない場合にサブスクリプションした場合は、エラーになります。

キャラクタリスティクスのパーミション設定は、列挙型CBCharacteristicProperties のpropertiesプロパティから読み出せます。列挙型CBCharacteristicPropertiesには、読み出し権限以外にも多くの権限が定義されています。それぞれの権限はビット・フラグで、論理和で設定できます。

読み出し権限があることを確認するならば、CBCharacteristicPropertyRead とCBCharacteristicのpropertyプロパティの論理和が0でないことを、確認します。通常は、ペリフェラルの仕様書に、キャラクタリスティクスのパーミション設定が明記されています。ですから、iOSアプリケーションでパーミション設定を調べることは、あまりありません。

キャラクタリスティクスの値変更通知を受け取るには、CBPeripheralクラスのsetNotifyValue:forCharacteristic:メソッドを使います。サブスクリプションの権限がないキャラクタリスティクスに、このメソッドを呼び出しても、なにも変化しません。

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

setNotifyValue:forCharacteristic:メソッドには引数が2つあります。最初の引数は、通知を受け取るか受け取らないかを指定するBooleanの値です。YESまたはNOを与えます。2つ目の引数は、通知を受けたいキャラクタリスティクスのCBCharacteristicのインスタンスです。

通知設定のデフォルト値はNOです。また、セントラルとの通信が切断した時に、ペリフェラルは通知設定をデフォルト値NOに初期化します。ですから、サブスクリプションをするならば、セントラルはペリフェラルに接続した都度、設定します。

サブスクリプションをYESに設定すると、キャラクタリスティクスの値が更新されると、その更新値が
CBPeripheralDelegateプロトコルの peripheral:didUpdateNotificationStateForCharacteristic:error: メソッドで通知されます。

1
2
3
4
5
6
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@",
} ...

引数の意味や使いかたは、値読み出しの peripheral:didUpdateValueForCharacteristic:error:メソッドのそれと、同じです。

キャラクタリスティクスへの書き込み

ペリフェラルへの値の書き込みは、読み込みと同じくキャラクタリスティクスを通じて行います。値の書き込みは、ペリフェラルに保存されているデータを変更する意味のほか、例えばエアコンの赤外線リモコンに開始/停止の動作指定や温度設定のボタンがあるように、動作や目標値を指示する意味もあります。

1
2
NSLog(@"Writing value for characteristic %@", interestingCharacteristic); [peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
type:CBCharacteristicWriteWithResponse];

キャラクタリスティクスに書き込む CBPeripheral クラスの writeValue:forCharacteristic:type: メソッドには、3つの引数があります。最初の引数には書き込みたいバイト・データを収めたNSDataのインスタンスを、2つ目の引数には書き込み対象のキャラクタリスティクスの CBCharacteristic のインスタンスを、最後の3つ目の引数には書き込みタイプを表す定数を与えます。

サンプルコードの最初の引数dataToWriteのバイト・サイズは20バイト以下にします。キャラクタリスティクスに書き込めるバイト・データのサイズは20バイト以下です。20バイトよりも大きいデータを与えた場合は、その部分のバイトデータはペリフェラルに送信されません。

キャラクタリスティクスに書き込めるデータ・サイズの上限は、Bluetooth LEのアトリビュート・プロトコル(Attribute Protocol, ATT)によるものです。ATTのプロトコル・データ・ユニット(Protocol Data Unit, PDU)の最大長 Maximum Transfer Unit(MTU)、ATT_MTU と表します、はデフォルト値が23バイトです。ヘッダを除くと、1つのPDUで送れるデータ・サイズは20バイトになります。これが、キャラクタリスティクスに1回で書き込めるデータ・サイズを決めています。

ATTには、セントラルとペリフェラルがそれぞれのATT_MTUのサイズを交換しあって、デフォルト値23バイトよりも大きなATT_MTUを扱えるようにする仕組みがあります。しかしiOS5およびiOS6は、このATT_MTUのサイズ交換に対応していません。

3つ目の引数typeは列挙型 CBCharacteristicWriteType の値です。この列挙型は、2つの値 CBCharacteristicWriteWithResponse (= 0)および CBCharacteristicWriteWithoutResponse を定義しています。このサンプルコードは、引数typeに CBCharacteristicWriteWithResponse を指定しています。

この書き込みタイプは、Bluetooth LEの通信プロトコルの2つの書き込み方法に対応しています。CBCharacteristicWriteWithResponseを指定すると、セントラルはライト・リクエストでペリフェラルに書き込みを行います。ペリフェラルは、書き込み処理の成功失敗に関係なく、処理結果をセントラルに返します。この結果はデリゲートを通じてiOSアプリケーションに返されます。これは、ペリフェラルの不揮発メモリの書き込みなどの、書き込みの完了を必ず確認しなければならない場合に使います。

CBCharacteristicWriteWithoutResponse を指定すると、セントラルは、ライト・コマンドを使って書き込みます。ペリフェラルは、書き込み処理が成功しても失敗しても、セントラルに結果を返すことはありません。これは、例えばエアコンのリモコンの動作開始指示ボタンのような、動作を指示する場合などに使われます。

書き込みをしたのに、ペリフェラルの動作に反映されないなどの、書き込み処理の失敗は、大抵ペリフェラルの都合によるものです。セントラルのライト・コマンドが通信路で失われて、ペリフェラルが受信できないことは、ありえません。接続している限り、リンク層がセントラルとペリフェラル間に信頼できる通信を提供しています。コマンドは無視してもよいため、処理時間がないなどの理由があると、ペリフェラルはコマンドの処理をスキップするかもしれません。

キャラクタリスティクスが、どの書き込みのタイプに対応しているかは、キャラクタリスティクスのパーミション設定で判別ができます。CBCharacteristicクラスのpropertyプロパティと、
CBCharacteristicPropertyWrite との論理積が0でなれば(ビットが立っていれば)、そのキャラクタリスティクスはレスポンスありの書き込み対応しています。CBCharacteristicPropertyWriteWithoutResponseとの論理積が0でないならば、レスポンスなしの書き込みに対応しています。この2つのパーミションは、それぞれ独立に設定できるので、キャラクタリスティクスが書き込み可能であれば、パーミション設定には3通りあります。

writeValue:forCharacteristic:type: メソッドの引数typeに CBCharacteristicWriteWithResponse を指定したとき、CBPeripheralDelegateプロトコルのperipheral:didWriteValueForCharacteristic:error: メソッドに、書き込み結果が返されます。

1
2
3
4
5
6
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@",
} ...

引数は3つあります。読み込み処理の場合と同じように、エラーが発生すれば引数errorにその内容が渡されます。正常に処理できたならば、引数errorはnilです。

ペリフェラル・マネージャーの役割

Bluetooth LEにはセントラルとペリフェラルという2つの役割があります。iOS6から、iOSアプリケーションがペリフェラル・ロールになれるCBPeripheralManagerクラスが提供されています。ペリフェラル・ロールは、Bluetooth LEの、リンク層のスレーブ、ジェネリック・アトリビュート・プロトコル層ではサーバの役割を担います。

ペリフェラル・ロールのiOSアプリケーションは:

  • アドバタイジング・パケットの送信
  • セントラルとの接続および切断
  • 読み出しおよび書き出し要求の処理
  • 通知処理

を行います。CBPeripheralManagerクラスの役割は:

  • アドバタイズ
  • サービスおよびキャラクタリスティクスのデータベースの作成と公開
  • セントラルからのリクエストおよびコマンドの処理

です。ローカル・デバイスにペリフェラル・ロールを実装するサンプル・コードで、このこの3つの役割の実装を見ていきます。

ペリフェラルのマネージャーを開始する

ペリフェラルの役割はCBPeripheralManagerクラスが提供します。CBPeripheralManagerクラスのinitWithDelegate:queue:メソッドでインスタンスを生成します。

myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

initWithDelegate:queue:メソッド
の引数は2つあります。最初の引数はペリフェラル・ロールのイベントを受け取るCBPeripheralManagerDelegateプロトコルを実装したデリゲートを指定します。ここではselfを与えています。2つめのquequeは、通信処理を行なうキューを指定します。nilを指定した場合はメインキューが使われます。

CBPeripheralManagerクラスのインスタンスは、iOSアプリケーションに1つだけ持つようにします。CBPeripheralManagerを2つ以上インスタンスした時の振る舞いがどうなるかは、わかりません。

CBPeripheralManagerDelegateプロトコルは、必ず実装しなければならないperipheralManagerDidUpdateState:メソッドと、その他のオプションのメソッドがあります。peripheralManagerDidUpdateState:メソッドは、Bluetoothの電源やペリフェラル・マネージャーの状態を伝えます。その他のメソッドは、ローカルのデータベースに接続およびアクセスするセントラルについての情報を提供します。

ペリフェラル・マネージャーの状態

CBPeripheralManagerのstateプロパティは、ペリフェラル・マネージャーの状態を伝える列挙型CBPeripheralManagerStateの値です。CBPeripheralManagerのインスタンスを作成したときは、stateプロパティの値はCBPeripheralManagerStateUnknownです。このstateプロパティが変化すると、その都度CBPeripheralManagerDelegateプロトコルのperipheralManagerDidUpdateState:メソッドが呼ばれます。

列挙型CBPeripheralManagerStateの値とその意味は次のとおりです:

  • CBPeripheralManagerStateUnknown
    • インスタンスした直後の、初期値を示します。すぐにペリフェラル・マネージャーの状態を表す値に変更されます。
  • CBPeripheralManagerStateResetting
    • システムサービスとの接続が一時的に失われたことを示します。この値はすぐに更新されます。
  • CBPeripheralManagerStateUnsupported
    • この機種が、Bluetooth LEのスレーブ/サーバをサポートしていないことを示します。
  • CBPeripheralManagerStateUnauthorized
    • このアプリケーションに、Bluetooth Low Energy のスレーブ/サーバを使う権限がありません。
      -CBPeripheralManagerStatePoweredOff
    • Bluetoothがオフになっています。
      ー CBPeripheralManagerStatePoweredOn
    • BluetoothがONで、利用できます。

Bluetooth LEに対応した機種であれば、ペリフェラル・マネージャーの状態は、CBPeripheralManagerStatePoweredOn状態とCBPeripheralManagerStatePoweredOff状態のいずれかです。

ペリフェラル・マネージャーは、アドバタイジングを開始する前に、ローカルのデータベースを構築してサービスおよびキャラクタリスティクスの設定を行います。この設定は、CBPeripheralManagerStatePoweredOn状態のときのみ、実行できます。

もしもペリフェラル・マネージャーがアドバタイジングをしている間に、stateプロパティがCBPeripheralManagerStatePoweredOff状態になると、アドバタイジングは停止されます。
電源がOnになっても、アドバタイジングは自動的に再開されません。ペリフェラル・マネージャーが明示的にアドバタイジングを再開します。また、Off状態になった時に、ローカル・データベースは全てクリアされます。このため、ペリフェラル・マネージャーは、サービスも再度追加しなければいけません。

サービスとキャラクタリスティクスの構築

Bluetooth LEのペリフェラルのデータベースは、ハンドル、タイプ、バリューの3つのフィールドで構成されるアトリビュートの集合です。アトリビュート・プロトコルが、タイプやハンドルを指定してアトリビュートにアクセスするプロトコルを提供しています。そして、ジェネリック・アトリビュート・プロファイルは、このアトリビュートを使って、サービスとキャラクタリスティクスという概念を導入します。キャラクタリスティクスは、値を保持するものです。サービスはキャラクタリスティクスの集合を表します。

iOSアプリケーションが、内部のデータを外部に公開するには、まずペリフェラル・マネージャーのデータベースを構築します。セントラル・ロールでは、サービスおよびキャラクタリスティクスは、それぞれCBServiceクラスおよびCBCharacteristicクラスに対応していました。ペリフェラル・マネージャーは、サービスおよびキャラクタリスティクスの編集に、それぞれCBMutableServiceクラスおよびCBMutableCharacteristicクラスを使います。CBMutableServiceクラスはCBServiceクラスを、またCBMutableCharacteristicクラスはCBCharacteristicクラスを継承します。

まずサービスとキャラクタリスティクスのUUID、そしてキャラクタリスティクスのパーミション設定と役割を定義します。UUIDを表すCBUUIDクラスおよびUUIDの生成方法はで述べました。Bluetooth SIGが公開しているサービスを実装するならば16ビットのUUIDを使います。任意のサービスを定義するならば128ビットのUUIDを生成します。

まずCBMutableCharacteristcクラスのインスタンスを生成します。CBMutableCharacteristicクラスのinitWithType:properties:value:permissions:メソッドを使います。

1
2
3
4
myCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID
properties:CBCharacteristicPropertyRead
value:myValue permissions:CBAttributePermissionsReadable];

initWithType:properties:value:permissions:メソッドには引数が4つあります。最初の引数initWithTypeはキャラクタリスティクスのUUIDを表すCBUUIDのインスタンスを与えます。引数valueは、キャラクタリスティクスの値を表すNSDataのインスタンスを与えます。引数propertiesには、キャラクタリスティクスの属性をCBCharacteristicProperties列挙型の値で与えます。引数permissionsには、キャラクタリスティクスのパーミションをCBAttributePermissions列挙型の値で与えます。この2つの列挙型は、いずれも列挙型の複数の値を論理和でまとめて指定できます。

引数valueがnilの場合は、そのキャラクタリスティクスの値は、変化するものとされます。リモートのセントラルがキャラクタリスティクスの値を読み出すと、それがデリゲートに通知されます。デリゲートに、値の返信処理の責任があります。

引数valueにNSDataのインスタンスを与えると、Core Bluetoothフレームワークは、そのキャラクタリスティクスの値は固定値として扱います。キャラクタリスティクスのプロパティは自動的に読み込み可能になります。引数valueの値はキャッシュされて、リモートがこのキャラクタリスティクスを読み出すと、そのキャッシュされた値が自動的にセントラルに返されます。このとき、ペリフェラル・マネージャーはデリゲートのメソッドを呼び出しません。

サービスは、キャラクタリスティクスの集合です。CBMutableServiceクラスのinitWithType:primary:メソッドでインスタンスを生成します。

myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];
myService.characteristics = @[myCharacteristic];

initWithType:primary:メソッドは2つの引数をとります。最初の引数は、そのサービスのUUIDを表すCBUUIDのインスタンスです。2つ目の引数はサービスがプライマリ・サービスかを示すBoolean型の値です。このサンプル・コードはプライマリ・サービスを指定しています。引数primaryをNOにすると、セカンダリ・サービスの指定になります。

このプライマリ・サービスとセカンダリ・サービスは、ペリフェラル・マネージャー内部のサービス定義で使われるものです。リモートのセントラルから見えるのは、プライマリ・サービスだけで、セカンダリ・サービスは見えません。セカンダリ・サービスは、補助的なキャラクタリスティクスをペリフェラル・マネージャー内部でまとめるのに用います。

サービスへのキャラクタリスティクスの追加は、CBMutableServiceのインスタンスのcharacteristicsプロパティに、CBMutableCharacteristcの配列を設定しておこないます。

キャラクタリスティクスのコンフィグレーション

リモートのセントラルから見える、ローカルのキャラクタリスティクスの振る舞いは、パーミションの設定で決まります。

  • セントラルにキャラクタリスティクスをサブスクライブさせるのを許可する
  • ペアリングしていないセントラルからのアクセスを制限、センシティブなもの

もしもローカルのキャラクタリスティクスの値が、しばしば変更されるものならば、リモートのセントラルにサブスクライブさせることを推奨します。そのために、キャラクタリスティクスにサブスクライブを許可するパーミション設定が必要です。これは、CBCharacteristicのpropertyプロパティに、CBCharacteristicPropertyNotifyを設定します:

1
2
3
4
myCharacteristic = [[CBMutableCharacteristic alloc]
initWithType:myCharacteristicUUID
properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify
value:nil permissions:CBAttributePermissionsReadable];

このサンプルコードはCBCharacteristicのインスタンス生成時に指定しています。

センシティブなデータの扱い

BLEデバイスに個人情報を記録する場合があります。例えばソーシャルメディアのプロファイルを提供するBluetooth LEペリフェラルであれば、メンバーのアカウントや名前、住所といった個人情報を保持するでしょう。そのようなデバイスは、誰もが読み書きできる状態では、困ります。信頼したデバイスのみに読み書きを許可する必要があります。

キャラクタリスティクスのプロパティには、読み書きおよびノーティフィケーションの暗号化を指定できます。

1
2
3
4
5
emailCharacteristic = [[CBMutableCharacteristic alloc]
initWithType:emailCharacteristicUUID
properties:CBCharacteristicPropertyRead
| CBCharacteristicPropertyNotifyEncryptionRequired
value:nil permissions:CBAttributePermissionsReadEncryptionRequired];

このサンプル・コードでは、キャラクタリスティクスは信頼するデバイスからのみ、読み出しおよびサブスクライブを受け付けます。リモートのセントラルがこのキャラクタリスティクスにアクセスすると、ローカルのペリフェラル・マネージャーはリモートのセントラルに、必要な権限がないというエラーを返します。そこで、リモートのセントラル・マネージャーは、次に、暗号化に必要な鍵を交換するため、ペリフェラルにペリフェラルを求めます。ローカルのiOSデバイスは、リモートのデバイスがペアリングを要求していることを、ユーザにダイアログ表示します。ユーザが、それを承認することで、ペアリングとボンディングが完了し、リモートのセントラルは、キャラクタリスティクスを読み出せるようになります。

セントラル・ロールとペリフェラル・ロールが両方共iOSデバイスであれば、セントラルがセキュアなキャラクタリスティクスにアクセスした時点で、それぞれのデバイスに、他のデバイスとのペアリングが必要であるダイアログを表示します。セントラル・ロールのデバイスには、ペリフェラルに入力すべきテキスト・コードが表示されます。ペリフェラル・ロールのデバイスに、そのテキスト・コードを入力してペアリングおよびボンディングが完了すれば、ペリフェラルはそのセントラルを信頼できるデバイスとみなします。

ペリフェラル・マネージャーへのサービス追加

キャラクタリスティクスを設定したCBMutableCharacteristcクラスのインスタンスができれば、次はペリフェラル・マネージャーのデータベースに、それらのサービスを追加します。これには、CBPeripheralManagerクラスのaddService:メソッドを使います。

[myPeripheralManager addService:myService];

ペリフェラル・マネージャーにaddService:メソッドでサービスとキャラクタリスティクスを追加すると、その内容はペリフェラル・マネージャーにキャッシュされて、変更することはできません。複数のサービスが複数ある場合はaddService:メソッドを繰り返し呼び出します。

ペリフェラル・マネージャーのデリゲートがperipheralManager:didAddService:error:メソッドを実装していれば、ペリフェラル・マネージャーはサービスの追加処理が完了するつど、このメソッドを呼び出します。もしもサービスの追加に失敗した場合は、このメソッドから原因を取得できます。

1
2
3
4
5
6
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didAddService:(CBService *)service
error:(NSError *)error {
if (error) {
NSLog(@"Error publishing service: %@", [error localizedDescription]);
} ...
アドバタイズメントの開始と停止

周囲にいるかもしれないセントラルにペリフェラルの存在を伝えるために、CBPeripheralManagerクラスのstartAdvertising:メソッドで、アドバタイズメントを開始します。

[myPeripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey :
@[myFirstService.UUID, mySecondService.UUID] }];

startAdvertising:メソッドの引数はオプションを指定したNSDictionaryのインスタンスです。このサンプル・コードでは、CBAdvertisementDataServiceUUIDsKey をキー値に、サービスのUUIDに対応するCBUUIDの配列をバリューにしています。

Bluetooth LEのアドバタイズメント・パケットのデータ・タイプは、フラグ、ローカル名、製造者特有のデータ、送信電力およびサービスUUIDの配列の5種類で、CBAdvertisementData.h に、それぞれに対応するキー値が定義されています。

startAdvertising:メソッドの引数の辞書に指定できる値は、 CBAdvertisementDataLocalNameKey と CBAdvertisementDataServiceUUIDsKey の2つです。CBAdvertisementDataLocalNameKey は、デバイスのローカル名を表し、NSStringのインスタンスをバリューに設定します。CBAdvertisementDataServiceUUIDsKey はサービスUUIDの配列を表し、CBUUIDの配列をバリューに設定します。

startAdvertising:メソッドを呼び出すと、ペリフェラル・マネージャーはデリゲートのperipheralManagerDidStartAdvertising:error:メソッドを呼び出します。もしもエラーがありアドバタイズメントが開始出きない場合は、引数errorで詳細情報が渡されます。

1
2
3
4
5
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
error:(NSError *)error {
if (error) {
NSLog(@"Error advertising: %@", [error localizedDescription]);
} ...

Core Bluetoothフレームワークは、下層の通信の詳細を隠すため、iOSアプリケーションから、アドバタイズメント・パケットの周期を設定することはできません。また、アドバタイズメントは、パケット・サイズに制約があるため、データ・サイズにも制約があることに注意が必要です。。さらに、iOSアプリケーションがフォアグラウンドかバックグラウンドかにより、アドバタイズメント・パケットの内容が異なります。

iOSアプリケーションがフォアグラウンド状態ならば、アドバタイズメント・パケットの最大データサイズ28バイトを、任意のデータ・タイプの組み合わせで使えます。もしも28バイトを超える場合は、ローカル・ネームに対してのみ、スキャン・レスポンスの10バイトの領域を使うことが可能です。ここでのデータ・サイズは、データ・タイプごとに2バイトのヘッダ情報が付加された後のサイズであることに、注意が必要です。

アドバタイズメント・パケットの28オクテットの領域に収まりきらなかったサービスUUIDsは、スキャン・レスポンスの領域に収められます。したがって、それらの収まりきらかなったサービスUUIDsは、iOSがアクティブ・スキャンをしたときに初めて発見されます。

セントラル・マネージャーのスキャン動作で述べたように、セントラルがペリフェラルを発見した時に、そのペリフェラルが接続対象かの判断は、ローカル・ネームもしくはサービスUUIDsで判断します。また、128ビットのUUIDは、16バイトあるので、アドバタイズメント・パケットに1つしか格納できません。ペリフェラルが意図したように発見されない場合は、startAdvertising:メソッドに指定したデータが、実際にはどのようにアドバタイジング・パケットに格納されているかを、把握することが必要です。

iOSアプリケーションがバックグラウンド状態ならば、アドバタイズメント・パケットにローカル・ネームは含まれません。また、全てのサービスUUIDsは、全てスキャン・レスポンスからのみ取得できます。

読み出しリクエストへの応答


セントラルがペリフェラルのキャラクタリスティクスに読み書きリクエストを送ると、それが固定値でないならば、ペリフェラル・マネージャーはデリゲートのperipheralManager:didReceiveReadRequest:メソッドを呼び出します。キャラクタリスティクスのレスポンス処理は、iOSアプリケーションがすべき処理です。

値が固定値ではないキャラクタリスティクスは、CBMutableCharacteristicクラスのinitWithType:properties:value:permissions:メソッドで、valueにnilを設定したものです。

1
2
3
4
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
...

peripheralManager:didReceiveReadRequest:メソッドは2つの引数をとります。最初の引数peripheralはペリフェラル・マネージャーを示します。2つ目の引数requestはセントラルからのリクエストを表すCBATTRequestクラスのインスタンスです。

CBATTRequestクラスは、プロパティのみでメソッドを持たないバリュー・オブジェクトです。プロパティは次の4つです。value以外、読み込みのみです。

  • CBCentral *central
    • リクエストを発生させたセントラルです。
  • CBCharacteristic *characteristic;
    • 対象となるキャラクタリスティクスです。
  • NSUInteger offset;
    • データのオフセットを表します。
  • NSData *value;
    • 値です

CBCentralクラスは、1つだけのプロパティ CFUUIDRef UUID のプロパティを持ちます。CFUUIDRefは128ビットのUUIDを扱う構造体のポインタです。CBUUIDクラスの + (CBUUID *)UUIDWithCFUUID:(CFUUIDRef)theUUID; クラスメソッドでCBUUIDのインスタンスに変換できます。このプロパティはセントラルの識別に使います。

リード・リクエストを受け取ったデリゲートは、セントラルが要求したデータを、CBATTRequestクラスのvalueプロパティに設定して返します。このために、まず対象となるキャラクタリスティクスが何かを調べます。これは、引数requestのcharacteristicプロパティのUUIDで判別できます。次のステップは、リード・リクエストのオフセット位置が、キャラクタリスティクスの値の範囲を超えていないことを確認します。

1
2
3
4
5
if (request.offset > myCharacteristic.value.length) {
[myPeripheralManager respondToRequest:request
withResult:CBATTErrorInvalidOffset];
return;
}

このオフセット値は、キャラクタリスティクスのデータが長くて、アトリビュート・プロトコルの1つのPDUに収まらない時に、複数のトランザクションに分割して読みだすときに使います。オフセット値が正しいと確認したのち、CBATTRequestのvalueプロパティに値を設定して、ペリフェラル・マネージャーのrespondToRequest:withResult:メソッドを呼び出します。Bluetooth LEの、リクエスト-レスポンスは1対1に対応しています。ですから、peripheralManager:didReceiveReadRequest:メソッドが呼び出されたら、対応するrespondToRequest:withResult:メソッドを、かならず1度だけ呼び出すようにします。

1
2
[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
...

respondToRequest:withResult:メソッドは引数を2つ取ります。最初の引数requestは、デリゲートの呼び出しで渡されたCBATTRequestのインスタンスのvalueプロパティを設定したものです。2つ目の引数は、CBATTError列挙型の変数です。

もしも、ペリフェラルが公開しているキャラクタリスティクスにないキャラクタリスティクスを、リモートのセントラルが要求してきたなど、正常に処理ができない場合は、respondToRequest:withResult:にエラーコードを指定します。

書き込みリクエストへの応答

リモートのセントラルからローカルのペリフェラルへの書き込みの処理は、読み込みと同じです。セントラルが送信する、1つかそれ以上のキャラクタリスティクスへの書き込みリクエスを、ペリフェラル・マネージャーが受信すると、デリゲートのperipheralManager:didReceiveWriteRequests:メソッドを呼び出します。

peripheralManager:didReceiveWriteRequests:メソッドの引数は2つです。最初の引数は、ペリフェラル・マネージャーのインスタンスです。2つ目の引数はCBATTRequestのインスタンスの配列です。この引数のCBATTRequestのインスタンス1つ1つが、個別の書き込みリクエストです。

もしも2つ上の書き込みリクエストがきたときは、デリゲートは、それらのリクエストを1つのまとまりとして、アトミックに処理をします。一連の書き込みリクエストのうち1つが失敗したならば、一連の書き込み処理は実行されるべきではありません。

myCharacteristic.value = request.value;

書き込みリクエストの内容が正しいならば、対応するキャラクタリスティクスに値を書き込みます。このサンプル・コードにはありませんが、読み込み時と同じく、書き込みを実行する前に、オフセット値がキャラクタリスティクスの値の範囲を正しく示しているかを確認します。

読み込み時と同じく、リクエストとレスポンスは1対1に対応します。デリゲートのperipheralManager:didReceiveWriteRequests:メソッドが呼ばれたら、そのレスポンスとなるrespondToRequest:withResult:メソッドを、1回だけ呼び出します。

respondToRequest:withResult:メソッドの最初の引数は、respondToRequest:withResult:メソッドで渡されたCBATTRequestのインスタンスです。2つ目の引数はCBATTError列挙型の変数です。

1
2
[myPeripheralManager respondToRequest:[requests objectAtIndex:0]
withResult:CBATTErrorSuccess];
セントラルへの通知

リモートのセントラルは、ローカルのペリフェラル・マネージャーの1つあるいはそれ以上のキャラクタリスティクスをサブスクライブできます。サブスクライブされたキャラクタリスティクスの値が変更されると、ペリフェラル・マネージャーは、その更新値をリモートのセントラルに通知します。

接続しているセントラルがキャラクタリスティクスをサブスクライブを要求した時、ペリフェラル・マネージャーはデリゲートのperipheralManager:central:didSubscribeToCharacteristic:メソッドを呼び出します。

1
2
3
4
5
- (void)peripheralManager:(CBPeripheralManager *)peripheral
central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"Central subscribed to characteristic %@", characteristic);
...

peripheralManager:central:didSubscribeToCharacteristic:メソッドは3つの引数、ペリフェラル・マネージャー、セントラル、そしてサブスクライブされるキャラクタリスティクスのインスタンスを取ります。iOSアプリケーションが、このキャラクタリスティクスがサブスクライブされていることを、覚えておきます。

iOSアプリケーションは、サブスクライブされているキャラクタリスティクスの値を更新するときに、ペリフェラル・マネージャーのupdateValue:forCharacteristic:onSubscribedCentrals: メソッドを呼び出します。

1
2
3
NSData *updatedValue = // fetch the characteristic's new value
BOOL didSendValue = [myPeripheralManager updateValue:updatedValue
forCharacteristic:characteristic onSubscribedCentrals:nil];

updateValue:forCharacteristic:onSubscribedCentrals:メソッドは3つの引数をとります。最初の引数は、更新された値を示すNSDataのインスタンス、2つ目の引数はキャラクタリスティクス、最後の引数は通知を送るCBCentralオブジェクトの配列です。

updateValue:forCharacteristic:onSubscribedCentrals:メソッドの最後のCBCentralの配列に、nilを指定すると、そのキャラクタリスティクスをサブスクライブする全てのセントラルに通知が送信されます。引数にCBCentralの配列を与えれば、その配列のセントラルのうち、そのキャラクタリスティクスをサブスクライブしているものに、通知が送信されます。配列の中に、そのキャラクタリスティクスをサブスクライブしていないセントラルがあっても、そのセントラルに通知が送信されることはありません。通知を送るセントラルの範囲を限定したい場合に使います。

updateValue:forCharacteristic:onSubscribedCentrals:メソッドの戻り値はBoolean型です。アップデートが送信できるならばYESが、送信キューが満杯で通知ができないならばNOが返されます。もしも戻り値がNOであれば、送信キューに空きができたときに、ペリフェラル・マネージャーはデリゲートのeripheralManagerIsReadyToUpdateSubscribers:メソッドを呼び出します。もしも望むならば、このメソッドが呼び出されたときに、再度、通知処理を行います。

この通知で送られるキャラクタリスティクスのデータ・サイズは、セントラル・マネージャーの場合と同じく、アトリビュート・プロトコルのATT_MTUで制約されます。もしもキャラクタリスティクスの値が、サイズ制約を超える場合は、それぞれのセントラルが、readValueForCharacteristic:メソッドを使ってオフセット値を指定して、そのキャラクタリスティクスのすべての値を読み出すべきです。

バックグラウンド処理

iOSアプリケーションはバックグラウンド実行に対応できます。しかし、iOSアプリケーションでは、電力やメモリなどのリソースの消費量が問題になります。そのため、iOSアプリケーションがフォアグラウンドかバックグラウンドにあるかで、無線を使うCore Bluetoothフレームワークの振る舞いが異なります。iOSアプリケーションのマルチタスキングの詳細は、iOSアプリケーション開発ガイドに掲載されたiOSアプリケーション プログラミングガイドの、アプリケーションの状態とマルチタスキング、の章を参照してください。

通常のiOSアプリケーションは、セントラルおよびペリフェラルいずれも、バックグラウンドでは動作が不可にされます。iOSアプリケーションのバックグラウンド・モードを設定してあれば、Bluetooth LE関連のイベントが発生すれば、対応するデリゲートのメソッドが呼び出されます。イベントの処理は、iOSアプリケーションが全ての処理を行う場合と、ユーザに通知表示を出すか否かだけをiOSに伝えて、あとの実際の通知処理はiOSに任せる場合の、2通りがあります。

フォアグラウンド状態のみiOSアプリケーション

バックグラウンド・モードをなにも指定しない通常のiOSアプリケーションは、バックグラウンド状態に入ると、短い実行時間ののちにサスペンド状態に移行します。サスペンド状態ではBluetooth関連のイベントは通知されず、システムがキューに貯めています。iOSアプリケーションがフォアグラウンド状態に戻った時に、キューにあるイベントが通知されます。ですから、もしもリモートとの接続が切断しても、それが通知されるのは、iOSアプリケーションがフォアグラウンドに戻った時です。

バックグラウンド・モードを指定しないセントラル・ロールのiOSアプリケーションは、フォアグラウンドでスキャンをしていても、バックグラウンドに入ると、スキャンは停止します。ペリフェラルの発見および切断のイベントも通知されません。

バックグラウンド・モードを指定しないペリフェラル・ロールのiOSアプリケーションは、バックグラウンド状態ではアドバタイズメントが停止されます。すでに接続が確立しているリモートのセントラルとの接続は、バックグラウンド状態になっても切れません。しかし、リモートのセントラルがダイナミックなキャラクタリスティクスの値にアクセスすると、エラーが返されます。

ユーザへのアラート通知

iOSアプリケーションが、バックグラウンド・モードを設定していなくて、バックグラウンド状態でBluetoothのイベントをアプリが処理できない場合でも、設定でiOSにBluetoothのイベントをユーザに通知させることはできます。

ユーザに通知できるのは、ローカルのiOSアプリケーションがセントラル・ロールのとき、リモートのペリフェラルとの接続が確立したこと、切断したこと、およびペリフェラルからのノーティフィケーションを受信したこと、の3つです。これらをiOSに通知させるかは、ペリフェラルに接続するときに呼び出す、セントラル・マネージャーのconnectPeripheral:options:メソッドのオプションで指定します。

connectPeripheral:options:メソッドのオプションには、NSDictionaryのインスタンスを指定します。辞書に使えるキーは以下の3つです。バリューはBoolean型変数をおさめたNSNumberのインスタンスです。キーを指定しなければ、NOを指定したのと同じで、ユーザ通知をしません。3つのうち2つのキーは、iOS6以降で利用できます。iOS5では、指定しても無視されます。

  • CBConnectPeripheralOptionNotifyOnConnectionKey (iOS6以降)
  • CBConnectPeripheralOptionNotifyOnDisconnectionKey
  • CBConnectPeripheralOptionNotifyOnNotificationKey (iOS6以降)

CBConnectPeripheralOptionNotifyOnConnectionKey はiOS6以降で有効です。バリューがYESのとき、iOSアプリケーションがサスペンドしている時にペリフェラルとの接続が確立すれば、iOSがユーザにアラート表示を行います。

CBConnectPeripheralOptionNotifyOnDisconnectionKey のバリューがYESのとき、iOSアプリケーションがサスペンドしている時にペリフェラルとの接続が切断すれば、iOSがユーザにアラート表示を行います。

CBConnectPeripheralOptionNotifyOnNotificationKey はiOS6以降で有効です。iOSアプリケーションがサスペンドしている時に、ペリフェラルからインディケーションがきたら、iOSはユーザにその内容を通知します。

バックグラウンド・モード

バックグラウンドでも、ペリフェラルからの通知や送られてくるセンサ・データの保存など、何かしらの処理が必要ならば、iOSアプリケーションのバックグラウンド・モードを指定します。バックグラウンド・モードは、iOSアプリケーションが任意の時間に任意の処理を行うためのものではなく、外部からのイベントを処理するためのものです。バックグラウンド・モードが指定されたiOSアプリケーションは、Bluetooth LEのイベントが発生するたびに、10秒間の実行時間を与えられて、サスペンド状態から実行状態になります。

バックグラウンド・モードは、iOSアプリケーションのInformation property list (Info.plist) ファイルにモードを宣言します。UIBackgroundModes keyに、セントラル・ロールならばbluetooth-centralを、ペリフェラル・ロールならばbluetooth-peripheralを設定します。もしもiOSアプリケーションが、セントラルとペリフェラルの両方の役割を持つならば、2つのバックグラウンド・モードを指定します。

Xcodeで
Info.plistファイルを開くと、プロパティ・リスト・エディタは生のキー名ではなく、人間がわかりやすい表記で表示します。生のキー名を表示したい時は、エディタ・ウィンドウのどこでもいいので、コントロール・キーを押しながらクリックをします。コンテキスト・メニューが表示されるので、その中の”Show Raw Keys/Values item”をチェックします。

セントラル・ロールでのバックグラウンド実行

iOSアプリケーションのInfo.plistファイルに、UIBackgroundModesキーにbluetooth-centralを設定すれば、セントラル・ロールのiOSアプリケーションはバックグラウンドで実行されます。バックグラウンド状態でペリフェラルのスキャンができます。またiOSは、ペリフェラルの発見と接続完了、読み書きと切断のイベントで、バックグラウンドでも、iOSアプリケーションをスタンドバイ状態から起こして、CBCentralManagerDelegateプロトコルの該当するメソッドを呼び出します。

ただし、iOSアプリケーションがフォアグラウンド状態とバックグラウンド状態のときでは、電力消費量を抑えるために、Bluetooth LEの振る舞いが異なります。Bluetooth LEの通信接続自体はフォアグラウンド状態の時と同じです。

バックグラウンド状態では、重複するペリフェラルのアドバタイジングを都度知らせるスキャン・オプションCBCentralManagerScanOptionAllowDuplicatesKeyが無視され、ペリフェラルの発見は1度だけ通知されます。また、定常的に電力を消費するスキャンは、ペリフェラルをスキャンする全てのiOSアプリケーションがバックグラウンド状態であれば、12分に1回程度と、低頻度になります。このため、バックグラウンド状態ではペリフェラルの発見に、時間がかかります。フォアグラウンド状態のiOSアプリケーションがスキャンをしていれば、バックグラウンド状態のiOSアプリケーションにも、フォアグラウンド状態と同じスキャン周期が与えられます。

ペリフェラル・ロールでのバックグラウンド実行

iOSアプリケーションのInfo.plistファイルに、UIBackgroundModesキーにbluetooth-peripheralを設定すれば、ペリフェラル・ロールのiOSアプリケーションはバックグラウンドで実行されます。バックグラウンド状態でアドバタイズメントができます。またiOSは、セントラルからの読み書きやサブスクライブのアクセスがあると、iOSアプリケーションをスタンドバイ状態から起こして、CBPeripheralManagerDelegateプロトコルの該当するメソッドを呼び出します。

バックグラウンド状態でのアドバタイズメントは、フォアグラウンド状態のときとは、振る舞いが異なります。iOSはアドバタイズメント・パケットの長さを可能な限り短くすることで、電波の送信時間を短くして、電池消費量を最小にしようとします。

アドバタイズメント・データに指定したCBAdvertisementDataLocalNameKeyキーが無視されて、ペリフェラルのローカル・ネームはアドバタイジングされません。CBAdvertisementDataServiceUUIDsKeyキーで指定したサービスUUIDsは、アドバタイズメント・パケットで送信されません。リモートのセントラルが、スキャン・リクエストすることで、サービスUUIDsが取得できます。また、アドバタイズメントしているiOSアプリケーションが全てバックグラウンド状態のときは、アドバタイジングの周期自体が下がります。フォアグラウンド状態のiOSアプリケーションがアドバタイジングしていれば、バックグラウンド状態のiOSアプリケーションにも、フォアグラウンド状態と同じアドバタイズメントの周期が与えられます。

バックグラウンド・モードのベストプラクティス

iOSアプリケーションは、電池消費量を特に気にします。個別の実装ごとに、こうなればこうなると考えていくのは大変です。バックグラウンド・モードの使いかたの指針をいくつか示します。

  • Bluetoothの利用をユーザが選択できるインタフェース設計
  • 目的に合わせたバックグラウンド・モードの利用
  • バックグラウンドでの初利を最小の時間に抑える

iOSアプリケーションは、Bluetooth LEを利用する場面では、セッションベースのインタフェースを提供します。例えばスキャンがいつ開始して、いつ終了するかが、画面の表示遷移とひもづけられていれば、ユーザから見てなにをしているのかが明確になります。また、バックグラウンド・モードでデータ同期する/しないなど、Bluetooth LEを利用する機能を、ユーザ設定画面で提供することもよいでしょう。

Bluetooth LEのバックグラウンド・モードは、それを使う必要があるiOSアプリケーションが使うべきものです。Bluetooth LEを必要としないのに、バックグラウンドでのiOSアプリケーションの動作を得るためだけに、このバックグラウンド・モードを使うべきではありません。

また、Bluetooth LEのイベントごとに、iOSアプリケーションには10秒の実行時間が与えられます。この10秒をすべて使うのではなく、必要なタスク処理が完了すれば、iOSに処理を戻して、iOSアプリケーションの実行時間を最小にします。

Core Bluetoothフレームワークのベスト・プラクティス

Core Bluetoothフレームワークは、iOSアプリケーションに、Bluetooth LEの通信の詳細を隠蔽して、抽象化された振る舞いを提供します。これによりiOSアプリケーション開発者は、Bluetooth LEの通信規格を理解しなくても、求める振る舞いを実装できます。

しかし、Bluetooth LEを利用する限り、必ずリモートのペリフェラルまたはセントラルが振る舞いに関わります。そして、ローカルとリモートのデバイスの間を電波がつなぐのです。高度に抽象化されても、なおiOSデバイス単体で完結する通常のiOSアプリケーションにはない配慮も必要です。ここでは、Core Bluetoothフレームワークを使うノウハウを述べます。

スキャンは必要最小限に

Bluetooth LEで電力を消費するのは、スキャンです。いつくるかタイミングが分からない、8桁も違う受信信号電力を検出するため、高周波回路が最も電力を消費する状態です。次に電力を消費するのが、アドバタイズメントです。無線通信は、超低消費電力無線通信の名称が示すとおり、高周波回路の消費電力は少量です。ただし、イベントを処理するために、iOSアプリケーションが動作すれば、その分の電力消費が生じます。したがって、スキャンを最小に、アドバタイズメントは利用時間をなるべく少なくするように、注意します。

リモートのペリフェラルの発見のために、セントラル・マネージャーのscanForPeripheralsWithServices:options:メソッドを呼び出せば、スキャンが開始されます。このスキャンはタイムアウトしません。ペリフェラルを発見したならば、セントラル・マネージャーのstopScanメソッドを呼び出して、iOSにスキャンの停止を指示します。

stopScanメソッドを呼び出しても、直ちにそのiOSデバイスのスキャンが停止するとは限りません。Bluetooth LEは複数のiOSアプリケーションで利用されます。そのiOSアプリケーションがstopScanメソッドを呼び出しても、他のiOSアプリケーションがスキャンを実行していれば、そのiOSデバイスのスキャンは実行され続けるでしょう。

iOSアプリケーションがバックグラウンド状態になったとき、バックグラウンド・モードが指定されていない場合は、スキャンは停止します。<TBD物理的にスキャンが停止するの? また、フォアグラウンドになったら再開するの? /> バックグラウンド・モードにbluetooth-centralを指定していれば、スキャンは継続して実行されますが、ペリフェラルを見つける周期はフォアグラウンド状態のときよりも長くなります。

重複するアドバタイズメント・パケットの検出は最小に

セントラルに接続していないペリフェラルは、周囲のセントラルに存在を伝えるために、アドバタイズメント・パケットを送信し続けます。

セントラル・マネージャーのscanForPeripheralsWithServices:options:メソッドは、アドバタイズメント・パケットを受信してペリフェラルを発見した時に、デリゲートのcentralManager:didDiscoverPeripheral:advertisementData:RSSI:メソッドを呼び出します。また、同じペリフェラルでも、アドバタイズメント・パケットのデータが、以前に受信したデータと異なる場合にも、このデリゲートを呼び出します。

アドバタイズメント・パケットを受信した都度、デリゲートを呼び出させたいときは、scanForPeripheralsWithServices:options:メソッドのオプションで、CBCentralManagerScanOptionAllowDuplicatesKeyのバリューをYESに設定します。これは、受信信号強度をモニタし続けて、ペリフェラルが十分近くに来た時に初めて接続させたいときに、使ったりします。しかし、連続してスキャンをし続けるため、バッテリー消費量が大きくなります。必要な場合にのみ使用します。

サービスの検索を最小に

たいてい、ペリフェラルには数多くのサービスがあり、iOSアプリケーションが利用するのは、その一部分です。ペリフェラルにどのようなサービスおよびキャラクタリスティクスがあるかを検索すると、その検索データのやり取りのために、無線通信の時間と処理時間がかかります。

iOSアプリケーションの設計段階で、ユースケースに求められるペリフェラルのサービスとキャラクタリスティクスは決まります。ですから、検索は利用するものだけに限定することを勧めます。サービスを検索するCBPeripheralクラスのdiscoverServices:メソッドには、検索したいサービスのUUIDsを引数に指定できます。同様に、CBperipheralクラスのscanForPeripheralsWithServices:options:メソッドも、検索したいキャラクタリスティクスのUUIDsを引数に指定できます。

1
[peripheral discoverServices:@[firstServiceUUID, secondServiceUUID]];

サブスクライブと読み出しの使いかた

リモートのペリフェラルのキャラクタリスティクスの値が変化する場合、その値の読み出し方法には、ポーリングとサブスクライブの2つの方法があります。ポーリングは、ローカルのセントラルから一定期間でreadValueForCharacteristic:メソッドを呼び出して、明示的にキャラクタリスティクスの値を読み出す方法です。サブスクライブは、setNotifyValue:forCharacteristic:を1度呼び出しておけば、リモートのキャラクタリスティクスの値が変更される都度、リモートのペリフェラルからセントラルに通知される方法です。

キャラクタリスティクスの値の変化をモニタするには、サブスクライブを使います。ただし、そのキャラクタリスティクスのpropertyプロパティをみて、インディケーションまたはノーティフィケーションのすくなくとも1つをサポートしていなければ、サブスクライブはできません。

ただし、あまりに頻繁に値が変化するキャラクタリスティクスは、ポーリングを使ったほうが消費電力を抑えられるかもしれません。

ペリフェラルの切断

もうペリフェラルを使わないならば、接続を切断すれば無線をより使わなくなり、電力消費量を抑えられるでしょう。

キャラクタリスティクスがノーティフィケーションを停止した場合は、もう値の更新を通知してくることはありません。これは、キャラクタリスティクスのisNotifyingプロパティがNOになっていることで確認できます。サブスクライブしている全てのキャラクタリスティクスのノーティフィケーションが停止したならば、もうペリフェラルと接続しつづける必要がないので、切断します。

またペリフェラルから必要なすべてのデータを読みだした場合も、接続し続ける必要はないので、切断します。
<!–
この説、なにが言いたいんだろう。

どっちの場合でも、持ってるだろうサブスクライブををキャンセルしろ、
ペリフェラルから切断する。

キャラクタリスティクスの値のサブスクライブは、
setNotifyValue:forCharacteristic: methodの最初のパラメータをNO
ペリフェラルへの接続をキャンセルできる、cancelPeripheralConnection: method of the CBCentralManager class,

1
[myCentralManager cancelPeripheralConnection:peripheral];

–>

ペリフェラルの切断には、CBPeripheralクラスのcancelPeripheralConnection:メソッドを呼び出します。このメソッドは、処理をブロックすることなく直ちに返ってきます。しかし、ペリフェラルとの実際の切断処理は、iOSがキャッシュして、後に実行されます。このため、ペリフェラルとの切断がいつ完了するかは、iOSアプリケーションからはわかりません。

ペリフェラルへの再接続

ペリフェラルに接続する方法は、今までに述べたペリフェラルを発見して接続する以外に、過去に一度接続したことがあるペリフェラル、また現在iOSデバイスに接続しているペリフェラルに接続する方法があります。

ペリフェラルに接続した時、iOSはそのペリフェラルをそれぞれ識別するUUIDを生成します。このUUIDを保存しておくと、あとでペリフェラルに再接続したいときに、セントラル・マネージャーのretrievePeripherals:メソッドを呼び出す時のペリフェラルの指定に使えます。

[myCentralManager retrievePeripherals:savedPeripheralUUIDs];

iOSデバイスが過去に接続したことがあるペリフェラルであれば、セントラル・マネージャーのretrievePeripherals:メソッドに、問い合わせたいペリフェラルのUUIDの配列を与えて呼び出します。UUIDはCFUUIDRefのインスタンスで表します。CBUUIDはBluetooth LEのサービスやキャラクタリスティクスの識別に使います。CBUUIDクラス自体がUUIDを表すのですが、これはBluetoothのために作られたクラスなので、ペリフェラルの識別のUUIDには、Bluetoothに関係がないCFUUIDRefが使われます。

セントラル・マネージャーは、指定されたUUIDの配列の中から、過去に接続したことのあるペリフェラルのUUIDを見つけます。CBCentralManagerDelegateプロトコルのcentralManager:didRetrievePeripherals:メソッドで、発見したペリフェラルに対応するCBPeripheralのインスタンスの配列を返します。もしも該当するペリフェラルが1つもなかった場合は、空の配列を返します。

取得したペリフェラルの配列のなかから、画面に表示してユーザが選択する、あるいはiOSアプリケーション内部で判定して、接続したいペリフェラルを選択します。ペリフェラルへの接続には、スキャンの後に接続した時と同じく、セントラル・マネージャーのconnectPeripheral:options:メソッドを呼び出します。このとき、そのペリフェラルがiOSデバイスとすでに接続していれば、セントラル・マネージャーはデリゲートのcentralManager:didConnectPeripheral:メソッドを呼び出します。

centralManager:didConnectPeripheral:メソッドを呼び出しても、ペリフェラルが周囲にいなければ、接続はできません。また、Bluetooth LEデバイスには、プライバシーを保護するために、デバイスの特定を防ぐ、ランダム・アドレスという仕組みがあります。これは、リンク層のアドレスを一定周期で変更します。ランダム・アドレスを使うペリフェラルは、たとえ周囲にあったとしても、前回接続した時とアドレスが異なるので、再接続ができません。この場合は、再度スキャンをしてペリフェラルの発見から、接続をやり直します。

接続しているペリフェラルのリストを取得する

ペリフェラルに再接続する残る1つの方法は、セントラル・マネージャーのretrieveConnectedPeripheralsメソッドを呼び出して、現在iOSデバイスに接続しているペリフェラルを取得することです。

retrieveConnectedPeripheralsメソッドを呼び出すと、
セントラル・マネージャーは、いまiOSデバイスに接続している全てのペリフェラルを、デリゲートのcentralManager:didRetrieveConnectedPeripherals:メソッドで通知します。

centralManager:didRetrieveConnectedPeripherals:メソッドの2つ目の引数はCBPeripheralのインスタンスの配列です。もしもiOSデバイスに接続しているペリフェラルがない時は、空の配列が渡されます。

iOSデバイスとペリフェラルとのBluetooth LEの接続が確立しているときでも、iOSアプリケーションとペリフェラルとの接続は、ペリフェラルを発見した時と同じ手順です。セントラル・マネージャーのconnectPeripheral:options:メソッドを呼び出して接続を行います。iOSアプリケーションとの接続が確立すれば、セントラル・マネージャーはデリゲートのcentralManager:didConnectPeripheral:メソッドを呼び出します。

アドバタイジング・データの考慮

iOSアプリケーションがペリフェラル・ロールのとき、そのアドバタイジング・データは、ペリフェラル・マネージャーのstartAdvertising:メソッドに渡す辞書で指定できます。しかし、アドバタイズメント・パケットのペイロードにはサイズ制約があります。またバックグラウンド状態では、iOSがパケット・サイズを最小にしようとします。このため、指定したアドバタイズメント・データと、実際に送信されるアドバタイジング・データの対応を知っておくことが大切になります。

アドバタイズメント・パケットは、ペリフェラルに関する情報をブロードキャストするのに、使います。そのアドバタイジング・データは、ペリフェラルのローカル名およびサービスのUUIDsを入れられます。指定できるキーは:

  • CBAdvertisementDataLocalNameKey
  • CBAdvertisementDataServiceUUIDsKey.

これ以外のキーを指定すると、エラーとなります。

アドバタイジング・データは、iOSアプリケーションがフォアグラウンド状態のとき、28バイトまでの制限があります。これはBluetooth LEの、パケットの最大サイズによる制約です。この28バイトは、キーごとに必要な2バイトのヘッダと、そのバイト・データの合計値が、それ以下になることを意味します。

もしも指定したデータが28バイトを超える場合は、アクティブ・スキャンでペリフェラルが返すパケットの空き領域10バイトが使われます。ただし、この領域を使えるのは、ローカルネームだけです。 そのため、アクティブ・スキャンをしない限りローカルネームを見つけられません。

iOSアプリケーションがバックグラウンド状態にあると、全てのサービスUUIDsは、オーバフロー領域に置かれます。またローカルネームはアドバタイジングされません。

これらの制約から、アドバタイジングするサービスUUIDは、プライマリ・サービスを特定するものだけにしておくことを勧めます。

  • ユーザにアドバタイズメントするか決めさせる

トラブルの原因切り分けと対応

Appcessoryという単語が示す通り、Bluetooth LEデバイスとiOSアプリケーションそしてネットワークの先にあるサービスが連携して初めて、魅力あるBluetooth LEデバイスという製品になります。しかし、意図せぬ振る舞いが、開発中また販売後に起きるかもしれません。ここでは、iOSアプリケーションとBluetooth LEデバイスが意図しない振る舞いをすることを、トラブルと呼びます。

トラブルが発生した場合は、原因の切り分けが必要です。そのためには、トラブルを再現する方法や発生条件を明らかにすることが必要です。これらのタスクは、ユーザから情報を集めるような対人タスクと、与えられた条件から現象を論理的に分析する純粋な技術タスクに分けられます。具体的なトラブルは予測困難ですが、発生しうるトラブルの種類を列挙して、タスクの割り振り役および分野ごとの担当者などを、開発チームで事前に決めておくことが重要です。

トラブルの原因が設計によるものであれば、それは、iOSアプリケーション単体によるもの、Bluetooth LEデバイス単体によるもの、および両者の振る舞いの組み合わせによるもの、の3通りです。また、それが分析できるのはBluetooth LEデバイスの設計者か、iOSアプリケーション開発者のいずれかです。

ほとんどのトラブルは、iOSアプリケーション開発者の初歩的なミスが原因です。例えば、CBPeripheralのインスタンスをリテインし忘れたために接続が解除された、のようなケアレスミスによるものが多いです。開発過程での、ケアレスミスによるトラブルで発生するやりとりを防止するには、Bluetooth LEデバイス開発側が、iOSアプリケーションのCore Bluetoothフレームワークを使う部分まで含めて、開発を担当することが有効です。

iOSアプリケーションとBluetooth LEデバイスの振る舞いの組み合わせがトラブルの原因の場合は、両者の振る舞いをログを取りながら突き合わせるか、あるいはBluetooth LEの通信パケットをロギングして解析します。いずれの場合も、iOSとファームウェアおよび通信の知識が不可欠になるため、領域ごとの担当者が個別に担当するのではなく、同じ場所と同じ時間を共有して対処にあたるように、事前に決めておくことが重要です。

使用しているBluetooth LEデバイスが、市販品とその付属ライブラリを利用しているのか、または独自に設計しているのかで、対処が異なります。市販品の場合は、ライブラリのソースコードが公開されているならば、ソースコードを読むことが原因を見つける早道です。もしもソースコードが公開されていないならば、Bluetooth LEの通信パケットをロギングして解析するほかありません。独自に設計したデバイスを使用しているならば、ファームウェアのソースコードもつきあわせて、開発者間で振る舞いを1つ1つ確認していくのが、早道です。

開発環境とターゲット

4S以降、環境iOS5以降
Xcode

参考情報源

https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/AboutCoreBluetooth/Introduction.html

https://devforums.apple.com/community/ios/core/cbt

https://lists.apple.com/mailman/listinfo/bluetooth-dev

CoreBluetoothフレームワーク

セントラルとペリフェラル
iOS5とiOS6

クラス構造

CBCentralManager

雑渡した使いかた、引数の意味。
セントラル
デバイスの発見、接続
サービスおよびキャラクタリスティクスの発見

排他制御ってどうなっている。
本体の二つのアプリから同時に接続出来ちゃったりするのかしら。

UUIDの意味

CBPeripheralManager

ペリフェラル

使いかた

オブザーバ

アドバタイジング・パケット。受信。
UUID、Duplicate key。

バックグラウンド

消費電力、頻度。BGで一二分に一回とか。
他のアプリが動いていると、とか。

デバイスの管理

実際の開発例

センサータグで
オリジナルの機器、スニッフィング

123456789012345678901234567890123456789012345678





























1===============================================

Bluetotoh LEに対応するiOSデバイスとOSのバージョン

Bluetooth LEに対応するには、ハードウェアとOSがそれぞれ対応しなければなりません。

Bluetooth LEはBluetooth4の規格の一部です。スマートフォンのハードウェアの仕様に、Bluetooth4対応と書かれていれば、それはBluetooth LEにも対応しています。iOSデバイスは、2011年10月に発売されたiPhone4S以降の全てのデバイスがBluetooth4に対応しています。2013年8月時点で、Bluetooth4に対応するiOSデバイスは、iPhone4S、iPhone5, 第5世代iPod touch、第3および第4世代iPad、そしてiPad miniです。

Bluetooth LEに対応するOSのバージョンは、iOS5およびiOS6です。Core Bluetoothフレームワークは、iOS5以降のSDKに含まれています。初めてBluetooth LEをサポートしたiOS5はセントラルの機能を提供しています。次のiOS6では、セントラルに加えてペリフェラルの機能にも対応しました。セントラルおよびペリフェラルについては次の節で述べます。

iOS5およびiOS6は、iPhone3GSおよびiPhone4などの、ハードウェアがBluetooth4に対応していないiOSデバイスにも対応しています。Core Bluetoothフレームワークは、アプリケーションが実行されたデバイスがBluetooth LEに対応しているかいなかを、デリゲートを通してアプリケーションに知らせます。

Bluetooth LEを使うアプリケーションの振る舞いが、機種により異ならないかは、大きな関心事です。もしも振る舞いが異なるならば、どのiOSデバイスでどのように異なるのか、またその理由を理解して、テスト項目に入れなければなりません。筆者の知る範囲では、iOSデバイスの機種間で振る舞いが異なることはありません。

デバイスごとの差異が生じるうる要素には、アンテナ設計、無線通信の半導体およびその内部で動作するファームウェア、そしてiOS側の通信制御プログラムがあります。

アンテナは機種ごとに異なります。アンテナ自体の利得および指向性が異なるかもしれません。Bluetooth LEで通信をするだけであれば、利得の違いは通信可能距離の大小として見えます。検出される電波強度の絶対値が異なりますから、電波強度を用いる応用例では、その違いが振る舞いを変えるかもしれません。例えば、電波強度による近接検出ならば、近接と判断する距離が機種により異なってくるでしょう。

無線通信の半導体は、Bluetooth規格に従い実装されるので、半導体による機能の違いはないはずです。またiFixitが報告しているiPhoneの内部構成をみると、無線通信の半導体は、iPhone4SとiPad3はBroadcom社のBCM4330、iPhone5とiPad miniとiPod touch 5th Genは、同じくBroadcom社のBCM4334を採用しています。BCM4330からBCM4334の変更点は、半導体の製造プロセスが65nmから40nm LPに変更され、受信動作時のピーク電流が68mAから36mAに半減したことです。ですから、ただし、受信動作による電池消費量は、機種によって2倍違うでしょうが、ハードウェアの機能の違いはないでしょう。

iOS側の通信制御プログラムは、iOSのメジャー番号が変わると異なることがあります。またマイナーバージョンで、バグの修正や動作をより安定にする変更が入ります。動作保証対象となるiOSのバージョンごとに、動作の確認が必要です。

BLEの通信仕様

Bluetooth Low Energyの無線通信技術

この章は、組み込み機器開発およびiOSアプリケーション開発に必要な無線通信技術を要点をおさえて解説します。

Bluetooth Low Energy(以下、Bluetooth LE)の無線技術の知識は、独自のBluetooth LEデバイス設計に必要です。また、iOSアプリケーション開発に使うCoreBluetoothフレームワークのクラスや使い方そして振る舞いの理解に、役立ちます。

さらに詳細にBluetooth LEの無線通信技術を知りたいときは、Robin Heydon, Bluetooth Low Energy: The Developer’s Handbook, Prentice Hall, 2012、を参照してください。

Bluetooth4.0の仕様書は、PDF形式でだれでも無償で入手ができます。しかし、それは2000ページを超える膨大なものです。Bluetooth4.0の半導体やスタックを設計するのではないのですから、仕様書は読むものではなく、詳細な数値や記述を調べる辞書として利用します。

Bluetooth LEのアーキテクチャ

Bluetooth LEのアーキテクチャを階層表示したのが図です。通信技術は異なる機種間でデータをやりとりする技術です。0/1のビット・データのやりとりにはじまり、接続管理やビット・データのかたまりの意味づけなど、いくつもの役割が組みあわさって、はじめて通信ができます。Bluetoothは、通信の手順やデータ構造をプロトコル、機器の振る舞いをプロファイル、と呼びます。

物理層からGATTまでの階層は、それぞれ下層の機能を使って、相手の同じ階層のプロトコルと通信します。ジェネリック・アクセス・プロファイルは、機器の振る舞いを定義します。機器の振る舞いは、物理層からGATTまでのレイヤそれぞれの役割を通じて実現されるので、ジェネリック・アクセス・プロファイルは、特定のレイヤのものではなく、物理層からGATTまでの全てのレイヤに影響を与えます。物理層からGATTまでの階層は、相手の同じ階層のプロトコルと通信します。レイヤの役割は以下のとおりです:

  • Generic attribute profile (GATT, ジェネリック・アトリビュート・プロファイル)
    • サービス/キャラクタリスティクスを提供。
      -Attribute protocol(ATT, アトリビュート・プロトコル)
    • アトリビュートという値をやりとりする
  • Logical Link Control and Adaptation Protocol(L2CAP, ロジック・リンク・コントロール・アンド・アダプティブ・プロトコル)
    • 論理チャンネルの提供
  • Logic link(ロジックリンク)
    • 近接デバイスの発見と接続、通信
  • Physical layer(物理層、フィジカル・レイヤ)
    • 無線通信

コントローラとホスト

Blueoothのアーキテクチャは、コントローラ、ホストそしてアプリケーションの3つに分けられます。コントローラは、電波の送受信、パケット通信、接続管理をおこないます。高周波回路とその制御回路、専用回路またマイクロコントローラで実行されるソフトウェア、で実装されます。ホストは、Bluetoothのプロトコルやプロファイルごとの通信の多重化など、Bluetoothの複雑な通信機能を提供します。アプリケーションは、ユーザが作るアプリケーションです。この2つはソフトウェアです。

ホスト・コントロール・インタフェース

コントローラとホストの間に、Host Control Interface (HCI)という、コマンドとデータの通信仕様があります。HCIはコントローラとホストを、論理的に、また物理的に分離します。ホストとコントローラを1つの半導体に実装する1チップ構成と、別々の半導体で実装する2チップ構成のいずれかがとれます。

1チップ構成は、半導体チップの中にあるマイクロコントローラで実行されるソフトウェアで、コントローラの制御部分とホストが実装されます。このときHCIは、2つのソフトウェアを接続する(ライブラリ呼び出しなどの)、論理的な接続仕様として使われます。1チップ構成では、チップ内部のマイクロコントローラはユーザのアプリケーションも実行します。

コントローラをモジュール化すると、2チップ構成になります。モジュールとは、半導体や高周波回路の素子などを小さな基板に実装して部品化したものです。電波を出力する機器は各国の電波法の承認を取得しなければなりません。この電波を出力するモジュールが承認を取得していれば、機器本体で承認を取る必要がなくなる、メリットがあります。ホストは別のプロセッサで実装されます。ホストはモジュールのコントローラと、シリアル端子やSPI、USBなどの物理インタフェースを通じて、HCIで通信します。

規格にHCIがあるので、2チップ構成でも、どのメーカのコントローラでも共通に使えるようになります。例えば、パソコンでは、ホストはBluetoothスタックと呼ばれるソフトウェアで提供されます。どのメーカのBluetooth USBドングルでも、USBを通してHCIで通信することで、同じように使えます。

どのようなチップ構成でも、ユーザのアプリケーションは、ホストと同じプロセッサで実行されます。ですから、HCIの場合と違い、ホストとユーザアプリケーションの間のインタフェースは、規格に定義がありません。メーカーやOSなどの提供側が決めるもので、開発環境に依存します。

どちらの構成をとるかは、状況によります。機能が単純な周辺機器ならば、製造コストを最小にするため1チップ構成をとるでしょう。機能が複雑だったり、既存設計にBluetooth LEの通信機能を追加するなどで、1チップ構成ではユーザアプリケーションを処理できないならば、2チップ構成を選びます。モバイル機器やパソコンなど、モジュールを採用すれば、おのずと2チップ構成になります。

ホストのLogical Link Control and Adaptation Protocol(L2CAP)は、通信の多重化を行うレイヤです。2つのデバイス間のデータ通信を提供するコントローラを通じて、プロトコルごとに、指定したチャネルごとの独立した通信を提供します。頭文字で略すとLLCAPですが、Lが2つ並ぶのを数字で表して、L2CAP、と表記します。

iOSデバイスのBLEアクセサリの設計指針は、 スレーブからマスターへの通信パラメータの伝送は、L2CAPの接続パラメータアップデートで伝えるべきとしています。この設定を要求するのは周辺機器のみです。 設定できるパラメータは、通信を行う周期を与えるコネクション・インターバル、接続が失われたと判定するスーパービジョン・タイムアウトなどです。

周辺機器は、通信頻度や電池消費量のバランスがとれるように、適切な場面で適切なパラメータを設定します。例えば、周辺機器がiPhoneに初めて接続したときに、まとまった量のデータを一気に送信したいならば、インターバルを短くして実効通信速度をあげて、短い時間でデータを送ります。接続したあと、電池消費量をおさえたいならば、インターバルを長くします。

Attribute Protocol(ATT)とGeneric Attribute Profile(GATT)

iOSアプリケーション開発者が実装で直接触れるのは、ホストの上位層にある、Attribute Protocol(ATT)とGeneric Attribute Profile(GATT)です。このレイヤは、Bluetooth LEのサービスおよびキャラクタリスティクスを定義しています。

Bluetooth LEの周辺機器は、サーバとして、センサの値、動作設定値、内部状態などを公開しています。Bluetooth LEは、ある機能をサービスという単位にまとめます。1つのサービスは複数のキャラクタリスティクスを持ちます。例えばエアコンであれば、室内温度というサービスを作り、そのなかに気温センサの値というキャラクタリスティクスをもたせる設計をしたりします。

ジェネリック・アクセス・プロファイル

ジェネリック・アクセス・プロファイル(Generic Access Profile, GAP)は、すべてのBluetoothデバイスが実装すべき、ベースプロファイルの実装です。GAPは、物理層からGATまでのレイヤをまたいだ、デバイスの振る舞いを提供します。

GAPは4つのデバイスの役割を定義します:

  • ブロードキャスター
  • オブザーバ
  • ペリフェラル
  • セントラル

Bluetoothデバイスは、コントローラが対応していれば、この4つのどの役割になれます。しかし、同時に2つ以上の役割になることはできません。

ブロードキャスターは、送信するだけのアプリケーションです。Bluetooth LEのアドバタイジングで、データをブロードキャストします。ブロードキャスターは送信機能のみが必要で、受信機能は必要ありません。またコネクションをサポートしません。オブザーバは、受信するだけのアプリケーションです。オブザーバには受信機能のみが必要で、送信機能とコネクションは不要です。

ペリフェラルは、単1の接続をサポートします。コントローラのスレーブのみをサポートします。セントラルは、すべてのペリフェラルとの接続を開始して、複数の接続を管理するものです。

役割により必要な機能が異なるので、必要な機能だけを実装することで、製造コストの削減などができます。例えばブロードキャスターであれば受信回路やその制御機能をすべて削除することもできます。しかし実施には、設計開発から販売管理までを含めたコストから、セントラルに対応したデバイスのみが販売されています。

隣接デバイスとの無線通信

コントローラの機能は、隣接するデバイスとの通信です。Bluetooth LEのコントローラは、電波を送受信する物理層、隣接するデバイスとの接続とパケット通信を管理するリンク・レイヤ、そしてホストのインタフェースHCIで構成されます。Bluetooth LEの物理層の特性をに示します。

項目
周波数 2.400-2.4835 GHz
物理層のビットレート 1Mbps
通信送信電力 10mW ~ 10μW
伝達距離 150m (見通し)

周波数帯

Bluetooth LEが利用する周波数は、クラシックBluetoothと同じ2.4GHz帯を使います。周波数帯が同じなので、クラシックBluetoothとBluetooth LEを両方搭載したものでも、同じアンテナや高周波回路を使えます。

この2.4GHz帯は、ISM (Industry-Science-Medical、アイエスエム) バンドと呼ばれるいくつかある周波数帯の1つです。ISMバンドは、産業、科学、医療、という名称が示すように、もともとは無線通信以外の産業・科学・医療に高周波エネルギー源として利用するために指定された周波数帯です。身近なものでは、電子レンジは加熱に2.45GHzの周波数を使います。

電波は公共の資源ですから、その周波数は区分されて利用目的が定められています。ですから、新しい無線機器を普及させて一般で使うには、周波数区分という資源の割り当てが必要です。また、国際展開のために、それが国際的に認められて各国が受け入れることも必要です。

新しい周波数区分の割り当ては、大変な手間と時間がかかります。そのため、Bluetoothに限らず無線LANなどの空中線電力の小さい高度な通信装置には、2.4GHz帯をはじめとするISMバンドの利用を認めてきました。

Bluetoothが利用する周波数帯はBluetooth専用ではありません。無線LANをはじめとする様々な通信装置や電子レンジも利用していますから、他の機器からの混信が常に生じうる、賑やかな周波数帯です。

Bluetooth LEは、同じ周波数帯を利用する無線機器があることを前提に設計されています。混信を与えないように、また通信が頻繁に切断しないように、80MHzの周波数帯を2MHz幅に分割したチャネルと、適応周波数ホッピング方式を導入しています

変調方式

情報を送るために電波の波形を変化させることを変調と言います。変調は、送信する電波、情報を搬送する波なので搬送波と呼びます、の波の大きさ(振幅)または周波数および位相を時間変化させることです。この変調は0/1のデジタルデータを、四方八方に広がっていく電波にのせる技術です。

Bluetooth LEの変調方式は、に示すGaussian Frequency Shift Keying (ガウシアン周波数シフトキーイング、GFSK) という連続位相周波数偏移変調方式の1つです。周波数シフトキーイングは、0/1のビット・データごとに、一定の周期で周波数の遷移(シフト)量を切り替える(キーイング)変調方式です。

のように、250kHzの周波数偏移では、1マイクロ秒後に搬送波の位相が90度変化します。250kHzの周期は4マイクロ秒ですから、1マイクロ秒は1/4周期だからです。搬送波に比べればごく僅かな周波数遷移ですが、1マイクロ秒後たてば振幅の符号が正負に分かれるため、容易に情報を取り出せます。この1つの信号をシンボルと呼びます。Bluetooth LEのシンボル・レートは1Mbpsです。1シンボルは、振幅が正負どちらかにわかれる2状態なので、1シンボルで1ビットが伝送できます。ですからビット・レートは1Mbpsになります。

GFSKのガウシアンは、0/1の信号をガウスフィルタを通して滑らかにした波形で、周波数遷移をおこなうことから名づけられています。0/1の変化そのままで周波数遷移をすると、0/1の変化点で搬送波が急激に変化して、周波数占有幅が大きくなります。これは周波数帯域を効率よく使うための技術です。

物理層のビットレートは将来引き上げられるかもしれません。しかし、Bluetooth LEでは、ビットレートの向上は、より速い通信ではなく、さらなる低消費電力のためです。例えば、ビットレートが現在の1Mbpsから2倍の2Mbpsになれば、送受信回路の稼働時間が半分になります。半導体素子の電力の大部分が2.4GHzの高周波信号を送受信する回路で消費されますから、これは電池の稼働時間を大きく伸ばします。

チャンネル

電波での無線通信は、送信側と受信側がいること、それらが同じ時間に周波数を使っていること、で成り立ちます。また電波は四方八方に広がるので、自分たちの通信が他の通信の混信を引き起こしたり、逆に混信をうけます。

Bluetooth LEは、心拍センサや時計などの一般に普及する身に付けるデバイスで利用されます。そのために、公共の場所など、自分とその周囲の人たちが持つデバイスをあわせると、100や1000のデバイスが同時に通信をしている場面も生じます。さらに、Bluetooth LEを搭載するようなモバイル機器は無線LANも搭載しています。それらの無線通信との共存も必要です。

Bluetooth LEは、2.400 GHzから2.480GHzまでの80MHzの帯域を、2MHz幅で分割して、40のチャンネルとします。送信側と受信側が同じチャネルを使っていれば、同じ周波数をつかうことになります。また、あるチャンネルの通信は、周波数が十分離れていますから、隣接するチャンネルまたその他のチャンネルの通信に影響しません。ですから、違うチャンネルを利用すれば、複数の機器が同時に通信をしていても混信が生じません。

40のチャンネルには、それぞれ0から39までの番号が割り振られます。チャンネルは2種類にわけられます。チャンネル37からチャンネル39までの3チャンネルをアドバタイジング・チャンネル、チャンネル0からチャンネル36までの37チャンネルをデータ・チャンネルと呼びます。

アドバタイジング・チャンネルは、デバイスの発見と接続に使います。データ・チャンネルは、接続が完了したデバイス同士の通信に使います。アドバタイジング・チャンネルは、3つのうちどれか1つのチャンネルで通信ができるならば、機能します。データ・チャンネルは、適応周波数ホッピング方式と呼ばれる、通信につかうチャンネルを時間で次々に切り替えていく方式をつかうので、一部のチャンネルで混信をうけても通信は断絶しません。

Bluetooth LEを搭載するモバイル機器は、たいてい無線LANを搭載しています。そして、まずはデバイスを発見できなければ、通信がはじまりません。そのため、デバイスの発見に使うアドバタイジング・チャンネルには、無線LANと混信しにくいチャンネルが、3つ割り当てられています。

無線LANは、1チャンネルあたりの帯域が20MHz、14のチャネルを、5MHzづつずらして、隣接するチャンネルと帯域を重ねて配置されています。無線LANでよく使われるチャンネルは、1、6,および11で、それぞれの中心周波数は2.412 GHz、2.437 GHz、および2.462 GHzです。

これにBluetooth LEのチャンネルを重ねるとになります。
無線LANにかぎらず任意の無線通信の混信を避けるには、2.4GHzの帯域内の、なるべく離れたチャンネルを使うしかありません。チャンネル37 (2.402 GHz)とチャンネル39 (2.426 GHz)は、帯域の端に割り当ててあります。もう1つのチャンネル38 (2.480 GHz)は、帯域のなるべく中央で、かつ無線LANのチャンネルと重ならないものが選ばれています。

アドバタイジング・チャンネルの数が3よりも多ければ、より混信を避けられそうです。しかしそうすると、非接続時の周辺機器の電池消費量が、チャネル数分だけ増えてしまいます。周辺機器は、自分の存在を周囲に伝えるために、アドバタイズメント・パケットを送信します。アドバタイズメント・パケットは、一定の周期ごとに、3チャネルそれぞれで送信します。ですから、もしもアドバタイジング・チャネルを、2倍の6チャネルにすると、非接続時の周辺機器の電池消費量も単純に2倍になるでしょう。

パケット・フォーマット

通信でつかわれる、制御情報とデータを含む連続したビットのかたまりを、パケットと呼びます。Bluetooth LEのパケットのフォーマットはです。オクテットは8ビットの情報を表す単位です。通常使うバイトと同じ意味ですが、機種によっては8ビットではないバイトもありうるのです。そのため、通信では、曖昧さがない8を表すオクテットを使います。

パケットは、アクセス・アドレス、上位階層が送受信するデータであるプロトコル・データ・ユニット(Protocol Data Unit, PDU)とその誤り検出用の巡回検査符号(Cyclic Reundancy Check, CRC)の3つのエンティティで、構成されます。

パケットの長さは、80から376ビット(10 ~ 47オクテット)、ビット・レートが1Mbpsなので、80から376マイクロ秒の範囲になります。マイクロ(ギリシャ文字 μ と表記します)は10-6を示す接尾語です。1 / 1Mbpsで1マイクロ秒です。Bluetooth LEは、センサ値など少量のデータを決まったタイミングで通信するものなので、この最大376マイクロ秒の短いパケットのみで、通信がおこなわれます。

パケットは、エンティティごとに最下位ビットから最上位ビットの順に送出されます。バイトオーダは、リンクレイヤではなく、上位プロトコルによります。アドバタイジング・パケットは、最上位バイトから最下位バイトの順(ビッグエンディアン)に送出されます。デバイスとデータをやり取りするのに使うGATTプロファイルは、最下位バイトから最上位バイトの順(リトルエンディアン)に送出します。PDUを直接読み書きするときは、このネットワーク・バイトオーダーに注意します。

パケットの先頭のプリアンブルは、信号の強さと0/1のビットを読み取るタイミングの検出に使われます。

プリアンブルは、0/1が交互に続く8ビットの値 10101010b または 01010101b (接尾語 b は2進数表記を示す)です。2つの値のうち、末尾のビットが続くアクセス・アドレスの先頭ビットと異なるもの、境界に同じビット00bまたは11bが連続しないもの、が送出されます。

Bluetooth LEは、受信回路のダイナミック・レンジ(受信可能な最大の信号と最小信号の比)が80dBあります。つまり受信電力で8桁、電圧で4桁の範囲の信号を受信します。このダイナミック・レンジは、受信信号が信号の強さに応じて増幅率を自動で調整することで、実現されます。もしもプリアンブルがなければ、増幅率を調整している間に、データを含む信号が来てしまい、取りこぼしてしまいます。

プリアンブルは、信号の読み出しタイミングの同期に必要です。変調信号の0/1の変化し終わったタイミングがわかれば、シンボル・レートが1Mbpsとわかっているので、あとは1マイクロ秒ごとに値を読み出すだけです。プリアンブルの0/1を繰り返す波形で、読み出しタイミングを同期します。

アクセス・アドレスは、2つのデバイス間で接続ごとに割り振られるランダムな値です。アクセス・アドレスは、そのパケットがどの接続のものかを区別する識別子で、物理インタフェースとは無関係のランダムな値です。イーサネットにあるような、物理インタフェースに割り振られた唯一の固定のアドレスではありません。

アクセス・アドレスは32ビットありますが、実際に利用できるアドレスは31ビット分です。ですからピコネットの最大同時接続数は、アドレス数の制約からは、231になります。31ビットに制約されるのは、周波数変調信号の復調回路に、受信信号から自分の周波数のずれを自動調整する機能があるからです。000…0bや111…1bのように同じ値が連続すると、同じ周波数がずっと受信されます。すると、この自動調整機能の働きで、復調の基準とする周波数がずれていき、ただしい復調ができなくなります。これを避けるために、アクセス・アドレスは、32ビットの任意の6ビットをとりだしたとき、うち2ビットで0/1が変化している値を使います。

プロトコル・データ・ユニット(Protocol Data Unit, PDU)が、パケットが運ぶデータです。2オクテットから39オクテットの長さがあります。もしもPDUに連続する0/1があると、アクセス・アドレスとおなじ不都合が生じます。それを避けるために、PDUにはホワイトニングという、0/1を適当に変化させる変換処理がおこなわれます。これは送受信回路内部で処理されるので、アプリケーション側からは見えません。

CRC(Cyclic Redundancy Check, CRC)は、PDUのエラー検出のための巡回検査符号です。2および4ビットまたは奇数個のエラービットがあるとき、誤りだと検出できます。つまり、1,2,3,4,5,7,9… つのビット・エラーを検出できます。より強力な誤り検出が必要なときは、PDUの暗号化を使います。PDUが正しく暗号化されているかをチェックするためのメッセージ・インテグリティ・チェック(Message Integrity Check, MIC)が追加されます。

発見と接続

リンク層は、電波が届く範囲にある隣接デバイスの発見と、発見したデバイスとの接続および双方向通信を提供します。これらの仕組みと処理の流れを、通信制御の仕組み、やりとりするPDUのフォーマット、そしてパケットの送受信の手順とタイミング、の3点から述べます。

デバイスの発見とピコネットへの参加

デバイスの発見と接続の流れは、リンク層の状態遷移図を使うとわかりやすくなります。リンク層には5つの状態:

  • スタンドバイ(Standby)
  • アドバタイジング(Acvertising)
  • スキャニング(Scanning)
  • イニシエーティング(Initiating)
  • コネクション(Connection)

があります。スキャニングには、パッシブ・スキャン(Passive Scan)とアクティブ・スキャン(Active Scan)またコネクションには、マスター(Master)とスレーブ(Slave)のサブステートがあります。

スマートフォンが周辺機器に接続するまでの、状態遷移を見てみます。周辺機器がアドバタイジング・パケットを送信して、スマートフォンがそれを受信してデバイスを発見、そして接続をします。この時、スマートフォンの働きをするものをスキャナ(Scanner)、周辺機器の働きをするものをアドバタイザ(Advertiser)と呼びます。

スキャナとアドバタイザのリンク層は、どちらも最初はスタンドバイ状態にあります。スタンドバイ状態は、送受信を何もしません。

アドバタイザはアドバタイジング状態に遷移して、一定期間ごとにアドバタイジング・パケットを送信します。

スキャナはスキャニング状態に遷移してアドバタイジング・パケットを受信します。アドバタイジング・パケットを受信するだけなのが、パッシブ・スキャンです。アクティブ・スキャンは、アドバタイジング・パケットを受信したあとに、アドバタイザにリクエストを送り、さらなる情報を取得します。

スマートフォンがスキャンして得た情報から、接続先を決めます。スマートフォンはイニシエイティング状態に遷移します。この時のスマートフォンの役割をイニシエータと呼びます。イニシエータは、接続したいアドバタイザからのアドバタイジング・パケットを受信したあとに、接続要求を送信して、コネクション状態に遷移します。

ピコネットは、1つのマスターに複数のスレーブが接続するスター型のネットワークです。スレーブ同士が通信することはありません。接続が完了すると、イニシエータはマスター、アドバタイザはスレーブの役割になります。

状態遷移の経路から、スレーブには以下の制約があります:

  1. 同時にマスターかつスレーブには、なれない。
  2. スレーブは同時に2つ以上のマスターと接続しない。

スキャナやアドバタイザといった役割は、リンク層の制御機能で作られます。ですから、たいていのBluetooth LEの半導体では、ソフトウェアでどの役割をもたせるかが決められます。スマートフォンがアドバタイザになることも、また周辺機器がマスターでスマートフォンがスレーブになるピコネットを作ることもできます。

また、1つのコントローラが同時にスレーブかつマスターになることはできません。コネクション状態に遷移したスレイブはアドバタイジング・パケットを送出しませんから、スレーブは同時に2つ以上のマスターとは接続しません。スレーブは、かならず1つのピコネットに属します。

アドバタイジング・パケット

デバイスの発見のためにアドバタイザが送信するパケットが、アドバタイジング・パケットです。

周波数帯域は、チャネル37, 38, 39、の3チャネルを使います。デバイスが確実に発見されるように、3つのチャネルをすべて使うことが推奨されます。アドバタイジングに使うチャネルは、アドバタイザのファームウェアから設定できます。デバックで無線通信を傍受(スニッフィング)するときは、スニッフィングをしやすくするために、チャンネルをどれか1つに限定することがあります。

アドバタイジングでの通信は、非接続の同報通信です。リンク層のアクセス・アドレスは、固定値 10001110100010011011111011010110b (0x8E89BED6) が使われます。

アドバタイジング・パケットは、一定周期のアドバタイジング・イベントごとに送出されます<TBDアドバタイジングイベントのタイミング/>。アドバタイジング・イベントごとに、アドバタイジング・パケット(ADV_IND と表記している)が、チャンネルごとに送出されます。チャンネルごとのパケット送出時間は10ミリ秒以下です。

イベントの周期 T_advEvent は:

$$T_advEvent = advInterval + advDelay$$

と表されます。

advIntervalはアドバタイジングの周期です。20ミリ秒から10.24秒までの、0.625ミリ秒の整数倍の値を設定します。advIntervalが短いほど、デバイスは発見されやすくなりますが、電波を送信する分、電池の消費量が大きくなります。この値設定は、アドバタイザのファームウェア設計次第です。例えば、電源を入れて30秒間は、ユーザが接続をしようとしているだろうから20ミリ秒で、接続されないままならば、その後は徐々に周期を長くして、デバイス発見に少し時間はかかるが電池消費量は抑える、処理にします。

advDelayは0から10ミリ秒のランダムな値です。もしも、全く同じ開始タイミングで同じ周期のアドバタイジングをしているデバイスが2つあると混信しつづけます。そのような状況を避けるために、アドバタイジング・イベントをランダムにずらしています。

スキャン

アドバタイジング・パケットのペイロードは37オクテットの情報を送れます。しかしアドバタイザの情報は、たいてい、このペイロードだけでは不足します。アドバタイザからより多くのデバイス情報を引き出すのが、スキャンです。

のスキャニングには、パッシブ・スキャンとアクティブ・スキャンがあります。パッシブ・スキャンはアドバタイザのパケットを受信するだけのスキャンです。アクティブ・スキャンは、アドバタイジング・パケットの終了から150マイクロ秒後に、SCAN_REQパケットを送信します。スキャン・リクエスト(SCAN_REQ)パケットを受信したアドバタイザは、アドバタイジング・パケットと同じ37オクテットのペイロードがあるスキャン・レスポンス(SCAN_RES)パケットを、150マイクロ秒後に返します。

アドバタイジング・パケットとSCAN_RESパケットは同じデータフォーマットに従います。ここで、アクティブ・スキャンは、パケットをやり取りする分だけ電力を消費します。そこでSCAN_RESパケットは、時間で変化しない情報を納めるものとします。スキャナが読み取ったSCAN_RESパケットをキャッシュすることで、スキャンは1度だけですむようにします。SCAN_RESパケットには、アドバタイザが持っている機能を表すサービスの識別子やデバイスの名称などが置かれます。

アドバタイザの周囲には複数のスキャナがいるときに、いくつかのスキャナが同時にSCAN_REQパケットを送信すると、パケットが衝突して、アドバタイザはパケットを正しく受信できなくなります。スキャナは、レスポンスが返ってこないときは、SCAN_RESパケットの送信を、ランダムな回数で間引いていきます。

ペイロードのフォーマット

アドバタイジング・パケットのPDUは、2オクテットのヘッダと6〜37オクテットのペイロードがあります<TBDパケットフォーマット/>。このヘッダの、PDU TypeはPDUの種類を示します。TxAdd, RxAddはPDU Typeにより異なる意味を持ちます。Lengthはペイロードの長さをオクテット単位で示します。

PDUタイプ 役割 略語
0000 Connectable undirected advertising ADV_IND
0001 Connectable directed advertising ADV_DIRECT_IND
0010 Non connectable undirected advertising ADV_NONCONN_IND
0011 Scan request SCAN_REQ
0100 Scan response SCAN_RSP
0101 Connection request CONNECT_REQ
0110 Scannable undirected advertising ADV_SCAN_IND

ヘッダのPDU Typeは7タイプあります。このうち、アドバタイジングにつかうのは4タイプです。PDUタイプは、わかりやすく、短い略語であらわします。役割にあるConnectableは、接続要求ができることを、undirectedは不特定多数のデバイスへのアドバタイジング、directedは特定デバイスへのアドバタイジング、またScannnableは、のちに述べるスキャン要求ができること、を示しています。

PDUタイプ 無向/有向 スキャン要求 接続要求
ADV_IND 無向 できる できる
ADV_DIRECT_IND 有向 できない できる(特定デバイス)
ADV_NONCONN_IND 無向 できない できない
ADV_SCAN_IND 無向 できる できない

PUDタイプごとの機能をまとめたものがです。

ADV_INDとADV_DIRECT_INDは、接続を受け入れるタイプです。ADV_INDが、いわゆる通常のアドバタイジングです。不特定多数のデバイスにそのデバイスの存在をつたえて、接続要求があればそれを受け入れます。ADV_DIRECT_INDは、以前に接続したことのあるデバイスと高速に接続するためのタイプです。

ADV_NONCONN_IND と ADV_SCAN_IND は、接続要求を受け入れないタイプです。位置ビーコンのように、アドバタイジング・パケットのみで情報をブロードキャストするものに使います。

パブリック・デバイス・アドレスとランダム・デバイス・アドレス

ADV_INDタイプのペイロードは、6オクテットのアドバタイザのアドレスと、0から31オクテットのアドバタイジング・データとで構成されます

パケットのアクセス・アドレスは、通信ごとにランダムな値です。ADV_INDのペイロードにある、この48ビットのアドレス AdvA は、デバイスを特定するアドレスです。このアドレスには、
パブリック・デバイス・アドレス(Public device address)とランダム・デバイス・アドレス(Random device address)の2種類があります。AdvAがいずれかは、ヘッダのTxAddの値で示します。TxAddrが0ならばパブリック・デバイス・アドレス、1ならばランダム・デバイス・アドレスです。

の、パブリック・デバイス・アドレスは、Bluetooth SIGが企業ごとに発行した24ビットの識別子と、企業が製品1つづつに割り振る24ビットの識別子から構成されます。このアドレスは、製造時に書き込まれるデバイスに固有で唯一の値です。ランダム・デバイス・アドレスは、ハッシュ値とランダム値から構成されます。ランダム値は適当な一定時間ごとに変更されます。ハッシュ値は、デバイスが持っている128ビットのIRK(Identity Resolving Key)とランダム値から指定されたハッシュ関数で計算される値です。

ランダム・デバイス・アドレスはプライバシーを守るためにあります。パブリック・デバイス・アドレスは、デバイスそれぞれに割り振られた固有の値です。個人が持っているデバイスが、何かのきっかけでマスターとの接続が切断したとします。すると、デバイスは再接続をするためにアドバタイジングを始めます。このアドバタイジング・パケットは、誰でも傍受できます。ですから、アドバタイジング・パケットのアドレス AdvA から特定のデバイスの追跡が可能です。

ランダム・デバイス・アドレスを受信したスキャナは、事前にアドバタイザから受け取ったIRKを使い、ハッシュ値を計算します。このハッシュ値が、アドバタイジング・アドレスのハッシュ値と同じならば、それが目的のデバイスだとわかります。

アドバタイジング・データのフォーマット

Generic Access Profile(ジェネリック・アクセス・プロファイル、GAP)は、デバイスの発見と接続、そして通信データを暗号化するときは、鍵の交換をどのように行なうかを定義するものです。このGAPは、振る舞いを定義するものなので、リンク層から後に登場するGeneric Attribute Protocol(GATT)まで、層をまたいだ定義になります。次節のアドバタイジング・データのフォーマットは、GAPのシナリオから定義されています。

のAdv Dataのフォーマットはです。アドバタイジングの1つの情報をAD structureという単位で、それらの配列になっています。AD Structureは、1オクテットのLengthと、それに続くLengthオクテットのDataです。Adv Datの長さは、1パケットのペイロードの制約で、31オクテットまでです。

AD structureのDataは、1オクテットのAD typeと(Length-1)オクテットのAd Dataで構成されます。AD typeは、いくつもありますが、知っておくべきものは次の5つです:

  • Flags
  • Local Name
  • Manufacturer Specific Data
  • TX Power Level
  • Service UUIDs
Flags

デバイスがもつ発見や接続の機能を示すのがFlagsです。AD typeの値は0x01です。ビット0/1は、それぞれ論理値false/trueに対応します。Flagsは、アドバタイジング・パケットに1つだけ含めます。

AD type ビット 記述
0x01 0 LE Limited Discoverable Mode
1 LE General Discoverable Mode
2 BR/EDR Not Supported
3 Simultaneous LE and BR/EDR to Same Device Capable (Controller)
4 Simultaneous LE and BR/EDR to Same Device Capable (Host)
5..7 Reserved

Bluetooth LEのみをサポートするシングルモード・デバイスは、BR/EDR Not Supported は’1’、Simultaneous LE and BR/EDR to Same Device Capable は、ホストとコントローラいずれも’0’になります。

Limited Discoverable Modeは、デバイスを発見できる時間制限があることを示します。接続が切れたからといって、不用意にアドバタイジングをさせたくない場合に使います。General Discoverable Modeは、常にデバイスが発見できるモードを示します。通常はこのモードを使います。

Local Name

Local name(ローカル ネーム)は、ユーザ・インタフェースの表示名などに使われる、ユーザが読めるデバイスの名称を示します。これはアドバタイジング・データかスキャン・データのいずれかに1つだけ含めます。文字列はUTF-8で符号化されます。C言語の’\0’のような、文字列の終端記号は必要ありません。

AD typeは、Shortened local name(ショーテンド ローカル ネーム)とComplete local name(コンプリート ローカル ネーム)の2つがあります

AD type 記述
0x08 Shortened local name
0x09 Complete local name

Complete local nameは、デバイスの完全な名前です。しかしAdv Dataは最大31オクテットですから、ローカルネームが29バイトよりも大きいと、収まりません。この場合には、Shortened local nameを使います。これは完全なデバイス名の先頭部分を取り出したものです。例えば、完全なローカルネームが‘BT_Device_Name’ならば、短縮したローカルネームは、例えば‘BT_Dev’となります。人間が読んで意味がわかる区切りで切り出すとよいです。

Shortened local nameが使われた時、完全なローカルネームは、上位層のGATTを通して、Device name characteristic から読み出せます

Manufacturer Specific Data

Manufacturer Specific Data(マニュファクチャラ スペシフィック データ)は、それぞれの企業の任意データに使われます。AD type は 0xFFです。Ad Dataは、先頭2オクテットが
Bluetooth SIGが企業に発行した識別子、そして任意長のバイナリ・データが続きます。企業の識別子はCompany Identifiers documentsにリストがあります。

位置ビーコンのような、非接続で周囲の不特定多数のBluetooth LEデバイスに同報するときに、データの格納に使えます。

Tx Power Level

送信電力を表します。AD typeは0x0Aで、Ad Dataは符号付きバイトの値のみがあります。値の単位はdBmで、-127 から +127dBm までの値を示します。

Tx Power Levelは、アドバタイザとの距離の推定に使います。この値と受信したパケットの受信信号強度(Received Signal Strength Indication, RSSI)から、伝搬損失(pass loss)は次の式で求められます:

$$ 伝搬損失 = Tx Power Level – RSSI $$

デバイス間の距離が離れるほど伝搬損失が大きくなりまが、残念ながら、電波の直接波と反射波の干渉で生じるフェーディング、アンテナの放射パターンなど、様々な要素により、伝搬損失は単純な距離の関数とはなりません。デバイスが近いか遠いかを推定する、ことには使えます。

Service UUIDs

Bluetooth LEのサービスは、機能を表します。例えば、エアコンであれば、温度設定といった制御値の入力機能、あるいは現在の室温といった計測機能が、それぞれ独立したサービスになります。機能の識別子にはUUID(Universally Unique Identifier)という128ビットの値が使われます。しかし128ビットそのままでは通信に時間がかかるので、Bluetooth SIGが承認したサービスには16ビットに短縮したUUIDが割り当てられています。

のAD typeは、アドバタイザが持っているサービスのUUIDの配列を示します。31オクテットに収まる範囲で、主たる機能を表すサービスのリストを列挙します。

概要 備考
0x02 16-bit Service UUIDs More 16-bit UUIDs available
0x03 16-bit Service UUIDs Complete list of 16-bit UUIDs available
0x04 32-bit Service UUIDs More 32-bit UUIDs available
0x05 32-bit Service UUIDs Complete list of 32-bit UUIDs available
0x06 128-bit Service UUIDs More 128-bit UUIDs available
0x07 128-bit Service UUIDs Complete list of 128-bit UUIDs available

接続と通信

アドバタイザが送信したアドバタイジング・パケットに、イニシエータがコネクション・リクエスト・パケット(CONNECT_REQ)を返信すると、接続状態になります。接続した2つのデバイスの役割は、イニシエータがマスターに、アドバタイザがスレイブに、それぞれ切り替わり、接続状態(のCONNECTED)に遷移します。

アドバタイジングはアドバタイザがパケットの送出タイミングを制御していました。接続後はマスターがタイミングを制御します。接続後、マスターは一定期間ごとに、スレイブにパケットを送信します。スレイブはマスターからのパケットに返信する形で、データを送ります。マスターは複数のスレイブと接続します。マスターがスレイブに最初に送るパケットの送信タイミングで、スレイブそれぞれとの通信タイミングが重ならないようにします。

通信のタイミング

く図が接続確立と通信のタイミング図です。アドバタイジング・パケットの送信完了から150マイクロ秒後に、イニシエータはCONNECT_REQを送信します。イニシエータはアドバタイザがCONNECT_REQを受信できたかを、確認しません。もしもアドバタイザがCONNECT_REQを受信しておらず、接続状態に遷移していない場合は、マスターがしばらく通信できないので、接続が切断したと判断します。

CONNECT_REQパケットには、の接続パラメータが入っています。パラメータの複合語は、先頭は小文字につづく単語は先頭を大文字にして連結(CamelCase、キャメルケース)します。

transmitWindowOffset(transmit window offset、トランスミット・ウィンドウ・オフセット)は、CONNECT_REQを送信してから、次のtransmit window sizeが始まるまでの、オフセット時間を示します。

transmitWindowSize (transmit window size、トランスミット・ウィンドウ・サイズ)は、最初のデータ・パケットを送信できる期間を示します。

connInterval(connection interval、コネクション・インターバル)は、チャンネルをホップする周期を示します。

connSlaveLatency(connection slave latency、コネクション・スレイブ・レイテンシ)は、スレイブが送信するデータがなにもないときに、コネクション・イベントを無視出来る回数を示します。

connSupervisionTimeout(connection supversion timeout、コネクション・スーパービジョン・タイムアウト)は、接続が失われたと判断する、コネクション・イベントの失敗回数を示します。

名称 値の制約 値の範囲
transmitWindowOffset 1.25 ms 単位 0 ms 以上 connInterval 以下
transmitWindowSize 1.25 ms 単位 1.25 ms 以上、10 msもしくは(connInterval - 1.25 ms) 以下
connInterval 1.25ms 単位 7.5 ms 以上 4.0 s 以下
connSlaveLatency 整数 0以上最大値以下 *1
connSupervisionTimeout 10ms 単位 100ms 以上 32秒 以下 *2

1 最大値は ((connSupervisionTimeout / connInterval) - 1) かつ 500以下。
2 (1 + connSlaveLatency) * connInterval よりも大きいこと

マスターは、CONNECT_REQを送信してから、(1.25ミリ秒 + transmitWindowOffset)あと、transmitWindowSize の期間中に、最初のデータ・パケットを送信します。マスターは、この最初のデータ・パケットから、一定期間ごとに、スレイブにデータ・パケットを送信します。マスターは複数のスレイブと接続するので、他のスレイブと通信タイミングが重ならないように、この最初のデータ・パケットの送信タイミングで調整します。その調整幅が transmitWindowSize になります。

最初のデータ・パケットの送信タイミングから一定期間 connInterval ごとに、コネクション・イベントが発生します。マスターとスレイブは、ともに送信すべきデータがあり、送信するパケットがコネクション・インターバルに収まるかぎり、データ・パケットをやりとりします。

このコネクション・イベントで、マスターとスレーブで通信タイミングを同期することで、高周波回路の消費電力を必要最小限にしています。スレーブは、マスターがパケットを送信するタイミングにあわせて、受信回路を動かして、消費電力の大部分を占める高周波回路の駆動時間を必要最小限にします。

コネクション・イベントごとに、通信につかう周波数帯域、チャンネルを切り替えます。チャンネルの切り替えは、チャネル番号ごとに順番に1つづつ移動するのではありません。マスターとスレーブが接続時に交換するパラメータ、次の節で述べるLLDataのChM と Hop の2つの変数で計算したチャネルに、ホッピングしていきます。もしもあるチャンネルで通信ができなくても、そのチャンネルで再接続や再送処理をすることはありません。次のコネクション・イベントで、別のチャンネルに切り替えて、通信を継続します。

スレーブに送るべきデータができたときから、そのデータがマスターに送信されるまでの時間を、遅延時間 レイテンシ、と呼びます。コネクション・インターバル connInterval が20ミリ秒であれば、スレーブは20ミリ秒ごとにデータを送信する機会があるので、レイテンシは、最大が20ミリ秒、平均すれば10ミリ秒になります。connIntervalを短くするほどレイテンシは小さくなりますが、それだけ無線通信の電力消費量も大きくなります。小さいレイテンシと超低消費電力を両立させるための工夫が、コネクション・スレーブ・レイテンシです。

コネクション・スレーブ・レイテンシ connSlaveLatency は、スレイブがコネクション・イベントを無視できる回数を表します。スレーブに送信すべきデータがないとき、スレーブは connSlaveLatency までコネクション・イベントを無視できます。スレーブは送信すべきデータがなくても、(connSlave Latency + 1)回目のコネクション・イベントには返信をします。もしも(connSlave Latency + 1)回目のコネクタ・イベントで、スレーブがマスターからの返信を受け取れなかった場合は、通信が成立していないものとみなして、通信できるまで、その後のコネクション・イベントに返信をします。

例えば connInterval が20ミリ秒、connSlaveLatencyが 5 とします。スレーブに送信すべきデータがないならば、5回のコネクション・イベントを無視できます。そして6回目のコネクション・イベントに返信をします。つまり、レイテンシは20ミリ秒のままで、スレーブの無線通信頻度を100ミリ秒、1/5 の電力に抑えられます。

コネクション・スーパービジョン・タイムアウト connSupervisionTimeout は、通信が切断したと判断する時間です。マスターおよびスレーブはそれぞれ、通信をしない時間が コネクション・スーパービジョン・タイムアウト を超えたならば、通信が切断したと判断します。スレーブは、コネクション・スレーブ・レイテンシ だけパケットを無視できます。

コネクション・スーパビジョン・タイムアウトが最小値 $ (connSlaveLatency + 1 ) 1.25ミリ秒 $ のとき、connSlaveLatencyだけパケットを無視したスレーブと、その次のコネクション・イベントで通信ミスが生じれば、直ちに切断してしまいます。頻繁な切断を避けるには、connSupervisionTimeout を目安として connSlaveLatency の6倍程度に設定します。例えば connSlaveLatency が5、connIntervalが20ミリ秒のとき、conSupervisionTimeout は ( 5 20ミリ秒 ) * 6 = 600ミリ秒 に設定します。

CONNECT_REQ PDUペイロード

イニシエータがアドバタイザに送るCONNECT_REQのPDUのフォーマットはです。InitAおよびAdvAは、それぞれイニシエータとアドバタイザのアクセス・アドレスです。アドバタイザはAdvAが自分宛であることを確認して、接続状態に遷移します。LLDataは22オクテットの接続パラメータです。

AA は、接続した後のデータ・パケットのアクセス・アドレスです。アドバタイジング・パケットのアクセス・アドレスは固定値でした。データ・パケットのアクセス・アドレスには、イニシエータがランダムに生成したアクセス・アドレスの制約条件を満たす値が使われます。

CRCInit はパケットのチェックサムを検査するシフトレジスタの初期値を指定します。CRCInitは、イニシエータがランダムに生成します。シフトレジスタは巡回検査符号を計算するためのハードウェアです。Bluetooth LEの巡回検査符号は、
多項式 $ x^24 +x^10 +x^9 +x^6 +x^4 +x^3 +x +1 $ を24個のシフトレジスタで実装した回路で計算されます。パケットの受信処理を開始する都度、これらのシフトレジスタは、CRCInitの値で初期化されます。ちなみに、アドバタイジングのときは、シフトレジスタは固定値 0x555555 で初期化されます。

WinSize、WinOffset、Interval、LatencyおよびTimeoutは、それぞれ前節のtransmitWindowSize、transmitWindowOffsetvalue、connInterval、connSlaveLatency、およびconnSupervisionTimeの値を表します。WinSizeなどの値は2オクテットの整数です。接続パラメータの物理値は、これらの整数値それぞれにの値の制約の単位をかけたものです。例えば、$ transmitWindowSize = WinSize * 1.25ミリ秒 $ です。

ChMは、チャネルマッピングを表します。コネクション・イベントでどのデータ・チャネルを使うかを指定します。WiFiなどの、干渉するとわかっている周波数帯域のデータ・チャンネルを使わないようにして、干渉を回避するのに使います。ChMは4オクテットの値で、それぞれのビットがチャンネルに対応します。最下位ビットがチャンネル0、下位から36ビット目がチャンネル36に対応します。チャンネルを指定するときは、チャンネル10とチャンネル11の間には、アドバタイジング・チャンネル38があり、データ・チャンネルだけをみると周波数帯域が1つ飛ぶことに、注意します。

hopIncrementは、データ・チャンネルの切り替え順番を指定します。あるコネクション・イベントでチャンネル A を使った時、次のコネクション・イベントで使うチャンネル B は
$ B = (A + hopIncrement) mod 37 $ で求めます。この式の mod は剰余の演算子です。例えば、hopIncrementが5のとき、あるコネクション・イベントでチャンネル3を使っていれば、次のコネクション・イベントは、$ ( 3 + 5 ) % 37 = 8 $ から、チャンネル8をつかいます。もしもチャンネル・マッピングで利用不可のチャンネルが指定されている場合は、そのチャンネルは使いません。利用可能なチャンネルがでるまで、

hopIncrementの値が小さすぎると、周波数があまり離れていないチャンネルに移動します。もしもWiFiなどの混信があると、チャンネル・ホッピングとしても影響を受けやすくなります。また16よりも大きくしていっても、剰余演算なので、逆にホッピング先が近くなります。ですから、値を大きくする意味がありません。37は素数なので、5から16までのどの値をとっても、37のチャンネルを、いつかは選択することになります。

SCAはマスターのスリープ・クロックの精度を表します。これは、マスターがコネクション・イベントで送信するパケットを、スレーブが取りこぼさないために必要です。マスターもスレーブも、高精度の発振回路を搭載していますが、それでもppm(百万分の1)の桁のずれが生じています。スレーブは、発振回路のずれから生じるコネクション・イベントのパケット送信タイミングのずれ時間分だけ、受信回路の動作開始時刻を前にずらします。

通信

接続したマスターとスレーブのデータ・パケットのPDUののフォーマットはです。2オクテットのヘッダと、0~33オクテットのペイロード、そして通信を暗号化していてペイロードが0より大きいときは、4オクテットのMessage Integrity Check(MIC)がつきます。

データチャネルPDUの最大長さは、パケットフォーマット()のPDUの最大長さ39オクテットで制約されます。暗号化をしていないならば、MICの4オクテットをペイロードとして使えそうなものですが、暗号化の有無でバッファ長が変化しない設計になっています。

データ・チャネルPDUは、上位層 Logical Link Control and Adaptation Protocol (L2CAP)のパケットを分割送信します

データ・チャネルPDUのヘッダは次ののフォーマットです。

フィールド名 記述
LLID ロジカル・リンクID (Logical Link Identifier)
パケットの種類を示す。
00b = Reserved
01b = LL Data PDU (Continuation)
10b = LL Data PDU (Start)
11b = LL Control PDU
NESN Next Expected Sequence Number
SN シーケンス番号 (Sequence Number)
MD モア・データ (More Data)
Length ペイロードとMICのオクテット単位の長さ

データチャンネルPDUは、LLIDの値で、ロジカル・リンク・データPDU(LL Data PDU)と、ロジカル・リンク・コントロールPDU(LL Control PDU)の2種類にわけられます。

ロジカル・リンク・データPDU

ロジカル・リンク・データPDUは、上位層(Bluetooth LEではL2CAP)のパケットの分割転送と受信時の再構築に使われます。

上位層(L2CAP)のパケット長がデータ・チャネルPDUのペイロードよりも長いとき、上位層からのパケットは複数のロジカル・リンク・データ PDUに分割して送信されます。一連のパケットの列は、最初のパケットのLLIDが 10b(Start、始めの) 、それに続くパケットの LLIDを 01b (Continuation、続きの)にして判別します。

上位層のパケット長がデータ・チャネルPDUのペイロードにおさまるときは、LLID 01bのパケット1つだけで、その後にLLID 01bのロジカル・リンク・データPDUは続きません。

ロジカル・リンク・データPDUに、上位層のパケット長を示すフィールドはありません。リンク層は、上位層からデータが来れば、順次それをStartとそれに続くContinuationのデータ・パケットに分割するだけです。ですから、リンク層は上位層のパケット長を事前に知る必要はありません。

Lengthはペイロードのオクテット長を示します。LLID 10b (Start)のデータPDUのLengthは0より大きい値、LLID 01b (Continuation)のLengthは0を含む値がとれます。LLIDが 01b (Continuation)でLengthが0のパケットを、エンプティ・パケット(Empty packet)と呼びます。

エンプティ・パケットは、フロー制御に使います。Continuationのパケットで
送信すべきデータ揃っていないときは、エンプティ・パケットを埋め草として使います。またコネクション・イベントで、マスターからスレーブにパケットを送信するとき、マスターから送信すべきデータがないとき、また逆にスレーブがマスターにデータはないが接続を確認するために返信するときにも、エンプティ・パケットを使います。

Sequence number(シーケンス番号、SN)とNext expected sequence number(ネクスト・イクスペクテッド・シーケンス番号、NESN)は、信頼できるパケット通信とフロー制御に使われます。

SNとNESNは1ビットの値です。マスターとスレーブはコネクション状態に入るときに、SNとNESNをそれぞれ0に初期化します。SNは自分が送信するパケットのシーケンス番号を、NESNは相手に次に送ってほしいパケットのシーケンス番号を設定します。

マスターがコネクションイベントで、(SN = 0, NESN = 0)のデータ・チャネルPDUをスレーブに送ったとします。スレーブがこのパケットを受信して、次のパケットの送信を求めるならば、スレーブは NESN = 1 のデータ・チャネルPDUを送信します。マスターは、スレーブが NESN = 1 を送ってきたのをみて、直前に送信したSN=0のパケットをスレーブに送信できたと確認できます。マスターは次はSN = 1のパケットを送信します。

マスターが(SN = 0, NESN = 0)のデータ・チャネルPDUをスレーブに送った時、スレーブがパケットのビットエラーを検出したとします。スレーブは、マスターに SN = 0 のパケットを再送してもらうために、NESN = 0 のデータ・チャネルPDUを送信します。マスターは、スレーブがNESN = 0 を送ってきたのをみて、SN = 0 のパケットを再送します。

この仕組は、フロー制御にも使えます。例えば、スレーブの受信バッファがいっぱいで、パケットのペイロードを読み込めないときは、マスターにそのパケットの再送を要求します。

送信側から見て、送信先がパケットの受信に失敗する原因は、電波状態が悪いなどで、パケットにビットエラーが発生またはパケット自体を検出できていない場合と、先のフロー制御のように送信先の処理速度が追いつかない場合の2つがあります。送信側から見て送信先から返ってくる反応は、何も返ってこない、再送を要求するパケット(直前に送ったパケットのSNが NESN に設定されている)が返ってくる、および正常に受信できた(直前に送ったパケットのSNと違う値が NESN に設定されている)の3通りです。何も返ってこない場合は、次のコネクション・イベントで通信を再同期します。なにかしらパケットが返ってきた場合は、送信側は、再送もしくは次のパケットを送信します。

SNおよびNESNは1ビットの値です。マスターとスレーブはピコネット内で交互にパケットをやりとりします。ですから、パケット伝送の遅延時間が大きい場合にスループットを上げる目的で使われる、パケットの先送りがありません。ですから、SNおよびNESNは1ビットでよいのです。

MD(モア・データ、More Data)は、接続先デバイスに送信すべきデータがまだあることを示す1ビットの値です。MDが1ならデータが未だある、0ならば送信すべきデータはもうない、ことを示します。MDが1である限り、パケットのやり取りが継続します。もしもコネクション・インターバルの終わりになっても、MDが1ならば、そのコネクション・イベントは自動的に延長されて通信が継続します。逆にMDが0であれば、それ以上のデータはないとわかるので、直ちにスリープ状態に入るなどして省電力化ができます。

ロジカル・リンク・コントロールPDU

ロジカル・リンク・コントロールPDUは、リンク層の接続制御に用います。ロジカル・リンク・コントロールPDUのフォーマットは、1オクテットのopcode(オペコード)と0から22オクテットのCtrData(コントロール・データ、control data)で構成されますは、オペコードの値とその意味です。

オペコード コントロールPDU 名
0x00 LL_CONNECTION_UPDATE_REQ
0x01 LL_CHANNEL_MAP_REQ
0x02 LL_TERMINATE_IND
0x03 LL_ENC_REQ
0x04 LL_ENC_RSP
0x05 LL_START_ENC_REQ
0x06 LL_START_ENC_RSP
0x07 LL_UNKNOWN_RSP
0x08 LL_FEATURE_REQ
0x09 LL_FEATURE_RSP
0x0A LL_PAUSE_ENC_REQ
0x0B LL_PAUSE_ENC_RSP
0x0C LL_VERSION_IND
0x0D LL_REJECT_IND
0x0E-0xFF 将来のために予約

LL_CONNECTION_UPDATE_REQ と LL_CHANNEL_MAP_REQ は、コネクションのパラメータおよびチャンネル・マッピングを更新するオペコードです。やり取りするパラメータは、コネクション時のLL Data と同じものです。これらのオペコードは、マスターのみが使えます。それは、これらの接続パラメータの値を決められるのは、ピコネットの通信を制御しているマスターのみだからです。もしもスレーブが接続パラメータを更新したいときは、リンク層の上位層のL2CAPの、LE signaling channnel を使います。

LL_CONNECTION_UPDATE_REQ のCtrlDataのフォーマットの WinSize、WinOffset、Interval、Latency、および Timeout は、それぞれ の transmitWindowSize、transmitWindowOffset、connInterval、connSlaveLatency、および connSupervisionTimeout と同じ意味を持ちます。

Instant は、どのコネクション・イベントで、接続パラメータを更新するかを指定します。マスターとスレーブはそれぞれ、コネクション状態になってからのコネクション・イベントの数を connEventCount というカウンタで計測しています。Instantが示すコネクション・イベントのタイミングで、の接続開始時の処理を行い、アンカー・ポイントの再設定を行います。 LL_CHANNEL_MAP_REQ も、これと同様です。

オペコード 0x03 (LL_ENC_REQ) から0x0B (LL_PAUSE_ENC_RSP) は、パケットの暗号化のコマンド群です。LL_ENC_REQとLL_ENC_RSPは、暗号/復号処理に必要な情報をやり取りします。LL_START_ENC_REQ と LL_START_ENC_RSP は暗号化の開始を、LL_PAUSE_ENC_REQ と LL_PAUSE_ENC_RSP は暗号化の中断のコマンドです。

データ・チャネルPDU のヘッダのLengthフィールドは、ペイロードとMICを合わせた長さを示します。またパケットが暗号化されている/いないを示すフラグはありません。ですが、マスターとスレーブは、ロジカル・リンク・コントロールPDUで、今の通信が暗号化されている/いないがお互いにわかるので、暗号化を示すフラグは必要ありません。

LL_FEATURE_REQ および LL_FEATURE_RSP は、マスターおよびスレーブのリンク層の機能を示すデータをやりとりします。いまの規格は、暗号化サポートの1ビットのみを定めています。Bluetooth LEのリンク層の機能は、デフォルトですべての機能が有効になっています。接続時に、機能サポートを調べる必要はありません。もしも機能がサポートされていなければ、そのコマンドはリジェクトされます。そのエラー原因は、リジェクトで返される LL_REJECT_IND の、CtrDataにあるエラーコードから取得できます。

LL_VERSION_IND はコントローラのバージョン番号を示します。おもな用途は、接続時にトラブルが生じた時などの、デバックです。Bluetooth Controller の仕様番号、製造者番号、および実装番号が取得出来ます。仕様番号および製造者番号はBluetooth SIGが割り当てた値が使われます。

LL_UNKNOWN_RSP PDUは、LLコントロールPDUが予約されたオペコードや不正なCtrlDataを含むとき、そのレスポンスに使われます。

LL_REJECT_IND は、コマンドをリジェクトした時のエラーコードを返します。例えば、暗号化処理を要求したときに、相手先が暗号化処理を行えないときは、 ENCRYPTION MODE NOT ACCEPTABLE (0x25) が返されます。また、暗号化を必要とするGATTのキャラクタリスティクスへの読み書き等を、暗号化を開始せずに実行したときは、 INSUFFICIENT SECURITY (0x2F) が返されます。

リンク層は接続デバイスとの通信路を提供します。Bluetoothには、この1本の通信路で様々なプロトコルやアプリケーションの通信を提供するために、Logic Link Control and Adptation Protocol (L2CAP、エルトゥーキャップ、頭文字のLが2続くのをL2と略している)があります。

L2CAPは、チャンネルという考え方で通信を多重化します。上位層のパケットの分割と統合、チャンネルごとのフロー制御とパケット再送制御、エラー・コントロールを提供します。Bluetoothでは、例えばヘッドセットなど一定のビットレートでストリーミングをするものや、ファイル転送など、大容量の情報を可能な限り高速で送信するものなど、アプリケーションごとに必要とするネットワークの特性が異なります。L2CAPは、必要とする特性にあわせた通信路を提供します。上位層のプロトコルおよびアプリケーションからみると、他のプロトコル/アプリケーションの通信状態にかかわらず、相手側のプロトコルおよびアプリケーションとの通信が確保されていると見えます。

クラシックBluetoothのL2CAPは、チャンネルを確立するときに、フロー制御などのパラメータをやりとりするために、複雑でまた処理時間もかかります。クラシックBluetoothは、一度接続すれば長時間接続しつづける用途に使われるので、接続時にかかる時間は許容されます。

しかしBluetooth LEは、コネクションを維持するものではありません。簡単に接続して、簡単に切断してしまうものです。これにL2CAPを対応させるために、Bluetooth LEのL2CAPは、固定されたチャンネルのみが提供されます。この固定チャンネルでは、接続時のパラメータのやりとりはありません。

Bluetooth LEのL2CAPのチャンネルは、の3つの固定チャンネルだけです。

Channel ID 説明
0x0004 Attribute Protocol
0x0005 Low Energy L2CAP Signaling channel
0x0006 Security Manager Protocol

パケット構造

L2CAPのパケット構造は、2バイトのLengthとChannel ID、それに0から65535バイトまでのinformation payloadが続きます。

チャンネルID 0x0005 のシグナリング・チャンネルのフォーマットは、1バイトのコードと識別子、2バイトの長さと任意長のデータが続きます。コードは次の3つだけです:

  • Connection Parameters Update Request/ Response
  • Command reject

シグナリング・チャンネルのIdentifierは、コマンドを発行した側が、0x00以外の値を設定します。コマンドを処理した側は、レスポンスにその識別子を設定して結果を返します。コマンド発行側は、複数のリクエストを同時に発行したときでも、この識別子の値から、どのコマンドに対する結果なのかがわかります。Bluetooth LEは、リクエストが1つですから、この処理はとても単純になります。

Connection Parameters Update Request/Responseは、スレーブからホストへの接続パラメータの変更要求に使われます。このConnection Parameters Update Requestは、スレーブはいつでもマスターに送信できます。マスターは送信できません。もしもサポートされていないコマンドを受信したり、データ長が期待した長さ(23バイト)ではない場合は、Command rejectが返されます。

このリクエストを受信したマスターは、その変更パラメータを受け付けるならば、レスポンスで受け付けること(accepted) を返してから、ロジカル・コントロールPDUから接続パラメータを更新します。変更パラメータを受け付けないならば、レスポンスで受付拒否(rejected)を返します。マスターには、その他のスレーブとの接続や、WiFiやクラシックBluetoothとの共存などの制約があります。スレーブは、変更パラメータの値の範囲をなるべく広くとって、マスターが受け入れられるパラメータを選べるようにすべきです。

スレーブからのコネクション・パラメータのアップデート要求は、通信速度と省電力の両立に利用できます。マスターは接続時にスレーブから、ATT/GATTのサービスおよびキャラクタリスティクスを読み出します。ですから接続直後に通信速度が高いパラメータを選択すれば、素早い接続処理ができます。接続が完了した後は、アプリケーションに応じた通信頻度で、より低消費電力のパラメータに切り替えることで、省電力化ができます。これらの、パラメータの設定や切り替えは、スレーブの設計次第です。コネクション・インターバルが小さい値でも、スレーブ・レイテンシが高くできるならば、パラメータの変更は必要ないかもしれません。

Attribute Protocol と Generic Attribute Profile

Attribute Protocol (アトリビュート・プロトコル、ATT)は、ATTはアドレスとラベルつきの値の集合をやりとりするプロトコルです。ATTは、L2CAPまでで確保された接続したデバイス間の通信を使い、Bluetooth LEの特徴的な通信の機能や振る舞いを提供しています。

ATTの上位層にGeneric Attribute Profile(ジェネリック・アトリビュート・プロファイル、GATT)があります。このATTとGATTは、Bluetoothに統合される以前の規格では1つの層でした。これが、規格に取り込む際に、通信の手順を表すプロトコル、ATT、と機器の振る舞いを表すプロファイル、GATT、の2つの層に分離されました。ATTは、ATT単体ではなく、Bluetooth LEの設計方針およびATTの上位層のとあわせて理解すると、Bluetooth LEでのATTの意味がよくわかります。

ATTとGATTの特徴

ATTとGATTの特徴は:

  • すべてのアプリケーション使うシンプルな1つのプロトコル
  • 通信と振る舞いを分離
  • Bluetoothの認証の範囲で多種多様な周辺機器の開発を可能にする

です。

クラシックBluetoothは、メーカや機種を超えた相互運用を提供するために、通信プロトコルやデータ形式などの通信部分のみならず機器の振る舞いまでを含めたものを、プロファイルと定義します。例えばヘッドセットやファイル交換などの、アプリケーションごとにプロファイルが定義されます。Bluetooth SIGがプロファイルを定義していないアプリケーションの開発には、シリアル・ポート・プロファイルという、汎用通信のプロファイルを使います。この場合、通信プロトコルなどは、自ら開発しなければなりません。

Bluetooth LEでは、GATTの上層に、すべてのアプリケーションが作られます。GATTが通信の振る舞いを、アプリケーションが機器の振る舞いを、それぞれ担当するように層が分けられています。そして、Bluetooth LEでは、Bluetoothの認証範囲はGATTまでで、アプリケーションは含みません。したがって、Bluetooth LEは、Bluetoothのロゴ認証のもとで、多種多様なアプリケーション、つまり周辺機器、を一般に販売できます。

Bluetooth LEは、機器の振る舞いをプロファイルと呼びます。例えば心拍や血圧計など、メーカや機種を超えた相互運用が一般普及のために必要不可欠な分野があります。Bluetooth LEのプロファイルは、それぞれの分野で業界団体あるいは会社群がプロファイルを作成して、それをBluetooth SIGが承認することで成立します。GATTとアプリケーションが分かれているので、GATTというプロトコルはそのままで、分野ごとに多種多様なプロファイルが、必要になるたびに追加されます。

アトリビュート・プロトコル

Attribute Protocol(アトリビュート・プロトコル、ATT)は、アトリビュート・サーバ(ATTサーバ)とアトリビュート・クライアント(ATTクライアント)の通信プロトコルです。

ここで、アトリビュートは、アドレスとタイプでタグ付けされた値のことです。ATTサーバは、アトリビュートの集合を公開するものをいいます。ATTクライアントは、サーバが公開するアトリビュートを参照するものをいいます。サーバのアトリビュートは、クライアントから、発見、読み出し、書き込みができます。またサーバからクライアントに、インディケーション(indication)またはノーティフィケーション(notification)ができます。

アトリビュート

サーバが公開するアトリビュートの、サーバ内での論理表現は、アトリビュート・ハンドル (attribute handle)、アトリビュート・タイプ(attribute type)そしてアトリビュート・バリュー(attribute value)、アトリビュート・パーミッション(attribut permission)の4つで構成されます

アトリビュート・ハンドルは、ATTクライアントがATTサーバのアトリビュートにアクセスするときに使われます。アトリビュート・ハンドルは、2オクテットの0x0001から0xffffまでの値です。

アトリビュート・タイプは、アトリビュートのデータの型を示します。これは128ビット(16オクテット)のUUID (Universally Unique IDentifier)
RFC 4122です。しかし、128ビットのUUIDそのままを通信でやりとりすると、通信時間が長くなり電力を使います。そこで、Bluetooth SIGが、よく使うものには16ビット(2オクテット)のUUIDを割り当てています Assigned Numbers。この16ビットのUUIDは、次の128ビットのUUIDのxxxx部分を抜き出した短縮したUUIDです。

0000xxxx-0000-1000-8000-00805F9B34FB

16ビットのUUIDを128ビットのUUIDに戻すには、この128ビットのUUIDのxxxx部分を16ビットのUUIDで置き換えます。例えば 0x1801 ならば:

00001801-0000-1000-8000-00805F9B34FB

となります。

アトリビュート・バリューは0から512バイトまでの任意のバイト・データです。エンディアンは上位層の仕様が決めます。

アトリビュート・パーミッションは、アトリビュート・プロトコルで読み出すことはできません。上位層がどのアトリビュートが読み書きできるかを決めます。アトリビュート・プロトコルで、読み込みのみのアトリビュートに書き込みのリクエストをすれば、パーミッションがないので書き込めないとうエラー・レスポンスが返ってくるので、間接的にパーミッションを知ることができます。

リクエストとインディケーション

アトリビュート・プロトコルのアトリビュート・プロトコルPDU (Protocol Data Unit)のやりとりには、リクエスト、インディケーション、コマンド、そしてノーティフィケーションの4つがあります 図。たいていのアトリビュート・プロトコルPDUは、図 (a)の、シーケンシャルなリクエスト-レスポンス・プロトコルを使います。

クライアントからサーバにリクエストするときには、アトリビュート・リクエスト(ATTリクエスト)とアトリビュート・レスポンス(ATTレスポンス)のペアを、サーバからクライアントに通知するにはアトリビュート・インディケーション(ATTインディケーション)とアトリビュート・コンファメーション(ATTコンファメーション)のペアを使います。これらのやりとりをトランザクションと呼びます。

リクエストおよびインディケーションには、正常に処理できたならば、それぞれレスポンスもしくはコンファメーションが返されます。もしもエラーが生じた場合は、エラーメッセージが返されます。

トランザクションの期間は、ATTリクエストおよびATTレスポンスのペアでは、クライアントではリクエストの送信からサーバからのレスポンスを受信するまで、サーバではリクエストを受信してからレスポンスを送信し終えるまで、です。ATTインディケーションおよびコンファメーションのペアでは、サーバはインディケーションを送信してからコンファメーションを受信するまで、クライアントはインディケーションを受信してからコンファメーションを送信し終えるまで、です。

トランザクションが使えると、サーバとクライアントの間での処理要求や通知を確実に実行できます。ですが、トランザクションの排他処理や、トランザクションの失敗検出とその後処理をどうするのかを決める必要が生じます。

サーバおよびクライアントが実行するトランザクションは常に1つだけです。クライアントは、あるサーバにリクエストを送信したら、そのサーバからのレスポンスを受信するまで、同じサーバにリクエストは送りません。また、サーバがクライアントにインディケーションを送信した場合も、コンファメーションを受診するまで、インディケーションを送信しません。

リクエスト処理が完了するまでクライアントは待つので、フロー制御が得られます。また、サーバのリクエスト処理には、マイクロコントローラの計算時間や外部センサ回路を動かす処理などが必要かもしれません。もしもトランザクションが複数だと、資源の管理やロック機構が必須になります。トランザクションを1つにしているので、サーバにそのような仕組みを持たせずにすみます。

30秒以内にトランザクションが完了しないときは、タイムアウトとみなし、そのトランザクションは失敗とされます。上位層はトランザクション失敗を通知されます。トランザクションが失敗すると、それ以上アトリビュートPDUはやりとりされません。アトリビュートPDUのやりとりを再開するには、接続の切断と再接続をします。

トランザクションの途中で通信が切断した場合は、そのトランザクションは閉じられます。そのトランザクションがサーバの値を変更するものだった場合は、サーバの値がどうなるかは不定です。その時の振る舞いは、上位層の仕様で決めておきます。

サーバのアトリビュート・バリューのサイズは最大で512オクテットです。サイズが1つのアトリビュートPDUに収まらないアトリビュート・バリューの読み書きは、複数回のトランザクションで処理します。このために、アトリビュート・プロトコルには、オフセットを指定できるRead BlogリクエストとPrepare Writeリクエストが用意されています。もしも一連のトランザクションが、途中で失敗したり接続が切断した場合はサーバの値は不定になります。この時の振る舞いは、上位層の仕様で決めておきます。

コマンドとノーティフィケーション

アトリビュート・プロトコルには、クライアントからサーバへのアトリビュート・コマンド(ATTコマンド)、そしてサーバからクライアントへのアトリビュート・ノーティフィケーション(ATTノーティフィケーション)があります 図。これらはリクエスト/インディケーションと異なり、トランザクションもフロー制御もありません。トランザクションと無関係に、任意のタイミングで任意の回数の送信ができます。

例えば、クライアントがATTリクエストを送信して、まだレスポンスを受信していない時、トランザクションが終了していない時でも、クライアントはサーバにATTコマンドを送信できます。また、サーバも同様にトランザクションの途中でも、クライアントにATTノーティフィケーションを送信できます。

ATTコマンドとATTノーティフィケーションは、任意のタイミングに送信できるので、フロー制御はありません。リンク層の機能で、サーバおよびクライアントは、ATTコマンドおよびATTノーティフィケーションを確実に受信しますが、メモリ容量や計算時間が不足するなどした場合は、それらの処理を無視します。ATTコマンドおよびノーティフィケーションは、処理されるかが信頼できないもの、として扱います。

フロー制御がありませんから、例えば、クライアントから大量のコマンドがサーバに送りつけられるかもしれません。このようなことにならないように、上位層の仕様を決めておきます。

クライアントからのATTコマンドに対して、サーバが何かを返信したい場合、直ちに返信する必要はありません。トランザクションはないので、タイムアウトはありません。サーバは、ATTインディケーションかATTインディケーションノーティフィケーションで、そのうち通知をすればよいのです。

アトリビュートPDUフォーマット

アトリビュートPDFは、アトリビュート・オペコード(Attribute opcode)、アトリビュート・パラメータ、オーセンティケーション・シグネチャ(Authentication signature)が続きます 。図のATT_MTUは、アトリビュートPDUの最大長 Maximum Transfer Unit(MTU)を表し、デフォルト値は23オクテットです。

アトリビュート・オペコードは、オーセンティケーション・シグネチャ・フラグ(Authentication signature flag)、コマンド・フラグ(Command flag)そしてメソッド(Method)から構成されます。コマンド・フラグが1ならば、そのPDUはコマンドです。6ビットのメソッドにより、アトリビュート・パラメータのフォーマットとその意味が決まります。

また、オーセンティケーション・シグネチャ・フラグが1ならば、オーセンティケーション・シグネチャがあります。

オーセンティケーション・シグネチャは、通信の暗号化は必要ではないが、サーバの状態を変化させてしまう書き込み処理は、信頼できるクライアントからのみ受け付けたい場合に、用います。リンク層の機能を使い、通信自体を暗号化すれば、スニッフィングも防止できますが、しかしパケットのオーバーヘッドの増加と暗号化処理が必要になります。サーバはシグネチャから、アトリビュートPDUが信頼できるクライアントが送信したものだと、確認ができます。

このような用途なので、オーセンティケーション・シグネチャ・フラグが1になるのは、Writeコマンドのみです。また、リンク層で暗号化をしているときは、必要がないので、オーセンティケーション・シグネチャ・フラグは1にはできません。

ロング・アトリビュートとその扱い

長さが(ATT_MTU–1)オクテットを超えるアトリビュート・バリューをもつアトリビュートを、ロング・アトリビュート(long attribute)と呼びます。サーバのアトリビュート・バリューの最大長は512バイトですが、ATT_MTUのデフォルト値は23オクテットです。たいていATT_MTUはデフォルト値がつかわれますから、23オクテット以上のアトリビュート・バリューはたいていロング・アトリビュートです。

アトリビュート・プロトコルで、トランザクションで送信できるアトリビュート・バリューの最大長は(ATT_MTU–1)オクテットです。アトリビュート・プロトコルのリード/ライト・リクエストは、ロング・アトリビュートであっても、それを自動で分割/再結合したりはしません。その場合は、冒頭(ATT_MTU–1)オクテットだけをやりとりします。

ロング・アトリビュートの読み書きに、オフセット値を指定できるRead Blogリクエスト、Prepare Write/Execute Writeリクエストがあります。上位層は、これらのリクエストを使って、ロング・アトリビュートを、(ATT_MTU-1)オクテット単位で複数回のトランザクションに分割して、扱います。

Exchange MTUリクエスト/レスポンス

アトリビュート・プロトコルのATT_MTUを変更するのが、Exchange MTU リクエスト/リスポンスです。このリクエストで、クライアントはサーバに、クライアントが扱えるATT_MTUを伝えます。サーバは、レスポンスで、サーバが扱えるATT_MTUを返します。このときのATT_MTUの値は、ATT_MTUのデフォルト値23以上の値です。

もしも、サーバにメモリが少ないマイクロコントローラが使われていると、ATT_MTUは大きく取れないかもしれません。サーバはその場合、ATT_MTUの最小値でもある、デフォルト値23を返すでしょう。

アトリビュートへのアクセス

サーバが公開しているアトリビュートには次の6つのアクセス方法があります。

  • ファインド・リクエスト (Find request)
  • リード・リクエスト (Read request)
  • ライト・リクエスト (Write request)
  • ライト・コマンド (Write command)
  • インディケーション (Indication)
  • ノーティフィケーション (Notification)
ファインド・リクエスト

ファインド・リクエストは、クライアントが、サーバのアトリビュートの集合から指定条件に一致するアトリビュート・ハンドルを取得するのに、使います。サーバのアトリビュートをすべて読み出そうとすると、通信に時間がかかります。条件を指定することで、必要なアトリビュートのハンドルのみを素早く取得できます。ファインド・リクエストは、Find InformationとFind By Type Valueの2つがあります:

  • Find Information
  • Find By Type Value

Find Informationでは、アトリビュート・ハンドルの範囲を指定して、アトリビュート・ハンドルとアトリビュート・タイプを読み出します。クライアントは、得られたアトリビュート・タイプの配列から、サーバのデータベースの構造を知ることができます。

クライアントがサーバに送るFind Information リクエストは、取得したいアトリビュート・ハンドルの開始値から終了値を指定します。サーバは、Find Informationレスポンスで、アトリビュート・ハンドルとアトリビュート・タイプの配列を返します。

例えば、クライアントがサーバのATTハンドルとATTタイプを全て読み出したい時は、ATTアトリビュートの範囲を0x0001から0xFFFFに設定したFind Informationリクエストを送ります。このとき、Find Informationレスポンスが、0x0001から0x0004までのアトリビュート・ハンドルを返してきたとします。まだ0x0005以降のアトリビュート・ハンドルがあるかもしれません。そこでクライアントは、アトリビュート・ハンドルの範囲を0x0005から0xFFFFに設定したFind Informationリクエストを送ります。クライアントは、これを繰り返して、すべてのアトリビュートを読み出します。該当するアトリビュートがなければ、サーバはクライアントに、エラー・コード Attribute not found のエラー・レスポンスを返します。

Find Informationレスポンスは、アトリビュート・タイプのUUIDが、16ビットか128ビットかを示すフラグと、アトリビュート・ハンドルとアトリビュート・タイプの組の配列を返します。1つのアトリビュート・ハンドルとアトリビュート・タイプの組は、アトリビュート・ハンドルは2オクテットなので、16ビットUUIDならば4オクテット、128ビットUUIDならば18オクテットを占めます。

Find Informationレスポンスは、16ビットと128ビットのアトリビュート・タイプを混在させることはできません。ハンドルとタイプの組の配列には、最大 (ATT_MTU -2) オクテットつかえます。ですから、 1つのFind Informationレスポンスで送れるハンドルとタイプの組は、ATT_MTUがデフォルト値23のとき、16ビットUUIDならば5つ ((23-2)/4 = 5.3)まで、128ビットUUIDならば1つ((23-2)/18=1.2)です。

Find By Type Valueは、使い方はFind Informationと同じで、アトリビュートのタイプおよびバリューが指定できる点が異なります。ただし、指定できるアトリビュート・タイプは16ビットのものだけで、アトリビュート・バリューの長さは(ATT_MTU -7)オクテットまでの制限があります。Find By Type Valueは、GATTのサービスおよびキャラクタリスティクスの素早い検索に役立ちます。

リード・リクエスト

クライアントがサーバのアトリビュートを読み出すリクエストは、6つあります。Readは1つのハンドルを指定する1つのアトリビュート・バリューの単純な読み込みを、Read Mutipleは複数のハンドルを指定しての読み込みを、Read Blobは長さが(ATT_MTU-1)を超えるアトリビュート・バリューの読み出しを行います。Read By TypeおよびRead By Group Typeは、アトリビュート・タイプを指定した読み出しです。

  • Read
  • Read Multiple
  • Read Blob
  • Read By Type
  • Read By Group Type

クライアントが送信するアトリビュート・ハンドルを指定したReadリクエストに、サーバはReadレスポンスを返します。レスポンスのアトリビュート・バリューの長さは最大(ATT_MTU-1)、MTUがデフォルト値23のとき22オクテット、です。

Read Multipleリクエストは、複数のアトリビュート・ハンドルを指定して、1回のトランザクションで読み出します。サーバはアトリビュート・バリューを連結したものをレスポンスで返します。このアトリビュート・バリューの長さは最大で(ATT_MTU-1)オクテットです。もしもアトリビュート・バリューを連結したものが、この長さよりも長い場合は、先頭の(ATT_MTU-1)オクテットが返されます。またレスポンスには、それぞれのアトリビュート・バリューの境界を示さないので、Read Multipleリクエストはアトリビュート・バリューが固定長のものに限られます。ただし、最後の要素は可変長でもかまいません。

ReadリクエストおよびRead Multipleリクエストは、アトリビュート・バリューの長さが(ATT_MTU-1)オクテットよりも短いものを読み出せます。バリューが短いものをRead Multipleリクエストでまとめて読みだすことで、Readリクエストを使うよりも、リクエストの回数を小さくできます。

Read Blobリクエストは、(ATT_MTU-1)オクテットよりも長いアトリビュート・バリューの読み出しに使います。Readリクエストとの違いは、2オクテットのオフセット値を指定できることです。レスポンスは、オフセット位置から(ATT_MTU-1)オクテットまでのアトリビュート・バリューを返します。

Read By Type リクエストは、アトリビュート・ハンドルはわからないが、アトリビュート・タイプがわかっている場合の、読み出しに使います。動作は、Find By Type Valueリクエストでアトリビュート・ハンドルを取得してから、Readリクエストで読み出すことと同じです。

Read By Group Type リクエストは、Read By Typeリクエストとほぼ同じですが、グループ・タイプアトリビュートの一連のアトリビュートの末尾のアトリビュート・ハンドラを返す点が異なります。

上位層が提供する概念に、アトリビュートのグループ化があります。これは、例えばC言語の構造体のように、複数のアトリビュートを1つのグループにしてデータ構造をつくることです。このために上位層は、グループをあらわすアトリビュート・タイプ、グルーピング・タイプと呼びます、を定義しています。

Read By Group Type リクエストは、グルーピング・タイプの読み出しと、そのグループの範囲を読み出すために使われます。

ライト・リクエスト

クライアントがサーバにアトリビュート・バリューを書き込むリクエストは5つあります。Writeリクエストは、1つのハンドルを指定した単純な書き込みを、Prepare WriteとExecute Writeは、(ATT_MTU-1)オクテットよりも長いアトリビュート・バリューの書き込みをおこないます。

  • Write
  • Prepare Write
  • Execute Write
  • Write Command
  • Signed Write Command

ライト・リクエストは、サーバの不揮発メモリに保存される設定情報など、サーバが確実に処理しなければならない書き込みに使います。ライト・リクエストが書き込めるアトリビュート・バリューの長さは最大(ATT_MTU–3)オクテット、ATT_MTUがデフォルト値23のとき20オクテット、です。

(ATT_MTU–3)オクテットよりも長いアトリビュート・バリューを書き込むときに、Prepareライト・リクエストとExecuteライト・リクエストを使います。

Prepareライト・リクエストで、クライアントはサーバに、オフセットとデータを送ります。この情報はサーバのキューに保持されます。Executeライト・コマンドで、クライアントがサーバに書き込みを支持すれば、キューにあるすべてのデータの書き込みが実行されます。Executeライト・コマンドを使って、クライアントはサーバのキューのクリア(書き込みのキャンセル)も指示できます。

ライト・コマンド(write command)は、リクエストではなくアトリビュート・プロトコルのコマンドです。読み込み処理は、クライアントが出した読み込みリクエストにサーバが値を返すので、必ずリクエストの形をとります。しかし書き込み処理は、サーバからリプライを返す必要はない場合があるので、コマンドが用意されています。

ライト・コマンドは、コントロール・ポイントへの書き込みに使われます。コントロール・ポイントは、サーバの装置等への動作指示を書き込むためのアトリビュートです。例えば、テレビのリモコンがクライアント、テレビ本体がサーバならば、リモコンのボタンを押してテレビに電源ONの指示を出す場合などに、ライト・コマンドが使われます。

Signeライト・コマンドは、シグネチャつきのライト・コマンドです。機器を動作させるような、重要な書き込みを、信頼できるクライアントのみに書き込みを許可したい場合に、使います。

インディケーションとノーティフィケーション

サーバからクライアントへの通知は、インディケーションまたはノーティフィケーションのいずれかを使います:

  • Handle Value Indication
  • Handle Value Notification

Handle Value Indicationはリクエスト/レスポンス、もHandle Value Notificationはコマンドである違いだけです。いずれも、アトリビュート・ハンドルと現在のアトリビュート・バリュー(長さは最大(ATT_MTU–3)オクテット)をクライアントに送ります。

ジェネリック・アトリビュート・プロファイル

ジェネリック・アトリビュート・プロファイル(Generic attribute profile, GATT)は、アトリビュート・プロトコルを使ったデータ交換の構造を与えます。その基礎構造は、サービスとキャラクタリスティクスという2つの要素で組み立てられます。

サービスは、機器の内部にある互いに独立した機能を表します。サービスというキャラクタリスティクスの集合です。キャラクタリスティクスは、外部センサー値や機器の内部状態および操作指示を読み書きするアトリビュートです。

GATTは、サービスとキャラクタリスティクスの発見、読み書き、そしてノーティフィケーションとインディケーションを提供します。

GATTは、サーバとクライアントの2つの役割を決めます。サーバは、アトリビュート・プロトコルのコマンドやリクエストを受けてレスポンスを返す役割です。クライアントは、アトリビュート・プロトコルのコマンドやリクエストを発行する役割です。このサーバとクライアントは、GATT層での役割です。リンク層のピコネットのマスターやスレーブなど、ATTTよりも下の層が決める役割とは、関係はありません。

サーバとクライアントの役割は物理的なデバイスに固定されるものではありません。ある機器がサーバになったりクライアントになることも、また同時にサーバかつクライアントになることも、利用場面次第であります。

サービスとキャラクタリスティクス

クラシックBluetoothでは、プロトコルは通信手順とデータ表現の仕様を、プロファイルは接続した機器の振る舞いの仕様を表します。Bluetoothのロゴがついていれば、製品やメーカを問わずに相互に接続できるのは、ヘッドフォンや着信通知といったアプリケーションごとに、機器の振る舞いまでを含めて仕様が決められて、規格認証の厳格な運用があるからです。

クラシックBluetoothには、オーディオのストリーミングや大容量のデータ・ファイル交換など、求められるネットワークの特性もデータ形式もまるで異なるアプリケーションがあります。そのために、プロトコルとプロファイルはアプリケーションごとに決められ、論理的通信チャンネルを提供するL2CAP層の上に作られます。

Bluetooth LEでは、すべてのアプリケーションはGATTの上に作られます。このGATTは、アトリビュート・プロトコルを使って、サービスとキャラクタリスティクスという仕組みを提供します。これがBluetooth LEで多種多様なアプリケーションが展開できる鍵になります。

GATTのサービスとは、アトリビュート・プロトコルのサーバが外部公開する、機能の単位です。例えば、ある機器がユーザに警告音を伝える機能、のように、機器の単1の機能を、サービス、という概念で扱います。サービスは、キャラクタリスティクスの集合です。キャラクタリスティクスとは、その機器の内部状態、操作指示、そして外部センサ値を読み書きするアトリビュートです。

GATTは、その機器の機能それぞれをサービスという単位で切り分けます。Bluetooth LEのサービスの概念は、ある機器の内部のハードウェアそれぞれの、互いに独立した機能の単位であり、けっして、ユーザやプロファイル(機器の使われ方)から見た個別機器の機能ではありません。例えばエアコンであれば、消費電力の計測、気温の検出、室温制御は、それぞれ個別に3つのサービスとして実装されるでしょう。ユーザ(プロファイル)からみると、これはエアコンという、1つのサービスにしそうです。しかし、Bluetooth LEのサービスは、その機器の内部にある互いに独立したハードウェアの機能、を指します。

このBluetooth LEのサービスが、機器の機能と、機器の使われ方を分離します。機器の使われ方、つまりプロファイルは、その機器のサービスの中から、必要なサービスを組み合わせて使います。

例えば、機器に音や振動でユーザにアラートを出す機能があるとします。この1つの機能が外部のクライアントにサービスとして公開されます。クライアントは、スマートフォンのアプリケーションなどでしょう。もしもそれが、スマートフォンの置き忘れ防止のアプリケーションならば、スマートフォンを置き忘れそうになったら、アプリケーションが機器にアラートを出させるでしょう。もしも、電話着信通知のアプリケーションであれば、電話着信を検出したら機器にアラートを出させるでしょう。サービスの利用方法は限定されていないので、アプリケーション側を作りサービスを組み合わせて、それまで考えられていなかった振る舞いでも、実現できます。

クラシックBluetoothと異なり、開発者が決めた任意のサービスおよびプロファイルを実装した機器を、Bluetoothの規格認証をうけてBluetoothロゴをつけて販売することができます。それは、Bluetooth LEの規格認証の範囲がGATT層までで、サービスやプロファイルはGATT層の上のアプリケーション層で作るからです。Bluetooth LEにも、クラシックBluetoothと同様に、メーカを超えた相互運用のために、Bluetooth SIGが承認したプロファイルおよびサービスの仕様があります。それらを実装するときは、それらの仕様に従います。

サービスとキャラクタリスティクスの例

懐中電灯を例にして、サービスとキャラクタリスティクスの使われ方を述べます。懐中電灯は、ライトのオンオフ状態などのデータを持っていますから、GATTでの役割はサーバになります。懐中電灯のサービスは、ライトの点灯状態の取得とそのオンオフ設定、そして明るさセンサーからの検出値の取得ができるものとします。

サーバ側のサービスとキャラクタリスティクス、そしてクライアントのプロファイルの設計するかは、Bluetooth SIGが定義しているものがあれば、それを使います。該当するものがない場合は、独自に設計します。該当するプロファイルがない場合でも、そのプロファイルに必要なサービスがBluetooth SIGで定義されていれば、その定義を利用すれば工程が少なくなる場合があります。

サービスに含まれるキャラクタリスティクスは次の3つのタイプに分類されます:

  • 外部の値
    • センサー値など、読み出すたびに値が変化するようなもの
  • 機器内部の値
    • デバイス内部の動作状態 (制御式の内部変数の値など)
  • コントロールポイント
    • 照明スイッチのOn/Offの操作など。
    • このような操作を書き込むものは、readableではなく、writable/notifiableに設計する

このサービスには、ライトのスイッチ、ステータス、そして明るさ、の3つのキャラクタリスティクスがあります。それぞれのキャラクタリスティクスは、コントロール・ポイント、機器内部の値、そしてセンサー値を表します。

スイッチのキャラクタリスティクスは、外部からの操作を書き込むものですから、パーミッションは書き込みのみ読み出し不可に設定されているでしょう。ここにオン、オフに対応する値を書き込めば、サーバである懐中電灯がライトを制御します。オンオフを表す値をどうするかなどは、開発者が仕様を作り定義します。

ステータスのキャラクタリスティクスは、懐中電灯の制御プログラムの内部状態を表します。ライトの制御プログラムの動作は、状態遷移図で表現されます。スイッチの操作により、オンとオフの2状態を遷移します。このステータスは、スイッチのキャラクタリスティクスへの書き込み値で遷移するかもしれませんが、サーバの内部の変数の値ですから、このステータス自体が外部から書き込まれることは決してありません。ですから、パーミッションは読み出しのみに設定されます。

最後の明るさのキャラクタリスティクスは、実際のライトの明るさの検出値を表します。ライトの明るさは、ランプの劣化や乾電池の残量低下などで変化するでしょうから、実際の明るさを検出したくなるでしょう。検出値は読みだすだけで、書き込むものではありません。ですから、パーミッションは読み込みのみに設定されます。また、一定の時間間隔で、サーバからクライアントに通知(ノーティフィケーションまたはインディケーション)が必要かもしれません。GATTのキャラクタリスティクスには、クライアントに通知をする、しないを設定する機能があります。

ここまでの懐中電灯のサービスを定義したあとで、クライアントから乾電池の残量も知りたいと要求が出されたとします。このときに、懐中電灯のサービスにバッテリー残量を表すキャラクタリスティクスを追加する必要はありません。Bluetooth SIGには、バッテリー状態を公開するBattery Serviceが定義されていますから、このサービスを追加します。懐中電灯のサービスと、このバッテリー状態のサービスを組み合わせて使うことで、追加要求を満たすプロファイルが実現できます。

サービス

GATTのサービスは、データと関連付けられた機器の振る舞いや機能をあらわすものです。GATTのプロファイルはサービスの集合です 。サービスの内容は、他のサービスへの参照とキャラクタリスティクスの集合です。サービスの定義は、サービスに必須のキャラクタリスティクスと、オプションのキャラクタリスティクスを定義します。

GATTのサービスとキャラクタリスティクスは、オブジェクト指向プログラミングのクラスとプロパティに例えられます。1つの独立した機能がクラスの定義です。アトリビュート・プロトコルのアトリビュートには、ちょうどプログラミングでいうメモリ・アドレスに相当する、ハンドルがあります。GATTは、サーバの複数のアトリビュートで、サービスの定義にしたがったサービスのインスタンスを公開しているようなものです。

サービスは、それぞれが独立した機能です。ですから異なる機器で、1つのサービスの定義を広く使えます。例えば、バッテリー状態を表すバッテリー・サービスが定義されています。このサービスは、電気車ならばその電池残量や容量、あるいは懐中電灯ならばその電池残量を表すでしょう。バッテリー・サービスはバッテリー・サービスとして振る舞うので、異なる機器でもプロファイルが求める機能が含まれていれば、利用ができます。サービスの使われ方、プロファイルは、クライアントの実装が決めます。

サービスは、他のサービスを参照して、そのサービスのキャラクタリスティクスを取り込むことができます。この参照されたサービスを included service と呼びます。他のサービスを取り込んだサービスは、included service自体の振る舞いを変更することは禁止されます。新しい値や振る舞いをキャラクタリスティクスとして追加することだけができます。included serviceの数や階層の深さに制約はありません。

このincluded serviceの仕組みは、サービスを継承して新しいサービスを作ること、後方互換性を保ちつつサービスを拡張することに使えます

例えば、バッテリー・サービスに、充電式電池に対応させるために電池の満充電容量の値を追加したいとします。この場合は、バッテリー・サービスを参照して、満充電容量の値のキャラクタリスティクスを追加した、新しいサービスBを定義できます。サービスBには対応していない更新されていないクライアントは、バッテリー・サービスを発見して、従来通りに動くでしょう。サービスBに対応した新しいクライアントは、拡張された満充電容量の値も読めるでしょう。

サービスには、プライマリ・サービスとセカンダリ・サービスの2タイプがあります。プライマリ・サービスは外部に公開されるサービスです。プライマリ・サービスは他のサービスのincluded serviceにもなれます。セカンダリ・サービスは外部に公開されず、他のサービスのincluded serviceとして使われるのみのサービスです。セカンダリ・サービスは、複数のサービスで定義を共有したいバリューをまとめるのに用いて、他のプライマリ・サービスから参照される使われ方をします。

アトリビュート・タイプ アトリビュート・バリュー
0x2800, プライマリ・サービス サービスのUUID(16もしくは128ビット)
0x2801, セカンダリ・サービス サービスのUUID(16もしくは128ビット)
0x2802, Includedサービス Included Service Attribute Handle
End Group Handle
Service UUID (16ビットの場合のみ)

アトリビュートをグループ化するサービスは、プライマリおよびセカンダリ・サービスの宣言から始まります。アトリビュート・ハンドルは任意の値が取れます。サービスの宣言は、バリューにそのサービスのUUIDを含みます。

そのサービスが他のサービスを参照している場合は、サービス宣言の次に、インクルード・ディフィニション(include definition)が続きます。インクルード・ディフィニションのバリューは、参照するサービス宣言のアトリビュート・ハンドル、それにそのサービス宣言の末尾を表すアトリビュート・ハンドルが続きます。もしも参照するサービスのUUIDが16ビットならば、そのUUIDが続きます。

キャラクタリスティクス

キャラクタリスティクスは、サーバの値や振る舞いの表現に使われます。キャラクタリスティクスの働きは、クライアントからサーバへの値の読み書きと、サーバからクライアントへの通知の2つです。キャラクタリスティクスの値とその読み書きの意図は、サーバで検出されたセンサなどの外部値、サーバの内部動作状態を表す値、そしてサーバへの動作指示、の3つに分類できます。

キャラクタリスティクスは、アトリビュート・ハンドルが連続する、アトリビュートの集合で表現されます。キャラクタリスティクスを構成するアトリビュートは、次の3つです:

  • デクラレーション (Declaration)
  • バリュー (Value)
  • ディスクリプタ (Descriptor(s))

デスクリプションは、キャラクタリスティクスの読み書きなどの属性を制限します。バリューは、そのキャラクタリスティクスの値そのものを表します。ディスクリプタは、バリューの単位や表記を表します。

キャラクタリスティクス・デクラレーション

キャラクタリスティクス・デクラレーションは、アトリビュート・タイプ 0x2803 のアトリビュートで表します。アトリビュート・ハンドラは任意の値が取れます。アトリビュート・バリューは、キャラクタリスティクス・プロパティ、そのキャラクタリスティクスのバリューのアトリビュート・ハンドラ、およびキャラクタリスティクスのUUIDで構成されます。

アトリビュート・タイプ アトリビュート・バリュー
0x2803 Characteristic Properties
Characteristic Value Attribute Handle
Characteristic UUID

このプロパティは、キャラクタリスティクスのパーミションをビット・フィールドで表す1オクテットの値です。クライアントは、このプロパティから、キャラクタリスティクス・バリューに使える
アトリビュート・プロトコルを知ることができます。

プロパティののBroadcastは、アトリビュート・プロパティにはないものです。もしもこのビットが1ならば、キャラクタリスティクス・デスクリプタに対応する値を書き込めば、キャラクタリスティクス・バリューがアドバタイズメント・データでブロードキャストされます。

プロパティ名 概要
Broadcast 0x01 アドバタイズメントでのブロードキャスト
Read 0x02 読み出し可能
Write Without Response 0x04 レスポンスなしの書き込み
Write 0x08 レスポンスありでの書き込み
Notify 0x10 ノーティフィケーション
Indicate 0x20 インディケーション
Authenticated Signed Writes 0x40 シグネチャ付きの書き込み
Extended Properties 0x80 プロパティの拡張定義

キャラクタリスティクス・バリュー・アトリビュート・ハンドルは、このキャラクタリスティクスのバリューを保持するアトリビュートのアトリビュート・ハンドラを示します。今のGATTの仕様では、キャラクタリスティクス・デクラレーションの次のアトリビュートが、キャラクタリスティクス・バリューになります。暗黙的なアトリビュートの並びに依存しないように、将来の実装が変更になっても対応できるように、このハンドラがあります。

キャラクタリスティクス・ディスクリプタ

キャラクタリスティクス・デスクリプタは、キャラクタリスティクス・バリューが文字列なのか数値なのかといった変数の型や単位等の情報の追加と、そのキャラクタリスティクス・バリューが変更された時にクライアントに通知する/しないといった振る舞いの指定に使います。

キャラクタリスティクス・バリューの値の説明や振る舞いは、サービスの仕様書に文章で記載されます。キャラクタリスティクス・デスクリプタは、そのサービスの仕様書にある内容をアトリビュートで表現します。キャラクタリスティクス・デスクリプタを読み出せば、もしもそのサービスの仕様を知らなくても、値の表示はできるはずです。

キャラクタリスティクス・デスクリプタは以下のとおりです:

  • Characteristic Extended Properties
  • Characteristic User Descriptor
  • Client Characteristic Configuration
  • Server Characteristic Configuration
  • Characteristic Presentation Format
  • Characteristic Aggregation Format

多くのデスクリプタがありますが、ほとんどはオプションです。

Characteristic Extended Properties

Characteristic Extended Properties は、キャラクタリスティクス・プロパティの追加定義を与えます。キャラクタリスティクス・デクラレーションのプロパティのExtended Propertiesが1の時に、現れます。このプロパティは、Reliable Write と Writable Auxiliaries の2つのフラグがあります。

Reliable Writeのビットが1ならば、キャラクタリスティクス・バリューの書き込みに、2段階の書き込み手順を使うことが許可されます。これは、
まずクライアントからサーバにアトリビュート・プロトコルのPrepare Write リクエスト/レスポンスでデータを送信します。次に、Execute write リクエスト/レスポンスで書き込みます。それぞれのリクエスト/レスポンスごとに、サーバはデータの正当性を確認して、エラーがあればそれをクライアントに通知できます。これにより、より信頼できるデータ書き込みができます。

Write Auxiliariesのビットが1ならば、Characteristic User デスクリプションのバリューへの書き込みが許可されます。

Characteristic User Descriptor

Characteristic User Descriptorは、キャラクタリスティクスを説明する人間に読める文字列です。Characteristic Extended Properties の Writable auxiliariesビットを1にして、ユーザに任意の文字列の書き込みを許可できます。

例えば、家の温度センサーなら、その温度がどの部屋のものかを表示したくなるでしょう。そのような場合に、キャラクタリスティクス・ユーザ・デスクリプタに部屋番号を記録しておけば、わかりやすくなります。

Client Characteristic Configuration

クライアント・キャラクタリスティクス・コンフィグレーションは、キャラクタリスティクスの振る舞いを設定するために、クライアントがサーバに書き込むものです。キャラクタリスティクスのプロパティの、notifiable もしくは indicatable が1ならば、必ずこのコンフィグレーションがあります。

クライアント・キャラクタリスティクス・コンフィグレーションは2ビットの値、1ビットがnotifications, もう1ビットがindications、を持ちます。クライアントがnotificationsに1を書き込めば、このキャラクタリスティクスのバリューが変更されたときに、サーバはクライアントにノーティフィケーションを送ります。indicationが1ならば、インディケーションを送ります。

クライアント・キャラクタリスティクス・コンフィグレーションは、クライアント固有の設定です。例えば、クライアントAとBの2つがサーバに接続している時、クライアントAがindicationを1にすれば、クライアントAにのみインディケーションが送信されます。またボンディングをしていないクライアントでは、接続時に全てのビットは初期値0に設定されます。

Server Characteristic Configuration

サーバ・キャラクタリスティクス・コンフィグレーションは、broadcastの1ビットの値だけを持ちます。このキャラクタリスティクスが含まれるサービスに関連した値が、アドバタイズメント・データでブロードキャストされます。どのようにブロードキャストされるかは、サービスの仕様が決めます。

ブロードキャストされるデータは、このサーバ・キャラクタリスティクス・コンフィグレーションを含むキャラクタリスティクス自体の値ではありません。このキャラクタリスティクスが含まれるサービスに関連するデータです。これは、値の意味や振る舞いを定義するのはサービスであって、キャラクタリスティクスそれ自体ではないからです。例えば、温度が20度、をブロードキャストしても意味がありません。例えば、室内が20度のように、ブロードキャストされたデータを説明するために、文脈が必要になります。

サーバ・キャラクタリスティクス・コンフィグレーションは、サーバ側の設定です。ですから、どのクライアントが設定しても、それはサーバの振る舞いを変更します。

Characteristic Presentation Format

キャラクタリスティクス・プレゼンテーション・フォーマットは、キャラクタリスティクスのバリューの変数型や表記フォーマットを表すのに使います。

サービスの仕様を知らない、キャラクタリスティクスを読み出すだけの汎用のクライアントでも、キャラクタリスティクス・プレゼンテーション・フォーマットを読み込むことで、その値がなにを意味するかを理解できなくても、人間が読める形でデータを表示できます。

このキャラクタリスティクスのフィールドは:

  • Format
  • Exponent
  • Unit
  • Namescape
  • Description

で構成されます。

Formatは、変数の型を表します。次のような型があります:

  • Boolean
  • unsigned 2-bit / 4-bit
  • unsigned/ signed 8 ~ 128 bit 整数
  • IEEE-754 浮動小数点型
  • 主に医療機器で使われる、2つの、固定サイズの固定小数点
  • UTF-8 / UTF-16

Unitは単位を表す16ビットのUUIDです。この割当は、Bluetooth SIGのassigned numbersに列挙されています。
<!–
のこる2つのフィールドは、合わせて1つの値とみなすべき。
namespace とdescription fieldは、値の付加情報。
namespaceは、1バイトのフィールドで、そのdescriptionフィールドをどこの組織が決めているかを表す。descriptionは符号なし16ビットの値。
例えばセンサーが外と中にあり、Descriptionの違いはそれだけなら、このフィールドで、その違いを見てやればいい。

Characteristic Aggregation Format
いくつかのValueの組み合わせで意味がある値がある。例えば地球上での位置は、緯度と経度で表す。そのような値のフォーマットを指定するのがAggregation Format。
例えばこの例なら、Aggregation formatは、正しい順番でその2つのCharacteristics descriptorを参照する。
–>

Bluetooth LEの解説本、第1章

Bluetooth Low Enrgyとは

Bluetooth3.0までのBluetoothと、Bluetooth4.0で統合されたBluetooth Low Energy(ブルートゥース ロー エナージー、以下 Bluetooth LE)は、技術も使われ方も、まったく異なるものです。

本書では、Bluetooth3.0までの技術と、Bluetooth LEを区別するために、このBluetooth3.0までの技術をクラシックBluetoothと呼びます。

この章は、クラシックBluetoothとBluetooth LEの違いを述べます。アプリケーション開発やハードウェアの開発と製造販売、使われ方それぞれで、Bluetooth LEだからできるようになったことが、たくさんあります。自分の担当分野が技術でなくても、企画立案やメンバー全員でのブレイン・ストーミングの前に、この章の知識をメンバー全員で共有しておけば、その後の進行が早く確実なものになります。

Bluetooth LEとクラシックBluetoothの違い

まず、これまでのBlueoothとBluetooth LEの違いを、かいつまんで見ていきます。項目ごとの詳細説明は次節以降を読んでください。

無線通信技術

クラシックBluetoothに対して、Bluetooth LEは:

  • Bluetooth3.0までの端末は、Bluetooth LEのみに対応した周辺機器と接続できない
  • リチウムコイン型電池1つで1〜2年間の無線通信ができるほど超低消費電力
  • 低頻度、少量データの通信に向く
  • 任意の機能を任意に実装できる
  • 近接検出、ブロードキャストができる

です。

Bluetooth4.0に統合されたBluetooth LEは、コンビニエンスストアでも売られているほど一般的な直径2cm程度のリチウムコイン型電池1つで、1年間程度の無線通信ができるほどの、超低消費電力無線通信技術です。

Bluetooth4.0は、Bluetooth3.0にBluetooth LEを統合したものです。Blueooth LEは、Bluetooth3.0までの技術とは異なる技術です。そのため、Bluetooth LEだけに対応した周辺機器(シングルモードと呼びます)は、Bluetooth3.0までの対応機器とは接続できません。

超低消費電力の無線通信技術には、ほかにもANT+やZigbeeなどの規格がありますが、Bluetoothというブランドに統合されたこと、そしてiPhoneが採用したことで、一気に世界に普及しました。

Bluetooth LEの使い道は、たまにしか通信をしないが(低頻度)、必要があればすぐ通信ができる(通信の遅れ時間、レイテンシが小さい)、分野です。

BluetoothLEの応用分野は、例えば、テレビのリモコンのようなデバイスです。リモコンは、普段は使われることがなく、たまにボタンが押されます。ですが、ボタンが押されたら直ちにテレビが反応しなければなりません。

クラシックBluetoothでリモコンを作ると、通信を保持するために、リモコンを使っていない間も電池を消費します。そのため充電し続ける必要があります。もしも電池節約のために、ボタンが押されたら通信接続する設計にすると、通信接続に数秒は時間がかかりますから、反応が悪くて使えません。

Bluetooth LEは、任意のオリジナルの機能を実装できる規格です。クラシックBluetooth規格は、ヘッドセットなど応用ごとに振る舞いを定義していました。そのため、規格の認証に、機器の振る舞いまでが含まれました。Bluetooth LEは、Generic Attribute Profile(ジェネリック アトリビュート プロファイル、GATT)までを規格として、このうえに実装されるデバイスの機能は規格に含みません。通信と機器の振る舞いを分離することで、ユーザは任意の機能の実装ができます。

Bluetooth LEデバイスは、近接検出や少量データのブロードキャストができます。Bluetooth LEは、そのデバイスをiPhoneに発見してもらうために、アドバタイズメント・パケットを送信します。このアドバタイズメント・パケットは、周囲の端末が受信できる、ブロードキャストです。このパケットの受信信号強度から、デバイスとの距離がおおまかに算出できます。また、このパケットには、20バイト程度の任意のバイト・データを入れられます。これを屋内測位や位置ビーコンに応用できます。

iOSアプリケーション開発

iOSアプリケーション開発では、クラシックBluetoothとBluetooth LEデバイスとで、扱いが違います。

  • ペアリングがなくてもよい
  • iOSを通さず、アプリケーションから直接、デバイスの発見と接続ができる
  • Bluetooth LE用のバックグラウンド・モードがある
  • iPhoneはセントラルとペリフェラル、どちらにもなれる

Bluetooth LEデバイスには、ペアリングをしなくても接続させることができます。クラシックBluetoothデバイスは、初めてiPhoneと接続するときに、ペアリングという接続認証が必要でした。

Bluetooth LEデバイスでは、鍵の交換をペアリング、交換した鍵などの情報を保存することをボンディングと言います。ボンディングをしていれば、再接続時にそれらの鍵を使い前回ペアリングしていた状態を復帰できます。ペアリングもボンディングもしない、ペアリングするがボンディングはしない、ペアリングをしてボンディングもする、の組み合わせが取れます。

Bluetooth LEのデバイスの発見、接続、通信そして切断も、すべてiOSアプリケーションが行います。iOSが関与することはありません。

iOSアプリケーションには、Bluetooth LE専用のバックグラウンド・モードが用意されています。バックグラウンドで、Bluetooth LEデバイスと接続して通信しつづける、あるいはデバイスを発見して接続をすることができます。

Bluetooth LEのネットワーク・トポロジーには、セントラルとペリフェラルという2つの役割があります。前節で例に上げたテレビのリモコンの場合は、テレビがセントラル、リモコンがペリフェラルです。iOS6では、iOSアプリケーションはセントラルにも、ペリフェラルにもなれます。つまり、iOSアプリケーション自体が、テレビのリモコンとして振る舞うことができます。

ハードウェア設計

  • Made for iPhoneプログラムは不要
  • 部品代は5ドル、モジュールは10ドル前後
  • モジュールを利用した場合、電波法の認証及びBluetoothの認証費用はかからない

Bluetooth LEは、Made for iPhone (MFi) プログラム の締結が必須ではありません。クラシックBluetoothでは、特定のiOSアプリケーションと接続するデバイスの開発には、Apple社とMFiプログラムの締結が必須でした。MFiプログラムを締結して初めて、ハードウェアやソフトウェアの開発情報が提供されます。

MFiプログラムが必須ではなくなったことで、ベンチャーの参入ハードルが極端に下がりました。Bluetooth LEデバイスと接続するiOSアプリケーションは、AppStoreの承認を得られます。ただし、デバイスにMFiロゴを掲載する場合は、ロゴの使用権を得るためにMFiプログラムの締結が必要です。

Bluetooth LEの電子回路を独自に設計した場合の製造コストは5ドル程度です。これらの電子回路を小さな基板にまとめて部品化したものを、モジュールと呼びます。モジュールの価格は10ドル前後です。

モジュールを使うことで、電波法の基準認定とBluetoothの規格認定および製品登録にかかる初期費用が省けます。

電波を出す回路は、外部に電波がもれ出ない試験環境で動作させるか、あるいは、それぞれの国ごとの電波法に基づく基準認証をうけなくてはいけません。認証を取得しているモジュールを利用すれば、製品ごとに認証をとる必要はありません。

また、Bluetooth LEロゴを表示して販売するには、Bluetooth SIGへの製品登録が必要です。製品登録には、Bluetoooth SIGへの、最低でも年間5000ドルのメンバー登録費用と、設計ごとの認証費用が必要です。

各社のモジュールは、それ単体でのBluetoothの製品登録が完了しています。無償のBluetooth SIGのメンバー登録をおこない、Bluetooth LEのモジュールを利用した派生製品として登録すれば(この登録は無償)、製品登録が完了します。

AndroidとWindows Phone8の対応

周辺機器の企画作成では、対応機種が多いほど、より多く売れる感じがするため、iOS以外のモバイルOSの対応を考えるかもしれません。OS(オーエス、Operating System、オペレーティングシステム)とは、アプリケーションに基本的な機能を提供する、システム全体を管理するソフトウェアのことです。

iOS以外のモバイルOSは、Android OSがあります。このほか、2013年には、Windows Phone 8、Tizen、Firefox OS、も登場するかもしれません。

http://www.android.com
http://www.mozilla.jp/firefoxos/
https://www.tizen.org/ja

一般開発者から見たBluetooth LE対応の条件は:

  1. ハードウェアがBluetooth4.0に対応している
  2. OSがBluetoth LEに対応している
  3. ソフトウェア開発環境からBluetooth LEが使える(開発環境が一般開発者に開放されている)

の3条件が必要です。

Androidは、ハードウェアの認定要件がありません。2013年7月時点での新製品でも、Bluetooth2.0, 3.0そして4.0が混在しています。特に低価格帯にBluetooth2.0が多いようです。ただ、Bluetooth3.0の機種が、発売後にBluetooth4.0の再認証を取得してきています。発売時点で、Android OSが公式にBluetooth LEに対応していないため、ハードウェアはBluetooth4.0に対応していても、Bluetooth3.0として発売したのでしょう。

Android OSに、2013年秋頃に公開定のAPI Level18で、はじめてBluetooth LEに対応します。セントラルのみに対応していています。これ以前から、半導体や端末メーカが、それぞれ独自にBluetooth LE開発ライブラリを提供していました。ライブラリごとに対応機種が限定されて、採用しにくいものでした。

https://developers.google.com/events/io/sessions/326240948

http://developer.samsung.com/ble

Windows Phone8は、2013年7月時点では、ハードウェア要件にBluetoothのバージョンの指定はありません。OSのサポートは、まったくありません。パソコンに使うWindows8は、ハードウェア認定要件でBluetoothは4.0を搭載することが決められています。

Tizen, Firefox OSは、Bluetoothを扱うライブラリにBluetooth LEが含まれていません。

Bluetooth LEの規格と取り巻く環境

規格成立までの歴史

Bluetooth Low Energyの研究は、2001年に始まりました。ノキア・リサーチ・センターは、Bluetooth規格を継承しつつ、より低消費電力で低価格な新しい無線通信技術の開発をはじめました。http://en.wikipedia.org/wiki/Bluetooth_low_energy

この低消費電力の通信技術は、紆余曲折を経てBluetoothとして統合されます。最初の研究成果は、まずBluetoot Low End Extensionとして2004年に公開されました。EU FP6プロジェクトMIMOSA(Microsystems Platform for Mobile Services and Applications)といった、パートナーと更に開発を進めた後に、この技術は2006年10月にWibree(ウィブリー)ブランドとして公開されました。

一旦はWibreeという独立した技術として公開されましたが、その後のBluetooth SIGメンバーとの交渉により、Wibreeを将来のBluetoothの低消費電力の規格として含めることに、2007年6月に合意がなされました。そして2010年6月に採択されたBluetooth コア仕様 バージョン4に、Bluetooth LEとして統合されました。

Bluetooth SMART READYとBluetooth SMARTロゴ

Bluetooth4.0に統合されたBluetooth LEは、Bluetooth3.0までの技術とは互換性がありません。ですから、Bluetooth LEのみに対応しているデバイスは、Bluetooth3.0までの端末と接続できません。一部分、下位互換性がなくなったため、これまでのように、1つのロゴで相互接続性を表せなくなりました。

そこで、Bluetooth4.0では、クラシックBluetoothの1つのロゴから、Bluetooth SMART READYとBluetooth SMARTロゴという2つのロゴになりました。<ロゴの図> https://www.bluetooth.org/ja-jp/bluetooth-brand/how-to-use-smart-marks

Bluetooth SMART READYは、クラシックBluetoothとBluetooth LEの無線通信機能をもち、それらがどちらか一方または同時に動作するものを示します。パソコンや携帯電話などに搭載されるのは、このBluetooth SMART READYです。Bluetooth SMART READYのロゴがあるものは、クラシックBluetoothとBluetooth LEのデバイス、すべてのBluetoothと接続できます。

Bluetooth SMARTは、Bluetooth LEの無線通信機能だけをもつデバイスを示します。Bluetooth3.0までの端末からは、このデバイスに接続できません。Bluetooth SMART READYの端末から、接続できます。キーホルダーや心拍計などの、周辺機器がこれを採用します。

Bluetooth 4.0の登場で、周辺機器は、Bluetooth3.0までに対応したものと、Bluetooth SMART READYのもの、そしてBluetooth SMARTの3種類になりました。周辺機器にも、Bluetooth SMART READYのものがあります。これは、まだBluetooth4.0では携帯電話とも接続するためだったり、あるいはスマート・ウォッチのように、クラシックBluetoothのメールや電話着信を通知するプロファイルを利用するためだったりします。

Bluetooth LEの無線通信

に、Bluetoot LEの特徴を示します。

Bluetooth LEの無線通信の概要

項目
周波数 2.400-2.4835 GHz
物理層のビットレート 1Mbps
通信送信電力 10mW ~ -10μW
伝達距離 150m (見通し)

Bluetooth LEが利用する周波数は、クラシックBluetoothと同じです。周波数が同じなので、デュアル・モードのデバイスは、アンテナや高周波回路をクラシックBluetoothとBluetooth LEで共用できます。

通信のビットレートは、1Mbpsです。これは0,1というビットの通信速度です。Bluetooth LEでは、データは決められた時間周期ごとにパケットの送受信でやりとりします。パケット自体の通信速度は1Mbpsですが、常にパケットが流れ続けられる規格ではないので、パケットのやり取りによる実際の通信速度は、300kbps程度になります。Bluetooth LEデバイス間の通信速度は、通信パラメータで大きく異なります。iOSデバイスでの通信速度は、おおよそ10Kバイト/秒です。

Blueooth LEの送信電力は、最大10mW、最小10μWです。最大出力電力時の通信距離は、見通し距離で150m程度になります。距離は、受信回路の感度や送受信アンテナの利得(高周波電力を電波にして空間に放つ効率)が高ければ、これより大きくなるかもしれません。また、建物の壁などの遮蔽物があったり、見通しではない環境では、通信距離は見通し距離よりもずっと短くなります。目安としてオフィスで30m程度は通信できます。

送信電力は、Bluetooth LEデバイス内部のソフトウェアから設定できます。ハードウェアで固定されないので、状況に合わせた、必要な通信距離が得られるような、送信電力の設定動作が可能です。例えば位置ビーコンであれば、位置を知らせたい範囲内に届く程度に送信電力を設定します。またiPhoneに取り付けるキーホルダのように、常に近距離にあるものならば、通信距離が数メートル程度になるように、送信電力を絞り込むこともできるでしょう。

Bluetooth LEのネットワーク

Bluetooth LEで、デバイスをこう使いたい、と考えるときに、このネットワークの仕組みを知っておくと、何ができるのかがわかり、便利です。

Bluetooth LEのネットワークは、ネットワークを制御する1つのマスターに多数のスレイブが無線接続します。

この1つのネットワークを、ピコネット(Pico net)と呼びます。ピコは、イタリア語で小さいを意味するpiccoloからとられた、SI単位系で10-12を表す接頭辞ピコのことで、ピコネットは、とても小さいネットワークの意味です。中心から周囲に広がる接続の様態が星のようなので、スター型ネットワークと呼ばれます。

<TBD: スター型の図、アドバタイズメント、iPhoneを中心にペリフェラル, iPhoneがペリフェラル>

マスターとスレイブのの接続手順は:

  • スレイブがアドバタイズメント・パケットを送出
  • マスターが、接続要求を出して接続
  • 通信
  • 切断

です。

まず、マスターがスレイブがあることを知らないといけません。スレイブは、機種情報や個体識別番号などが入ったアドバタイズメント・パケットを送出します。どのマスターでも、アドバタイズメント・パケットを受信できます。アドバタイズメント・パケットは、スレイブが、まだどのピコネットにも属していないときにだけ、送出できます。

Bluetooth LEの特徴は、スレイブの発見がとても早いことです。アドバタイズメント・パケットは、最小20ミリ秒周期(1秒に50回)で送出できます。アドバタイズメント・パケットは、は、アドバタイズメントのために割り当てられた特定の周波数範囲で送信することで、マスターはほぼ確実にこのパケットを受信できます。実際に、デバイスの発見に1秒もかかりません。

アドバタイズメント・パケットを受信してスレイブを発見したマスターは、接続要求を出して接続を確立します。接続している間、スレイブはそのマスターのピコネットに属します。接続したマスターとスレイブは、そのマスターとスレイブだけが知っている切り替え順番で、通信に使う周波数領域を切り替えながら、通信します。これを適応周波数ホッピング方式と呼びます。

同時に接続できるスレイブの上限数は、Bluetooth LEの規格では、およそ 231(約21億)です。クラシックBluetoothでは、規格で7に制約されます。同時接続上限数は、実際には、Bluetooth LEのプロトコル・スタックの実装次第で決まります。iOSデバイスは10です。ただし、iPhoneは、10のスレイブと接続しているときでも、どのスレイブからのパケットでも、アドバタイズメント・パケットの受信はできます。

スレイブは、同時に2つ以上のピコネットに属することはできません。必ず1つのピコネットに属します。スレイブとの接続は特定のピコネットに縛られることはありません。任意のマスターと接続できます。スレイブが別のピコネットに接続するときは、まず接続を切断してから、別のマスターとの接続処理をおこないます。

切断要求は、マスターからでも、スレイブからでも、出すことができます。また、マスターとスレイブは、それぞれ一定時間通信ができなければ、切断とみなします。接続切断したスレイブは、またアドバタイズメント・パケットを送出して、次の接続要求が来るのを待ちます。

Bluetooth LEのネットワークは、とてもシンプルな設計です。接続が切断しても再接続をしたりしません。クラシックBluetoothは、iOSの設定アプリケーションから、ユーザに、スレイブとのネットワーク接続をしてもらわなくてはなりません。Bluetooth LEは、しかし、スレイブの発見と接続が速いことに加えて、iOSアプリケーションがスレイブとの接続処理を直接実行できます。接続切断時は、iOSアプリケーションで再接続が必要だと判断すれば、iOSアプリケーションから接続処理を再開できます。これにより、なんの不便もありません。

プライバシーとセキュリティ

無線通信の電波は四方八方に飛んでいくため、その通信がいつどこで傍受されるかは、わかりません。Bluetooth LEの規格は、利用者のプライバシーと、通信内容が第三者に漏れないセキュリティを考慮しています。

まず、無線通信は、その通信内容から容易に人物を特定して追跡できるものであっては、なりません。Bluetooth LEのマスターとスレーブは、互いを識別するために、通信でアドレスを使います。もしも、誰かのスレーブのアドレスが分かっていれば、傍受した通信からそのアドレスを探すことで、その人がいつどこにいるかを容易に追跡できてしまします。

Bluetooth LEのアドレスには、パブリック・アドレスとランダム・アドレスの2つがあります。パブリック・アドレスは、製造企業が割り当てる固定値のアドレスです。このアドレスは製品が作られた時に書き込まれ、変化することはありません。もう1つの、ランダム・アドレスは、文字通り、ランダムなアドレスです。スレイブの電源を入れた時に、ランダムなアドレスが設定され、さらに15分毎に、あるルールに従って、別のランダムなアドレスに変更されます。

プライバシーを守るために、パブリック・アドレスを使うか、ランダム・アドレスを使うかは、そのデバイスの使われ方などで、設計者が決めます。

セキュリティーを確保するために、パケットのデータ自体を暗号化できます。スレーブはマスターに接続するときに、パケットのデータを平文(暗号化しない)とするか、AES(Advanced Encryption Standard)という暗号方式を使うかを、マスターに通知します。

プライバシーとセキュリティをどうするかは、利用場面にあわせて選びます。プライバシーとセキュリティを求めると、Bluetooth LEでは、スレイブとの接続時に、少し手間がかかります。

ランダム・アドレスを採用すると、例えば、スレーブの電源がOFF/ONされると、アドレスが再生成されます。そのため、iOSアプリケーションには、そのスレイブが以前に接続したものだと、識別できなくなります。ですから、例えばスレイブの電池交換をするたび、iOSアプリケーションでの接続設定が必要になります。

セキュリティーを採用すると、マスターが初めてスレイブと接続する時に、iOSアプリケーションから、iOSの設定アプリのBluetooth設定に表示が遷移します。スレイブの接続が完了すると、クラシックBlueoothと同じく、このBluetooth設定にデバイス名が表示されます。これは、暗号化につかう鍵がiPhoneに保存されたことを、示しています。このBluetoothの接続を削除すると、鍵が削除されます。

iOSと周辺機器のインタフェース

Bluetooth LEは、iOSと周辺機器との接続手段、インタフェースの1つです。Bluetooth LEはMFiプログラムが必須ではないので、たしかに取り組みやすいのですが、想定する利用場面に使えるのか、あるいは他により適切なインタフェースがないかを検討することは、必要です。

インタフェースは用途似あわせて、適切なものを選択します。ここでBluetooth LE以外のインタフェースをみてみます。

有線インタフェース

iOSには、有線インタフェースと無線インタフェースがあります。

有線インタフェースは、ドックコネクタがあります。iPhone4Sまでの30ピン ドックコネクタと、iPhone5以降のライトニング ドッグコネクタの2種類があります。ドックコネクタを通じて、USB2.0、480Mbpsのビットレートでの通信ができます。

ドックコネクタのハードウェア開発情報を入手するには、MFiプログラミングの参加が必須です。これに参加して提供される、ドックコネクタの認証チップは、Apple社の承認を受けた正規の製品であることを、ドックコネクタを通じてiPhoneに通知します。また、その製品に対応したiOSアプリケーションがiPhoneにないときは、ダウンロード画面を表示させます。

しかし機能実証のための試作で、MFiプログラムに参加するのは時間も費用もかかります。試作であれば、MFiプログラムに参加せずにドックコネクタを使うには、2つの方法があります。

  • シリアル・アダプタを利用する
  • MIDIインタフェースを転用する

ドックコネクタのシリアル・アダプタが60ドル程度で販売されています。115.2kbpsまでのシリアル通信ができます。試作に用途が限定されており、このアダプタに接続するiOSアプリケーションをAppStoreに出すことは、利用規約で禁止されています。

http://www.redpark.com/c2db9.html

演奏装置のインタフェース規格 MIDI(ミディ、Musical Instrument Digital Interface)のドックコネクタ・アダプタが一般に販売されています。MIDI規格は、演奏データをシリアル通信で送受信するものです。このインタフェースを転用して、サーボモータを動かすデモンストレーション例があります。iOSアプリケーションのAppStoreの承認は、この方法を使うiOSアプリケーションがAppStoreで見つからず前例が見つけられないので、わかりません。

http://youtu.be/S90heTt5-Ko

また、音響モデムの原理を利用して、イヤホン端子でデータ通信をするワザもあります。これは10kbps程度の通信ができます。iOSアプリケーションのストア承認は、AppStoreの担当者との交渉次第です。

http://web.eecs.umich.edu/~prabal/projects/hijack/

無線インタフェース

iPhoneの無線インタフェースは、WiFi、Bluetooth(クラシックBluetoothとBluetooth LE)です。それぞれのビットレートとMFiプログラムの要不要をまとめます。

名称 物理層のビットレート MFiプログラム 接続設定
無線LAN (802.11g) 54Mbps 不要 必要
Bluetooth2.0 3Mbps 必須 必要(※)
Bluetooth LE 1Mbps 不要 不要

※Bluetoothの標準プロファイルを利用する場合はMFiは不要。

無線LAN

無線LANは、最も高速な無線通信です。動画のストリーミングなど、高い通信速度が必要な用途には、無線LANを使います。通信速度が早いほど、電力消費量も大きくなります。ですから、大容量の電池を搭載していて頻繁に交換や充電をおこなう機器、例えばラジコンなどには、自然に適用できます。また、通信頻度や通信速度が低くてもよい分野に特化した、単三電池電池で数ヶ月も長期間動作するような、低消費電力の無線LANモジュールもあります。

http://www.gainspan.com/

無線LANの接続形態は、機器と1対1で接続するアドホック・モードと、複数の機器と無線LANルータを介して接続できるインフラストラクチャ・モードの2つのモードです。

インフラストラクチャ・モードは、無線LANルータが必要なので、屋内など移動しない場合に使えます。移動したり屋外での利用では、無線LANルータがあるとは限らないので、アドホック・モードを使います。周辺機器とアドホック・モードで接続すると、iPhoneの無線LANは、その機器に専有されます。もしもこのときに、外部ネットワーク接続が必要になると、WiFi以外の、3G通信かBluetoothでのテザリングを使うしかありません。

無線LANは、ユーザによる設定が必要です:

  • 無線LANの接続先の設定
  • iOSアプリケーションのダウンロード
  • 接続機器のIPアドレスの入力

無線LANの接続先は、設定アプリからユーザがおこなう必要があります。さらに、インフラストラクチャ・モードのときは、iOSアプリケーションに、接続先機器のIPアドレスの設定が必要になります。この無線LANの設定手順は、図入りで説明した説明書を別に用意しておくとよいです。設定アプリに画面遷移してしまうので、iOSアプリケーションに設定方法を表示する方法が使えません。ですから設定アプリを見ながら設定するときに説明書があれば、ユーザが迷いにくくなります。

無線LANを使う周辺機器には、MFiプログラムは必要ありません。MFiプログラムが必要ないということは、逆に、Apple社から、機器接続やiOSアプリケーションのインストール方法などの特別の配慮を受けられない、ことでもあります。

ユーザ自身が、機器に対応するiOSアプリケーションをダウンロードしなくてはなりません。機器に接続しても、iOSアプリケーションは自動でダウンロードされません。

また、例えばUPnP(ユーピーエヌピー,ユニバーサルプラグアンドプレイ、Universal Plug and Play)やBonjour(ボンジュール)など、ネットワークに接続すれば、ユーザが設定をしなくても機器が使用可能になる、ゼロ・コンフィギュレーション技術を使えば、IPアドレス設定などを不要に出来ます。しかし、iOSからゼロ・コンフィギュレーション技術は提供されていません。必要であれば独自に実装します。

クラシックBluetooth

ここでは、Bluetooth LEと区別するために、Bluetooth3.0での技術をクラシックBluetoothとよびます。

クラシックBluetoothは、常時接続してデータを送り続ける用途に適し、通信速度も高いので、ヘッドセットやテザリングなどに利用できます。クラシックBluetoothの接続は、iPhoneに多数の周辺機器が接続するスター型です。コインサイズの小さなリチウムイオン電池で、1週間程度の通信ができます。電池交換は頻繁すぎて不便ですから、充電式に設計されます。

クラシックBluetoothでは、通信のみならず接続機器の振る舞いまでを含んだ機能を、プロファイルと呼びます。パソコンで言えば、通信プロトコルのみならず、デバイスドライバの仕様までを含むものが、プロファイルと呼ばれます。

プロファイルには、キーボードやマウスなどの入力装置、メールや電話着信の通知など、様々なものが定義されています。利用目的にあったプロファイルがすでにあれば、それを利用して少ない開発労力で機器開発ができます。

Bluetooth Developer Portal, Profiles Overview

クラシックBluetoothデバイスとの接続に、まずペアリングが必要です。ペアリングはiOSが処理するため、ユーザは、設定アプリのBluetooth設定で、デバイスを選択してペアリングをします。接続が切れるたび、ユーザによる接続操作が必要です。

クラシックBluetoothのプロファイルに従う周辺機器には、MFiプログラムは不要です。しかし、利用できるプロファイルがなく、iOSアプリケーションと直接通信したい場合は、MFiプログラムへの参加が必須です。

プロファイルの利用例として、例えば、iOSデバイスと接続できるバーコードリーダがあります。バーコードリーダ専用のプロファイルはありませんが、例えばキーボードに使うHID(Human Interface Device Profile)を採用して、読みだしたバーコード情報をテキストで送信して、iOSアプリケーションに情報を伝えることができます。

クラシックBluetoothでは、任意の通信には、SPP(Seerial Port Profile)を使います。このプロファイルは、端末間の通信を決めるものです。接続先に、端末内部で動作しているアプリケーションまでを指定する仕組みはありません。

MFiプログラムに参加すると、iOSアプリケーションと接続するための認証チップを含むハードウェアなどの開発キットが提供されます。この認証チップは、Apple社が承認した周辺機器のみがiOSアプリケーションと接続できる排他的な振る舞いをしますが、同時に、SPPに欠けている、モバイル機器には必要な、周辺機器が指定されたiOSアプリケーションにのみ接続する仕組みも、提供しています。

アプリケーション+アクセサリ=アプセサリ(Appcessory)

Bluetooth LE は、アクセサリとアプリケーションがとても強く結びつきます。アクセサリとアプリケーションの2つが組み合わさって初めて魅力あるものになります。これを表す造語が、‘Appcessory’(アプセサリ)です。これは、周辺機器を意味するアクセサリ(accessory)と、そのアクセサリと接続するアプリケーション(application)の2つの単語を組み合わせた造語です。

http://developer.bluetooth.org/Pages/bluetooth-smart-developers.aspx

例えば、iPhoneの置き忘れ防止のキーホルダーがあります。これは、iPhoneとキーホルダーが離れたのを電波の受信強度で検出して、置き忘れしそうなときに、キーホルダーとiPhoneそれぞれからブザー音などの警告を出すデバイスです。このようにBluetooth LEデバイスは、デバイスとアプリケーション、両方あってはじめて意味があります。

Bluetooth LEの規格が、アプセサリを生み出した面があります。他の超低消費電力無線通信技術には、例えばZigbee、他のデバイスからの通信を他のデバイスに中継する、マルチホップ通信の機能があります。マルチホップ通信ができれば、広域に散らばった多数のデバイスがネットワークを形成して、例えばセンサネットワークなどを、デバイス単体で構築出来ます。

ですが、Bluetooth LEのネットワークは、スター型で、マルチホップ通信の機能はありません。アクセサリは隣接するiPhoneと直接接続することしかできません。ハブ・デバイスとアクセサリ、という1対1対応のみが可能です。

アクセサリとiOSアプリケーションの連携

アクセサリとiOSアプリケーションの連携には、その間をつなぐオペレティング・システムが、連携に必要な機能を提供していなければなりません。クラシックBluetoothと異なり、Bluetooth LEを使うiOSアプリケーションは、MFiプログラムがなくても一般開発者プログラムだけで、開発とAppStoreの承認がでるようになりました。それだけではなく、Apple社は、連携に不可欠の機能を提供しています:

  • デバイスの発見と接続
  • アプリケーションがドライバ相当の機能をもつ
  • セントラルとペリフェラルの両方になれる
  • ユーザ通知機能
  • バックグラウンド動作

デバイスの発見と接続

Bluetooth LEデバイスの発見と接続は、iOSアプリケーションの役割です。iOSは、接続などに、何も関与しません。クラシックBluetoothは、ペアリングおよびデバイスへの接続は、設定アプリでユーザが操作しなければなりませんでした。どのようなBluetooth LEデバイスがあるか、どのデバイスに接続するか、また切断した時の再接続処理などは、iOSアプリケーションの役割です。例えば、Bluetooth LEは、接続が切れたならば、切れたままにする通信規格です。もしも再接続が必要になる都度、ユーザに接続操作を要求していたら、ユーザはいらいらするでしょう。

アプリケーションがドライバ相当の機能をもつ

Bluetooth LEデバイスとの接続はiOSアプリケーションに任されているため、パソコンのデバイス・ドライバに相当するソフトウェア、接続したデバイスの動作を制御する機能も、iOSアプリケーションに実装することになります。パソコンのように、iOSにドライバ・ソフトウェアをインストールすることは、不可能です。もしもそんなことを許せば、セキュリティや安定動作などの、iOSが提供する価値が損なわれるでしょう。

たいていの場合、iOSアプリケーション開発者がドライバ相当の機能を実装することはありません。市販されているBluetooth LEデバイスは、たいてい製造開発会社からiOSアプリケーション開発者に、そのデバイスを使うためのソフトウェア開発キットが提供されています。そのデバイスに対応するiOSアプリケーションの数が、売上につながるからです。

また独自仕様のBluetooth LEデバイスを開発する場合は、iOSアプリケーション開発者ではなく、デバイス開発者がドライバ部分を担当します。これは、デバイスの正常動作を確認するために、ドライバ部分も含めて作ることが、必要だからです。

セントラルとペリフェラルの両方になれる

Bluetooth LEのネットワークは、1つのセントラルに複数のペリフェラルが接続する、1対N接続のスター型です。1つのiPhoneに複数の周辺機器が接続するため、iPhoneをセントラル、周辺機器をペリフェラルにします。

iOS5は、セントラルの機能を提供しています。iOS6は、セントラルに加えてペリフェラルの機能も提供されます。iPhoneがペリフェラルになると、例えばBluetooth LEのワイヤレス・マウスとして振る舞うようiOSアプリケーションが作れますから、iOSアプリからパソコン操作もできるでしょう。

iPhoneがペリフェラルになると、Bluetooth LEデバイスのプロトタイピングが容易になります。iOS5の時代は、ペリフェラルになる組み込み機器向けのBluetooth LEデバイスの開発セットを使うほかありませんでした。このために、組み込み機器の開発手順の学習が必要でした。さらに、たいていのプロトタイピングは加速度センサを必要としますが、その回路も作らねばなりませんでした。iOS6では、これがiOSアプリケーション開発のスキルで、iPhoneの豊富なセンサがそのまま利用できるようになりました。

ユーザ通知機能

iOSはBluetooth LEの通信には関与しません。しかし、iOSアプリケーションがアクセサリに接続したままで終了すると、そのアクセサリとiPhoneとの接続は維持されます。このときに、アクセサリがiOSアプリケーションに通知を送信すると、それを受信すべきiOSアプリケーションが停止しているので、その通知があったことが通知センターに表示されます。

アプセサリは、アプリケーションと強く結びつきます。しかし、iOSアプリケーションは、メモリ不足により強制終了されるなど、常に実行されているとは保証ができません。このユーザ通知表示は、iOSアプリケーションが実行されていないときでも、最低限の機能の提供に使えます。

例えば、置き忘れ防止のキーホルダーに使えます。キーホルダーは、通信の電波強度が弱くなればiPhoneとの距離があいたとして、置き忘れの警告をiPhoneに送信します。対応するiOSアプリケーションが起動していれば、適切な音や表示が出せますが、iOSアプリケーションが動いていなくても、最低限の通知はできます。

この通知は補助的なもので、必ず動作するものではありません。もしも、アクセサリとiPhoneの接続が切断すれば、iOSは再接続をしませんから、切断したままになります。そして、アクセサリとの接続が切断しても、ユーザには何も通知されませんから、ユーザが切断に気づくことはありません。もしも、ユーザが必ず通知が来るものだと思っていると、この振る舞いはユーザには見えませんから、期待と異なる商品と受け取られます。

バックグラウンド動作

iOS4から、音楽再生やVoIPなど分野を限定して、アプリケーションのバックグラウンド・モードが導入されました。iOS5では、Bluetooth LEのセントラルとしてのバックグラウンド・モードが、iOS6では、それに加えてペリフェラルでのバックグラウンド・モードが、追加されました。

iOSアプリケーションのバックグラウンド動作は、アプリケーションが自分で好きなだけ処理をおこなうものでは、ありません。Bluetooth LEのバックグラウンド動作では、Bluetooth LEの発見や通知などのイベントが発生するたびに、iOSアプリケーションに10秒間の処理時間が与えられます。<TBD ペリフェラルでのBG動作>

このバックグラウンド・モードを使い、例えばBluetooth LEデバイスとの通信が切断したときに、前節の通知と異なり、iOSアプリケーションから再接続処理を行うことも、またユーザに切断を通知することもできます。

iPhoneはハブ・デバイス

アクセサリは、安価で小さいことが求められます。コストと大きさは、電池容量や液晶などの表示器を、小さいものに制約します。アプセサリはiPhoneの機能:

  • iOSアプリケーションの表示やユーザ・インタフェース
  • 計算処理能力や記憶媒体、データの統合
  • 3Gを含めたネットワーク接続

を、アクセサリの機能として利用します。

iPhoneの液晶とユーザインタフェースが利用できると、アクセサリの表現力がぐっと広がります。アクセサリは、たいてい本体が小さいので、液晶表示器自体の大きさも小さくなります。操作手段も、本体が小さいと、小さな押ボタンを2~3個搭載するのが、精一杯です。本体の製造価格を抑えると、まず液晶が決まった数字やキャラクタを表示する簡素なものになります。ドット表示で任意の文字と図形とを表示するには、高精度のドットマトリクス方式の表示器が必要です。このように、アクセサリは本体の大きさと製造費用の2点でユーザ・インタフェースが制約されます。

例えば、ありきたりの万歩計がiPhoneのユーザ・インタフェースを使えるようになったとします。アクセサリ単体ではその日の歩数の表示だけです。最近のマイクロコントローラの処理能力であれば、万歩計が1時間あたりの歩数を記録することは容易に実現できます。ですが、万歩計自体の小さな液晶では、それを魅力的に表示できません。iPhoneの液晶表示であれば、iOSアプリケーションの実装次第で、例えば、その日の1時間あたりの歩数を、運動の強度にあわせたカラーバーでグラフ表示など、アクセサリ単体ではできない表示が、追加コストをかけずに容易に得られます。

iPhoneの記憶媒体は大容量で、端末にデータを貯めていけます。先ほどの万歩計を例に取れば、iPhoneはギガバイト単位の記憶容量があります。ですから、何百年分でも、過去の履歴データを保存できます。そして、アクセサリが壊れたり失われたりすると、そこに記録されたデータも失います。アクセサリは消耗品ですから、長期間の過去データを置いてもデータを失うようでは、意味がありません。

iPhoneは、すべてのアクセサリのデータが集積する、ハブとして機能します。また、その強力な計算処理能力で、集積したデータを統合して解析もできます。例えば、体重管理が目的のとき、万歩計以外にも心拍センサや体重計など複数のBluetooth LEデバイスで、計測をしていたとします。歩数データに心拍データを加えることで、消費カロリーをより高精度に計算できるでしょう。また体重計のデータを統合することで、運動の成果と食事内容の提案を、iPhoneから出せるようになります。

iPhoneは、集積したデータをネットワークに同期させるハブとして機能します。例えば、あるiPhoneが読みだした万歩計のデータは、Apple社のiCloudなどを使えば、iPadなどの他の自分の端末とネットワーク同期します。また、ネットワークを通じてサーバにデータを保存できます。

また、iOSアプリケーションがソーシャル・ネットワーク・サービスと連携すれば、例えば万歩計ならば毎日の運動量を仲間と共有したり競争ができます。このソーシャル・ネットワークは、サービスの魅力であり、かつ、買い替え時に他社への乗り換えの抑止にもなります。Bluetooth LEアクセサリ単体のハードウェアは単純で、設計製造を請け負う企業と通じて、参入が容易です。ですから類似製品がいろいろ出てきます。ですが、アプリケーションを通して、ソーシャル・ネットワークのつながりが形成されていれば、仲間が一斉に他社製品に乗り換えでもしない限り、次も同じ会社の製品を購入するでしょう。

iPhoneを拡張するアプセサリ

iPhoneは加速度をはじめとする多くのセンサを内蔵していて、万能です。しかしiPhone自体の大きさや頻繁に充電が必要なことがそぐわない用途も、あります。iPhoneから見てアプセサリは:

  • 機能の一部切り出しや小型化
  • iPhoneにない機能を拡張

します。

アクセサリは何かの検出をするものがほとんどです。たとえiPhoneが持っているセンサであっても、アクセサリとしてそれを切り話すことで、小型化や電池交換頻度の低減、また破損するかもしれない場面でも安心して使えるようになります。

例えば、万歩計ではどうしょううか。iPhone自体にも加速度センサがあります。ですから、iOSアプリケーションで万歩計は作れます。しかしそれでは、ユーザは計測のためにiPhoneを絶えず持ち歩いていなければなりません。また、カバンに入れられたりして、歩数の計測データが取れない状態になるかもしれません。

万歩計単体をアクセサリにすれば、安価で親指の先程度の小型なものになります。たいてい1台だけを持っているiPhoneの場合と違い、いくつも購入しておいて、それぞれのズボンのポケットに入れておく使い方ができます。万歩計単体ならば、コイン型電池で半年程度動作しますから、入れたままにしておけますし、電池残量が少なくなっても、Bluetooth LEで通信をしてiPhoneからユーザに交換をうながすこともできます。iPhoneの機能の一部を、アクセサリとして切り離すことで、アクセサリの存在を忘れてしまえる使い方ができるようになります。

また自転車などのフィットネス分野では、iPhoneを自転車に固定して利用する場合、衝撃による破損や直射日光による温度上昇で動作が停止するかもしれません。このような過酷な状況では、利用場面に合わせて設計したアクセサリを利用することで、破損でiPhone自体を壊す恐れがなくなり、またアクセサリならば破損しても買い換えも容易です。

またiPhoneに搭載されていないセンサや通信機能を、アクセサリで拡張できます。例えば、糖尿病などで日常的に血糖値を測定しなければならない方のために、血糖値測定器があります。血糖値センサをBluetooth LEでiOSアプリケーションとつなぐことで、iPhoneを血糖値測定装置に拡張できます。

アプセサリのブレインストーミング

アプセサリは、何が正解か誰も知らない、新しいものです。そのため、アプセサリの企画では、ブレインストーミングという会議方法をとることがあります。ブレインストーミングは、参加メンバーがアイディアを出しあい、そのアイディアを批判せず前向きに展開し続けることで、アイディアの連鎖反応や発想の誘発を期待する会議手法です。

しかし、Bluetooth LEに限りませんが、会議方法に工夫をこらして長い時間を費やしても、成果が出ないことがあります。多くの素晴らしいアイディア、それらの検討結果、そして膨大な議事録は残ります。しかし、その結果が事業として実行されず、何もユーザの手に渡らないならば、それは成果が出せなかったことになります。

新規事業を立ち上げる目的は、新たな利益の源泉を得ることです。そのために、これまでにない製品もしくはサービスを考えます。この、考える、がくせものです。企画会議の参加者は、営業、技術開発、製造、ときには経営など、背景知識や立場が様々です。メンバー間で、メンバーの考え方、背景知識、着目している領域を、お互いに確認を取り合える、高い議論の能力が、参加者全員に求められます。

分野をまたぐ協業には、技術でできること/できないことを判別する知識、あるいはBluetooth LEの規格を作る過程ですでに考え尽くされている、利用分野や利用場面の知識の、共有が必要です。技術でできることそれ自体は、誰が考えても、同じものに行き着きます。そのようなものは、自分で考えなくても、知識を持てばよいだけです。

ブレインストーミングは、突飛なアイディアも否定せず前向きに議論を展開していくのが、肝心です。それが会議にならないように、また、議論の焦点だけはあわせることが、主催者の手腕の見せ所です。例えば、ハードウェアの機能を話していて、その大きさに影響する議論がでてきたとします。このとき、大きさは外形デザインに影響するからと、いきなりデザインの話を切り出す人がいたとします。デザインの話に切り替わるのはよいのですが、しかしデザインの話だと節目をつけてメンバー全員の頭を切り替えないと、終わることのない楽しい雑談会の始まりになります。

プロトタイピングのすすめ

プロトタイピング(Prototyping)は、プロトタイプ(Prototype)を作り、早期に設計などを検証する手法です。とくに、参加者の分野が様々なブレインストーミングでは、お互いの認識のすり合わせにプロトタイプが使えます。

これは著者個人の体験ですが、粘土をこねて外形を作った、あるいはiPhoneで機能するプロトタイプを作り、それを手で触れていると、そこから驚くような発見や広がりそして展開が、急速に出ます。アプセサリの魅力は、アクセサリとアプリケーションの連携が生み出す体験です。形あるプロトタイプに触れる体験を通して、その背後にある形のないアプセサリの体験を、肌で感じられます。

形があると、つい目を奪われますが、、しかしアクセサリはアプセサリの要素にすぎません。アプセサリの体験という大きな絵を見るための、ジグソーパズルの1つのピースとして、役立ちます。

プロトタイピングにかける期間は、できればブレインストーミング中に作製、時間がかかるならば、前回のブレインストーミングの内容を覚えていて、過熱した頭が冷える程度の期間、例えば2〜3日程度、が望ましいでしょう。体験ができればいいので、実際に実装する必要はありません。その期間でできることを、工夫します。ネットワーク・サービスが必要ならば、決め打ちの画面や、紙に書いた画面をカメラで取り込んだものでも、十分でしょう。Bluetooth LEデバイスは、iPhoneのセンサで間に合うことがほとんどで、また市販されている試作用のBluetooth LEデバイスもあります。

ブレインストーミングでアイディアを出しつくしたならば、そのなかから、面白さや楽しいさ、事業として継続できるかなどの尺度で、ふるいにかけます。アイディアのなかには、必要とするセンサが販売されていない、非常に高価な部品を必要とする、電池や回路の工夫ではどうにもできない形状や大きさを求めている、ものがあります。プロトタイプにはいる前に、そのような明らかに実現不可能なものは、実装についての知識がある担当者が、メンバーにその理由を説明して、変更を加える、もしくは不採用にします。

プロファイルと一般的なアクセサリ

プロトタイピングに、市販されているアクセサリを流用できると便利です。

無線で接続した装置の振る舞いを、Bluetooth LEはプロファイルと呼びます。Bluetooth LEの規格は、独自の実装を許すように作られています。自社の装置と、その装置とのみ接続するアプリケーションを開発するならば、独自に仕様を決めて実装すればよいのです。

しかし、一般的に、業界には、装置の製造会社、アプリケーション開発会社など、複数の会社があります。このような会社をまたぐ場合にアプセサリを作っていくには、どこかの会社の独自プロファイルではなく、一般化した振る舞いの定義が必要です。

Bluetooth SIGは、ある業界に関わる会社群が一般化したプロファイルを提出すれば、それをプロファイルとして承認します。この仕組みにより、特定の企業に依存しない一般化されたプロファイルのもとで、装置やアプリケーションの開発ができます。また、新たな装置のプロファイルが、今後も追加されていくでしょう。

2013年7月の時点で、Bluetooth SIGが承認しているプロファイルは、つぎの14つです。

Profiles | Bluetooth Development Portal

  • アラート通知 (Alert Notification)。
  • 血圧計 (Blood Pressure)。
  • 自転車のパワーメータ (Cycling Power)。
  • 自転車の速度とペダル回転数 (Cycling Speed and Cadence)。
  • デバイスの発見 (Find Me)
  • 血糖値 (Glucose)
  • 体温計 (Health Thermometer)
  • 心拍 (Heart Rate)
  • 入力装置 (HID OVER GATT)
  • 位置と経路誘導 (Location and Navigation)
  • 電話の警告(Phone Alert Status)
  • 近接 (Proximity)
  • ランナーの速度とペース (Running Speed and Cadence)
  • 時刻 (Time)

これらのプロファイルの分野は、大きくフィットネスとヘルスケアそしてモバイル機器の3つに分けられます。Bluetooth LEの規格制定時から、応用を考えられていた分野で、プロファイルも、いち早く成立しています。機能は、センサと通知に分類できます。スマートフォンから腕時計などに、メールや電話着信を通知するアラート通知は、クラシックBluetoothから提供されてきた機能です。

アクセサリの実例

アプリケーションやネットワークそして事業までを統合したアプセサリは、まだ少なくこれから登場するでしょう。アクセサリ自体は、2012年の夏あたりから次々登場しています。いくつかを紹介します。

1つは、プロトタイピングのための開発キットやアクセサリです。これらは、早い時期から登場しています。まずBluetooth LEの半導体やモジュール会社が、組み込み機器開発者向けキットの提供を開始しました。iOSアプリケーション開発者のなかにも、Bluetooth LEデバイスの開発をしてみたい要望があります。次に登場してきたのが、その要望にあわせて、組み込み機器開発者でなくても使いやすい、より安価な開発キットです。

日本の技術基準適合証明を取得しているプロトタイピングに向いたものに:

があります。

SBLBLE(サブレー)はマイクロチップ・テクノロジー・ジャパン株式会社のPICマイコンを採用した開発キットです。市販のUSB Bluetooth4アダプタを挿して使います。Konashiは、iOSアプリケーション側の開発がObjective-Cに加えてJavaScriptでも開発できます。ウェブ・サービスの開発者が、フィジカル・コンピューティングをiOSでおこなう場合の唯一の選択肢です。センサータグは、テキサス・インスツルメンツ社の半導体の評価キットで、25ドルと低価格です。次のセンサを搭載しています:

  • 放射温度センサ、温度センサ
  • 湿度センサ
  • 圧力センサ
  • 加速度計
  • ジャイロスコープ
  • 磁力計

必要なセンサが上記にあるプロトタイピングであればセンサータグを、LEDの点灯など低速の外部装置制御をiPhoneから行うならKonashiまたはSBBLEを、任意のファームウェアを開発したいときはSBBLEを選択するとよいでしょう。

キーホルダーまたはタグは、よくあるアクセサリです:

キーホルダーは、iPhoneと接続して使うものです。iPhoneを置き忘れしそうになった時にキーホルダーから警告音を出したり、キーホルダーのボタンを押すとiPhoneから音が出る機能でiPhoneを探すのに利用します。タグは、代表的なものにStickNFindがありますが、ものに取り付けておいて、ものを探すのに使うものです。その機能は、キーホルダーのボタンを省略したもので、おおよその距離をiPhoneで確認すること、iPhoneから操作してタグから音や光を出すこと、ができます。

たいていのキーホルダーやタグは、仕様が公開されていたり、ソフトウェア開発キットが提供されていて、アプリケーション開発に利用ができます。近接検出または光や音の出力を必要とするプロトタイピングに利用できます。

この他にも:

などが市販されています。

ハードウェアとソフトウェアの開発体制

アプセサリのiOSアプリケーションは、ハードウェアと密接につながります。そのため、通信部分とドライバ相当は、両方をよく理解して協調して設計することが大切です。Objective-Cもできるハードウェア開発者が担当することが最適です。

iOSアプリケーションからみて、ハードウェアが期待した振る舞いをしないときは、問題がどこにあるのかを切り分けることが困難です。原因は、iOSアプリケーションのドライバ層の使い方の間違い、Bluetooth LEの通信、ハードウェアの設計、これら4つのどれかにあります。しかし、iOSアプリケーション開発者からは、通信以降を調べる手立てがありません。それらを調べられるのはハードウェア担当者です。

なにかおかしいことが起きた時に、原因箇所の特定には時間がかかります。このときに、ハードウェアの振る舞いを確認できるデモンストレーション・アプリケーションを作製しておけば、iOSアプリケーションとドライバ相当部分が原因かそうでないかが、すぐに切り分けできます。iOSアプリケーション以外に原因があるときは、通信プロトコルを見たり、ハードウェアの設計内容を確認していきます。それはハードウェア設計の担当領域です。

まとめ

BTLEハンズオンを開催しました

Bluetooth Low Energyのハンズオン(実物を手にした実践的な講座)を、大垣市ドリームコアで、2013年3月22日に開催しました。

このハンズオンは、
有限会社トリガーデバイスと株式会社間チルダが受託した2012年度岐阜県スマートフォンアプリ開発関連人材育成事業におけるスタッフと研修生が担当しました。
このハンズオンの会場になったドリームコアは、とてもユニークな外観の建物で、長年に渡りiOSアプリ開発者を始めとしたIT人材育成に注力し続けてきた岐阜県大垣市にあります。ちょっとしたハードウェアと無線でつながるBluetooth Low Energyは、これまで実現が不可能だったアプリおよびサービス開発を可能にする鍵になる技術です。その技術の使い方を、実践的なTipsをからめて、ハンズオンにまとめました。

参加募集はATNDで行いました。10名の枠が2時間ほどで埋まり、BTLEへの興味の高まりを感じました2013年3月22日 BluetoothLE体験講座(iOS):iOS開発者向けFacebook BTLEグループに掲載したのも、効果があったのかもしれません。

プレゼン資料は以下のとおりです:

これまでのCoreBluetoothフレームワークを取り上げたiOSアプリ開発本など、いくつか情報はありましたが、いずれもアプリケーション開発者の視点からのもので、物足りなさを感じていました。BTLEは、ハードウェア、アプリ、そしてサービスが一体になり企画されたときに、真価を発揮すると思っていたからです。

このプレゼンテーション資料は、CoreBluetoothフレームワークの使い方はもちろんですが、BTLEのプロトコルスタック(ATT,GATT)、そしてBTLEを本当に理解するための3つのキーワード、サービス/キャラクタリスティクス/プロファイル、を掘り下げて述べて、さらに、CoreBluetoothフレームワークの作りと絡めて、まとめました。

”これまで不可能だったことが可能になる”キーテクノロジーの活用情報として、参考になればと思います。

勝手に関連スライドのリンク

Octopressのカスタマイズ

Octopressを使ってブログを書いているのですが、見た目の調整をしたので、その作業メモ。やりたいことは:

  1. サイドバーにアマゾンリンクをのせたい。
  2. 見出しの文字が大きいのを調整したい。

サイドバーにアマゾンリンクをのせたい。

サイドバーは、1.どのファイルをサイドバーに入れるかを _config.yml ファイルに設定、2.読み込むべきファイルを作る、の2ステップです。
まず _config.yml を編集。アマゾンリンクは自己紹介と一緒のファイル custom/asides/about.html に入れることにしました。表示させたくないasidesをコメントアウトしました。

1
2
3
# To add custom asides, create files in /source/_includes/custom/asides/ and add them to the list like 'custom/asides/ custom_aside_name.html'
default_asides: [custom/asides/about.html, asides/recent_posts.html, asides/github.html, asides/twitter.html]
#asides/delicious.html, asides/pinboard.html, asides/googleplus.html]

次に source/_includes/custom/asides/about.html を書きます。

1
2
3
4
5
6
7
8
<section>
<h1>About Me</h1>
<p>A little something about me.</p>
<h1>自己紹介</h1>
<p>iPhone 関連のハードウェア開発の開発者。個人事業主(屋号 リインフォース・ラボ)、合同会社わふう 代表社員。 </p>
<h1>著作紹介</h1>
<iframe src="http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=feb-hare-22&o=
</section>

見出しの文字を調整する

H1とH2の見出しの文字サイズを調整しました。 sass/custom/_styles.scss に設定を追加します。 sass はCSSと互換で、変数の使用などを拡張した表記フォーマットでRubyでパースしてCSSを作り出す仕組み、らしいです。ですから、スタイルはoctopressの仕組みではなく、sassで書きます。

参考 : Octopressのスタイル設定とカスタマイズ(http://tokkonopapa.github.com/blog/2012/01/03/customizing-octopress-styles/)

まずタイトルの見出しのフォントサイズを1.8emに設定、2つ目以降の見出しのタイトル下の空白を設定しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 article {
//タイトル見出し
h1 { font-size: 1.8em;}
// タイトル直下の見出し
h2 {
padding-top: 0.8em; //タイトル直下のスペース
font-size: 1.3em; //記事最初の見出しのフォントサイズ
}
h3 {
font-size: 1.1em;
}
.entry-content & h2:first-child, header + h2 { padding-top: 0; } // その他のタイトルの下余白を0に
h2:first-child, header + h2 { background: none; } //
}

ul {
margin-left: 2em;
}

Bluetooth Low EnergyのL2CAPの解説

L2CAPとは

Logical Link Control and Adaptation Protocol、略してL2CAPは、上位のプロトコルレイヤに、プロトコル/チャネルのマルチプレクサを提供する、接続および非接続のデータサービスです。2つのデバイス間のデータ通信を提供するロジックレイヤを通じて、プロトコルごと、指定したチャネルごとに、それぞれ独立した通信を提供します。

L2CAPは64kバイトまでのペイロードを送信できます。ロジックレイヤのMTUよりも大きいですが、パケットの分割/再構成をして、これを送信します。

Bluetooth Low EnergyのL2CAP

Bluetooth Low Energyは、クラシックBluetoothのあとに統合された規格です。その統合時に、BLEでもL2CAPをつかうか、または別の仕組みを使うべきかの議論があったそうです。L2CAPを使うソフトウェア群をすでに持つ会社と、それを持たない会社に分かれて、技術内容を論文にまとめて論議した結果、L2CAPを使う事になりました。

リンクレイヤのデータチャネルのPDUに入る、L2CAPのフォーマットを示します。
それぞれ2オクテットの、レンクスとチャネルID、そしペイロードが続きます。BLEのL2CAPはパケットの分割/再構成を提供しません。そのため、ペイロードは最大23オクテットです。

BLEは、すぐ接続できる、少量のデータを読み書きする、すぐ切断できる、ことを特徴としています。
このために、BLEでは、L2CAPのチャネルは次の3チャネルに固定されています:

  • 0x0004 Attribute Protocol
  • 0x0005 LE signaling channel
  • 0x0006 Security Manager

Attribute Protocolは、BLEデバイスの値の読み書きを提供します。
Security Managerは通信の暗号化を提供します。

LE signaling channelは次の3つがあります:

  • Command Reject
  • Connection Parameter Update Request
  • Connection Parameter Update Response

iOSデバイスのBLEアクセサリの設計指針は、
スレーブからマスターへの通信パラメータの伝送は、L2CAPの接続パラメータアップデートで伝えるべきとしています。
レイテンシと電池消費量に合わせて、適切なパラメータを選びます。

このL2CAPのパラメータ更新は、例えばスレーブがマスターに初めて接続したときに、まとまった量のデータを一気に送信したい時に、インターバルを短くして、その後インターバルを長くすることで、短い時間でデータを送信しつつ、電池の消費量を抑えることができます。

このコマンドは、スレーブからマスターにパラメータ更新するときに有効です。マスターはリンクレイヤのパラメータ更新で、いつでもパラメータ更新ができます。

Bluetooth Low Energy リンクレイヤの解説

ここまでで、クラシックBTと比較したBLEの違いと特徴、そしてBLEの物理層を述べました。
ここでは、規格書をなぞる解説ではなく、iOSのアプリ開発とiOSと連携するBLEデバイス開発に役立てるための知識を提供する視点で、BLEのリンクレイヤを解説します。

リンクレイヤは、あるデバイスとあるデバイス間で、どうやって無線通信をするかを決めます。
リンクレイヤが定義するのは、パケット、アドバタイジングそしてデータチャネルです。
これらを使って、リンクレイヤは:

  • 他のデバイスの発見、
  • データのブロードキャスト、
  • 接続の確立と維持、
  • 接続を通したデータ通信、

を提供します。

BLEのトポロジー

BLEのネットワークは、1つのマスターに複数のスレーブが接続する、スター型のネットワークです。
1つのリンクレイヤが、同時にマスターかつスレーブになることはできません。

BLEのネットワークは、クラシックBluetoothのピコネットとは、違います。
クラシックBluetoothのピコネットは、
同期したクロック同じ周波数ホッピングの順番で、
同じ物理チャネルで通信するデバイス群です。1つのピコネットの複数あるスレーブは、1つだけあるマスターにクロックを同期して通信をします。
あるデバイスは複数のピコネットに同時に参加出来ます。
(Blluetooth specification Version 4.0 [Vol 1] 4.1.2 LE Topology)

LEのトポロジーを示します。デバイスAは、
デバイスBおよびCと接続しています。このときデバイスAは、master role(以下、マスター)、デバイスBとCがslave role(以下、スレーブ)を果たしています。
デバイスBとCは、全く何も同期しておらず、ただデバイスAとそれぞれが接続して通信をします。

BLEには、接続していなくても、アドバタイジングを使って情報をやり取りする仕組みがあります。
アドバタイジング・チャネルにアドバタイジング・パケットを創出するものをadvertiser、
アドバタイジング・チャネルをモニタして、アドバタイジング・パケットを受信するものをscannerといいます。
上図のデバイスDおよびEのように、すでに接続しているデバイスAおよびCは、マスター/スレーブどちらになっているかにかかわらず、
advertiserにもscannerにもなれます。またデバイスH, I, Jのように、アドバタイズメント・パケットだけで構築するネットワークもあります。

接続時、マスターが、スレーブとの接続と通信タイミングを制御します。
より多くのメモリと電池を必要とする機能をマスターに寄せて、スレーブに求めるメモリと電池の要求量を小さくする、非対称に役割をふることで、
スレーブの消費電力と製造コストがより小さくなります。

BLEのトポロジーでは、リンクレイヤが同時にマスターかつスレーブにはなれません。
クラシックBluetoothでは、同時にマスターかつスレーブとなりピコネット間をつなぐものをscatterと呼びます。BLEにscatterをサポートしません。

しかし、マスターからスレーブに役割を切り替えることができます。
Bluetooth Low Energyの半導体で、内蔵マイコンがリンクレイヤを制御しているものは、ファームウェアでスレーブかつマスターどちれにもなれるものがあります。

リンクレイヤのステート・マシーン

BLEのトポロジーを、振る舞いだけでみていると複雑なので、その振る舞いのもとになっているリンクレイヤのステート・マシーンという基本概念を説明します。
ステート・マシーンは、状態とその遷移でふるまいを表すモデルです。

リンクレイヤは5つの状態があります。

  • Standby
    • 初期状態です。電波の送受信をしません。
  • Advertising
    • アドバタイズメント・チャネルでの、アドバタイズメント・パケットの送受信をします。
  • Scanning
    • スキャニングは、アドバタイジング・チャネルの電波を受信します。
    • スキャンには、パッシブスキャンとアクティブスキャンの2つがあります。
    • パッシブスキャンは、アドバタイジング・パケットを受信するだけです。
    • アクティブスキャンは、デバイスにSCAN_REQを送信して、アドバタイジング・パケットに収まりきらなかった情報をさらに取得します。
  • Initiating
    • 特定のデバイスからのアドバタイズメント・パケットの受信と、そのパケットへの応答をします。
  • Connection
    • 接続した状態です。

コネクション状態では、

  • Master role
  • Slave role

の2つの役割があります。Initiating stateからConnecting stateに遷移したものがMaster role、
Advertising stateからConnection stateに遷移したものがSlave roleです。

接続するまでのステート間の遷移は、
マスターは standby -> Initiating -> Connection、
スレーブは Standby -> Advertising -> Connection、
となります。

ここまでの説明だけなら、1つのリンクレイヤをステートマシーンで制御するだけだと理解しやすいのですが、BLEは複数のステートマシーンが持てます。
それぞれのステートマシーンがそれぞれ動作することで、例えば、あるステートマシーンがConnectionしているときに、別のステートマシンでAdvertisingをすることができます。

ですが、いくつかのステートマシーンの状態の組み合わせは禁止されています。
これは、同時にマスターかつスレーブにならないための、ルールです。接続状態にならなければよいので、すでにスレーブとして接続状態にあっても、接続状態にならないアドバタイズメント・パケットは送信できます。

その組み合わせずとその説明は以下のものです:

  • Connectionステートにあるとき、同時にMaster roleとSlave roleにすべきではない。
  • Slave roleでConnectionステートにあるリンクレイヤは、1つのコネクションだけをもつべきである。
  • Master roleでConnectionステートにあるリンクレイヤは、複数のコネクションを持ってもよい。
  • すでにリンクレイヤがSlave roleでConnectionステートにあるなら、リンクレイヤはInitiatingステートで動作すべきではない。
  • リンクレイヤがConnectionステートもしくはInitiatingステートで動作しているなら、リンクレイヤはSlave roleで接続状態に入ることになるアドバタイズメントをするAdvertisingステートで動作すべきではない。

パケット

リンクレイヤのパケットフォーマットを示します:

最も短いパケットで80ビット(送信時間 80マイクロ秒)、最も長いもので376ビット(送信時間 367マイクロ秒)です。

ビットオーダー

BLEのリンクレイヤのパケットおよびパケットのフィールドProtocol Data Unit(PDU)のフィールドは、リトルエンディアンです。

  • 表記b0は、最下位ビット(Least Significant Bit,LSB)を表します。
  • 最下位ビットから送信されます。

0x3(011b)を送信するときは、110bと送信されます(1が最初、0が最後)。

また8ビットを1オクテットとします。CRCとMICを除き、それぞれのフィールドは最下位オクテットから送信されます。
0x1234(2進数0001_0010_0011_0100)を送信するときは、0010_1100_0100_1000b、と送信されます。

プリアンブル

プリアンブルは、0x55(01010101b)もしくは0xAA(10101010b)の0/1が繰り返すパターンで、信号のゲイン調整やデータを復調するための同期に使われます。
0x55と0xAAのどちらを使うかは、続くデバイス・アドレスと並べた時に’1’や’0’が連続しないほうを、選びます。

デバイス・アドレス

32ビットのパケットアドレス:

  • アドバタイジング・アクセス・アドレス
  • データ・アクセス・アドレス

アクセスアドレスは、個々のデバイスが通信時に使うランダムなアドレス値です。アドバタイズメント・パケットでは、固定値0x8E89BED6が使われます。

10001110100010011011111011010110b (0x8E89BED6)

アドバタイズメント。先頭アドレス固定。4んービットの一致検出。
3チャネル。20ミリ秒から1.んー秒。時間は長く。重なると悲惨、0~10ミリ秒ランダムにずらす。

アドバタイジング・チャネル PDUヘッダー

PDUタイプは、アドバタイズメント・チャネルPDUのタイプを指定します。
TxAddとRxAddは、PDUタイプごとに定義されるフィールドです。PDUタイプがこれを定義していないときは、将来使うための予約済フィールドとします。
Lengthはペイロードの長さをオクテットで表し、6から37までの値を取ります。

接続に係るPDUタプは次の4つです:

  • ADV_IND
    • 接続可能、ダイレクトではない
  • ADV_NONCONN_IND
    • 接続しない、ダイレクトではない
  • ADV_SCAN_IND
    • スキャン可能、直接ではない
  • ADV_DIRECT_IND
    • 接続可能、直接

このうち、ADV_DIRECT_IND は、AppleのBluetoothアクセサリ設計指針で使うべきではないと、書かれています。

アドバタイジング・データがパケットのペイロードに収まらないとき(31バイトより大きい時)、マスターはスレーブにスキャンを要求して、さらに情報を取得出来ます。そのためのPDUタイプがSCAN_REQ(スキャン・リクエスト)とSCAN_REP(スキャン・リクエストへのリプライ)です。また接続要求がCONNECT_REQです。

  • SCAN_REQ
  • SCAN_RSP
  • CONNECT_REQ

アドバタイジング・データ

ADV_IND、ADV_SCAN_IND、ADV_NONCONN_INDのペイロードの構成は:

AdvAはアドバタイザーのアドレスを、AdvDataはアドバタイジング・データを表します。
TxAddの値は、アドレス値AdvAが、パブリック(TxAdd = 0)か、ランダム(TxAdd = 1)かを示します。

([Vol 6] 2.3.1)

アドバタイジング・データとスキャン・データのフォーマット

アドバタイジングとスキャン・データは、31オクテットを、いくつかのLength、Dataフィールドと、31バイトになるように0で埋めたものです。
このDataフィールドはAD TypeとAD dataで構成されます。AD Typeの実際の値は、https://www.bluetooth.org/Technical/AssignedNumbers/generic_access_profile.htm にあります。

31バイトのアドバタイジング・データでではアドバタイジングしたい情報が収まらない場合があります。その時に使うのがスキャンです。
アドバタイザは、受信したSCAN_REQに対して付加情報をSCAN_REPで返信します。このスキャンデータは、スタティック、であるべきです。
アドバタイジング・パケットは常に受信するものなので、そのデータが変化しても、マスターはそれをいちいち解釈するでしょう。しかしスキャンデータは、マスターが常に読み取ろうとするとは限りません。変化する情報を入れていた場合に、振る舞いが予測できなくなります。

iPhoneアクセサリでは、Bluetoothアクセサリに送信されたアドバタイジング・データは、 Bluetooth 4.0 仕様, Volume 3, Part C, Section 11 に記述されているように、 次の情報の少なくとも1つを含むべきです:

  • Flags
  • TX Power Level
  • Local Name
  • Services

アドバタイジング・データには次のタイプがあります:

  • Local Name
    • デバイス名です。完全もしくは短縮名です(section 3.2.2)。完全/短縮は、AD Typeで判別出来ます。
    • もしも短縮名ならば、device name characteristicを読み出せば、完全なデバイス名が取れます。
  • Flags
    • フラグビットをbooleanで含みます。LE physical channelで使われるフラグは:
    • Limited Discoverable mode
    • General Discoverable mode
    • VR/EDR Not Supported
    • Simultaneous LE and BR/EDR to Same Device Capable (Controller)
    • Simultaneous LE and BR/EDR to Same Device Capable (Host)
  • TX Power Level
    • 送信電力です。
  • Service UUIDs
    • サービスのUUID(16-bit, 128-bit)です。
  • Manufacturer Specific Data
  • Security Manager Out of Band
  • Security Manager TK Value
  • Slave Connection Interval Range
  • Service Solicitation
  • Service Data

LLコネクションパラメータ

ウィンドウサイズ、スレーブレイテンシ、スキャンインターバル、タイムアウト、をマスターからスレーブに設定する。
スキャンインターバルが決められているが、スレーブは、意図的に、データを送信しないでいい。タイムアウトするまでに、返答すれば接続は保たれる。

これは低レイテンシと低消費電力を両立するための工夫。例えばキーボードを考えれば、通信の遅延時間を20ミリ秒にしたいとする。
しかしキーボードが押されるイベントは、20ミリ秒よりもはるかに低い頻度でしか起こらない。
もしもスレーブが、20ミリ秒ごとに”データはない”という送信をすれば、その電波を出すために電力を消費する。

そこでインターバルがきても、データがないならば電波を出さないことで、低消費電力が得られる。
しかし20ミリ秒ごとにインターバルがあるので、送信すべきデータが来たならば直ちに次のインターバルでデータを送信できる。

データPDU

データPDUのフォーマット。暗号化するときはMICがはいる。
暗号化だけではなくて、CRCよりも強力なエラー検出が必要なときにも使える。

データの長さは、0~27。MICがあってもなくても、データ長はMICがある場合の最大値の27にしてある。
iOSでwrite~でデータを書き込むときに、最大27バイトで頭打ちになるのは、おそらくLL2CAPを通さずこのレイアを直接叩いているからかも。

BLEデバイスのバッテリー

BLEはもともと超低消費電力かつ低コストを目標に作られた規格です。BLEの、電池への配慮を見てみます。

BLEデバイスの開発話をしていると、ときには極端に小さくしたいなど、
デバイスの大きさや形状の要望も出てくるでしょう。この時に、
回路面積は半導体素子を並べて実装可能な配置での基板面積から見積もれるでしょう。そして電池の選定です。それには、回路と電池の両方の知識が必要です。

電池の選定で肝心なのは、
連続稼働時間に必要な電池容量と、ピーク電流がとれるか、の2つです。

電池の大きさは、容量と面積の2つがあります。容量が小さくなれば、当然、
電源容量が小さくなりBLEデバイスの連続稼働時間が小さくなります。そして面積が小さくなりすぎると、BLEデバイスが無線通信をするために必要な、ピーク電流、が取れなくなり回路の正常動作に影響を与えます。

連続稼働時間の目安は、1mAhで1日から2日、と覚えておきます。

BLEによく使われるのが、CR2032という電池です。
この電池の特性を見てみます。

カタログが、
http://industrial.panasonic.com/www-cgi/jvcr13pz.cgi?J+BA+4+AAA4003+CR2032+8+JP
にあります。

型番の先頭2文字は、電池の種類と形状を表します。Cは、二酸化マンガンリチウム電池(3.0 V)、形状は円形です。電池の電圧は電極材料で一意に決まります。現在の半導体製造プロセスで作られる回路の動作電圧は、1.8 ~3.3Vです。この二酸化マンガンリチウム電池の3.0Vという電圧は、その範囲にちょうどあてはまり、使い勝手がよいのです。

CR2032の容量は220mAhです。負極にリチウムを使い、容積の割に電源容量が大きいので、BLEデバイスの連続動作時間をより長くするのに、適しています。この電源容量は、大きな電流を短時間流すことを繰り返したり、動作温度が高い/低いなど、使い方と利用環境で大きく変化するので、注意します。

CR2032は、電卓やPCのバックアップ電源に使われていて、スーパーやコンビニで買える、入手性が高い電池です。入手性の高さは、BLEデバイスの使い勝手のよさにつながります。

BLEの“超低消費電力”は、電波の送受信を短時間で終わらせて、後の時間はスリープ状態で電力を使わない、間欠動作で実現しています。平均すれば“超低消費電力”、なのです。

CR2032は、標準負荷が0.2mAですが、ピーク電流は20mAほど取れます。
電波の送受信では、半導体メーカによりますがだいたい~15mAの電流が短い時間(数ミリ秒程度)流れます。標準負荷は小さいですが、ピーク電流がとれるので、BLE回路に適します。

ちなみに、CR2032は、連続して電流を流そうとしても、内部抵抗が高くなり、最大電流が電池側で制限されます。フィジカル・コンピューティングでLEDの足でボタン電池をはさんで光らせるバッジをみかけますが、電流制限抵抗がなくてもLEDが連続点灯するのは、この電池の電流制限の特性のおかげです。

電池で電気回路を年単位で連続動作させるには、電気回路自体が“超低消費電力”であることと、電池の自己放電特性が優れている、2つが必要です。自己放電は、電池メーカごとの性能、そして温度に大きく影響されます。二酸化マンガンリチウム電池の自己放電率は、目安として、1%/年以下です。

BLEの物理層

Bluetooth Low Energy(以下、BLE)の物理層の話をします。物理層とは、無線通信であればどんな電波を出して、相互に情報をやり取りするのかを決める層です。

BLEのハードウェアを使うiOSアプリケーションを開発するから、物理的なことは知らなくてもいいのではと思われるかもしれません。
しかしBLEは無線通信を使うために、例えば:

  • WiFiが周囲にあっても通信ができるのか
  • iOSデバイスと同時に何台までBLEデバイスは接続できるのか
  • 展示会場でデモンストレーションをするときに周囲に多数のBLEデバイスがある場合でも、接続するのか

という、この状況でも動くのかという、振る舞いの質問を利用者からされます。その質問に答えるには、物理層の基本知識は必要です。

自分が開発する部分がアプリケーションだけであっても、
ハードウェアと連携するアプリケーションは、
利用者からみれば、
そのハードウェアとアプリケーション、2つをあわせて1つの製品になります。

確実に動かすための知識として、物理層を理解してください。

まとめ

電波の混信/干渉

BLEは2.4GHzのISM帯を使います。
この無線帯域はWiFiや電子レンジなど多くの機器が利用しています。
BLEは、周波数帯域を40個のチャンネルに分割して、干渉や混信するチャンネルを避ける適応周波数ホッピングという仕組みを使います。

同時何台まで接続できるか

規格では、制約はありません。実装依存です。
60台くらいは大丈夫っぽい?

接続距離は

規格は送信電力の最小と最大を規定しています。
それぞれの場合の通信距離は、見通しで、最大のとき50m程度、最小のとき2.5m程度、です。

仕様はどこにある

BLEの規格は、Bluetooth SIGから公開されています。
https://www.bluetooth.org/Technical/Specifications/adopted.htm
のCore Version 4.0 Vol.6 がBLEの物理層からの規格を述べています。

2.4GHzISM帯を使います

BLEは2400MHzから2483.5MHzまでを使います。クラシックBTと同じ周波数範囲を使います。
BLEは、この周波数帯を2MHz幅 40個のチャンネルに分割して利用します。チャンネルごとの中間周波数fcは:

fc = 2402 + 2*k MHz, K=0,1 … 39

となります。例えばfcが2402MHzのチャンネルは、2401MHzから2403MHzまでの周波数を使います。

チャンネルは、周波数帯域を有効に使うための工夫です。例えば、
たくさんのBLEデバイスがあっても、それぞれが違うチャンネルを通信に使えば混信しません。

BLEは、40のチャンネルのうち、3つをアドバタイズメント・チャンネルに、のこる37つをデータ・チャンネルに割り当てます。

アドバタイズメント・チャンネルは、BLEデバイスの発見と接続に使うチャンネルです。
BLEデバイスは、デバイスを発見するためのアドバタイズメント・パケットを、3つのアドバタイズメント・チャネルそれぞれで送信しています。
iOSデバイス(セントラル)は、デバイスを発見するとき(スキャンするとき)、この3つのアドバタイズメントを受信して、デバイスを発見します。
BLEデバイスを発見すれば、データ通信に必要な接続処理をおこない、データ通信は、37つのデータ・チャネルを次々に切り替えながら、通信を続けます。

データ・チャンネルは、文字通り、データ通信につかうチャンネルです。
BLEは、適応周波数ホッピング方式をとっています。これは一定時間ごとにデータ通信につかうチャンネルを、ランダムな計算式で、
切り替えていく方式です。2.4GHz帯は、BLEだけではなくWiFiや電子レンジなど、様々な電波が飛び交っています。上図のように、もしもあるデータ・チャンネルが運悪くWiFiとぶつかり通信ができなくても、次の通信の機会でそのWiFiと重なっていないデータ・チャンネルに切り替われば、データ通信が継続出来ます。

BLEの適応周波数ホッピングの“適応”は、どのデータ・チャネルを使うかを、指定できる機能を指しています。例えば、WiFi チャネル1が使われているとわかっているならば、そこと重なるチャンネルは、そもそも使わないようにできれば、通信ができない状態を避けられます。

デバイスが発見できることは、とても重要です。
アドバタイズメント・チャネルは、上図の、37, 38, および39チャネルです。
2.4GMHzのWiFiの1, 6, 11チャネルと重ならならないチャンネルから、周波数帯域の下端/上端/真ん中の、なるべく周波数が離れたチャンネルを、
アドバタイズメント・チャンネルに割り当てています。

到達距離は見通し50mくらいです

BLEの送信電力は、最小 0.01mW (-20dBm) ~ 最大 10mW (+10dBm)です。一般のBLEの半導体の受信感度(0.1% ビットエラー)は-90dBmです。

伝送ロスは:

pass loss = 40 + 25 log (d)

ここで d は伝送距離(m)です。

これから、通信距離は見通しで最大50m程度です。

アドバンス: 変調方式は低消費電力で低コストの工夫があります

BLEの変調方式は、低消費電力かつ低コストで製造できるよう工夫されています。

クラシックBTは:

  • GFSK
    • BT=0.5
  • 変調指数 0.28 ~ 0.35
  • シンボルタイミング+- 20ppm

BLEは:

  • GFSK
    • BT=0.5
  • 変調指数 0.45 ~ 0.55
  • シンボルタイミング+- 50ppm

です。

変調指数が0.5の周波数変調を、ミニマムシフトキーイング(MSK: Minimum Shift Keying)といいます。BLEは、シンボル・レート1Mbps、1シンボル1ビットです。
ですから、ベースの変調周波数は、500kHzです。これから周波数遷移幅は250kHzになります。

この条件の時、1シンボルの期間で、搬送波に比べて変調波の位相が+-π/2ずれます。周波数変調ですが、連続に位相が遷移するので、I/Q復調器が使えます。

またシンボルタイミングは、クラシックBTの+-20ppmに比べ+-50ppmと緩くなっています。これは1パケットの最大ビット長を短くするなどで、得られています。
これらは、クラシックBTほど安定度が高い回路また精度がよい水晶でなくとも実現できる、低コスト化につながります。