Bluetoothスタック/ペリフェラル向け/設計内部情報

マイクロコントローラと、Bluetooth-USBドングルで動作する、Bluetooth4.0 LE(Low Energy)対応のソフトウエア・スタック・ライブラリの開発時の設計情報です。Bluetoothスタックの開発者や、より内側を知りたい人向け参考情報です。

Bluetoothについて

Bluetoothの全仕様は、インターネット上に公開(*1)され、誰でも見ることができます。その気になれば、独自に実装する事も可能です。ただし、かなりのページ数の英文文書で、読み解くには多くの時間を要します。後々のために、要約としてまとめておきます。

この仕様書で分かりにくくしているのが”LE”の存在です。Bluetooth仕様書は時間と共に、どんどん追加されてバージョンアップをしています。メジャーバージョンアップとなった4.0での目玉機能が「LE」です。これは全く別の思想のものを、無理やり合体した感のある仕様と感じました。LE自体は単純で、結局Key=Valueデータの交換ができるだけです。このKeyの使い方とValueのフォーマットを規定する事で、多くのアプリケーションに対応するのです。

従来の部分をClassicと呼ぶ事が多いようです。実は、こっちの仕様の方が複雑で、実装するのに時間がかかります。

*1) Bluetooth SIG – https://www.bluetooth.com/specifications/specs/core-specification/
“Bluetooth Core Specification”が一番基本の仕様書です。3000ページぐらいありますが、主に”Specification Volume 3″の部分がスタックに関係する箇所です。

あらすじ

私の認識している、Bluetoothの一番簡単な通信順をポンチ絵にしてみました。

基本的な流れ
①スキャン有効化
  従来のBluetoothでは「スキャン」、LEでは「アドバタイズ」と呼ばれる、一定時間間隔で名称等の自分の情報を発信する機能をオンにするため、HCIにコマンドを発行し、相手からの接続を待ちます。
  スキャンとアドバタイズは別々の設定ですので、名前や有効/無効も独立して設定可能です。
②コネクション受付
  相手がコネクション要求を出すと、HCIイベントが発生しますので、了承するHCIコマンドを発行する事で、コネクション(リンクとも呼ぶ)が確立します。
  コネクションには、HCIがハンドルと言う識別番号を付番します。
  コネクションレベルの事象の変化は、全てHCIイベントで通知されます。
  以降は、ACLパスを使った通信に移行します。
③チャネル開設処理
  1組のACLパスを使って、メッセージの多重化を行う為に、CIDと呼ばれる識別番号が電文ヘッダに付いています。
  このCIDで区別される論理通信パスを「チャネル」と定義します。この値は、予約されているものと、動的に割り当てるものがあります。
  予約されているCIDの場合、対応するアプリケーションに渡すだけで、特に開設処理というものはありません。
  ATTはこのパターンです。
  動的に割当てるには、「シグナリング」と呼ぶ制御電文のやりとりを行います。これでチャネルを新規開設します。
  シグナリング自体はCID=1の予約番号を使っての通信です。通信相手のアプリケーションを区別するために、PSMという番号を使います。PSMは予め決まっており、これに従って対応するアプリケーションを起動します。
  1つのアプリケーションで、複数のチャネルが出来ますので、多重処理させやすくなります。
  SDP,RFCOMMは、このパターンです。
④アプリケーション処理
  ACLパスで受けた電文は、CIDで振り分け、制御をアプリケーションに渡します。
  また、返信電文があれば、CIDを付加してACLパスに送出します。

コネクション確立(セキュアシンプルペアリング=SSP前提)

 バージョン2.1以上はSSPは必須の機能となり、必ず実装が必要です。とは言え、パス番号を表示したり入力したりする必要もなく、ある一定の通信手順をこなすようにしておけばOKです。
 下図はHCIのコマンド&イベントのタイムチャートです。Bluetooth仕様書に書いてある事を、より簡単に書いてみました。(Volume 2, Part F Message sequence charts, 4.2 Simple pairing message sequence charts) 左側が言い出しっぺ、右側が受け側です。(L2CAPコネクションが逆で、間違っています)

普通はOOB(Out of Band、Bluetooth以外の情報伝達方法を使用する)を使う程ではないと思うので、②から見て下さい。右側の立場で解説します。
まず「Write secure simple pairing enable」を出しておきます。
普通と違うのが「IO capability request」イベントがやってきますので、自分のCapabilityを返してやります。
Capabilityとは、相手確認の為に、6桁のパス番号を表示できるハードウエア機能を持っているのか、番号を入力出来るキーボードを持っているとかいった属性です。
IO capabilityの組み合わせによって、2種類の応答に分かれます。
 「User passkey request」イベントが来る場合:
   通信相手側に表示されている自動生成されたパスキー(6桁の番号)を、自機に入力してもらいます。
   入力中である事を「User passkey notification」コマンドで、相手に通知します。
   最終的に番号を「User passkey request reply」コマンドで通知して完了です。
   入力キャンセルされたら「User passkey request negative reply」コマンドを送ります。
 「User confirmation request」イベントがくる場合:
   自動生成された確認番号(6桁の番号)付きで来ます。通信相手も同じ番号を得ています。
   これを使って、実際に通信出来ている相手(機器)が間違いないかを人間系を巻き込んだ方法で行います。
   良ければ「User confirmation request reply」コマンドを、
   悪ければ「User confirmation request negative reply」コマンドを発行します。
続けてやってくる「Sinple pareing complete」イベントで、手順は一段落します。
その後、秘話性が要求されるコネクションが作られる前に、リンクキーの応答によるAuthenticationと、Encription暗号化有効のイベントが出て来ます。リンクキーの応答は、とにかく「Link key notification」イベントで通知されたリンクキーは覚えておき、「Link key request」イベントでリンクキーを求められたら返してあげれば良いだけです。暗号化は特に意識しなくとも、相手が要求したら与り知らぬ内に秘話通信となります。SSPでは、これらはセットです。
確認番号の対応については、表示器やキーボードを持たないデバイスでは、困った問題となります。機械的に自動応答するのが現実的だと思われます。無条件にOKを返せば、相手を選ばずにペアリングが完了します。これでは心もとないというのであれば、ペアリング許可スイッチを用意し、オンの場合のみOKを返すようにしておけば、ペアリング相手をコントロールする事ができるという感じです。

※参考(落とし穴)
デフォルトでは、上記のイベントは発生しませんのでご注意下さい。「Set event mask」コマンドで、全イベント通知するように変更して下さい。SSPはバージョン2.1と古い規格の機能ですが、イベントマスクのデフォルトの設定はバージョン1.x時代のイベントのみだからです。これはバージョン4(BLE)をサポートする際にも注意を要します。

チャネル開設

コネクションが成功し、デバイス間のリンクが張られたら、チャネルと呼ぶ仮想的な通信路を作ります。

 ACL上のメッセージを重畳・多重化し「チャネル」と呼ぶ仮想的な通信パスを実現します。簡単に言えば、全てのメッセージのヘッダにグループ番号を付けて、仕分けできるようにするものです。このグループがチャネルという単位で、グループ番号はCID (Channel Identifier) と呼ばれます。
チャネルには予約された番号を使う固定チャネルと、シグナリングで動的に作られるチャネルがある。

・シグナリング
 チャネルを開いたり、閉じたりする指示は、シグナリングチャネル(CID=0x001固定チャネル)にシグナリングプロトコルに従った電文をやりとりする事で行われます。電文自体は簡単でコネクション、コンフィギュレーション、ディスコネクションの3種類でそれぞれにリクエストとレスポンスがあります。

まず、コネクションリクエストで相手のサービス/アプリケーションを示す番号(PSM: Protocol/Service Multiplexer)が来ますので、新しいチャネル番号(CID=0x0040から動的に採番する)を作り、PSMに対応したプログラムコードとひも付けします。
次にコンフィグレーションで、最大電文長やサポートする機能などを交換し、問題なければチャネルはオープン状態となります。
新しいチャネル番号(CID)のメッセージが届いたら対応するのプログラムコードに引渡し、返信の電文には相手のCIDを付けて送り返すという、メッセージ振り分けを行う仕組みにより、仮想な通信パスつまりはチャネルを実現します。
通信終了はディスコネクション電文を、どちらの方向からでもリクエストする事ができます。あくまでもシグナリングチャネルで通信しなければなりません。アプリケーションのチャネルはアプリケーション間で決めた電文しか流れません。

正確なシグナリングの状態遷移図です。仕様書のVolume3, Part A, 6.State machineに同じ絵がありますが、これは自分での理解を深めるためと、ソースコードを起こす時の確認のために作ったものです。

SDP機能

SDP(プロトコル)は、相手が有しているサービス/機能を取得したり、逆に自分の持つ機能を知らせる仕組みです。保有するサービスのデータベースへのアクセス機能で、必ず実装しなければなりません。

Bluetoothのチャネルが確立したら、SDPデータベースの情報要求電文に従い応答電文を返す、これを単純に繰り返すのが主たる処理です。
要求電文は、3種類有ります。
  ①UUIDを検索し、合致するレコードのハンドル値を複数返す
  ②指定されたハンドル値のレコードを返す(レコード内は指定された属性ID値、範囲のみ抽出される)
  ③UUIDを検索し、合致する全レコードを返す(レコード内は指定された属性ID値、範囲のみ抽出される)
 UUIDは、サービスを示すUUIDのみを選んで探すという訳ではなく、全てのUUIDデータが検索対象になります。

プロファイルとサービス、プロトコル

ここまでがコア仕様で記述されている基本機能ですが、以降は、BTのオプション機能となります。仕様書でプロファイルとサービスやプロトコルを区別して文書化していますが、これが理解しづらいのです。強引な言い方ですが、

  • プロファイルは使い方の規定
  • サービスは具体的な機能
  • プロトコルは通信上の約束事

と私は理解しました。プロファイル仕様で、サービスの使い方や、プロトコルとの関係などを説明していますので、まずはプロファイルから読み進めると、解りやすいと思われます。

SPP/RFCOMM

単純なシリアル通信のサービス、またはアプリケーション間のバイトストリーム通信です。SPP(Serial port profile)は、既存のシリアルポートエミュレーション仕様「RFCOMM+TS 07.10」を、BTに適用する時の条件などを規定しています。

Bluetoothのチャネルが確立した後に、「GSM 07.10」の電文仕様でシリアル通信条件を交換する事で、ユーザデータの送受信が可能になります。1つのコネクション内に、複数の仮想シリアルポートを持たせられます。

A2DP/AVDTP

高品質の音声通信サービスです。A2DP(Advanced Audio Distribution Profile)と、AVDTP(A/V Distribution Transport Protocol)が関係する仕様書です。

Bluetoothのチャネルが確立した後に、AVDTPの電文仕様に従い、接続条件などの情報を交換します。以下は、その状態遷移図です

情報交換は、使用できるCodecの種類/サンプリング周波数などの具体的なパラメタを提示し、相手が了承したCodec/パラメタを受け入れるといった内容です。これがすめば、CONFIGUREDを抜けてOPEN状態となり、いつでもストリーミングできる状態になります。STARTされるとメディアパケットがストリーミングされてきます。なお、メディアパケットは状態遷移電文が流れるチャネル(制御チャネル)とは別に、専用のチャネルが新設されます。制御チャネルと、メディアパケットチャネルは明確な識別子などはありません。

AVDTPは、メディアパケットの転送については制御を受け持ちますが、音声データ仕様まで規定している訳ではなく、内容は何でもよく、上位APIのガイドラインを提示しているのに止まります。

上位とはA2DPの事で、メディアパケットの内容は、必須CodecのSBCのみが仕様書で公開されています。SBCのエンコードとデコード方法は、大変難しく理解に時間がかかりますが、ソースコード的な説明があり、実装することができました。その他のcodecはライセンス問題があるのでパスしました。

GATT/ATT

ATT(Attribute protocol)は、LEの主機能である、名前と値のデータベースにアクセスする機能です。データベースに格納するデータの規定はGATT(Generic attribute profile)で仕様書化されています。

ATTはサーバとクライアントがあります。コネクションレスで受信されたATT電文に従い応答電文を返す、これを単純に繰り返すのが主たる処理です。ATTサーバ自体は本当に単純で、いくつかの検索パターンがりますが、基本的には条件に合致するレコードのキーもしくは値を返信するだけです。最初のネゴシエーションとか全くありません。セキュリティについても、認証が必要だとレコードに記録されていたら、その時点で自デバイスが認証状態でなければ、拒否電文を返すだけです。相手は、きっとSMに認証要求したりして、認証状態を作った上で再度問い合わせてくるはずです。

データベース内のデータはGATTで細かく指定されています。SDPと同じ「保有サービス情報」を入れる約束になっていますので、SDPなしでもサービスを検索する事ができます。好き勝手にKey値を決めて構いませんが、体重計とか血圧計とか、多くのアプリケーション向けにKey値が用意されています。

SM(Security Manager)

 LEでのSSP(セキュアシンプルペアリング)に相当する機能です。認証や暗号化を要求された時、もしくは相手からのSMP問合せを受けた時に機能し、主に暗号化キーのやりとりをします。実際、暗号化するのはBluetoothコントローラが担います。本スタックでは、ペリフェラルの位置づけなので、自分から直接認証を要求し始める事はありません。ですから、SMPで認証関係の応答を行い、その結果、問題なければ認証/暗号化していると言う状態を更新するだけです。ただ、認証に至るまでの経過は暗号キーの計算や、相手のパスキー表示/入力の能力の有無で操作方法を変えるなど、やっかいな処理を要します。とは言え、暗号化キーと、そのキーの種類(暗号化のみ/認証=相手を特定)を保持・管理するだけです。

実装

外部仕様

 このスタックは、小型電子機器での使用を想定しているため、ペリフェラル関連だけを実装しています。おかげでコンパクトなコードサイズになっています。実装している機能は、以下の通りです。

仕様準拠Bluetooth バージョン4.0のペリフェラル機能の一部
デバイス検索機能検出可能なデバイスとなります(Scan enable、Advertise)
デバイスを探す事はできません
サービス情報照会機能SDP (Service Discovery Protocol)
ATT (Attribute protocol)
情報提供機能のみ
相手のサービスを検索する事はできません
認証・暗号化機能あり
設定の変更で、暗号化・認証を要求するようになります
認証機能はパスキー入力などのサブルーチンをアプリケーション側に用意する必要が有ります
LEのプライバシー機能は、ランダムデバイスアドレスの受け入れ(チェック)のみ可能
SecurityManager/ATTのデータ署名機能はありません
キーは電源が切れたら消失します
誤り検出、再送機能なし
アトリビュート照会機能ATTで小物内データの検索および更新機能、通知機能あり(Notify,Indicate)
相手のATTデータにアクセスする機能はありません
仮想シリアル通信SPP/RFCOMMでコネクション待ち
音声系機能AVDTP/A2DPのSink機能、PCMデータ出力、codecはSBCのみ対応

機能分割

本スタックは、以下のような機能分割を行なっております。薄緑内が本スタックの守備範囲です。箱内の記号はソースファイル名と連動します。

HCIトランスポート層

 図で言うところのUSBホストクラスドライバの部分です。今回はUSBドングルなのでUSBですが、UART接続のBluetoothモジュールを使いたいのであればUART用のコードを書けば良いわけです。

USBワイヤレスコントローラ/Bluetoothクラス

 普通売られているBluetoothドングルは、よほど特殊な物でない限りWireless Controller ClassのBluetooth-HCIホストコントローラサブクラス(*1)です。使用するマイクロコントローラのメーカーから提供されているUSBホスト用ライブラリは、通常よく使われるHIDクラスやマスストレージクラスがサポートされているのですが、Bluetoothドングルを使うための「Wireless Controller Class」までは、通常は用意されていません。
 メーカーから提供されているライブラリと仕様書(情報)に従い、新しいクラスを作る必要が有ります。クラスとは一般的には、以下の様な事を行います。
  ・USBスタックがクラスと結びつけする為の対応テーブルを事前に設定しておく
  ・USB機器(ドングル)が挿入された時の初期化を行う
  ・アプリケーションとのデータを橋渡しする
  ・抜かれた時の後処理を行う
 本クラスでは、初期化時にUSB側に4つのエンドポイントを開き、各エンドポイントとHCIインタフェース(*2)、つまりコマンド、イベント、ACL送信、ACL受信それぞれのデータを単純に送受信する事です。詳しくは、実装個別の説明を参照して下さい。

USBパケット分解、再組立

 Bluetoothコントローラのバッファ長より、USBのパケット長が短い場合があるので、分割およびび結合の処理が必要になります。後で説明するL2CAPレベルの電文分割とは別の話です。Bluetooth仕様書に判り易い絵(*3)があります。また、USB関係の処理を行うタスクと、Bluetooth関係の処理を行うタスクは別々なので、非同期でデータのやりとりを行う仕組みが必要です。それを吸収する為にはパイプ機構のようなものが必要です。

*1 公式クラスコード「http://www.usb.org/developers/defined_class/#BaseClassE0h」
*2 BLUETOOTH SPECIFICATION Version 4.0「Volume 4 Host Controller Interface [Transport Layer], Part B USB Transport Layer」
*3 BLUETOOTH SPECIFICATION Version 4.0「Volume 3 Core System Package [Host volume], Part A Logical Link Control and Adaptation Protocol Specification, 7.2.2 Recombination of L2CAP PDUs」1488/2302ページ

L2CAP

制御テーブル

 Bluetoothを制御するのに必要な、メモリ上の管理テーブルです。

強い関係は、削除された場合、関係するテーブルも削除する必要が有ります。
 サービス管理テーブルは完全に固定値です。扱うプロトコル/サービス(BR/EDR)の情報が格納されます。
 デバイス管理テーブルは、相手デバイス毎に、リンクが張られようとした時に追加されて行きます。削除される事はありません。ただし、自デバイスがリセットされたら全て消えます。よってリンクキーも消える事になりますので、再ペアリングが必要になります。
 リンク管理テーブルは、相手デバイスとのコネクションができた時に追加されます。Bluetoothコントローラが決めた、ハンドル値をキーとして、チャネルIDの採番カウンタなどの情報を保持します。
 チャネル管理テーブルは、リンクの中にチャネル(仮想通信パス)が作られた時に追加されます。固定チャネルも、動的チャネルも区別なく作られます。固定チャネルはリンク確立時に全て(サービス管理されているもの)作られます。 リンクまでは、おおむねBluetoothコントローラが制御し、L2CAP側は言われるがままの対応をするだけですが、チャネル以降はL2CAPソフトウエアが実現する機能なので、面倒な制御情報が入っています。

リソースマネージャ

 Bluetooth仕様書で記述されている「Resource Manager」の考え方を、正確に実装している訳でなく、処理分割を考える中で、偶然これに相当する部分が出来たので、このライブラリでもこの名称を使っています。
 機能分担は、下記にしています。
  ・Bluetoothコントローラと連携した、リンク確立までの状態制御
  ・電文の入出力・分割/組立・振り分け
 当初、これらを別々に実装していましたが、最終的には一緒にしないと出来ない部分があり、統合しました。

 Bluetoothコントローラと連携
 無線通信の面倒な部分は全てコントローラが担い、ホストとなるコンピュータは、このコントローラとの通信を行うだけで済みます。このコントローラとの通信を定めた仕様が「HCI」です。
  ・コマンド送信
  ・イベント受信
  ・データ送信
  ・データ受信
 の4つの接続を使い会話します。具体的な接続方法は、先の「HCIトランスポート層」がこの部分です。

 リンク確立までの状態制御
 アプリケーション間通信を行う為には、次の2段階で通信パスを作ります。
 ・相手Bluetoothコントローラ、自Bluetoothコントローラとのコントローラ間リンクを張る指示をする
   ・通信可能な相手Bluetoothコントローラを検索して、接続を要求する、もしくは
   ・相手からの接続を要求されるのを待って、接続を受理する
      これらの操作は、HCIコマンド送信/イベント受信で行います。
      「リンク」が確立されれば、データ送信/受信が出来るようになります。
      ※コントローラはBD_ADDRと呼ぶ6バイトの固有番号によって識別します
      ※コントローラをこのライブラリでは「デバイス」と呼んでいます
      ※相手によっては、パスコード(PIN)や認証を要求される場合があるし、要求する事が出来る
      ※暗号化はコントローラに指示するだけで、実際のエンコード/デコードはコントローラが行います
      ※リンクには「ハンドル」という一意の番号が付番されます
 ・シグナリング、相手アプリケーションとの論理通信チャネルを開設(チャネルマネージャ)
   確立されたリンクでのひと組のデータ送信/データ受信パス内に、複数の仮想的な通信パスを作り、
   同時に多数のアプリケーション間の通信が出来る様にします。
   チャネルはチャネル番号”CID”で管理され、ハンドル番号+送信元CID+送信先CIDの組みで一意に識別されます。
   相手アプリケーションを指定するのはPSMと呼ばれるBluetooth仕様書で定めた番号です。
   新しいチャネルを作るのに必要な上記の様な情報のやり取りを行なっています。
   具体的な説明は省略。Bluetooth仕様書に、状態遷移図がありますので、そのまま実装するだけです。
      ※シグナリング自体、一つの固定チャネルを使います
      ※固定チャネルは、シグナリングせずアプリケーションと直結します
      ※チャネル番号は0x40から自由に採番(私は連番にしました)、0x40未満は固定チャネル

 電文の入出力、分割/組立、誤り検出/再送制御、仕分け
 データ送信/データ受信パスに流れる電文は、
  ・アプリケーションのデータだけでなく、通信パスの開設を行う為の制御電文も入ります
  ・Bluetoothコントローラ内のバッファ長の制約で、分割しなければならない/分割されている、時があります
  ・オプションですが、誤り検出、再送制御を行う必要があります(これは実装していません)
 これらを処理するのが、この部分です。

チャネルマネージャ

 リソースマネージャ同様に、Bluetooth仕様書上の実装モデルとは違います。
 この機能は、HCIインターフェースのCOMMANDとEVENTの制御パスを使い、デバイス間のリンクが張られ、ACL通信パスが有効になった後に動き出します。ACL-Uしか相手にしません。
 チャネルマネージャはACL上のメッセージを重畳・多重化し「チャネル」と呼ぶ仮想的な通信パスを実現します。簡単に言えば、全てのメッセージのヘッダにグループ番号を付けて、仕分けできるようにするものです。このグループがチャネルという単位で、グループ番号はCID (Channel Identifier) と呼ばれます。
チャネルには予約された番号を使う固定チャネルと、シグナリングで動的に作られるチャネルがあります。

タスク管理

 Bluetoothの通信処理を行う為には、どうしてもマルチタスク的な機能が必要になります。通常は、OSが提供するfork/exec等のマルチタスク関連のシステムコールを利用すれば済む事ですが、小規模のマイクロコントローラではOSを導入するのはメモリリソース的に考えてしまうものです。また、このライブラリの動作条件としてOSの使用が前提になってしまいます。
 このライブラリでは、仮想的に複数のタスクを持たせる為に、イベントループ内に直列に各タスクの処理を組み入れていくと言う手法を使っています。これは、 STM提供の標準USBライブラリと同じやり方です。
 コネクションとタスク間の結びつけを管理する必要があります。固定的な結びつけだけであれば、ハードコーディングで対応も可能ですが、動的に変化する関係を持ちますので、制御テーブル(チャネル管理テーブル)を使って管理します。

SDP機能

 現在の実装は、属性IDでの条件抽出機能はありません。必ず0x0000~0xffffが指定された物とみなされます。
SDPデータベースはプログラムコンスタントで、現在RFCOMM, PnP Information, GATTの3つのレコードが定義してあります。
 難しくないので、詳しくはソースコードをご覧下さい。

RFCOMMサーバ

難しくないので、詳しくはソースコードをご覧下さい。

AVDTPサーバとA2DP

 所定の状態遷移に従い電文のやりとりを行うだけですが、仕様書上必須の部分のみを実装しています。リカバリや分割/多重化メッセージなどは実装していません。また、仕様書に準ずるAPIを通して、A2DPに連携します。
 A2DPサイドの主たる処理は、メディアパケットの対応です。本スタックではSinkのみの対応ですのでSBCのデコード処理のみが、実装されています。仕様書上のデコードロジックそのままを実装しましたので、浮動小数点演算を多用している関係で、高速なCPUやFPUが前提となっています。STM32F4(168MHz+FPU)クラスが必要です。

ATT(Attribute protocol)サーバ

 難しくないので、詳しくはソースコードをご覧下さい。

SM(Security Manager)

 LEでのSSP(セキュアシンプルペアリング)に相当する機能です。認証や暗号化を要求された時、もしくは相手からのSMP問合せを受けた時に機能し、主に暗号化キーのやりとりをします。実際、暗号化するのはBluetoothコントローラが担います。本スタックでは、ペリフェラルの位置づけなので、自分から直接認証を要求し始める事はありません。ですから、SMPで認証関係の応答を行い、その結果、問題なければ認証/暗号化していると言う状態を更新するだけです。ただ、認証に至るまでの経過は暗号キーの計算や、相手のパスキー表示/入力の能力の有無で操作方法を変えるなど、やっかいな処理を要します。とは言え、暗号化キーと、そのキーの種類(暗号化のみ/認証=相手を特定)を保持・管理するだけです。
 難しくないので、詳しくはソースコードをご覧下さい。

STM版の固有情報

基本的なプログラム構造

 STMが提供するUSBホスト用のライブラリの処理構造に従います。
 USBプロセス処理は、短い時間間隔で呼び出す必要があるので、通常は無限ループ内で単純にコールするイベントループというプログラム技法を用います。ユーザ処理も、このループ内に記述する事になりますが、遅延無くUSBプロセスに制御を渡すには、あまり長い時間処理する事とは出来ません。アイドル時で毎秒1000回以下だと何かと問題が出ると思います。
 この、USBプロセス処理の中から、USBホストの状態遷移に従って、次々とコールバック関数が呼ばれます。USBデバイスと通常の通信が出来る状態になれば、アプリケーションハンドラが呼ばれ続けるようになります。
 Bluetoothのプロセス処理(bt_process)は、このアプリケーションハンドラ(USBH_USR__UserApplication)内から呼ばれるようにして下さい。また、初期化処理(bt_init)はUSBのセットアップが済んだタイミングでコールするようにして下さい。下図参照。

水色の箱は、ユーザが記述できる部分を表します。
RFCOMMでのデータ受信と、ATTデータが更新された場合は、 Bluetoothのプロセス処理からコールバックされます。
RFCOMMへのデータ送信と、ATTデータを更新する場合は、APIを呼び出す事で突き放し形で処理されます。完了待ちはしません。
より、具体的にはソースコード”main.cを参照して下さい。
 「ATTデータベース」はデータメモリ上に書換え可能なテーブルで保持しています。データは、ハードコーディングしておりソースファイルbt_att_db.[ch]で記述しています。利用の際は、このソースコードを編集して下さい。運用時の誤動作が出ない様、少なくとも、SERVICE-UUID値は変更して下さい。(通常はSERVICE-UUIDで通信相手を探しますので) なお、SERVICE-UUID, CHARACTERISTIC-UUID値はランダムで生成したもので意味はありません。OSXでは、ターミナルで「uuidgen」コマンドを実行すると簡単に生成できます。データを動的に追加削除する機能は用意していません。動的にできるのは更新のみです。

USBワイヤレスコントローラ/Bluetoothクラス

 STMから提供されているUSBホスト用ライブラリは、HIDクラスやマスストレージクラスのみのサポートで、Bluetoothドングルを使うための「Wireless Controller Class」は、当然の事ながらありません。
公式クラスコード「http://www.usb.org/developers/defined_class/#BaseClassE0h」
 新規で作らなければならない部分です。STMからUSBライブラリについての技術資料「CD00289278 UM1021 USB On-The-Go host and device library」が公開されていますので、これとBluetooth仕様書のHCIトランスポートに関する部分*1を見比べながらの整合となります。
とは言え、STMの技術資料は十分ではなく、ドキュメントだけでは実装できません。その為、既存のソースコードを参考にしながら、手探りでの開発となってしまいます。
インタラプト転送に関しては、確実に1ms単位でのポーリングを行わなければならないと言う、シビアなタイミングを満足させる必要があり、実際、遅延が許されない時があるので、ポーティングに苦労します。
*1: BLUETOOTH SPECIFICATION Version 4.0「Volume 4 Host Controller Interface [Transport Layer], Part B USB transport layer」

STM標準USBライブラリの改善

STMicroelectronics社が提供してくれているUSBスタックのライブラリを利用していますが、一部不具合が出ていました。
不具合内容:
 USBデバイスが認識されない事がある。
 特定の型式のデバイスで必ず発生。(個体差があるかどうかは未確認)
 バススピード認識までは行われるが、エニュメレーションがされない。固まったように見える。
 USBデバイスが接続されたり電源が供給開始された直後に、ホストからデバイスに対してSETUPパケットを送信するが、デバイスによっては応答がこない場合があり、状態遷移が進まなくなる。
 ホットプラグせず、電源供給が途切れないようにすると、正常に動作するデバイスもあるので、若干の待ち時間が必要なのかもしれない。それでもダメなデバイスもあるので、電源投入タイミングの問題ではなく、リセット後の待ち時間の方だと考えられる。
改善方法:
 ライブラリのソースコードを修正。
 SETUPパケットに対する応答に、タイムアウト監視するようにし、再送制御機能を付加する事で、この問題に対応させる。
修正内容:
 「STM32_USB_HOST_Library V2.0.0」の「usbh_core.c」に、2カ所数行を追加して下さい。
592行目あたり //ADD 2013.12.14のコメントが付いている1行を追加

657行目あたり //ADD 2013.12.14のコメントが付いている6行を追加

背景

 色々な小物を考える時、近距離無線でのデータ通信が低コストで行えれば、かなり理想に近い物にできるのにと、検討を断念する場面は多いのでした。電波法の障壁もあり、なかなか解決法が見つからないでいたのですが、Bluetooth-USBドングルの存在が、この現状を変えられると考えました。
 しかし状況はそんなに単純ではなく、組込みシステム用の無償のBluetoothスタックが(せっかく)存在するのに、iPhoneでの自作対応が難しく(ライセンス契約要)、はなはだ残念な思いをしていました。
 所が、iPhoneでもATT(Core Bluetooth)を利用するのは何の制限が無い事から、ATTとは何じゃ?このルートを開拓すれば、iPhoneを自作ワールドに取込めるのではと考え始めました。昨今Bluetooth-LEにシフトしつつあるし興味が拡大。
 この度、まとまった時間が出来ましたので、思い切って自力開発しました。まだまだ、対応させたいプロトコル・プロファイルがありますが、単純なアプリケーションに適用できる最低限の機能の、STM32F4用のコードが出来ましたので公開させて頂きます。私自身はiPhone燃費計に利用しています。

Copyright©2013-2019 Toyohiko TOGASHI


投稿日

カテゴリー:

投稿者:

タグ:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です