Bluetoothスタック/ペリフェラル向け/STM32版

 STM32F4-Discovery評価ボードと、Bluetooth-USBドングルだけで動作する、Bluetooth4.0 LE(Low Energy)対応のソフトウエア・スタック・ライブラリです。ATT(Attribute protocol)と、クラッシックの一部(SDP、RFCOMM、A2DP/sink)をサポートしています。BluetoothLEではセントラル、ペリフェラル、オブザーバ、ブロードキャスターという立ち位置を定義していますが、これはペリフェラル(周辺機器)向けの機能で、お手軽に組込み用途に利用できます。PIC32MX版もあります。

外部仕様

Bluetoothサポート概要

仕様準拠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のみ対応

動作条件

対応CPUボード: STM32F4-Discovery
 USBドングル: 市販品はおおむね使用可能
 多重動作: マルチタスク不可
 開発環境:System Workbench for STM32 / Ac6 Tools(略称SW4STM32)
 ライブラリ:STM32CubeMX
 使用プログラムメモリ量: 約100kB
  (USBスタック、Cランタイム等全て含んだサンプルの全サイズ、コンパイラ最適化は-Osを使用)

権利関係

GPL v3に準じますが、暗号化に関する部分にオープンソース rijndael-alg-ref.c を使っていますので、ご利用の際には注意願います。
Bluetooth仕様の情報源については、次の公開情報を元にしています。私の英語読解力の問題があり、誤解の可能性が高いので、重要な場面では使わないようにして下さい。
●BLUETOOTH SPECIFICATION Version 4.0
Bluetooth SIG 開発者向けサイトdeveloper.bluetooth.orgの公式仕様書
●ETSI TS 101 369 V6.3.0 (1999-03) Technical Specification
(GSM 07.10 version 6.3.0 Release 1997)
European Telecommunications Standards Institute
RFCOMM仕様で参照されているドキュメント
●CD00289278 UM1021 USB On-The-Go host and device library
STM公式サイトで公開しているユーザマニュアル
●Federal Information Processing Standards Publication 197 (FIPS-197)
 ADVANCED ENCRYPTION STANDARD (AES)
  Rijndael Algorithm: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
  Reference source code: rijndael-alg-ref.[ch] rijndael-api-ref.[ch]

ダウンロード

 SW4STM32プロジェクトファイル:
 スタック本体とサンプルプログラムが含まれます。

「Bluetooth4P_STM32F4Discovery 0.4d.zip」 (17.3MB) バージョン0.4d/2019.4.17更新
 前版(v0.3)からの変更点:
  A2DPのサポート
  STM32CubeMX(コード生成ツール&ライブラリ)の適用

サンプルの動かし方

 ダウンロードしたファイルをSW4STM32のホームディレクトリに展開し、プロジェクトを開いてください。ビルドしてF4DiscoveryをUSB接続して実行してみてください。緑LEDが2秒周期で点滅します。マイクロUSBコネクタにBluetooth-USBドングルを接続すれば、サンプルプログラムの機能を確認できるはずです。サンプルプログラムは、以下の機能を持ちます。

RFCOMMの機能

1秒間隔で”Hello world.”のメッセージが表示されます。

WindowsPCの場合

 Bluetoothのブラウズやデバイス設定等の機能を使うと、「SYM32F4-Discovery」という名前が出ているはずです。これでペアリングを行ってみて下さい。認証を聞かれたら「無し」を指定。作られた新しいCOMポートにハイパーターミナルで接続すると”Hello world”の文字が出てくるでしょう。

Macの場合

 「Bluetoothデバイスを設定」もしくは「Bluetooth環境設定」でペアリングを行って下さい。ターミナルを開き、”ls /dev/cu.*”コマンドで新しく出来たシリアルポートを確認し、
 ”cat /dev/cu.XXXXXXX”コマンドでアクセスして下さい。
 ”screen /dev/cu.XXXXXXX”コマンドもあります。終了はCtrl+a+\です。

ATTの機能

ATTは、GATT標準の「Bluetooth心拍計」をエミュレートしています。

WindowsPCの場合

 確認できていません。

iPhone(iOS)の場合

 標準Bluetooth心拍計をサポートするアプリケーションソフトウエアは、あまりないようですが、iOSは心拍計だけは標準サポートされており、iPhoneをお持ちの方は簡単に確認する事が出来ます。

設定のBluetoothを開くと1分ぐらいで「Heart Rate Sensor」が出てくるはずです。タップすると特に応答なくペアリング完了。心拍データを確認するには標準インストールされている「ヘルスケア」アプリケーションを使います。(残念ながらiPadにはありません)

 バイタルをタップすると心拍数が60〜90まで1秒間隔で変化するのを見る事が出来ます。特に設定は必要はなく、データソースに自動的にデバイスが追加されます。(”BLE ATT SAMPLE”は開発中の名前)

 他のアプリケーションとしては、ツールがいくつか有ります。LightBlue、BLExplr、BTLExplorer、SmartDiscoverなどが「App Store」で無料でダウンロードできます。出来が良いのがLightBlueで、以下スクリーンショットです。

 起動してしばらくすると、ペリフェラルデバイス「Heart Rate Sensor」がいるはずです。中を確認すると「Heart Rate」のサービスが出てくるはずです。GATT仕様通り心拍計の3つの属性(キャラクタリスティクス)があります。
   ①Heart Rate Measurement
    心拍数[BPM]
    直接読みだす事はできませんが、Notify機能(Listen for notification)で値を確認できます。
   ②Body Sensor Location
    これは測定部位を表し2はWristを示します。固定値です。
   ③Control Point
    ①の値をリセットする指示。本サンプルでは本来の機能は有しませんが、
    0x01を書込むとDiscovery上の「オレンジLED」が点灯し、それ以外の値だと消える動作となります。

Macの場合

Xcodeサンプルプログラム – GATT対応Bluetooth心拍計ビューア

 入手容易なアプリケーションは見つけられませんでしたが、Xcodeのサンプルコード「HeartRateMonitor」がありましたので、動作確認に使えます。

Apple Developerからソースコードをダウンロードして、xcodeで開いて以下のコード修正をしてビルドして下さい。

//#import <IOBluetooth/IOBluetooth.h>
#import <CoreBluetooth/CoreBluetooth.h>

IOBluetooth.hをCoreBluetooth.hに変更するだけです。このサンプルは良くできていて、心臓が鼓動します。また、他のサンプルとしてBluetooth体温計?「HealthThermometerClient」もあります。それに対応する為のATTデータベースのソースファイルも本サンプルに入れてあります。

Bluetooth Explorer – Apple提供の開発ツール

 Xcodeの追加ツールですが、Apple Developerのダウンロードから個別にインストールする必要が有ります。(多分、単独でも動かせると思われます)Additional_Tools_for_Xcode もしくは Hardware I/O tools for xcode という名前です。Bluetooth Explorerを起動し、Devices -> Low Energry Devicesを選択すると画面が出て来ます。

LightBlue – 無料で使えるATTツール

iOS用の物と同じで、App Storeから無料でダウンロード可能です。macOS版の出来はイマイチです。

A2DP/sinkの機能

 PCやスマートフォンから「Bluetooth4P」という名前のスピーカーデバイスが出てきますので接続します。成功すれば、「青LED」が点灯します。PC/スマートフォンで音楽などを再生させ、Discoveryボード上のヘッドフォンジャックにイヤホンなどを差し込むと聞こえるはずです。

アプリケーション適用

ユーザアプリケーションで、本スタックを利用するための設定方法や、インタフェース(API)を説明します。

共通

名称変更

 外部からBluetoothデバイスを検索した時に最初に表示される「名前」はソースコード上にハードコーディングされています。これを修正する事で、サンプルの名前「Bluetooth4P」を好きな名前に変更できます。動的に変更する機能はありません。名前はBR/EDRとLEで別々に設定できます。ソースファイルは「bt_config.h」で、#define定義を変更します。

#define BT_LOCAL_NAME  12,'B','l','u','e','t','o','o','t','h','4','P','¥0'

最初の数字はデータのバイト長です。必ず‘\0’を付加して下さい。

#define BT_LE_SCAN_RESPONSE  32,31,12,GAP_AD_Type_Shortened_local_name,\
                             'B','l','u','e','t','o','o','t','h','4','P',\
                       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

 最初の数字はデータ全体のバイト長で32固定です。2番目の数値は有効なデータのバイト長です。以降、GAPのADフォーマット仕様に従います。

BR/EDRとLEの機能無効化

 どちらかの機能を使いたくなければ、外部から見えなくする事が出来ます。これは、スキャンを出さないようにする、アドバタイズしないようにするというやりかたでの対応となります。 ソースファイルは「bt_config.h」で、#define定義をするか/しないかの指定で変わります。

#define BT_INIT_DETECT_ENABLE           // BR/EDR Inquiry scan enable
#define BT_INIT_LE_DETECT_ENABLE        // LE advertise enable

BT_INIT_LE_DETECT_ENABLEを無効にしても、APIでアドバタイズをオン/オフさせる方法も有ります。

プログラムインタフェース(API)

RFCOMM

概要

 プログラムインタフェースは単純です。データが受信されたらAPIがコールバックされます。送信したければAPIをコールするだけです。設定としては、認証・暗号化があります。通常は、セントラル側で必ずペアリング操作が行われますので、最低でも暗号化はされます。しかし、デフォルトでは認証なし(不特定の相手がペアリングされてしまう)のため、注意を要します。この辺りは(4)セキュアシンプルペアリング の設定を行う事で認証を要求するようにできます。

プログラムインタフェース(API)

A2DP/Sink

概要

 Bluetoothクラッシックには、2種類の音声系プロファイルがあります。一つは携帯電話用途のハンドセットやマイク付きイヤホンなどに使われるHands-Free Profile(HFP) やHeadset Profile (HSP) で、携帯電話品質の低ビットレートの音声を対象にしています。もう一つは、Advanced Audio Distribution Profile (A2DP) で、音楽を聴くに耐える品質を持ったものです。
 本スタックは、A2DPのシンク(吸い込み)方向のみの機能をサポートしています。吸い込みとは、音声データを受け取る機能で、スピーカやヘッドフォンを実装するときに使います。逆はソースで、マイクやCDプレーヤなどの音源を送り込む時の機能です。なお、Codecは「SBC」を内包しており、API上はPCMデータですので、とても扱いやすくなっております。
 基本的は、接続されればFIFOバッファにPCMデータが流れ込んできますので、オーバフローしないよう、逐次データを取り出して処理します。

設定

 系統(エンドポイント)の設定値は、ソースコードの「bt_a2dp_sink.h」にあります。初期値は、エンドポイント数:1系統、FIFOバッファの幅=1024サンプル、FIFOバッファの深さも=4、デフォルトのサンプリング周波数44100Hz、チャネル数=2(ステレオ)です。サンプル解像度(ビット深度)は16ビット固定です。

プログラムインタフェース(API)

 APIはありません。エンドポイントのメモリテーブルを参照/変更する事で、A2DPの機能を利用する方式です。
接続状態:
 inUseが1になるか、stateがA2DP_STATE_DISCONNECTだと、接続されている事を表します。
FIFOバッファ:
  バッファ本体名がpcmBufferで、書き込み位置ポインタと読み出し位置ポインタで仮想的にリング構造を実現する方式です。ポインタが、書き込み位置pcmWritePos>読み出し位置pcmReadPos、の条件になったらデータが出てきたという事です。また、pcmWritePos<(pcmReadPos+バッファ深さ)になったらオーバンラン/データ消失したという事ですので、強制的に追いつくようにして下さい。位置ポインタはリセットしない事で、大小の判定が容易になります。ただし、オーバフローした場合は問題がでますが、数千時間に1回の頻度、ちょっとノイズが出る程度の事なので、無視しても良いと思います。実際のバッファ位置は、ポインタ値%バッファ深さです。(%=剰余)

ATT

概要

 そもそもATTとはキー/値の単純な対応表です。これにキーとキーの間の関係ルールをかなり細かく統一(GATT)する事で、温度や湿度、重量、速度、電圧、電流などの一般的なデータ値の利用がしやすくなります。例えばGATTに準拠したBluetooth温度計(サービスと呼ぶ)デバイスを作れば、それを利用するスマートフォンアプリケーションは既に存在する可能性が高いでしょう。新しいデータ形式は各自自由に作る事も許されているので、独自データも扱えます。この場合は、提供側デバイスも、利用側アプリケーションも自分で作成する必要があります。
 何はさておき、ATTデータベースを設定する必要が有ります。このデータベースはアプリケーションからAPIを通してアクセスするのと、外部からも自由にアクセスされるという単純な仕組みです。外部から値が変更された時にコールバックされるAPIもありますし、変更したら自動的に相手に通知されるようにもできるので、タイミング取りも可能になっています。
 ATTは何もしなければ、暗号化も認証もされません。これらを適用したければデータベースの属性に追加設定を行う必要が有ります。また(5)セキュリティマネージャーの設定も行って下さい。

代表サービスUUIDの設定

 アドバタイズ(広告)メッセージにサービスUUIDを仕込むには、ソースファイル「bt_config.h」を編集します。

#define BT_LE_ADVERTISING           32,11,\
2,GAP_AD_Type_Flags,(GAP_AD_Flags_Simultaneous_LE_BR_EDR_Host       | \
                      GAP_AD_Flags_Simultaneous_LE_BR_EDR_Controller | \
                      GAP_AD_Flags_LE_General_Discoverable_Mode),      \
3,GAP_AD_Type_16bit_Service_UUIDs_Complete,HCI_UINT16_DATA(GATT_services_heart_rate), \
3,GAP_AD_Type_16bit_Service_UUIDs_Complete,HCI_UINT16_DATA(GATT_services_device_information), \
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

アドバタイズデータその物の定義になります。1つ目の数値はデータ全体のバイト長で固定的に32を指定します。2つ目の数値は有効なデータバイト長です。この例では2つの16ビットUUIDのサービスを指定しています。他の例は、サンプルソースファイルを参考にして下さい。

データベース作成

 データベースはデバイス上のRAMに展開しています。基本的にはキー/値の対応表でキーは静的なものになります。動的に変更が可能なのは値のみです。キーと値の初期値はソースコードでハードコーディングします。
 データベース本体のソースファイルは「bt_att_data.c」です。記述の仕方については、GATT(*1)仕様に従って組立てて行きます。ゼロから作るのは、かなり大変なので、サンプルを元に追加/削除するのが楽だと思います。3種類ほどサンプルファイルを入れてあります。

bt_att_data.Hart_Rate.c         GATT心拍計
bt_att_data.Health_Thermometer_secure.c GATT体温計(認証付き)
bt_att_data.Private_Sample.c       独自デバイス

仕様書は、基本部分のサービスしか記述されていませんので、全ての定義が示されている「https://www.bluetooth.com/specifications/gatt」を参照する必要が有ります。
*1 BLUETOOTH SPECIFICATION Version 4.0「Volume 3 Core System Package [Host volume], Part G Generic Attribute Profile (GATT)」

プログラムインタフェース(API)

 bt_attDbNotification()はユーザアプリケーションで用意する必要があり、通信相手が更新しても自分が更新しても区別なく呼び出されます。必ず以下のように bt_att_serverNotificationQueueAdd(通知依頼)を呼ぶようにして下さい。入れないとDB更新しても通知されなくなります。

void bt_attDbNotification(unsigned short handle, void *value, size_t valueLen){
  bt_att_serverNotificationQueueAdd(handle);
  switch(handle) {        // User action
  case <X>:
    break;
  case <Y>:
    break;
  default:
    break;
  }
  return;
}

SSP:セキュアシンプルペアリング(BR/EDRの認証)

概要

 クラッシックの認証機能で、事実上必須のセキュリティ機能です。ペアリングには不特定の相手を許すものと、ランダムに生成されたパスコードを入力してもらう事により相手を特定する2種類があります。両方とも暗号化はされます。何が違うかと言えば、デバイス側が不特定な相手とのペアリングを望まないという場合です。デフォルトは前者の設定ですので、セキュリティを気にする場合のみ、以下の対応を行って下さい。

ポリシー設定

   ① サービス(プロファイル/プロトコル)毎に、認証が必要かを設定します。
   ② デバイスにパスコードを表示したり入力したりする装備が有るかどうかを設定します。
   ③ 最後に、プログラムインタフェースでパスコードの表示、入力する処理を作って下さい。
 最初に①サービス内容を定義しているソースコードを修正します。ソースファイル「bt_config.h」の、#defineを修正して下さい。RFCOMMしか用意していません。コメントを入れたり抜いたりすれば良いです。

//#define BT_RFCOMM_PERMISSION      BT_L2CAP_SERVICE_PERMISSIONS_Allow
  #define BT_RFCOMM_PERMISSION      BT_L2CAP_SERVICE_PERMISSIONS_Encryption_Required
//#define BT_RFCOMM_PERMISSION      BT_L2CAP_SERVICE_PERMISSIONS_Authentication_Required

 次に②を定義しているソースコードを修正します。 ソースファイル「bt_config.h」の、BT_SSP_IO_CAPABIRITY #define行のどれかを選択します。実際の動きは、相手側の表示・入力機能の有無によって組み合わせ(*1)で決まります。

//#define BT_SSP_IO_CAPABIRITY      HCI_Parm_IO_Capability_NoInputNoOutput
  #define BT_SSP_IO_CAPABIRITY      HCI_Parm_IO_Capability_DisplayOnly
//#define BT_SSP_IO_CAPABIRITY      HCI_Parm_IO_Capability_DisplayYesNo
//#define BT_SSP_IO_CAPABIRITY      HCI_Parm_IO_Capability_KeyboardOnly

 組み合わせ表を自分の処理部分のみ書き出しました。これに従ってAPIが呼ばれます。自動応答の部分は、必ずOKを返すようにしておきます。NGをかえさなければペアリングOKとなり、緑部分は認証もOKとなります。

*1 BLUETOOTH SPECIFICATION Version 4.0「Volume 3 Core System Package [Host volume], Part C Generic Access Profile」Table 5.6: IO Capability Mapping to Authentication Stage 1

プログラムインタフェース(API)

SM:セキュリティマネージャー(LEの認証)

概要

 BR/EDRのペアリングと、LEでペアリングは別物です。LEの場合、Security Managerというソフトウエアが担います。デフォルトではノーセキュリティです。サービス(アプリケーション)からセキュリティ要因で拒否されたら、SMにペアリングを要求し、整った所で、再度サービス要求すると言う流れになっています。
 唯一のLEサービスであるATTの場合、アトリビュート単位にアクセス権限(暗号化/認証)を持っており、ペアリング状態を確認して、拒否応答を行います。あとはSMチャネルを通して相手からペアリング要求されますので、SMが双方のパスコードの表示・入力機能の組み合わせに従い、ペアリング操作方法が決まります。

ポリシー設定

   ① ATTデータベース上のアトリビュートに、暗号化/認証が必要かを設定します。
   ② デバイスにパスコードを表示したり入力したりする装備が有るかどうかを設定します。
   ③ 最後に、プログラムインタフェースでパスコードの表示、入力する処理を作って下さい。
 最初に①ATTデータベースを定義しているソースコードを修正します。ソースファイル「bt_att_data.c」の、テーブル初期値を修正して下さい。 保護したいアトリビュートのプロパティに以下のいずれかを加えます。
 ・ATT_DB_PROPERTIES_ENCRYPTION(暗号化)
 ・ ATT_DB_PROPERTIES_AUTHENTICATION(暗号化と認証<相手を特定>)
 次に②を定義しているソースコードを修正します。 ソースファイル「bt_config.h」の、BT_SMP_IO_CAPABIRITY の#defineのどれかを選択します。実際の動きは、相手側の表示・入力機能の有無によって組み合わせ(*1)で決まります。

//#define BT_SMP_IO_CAPABIRITY      SMP_IO_Capability_NoInputNoOutput
  #define BT_SMP_IO_CAPABIRITY      SMP_IO_Capability_DisplayOnly
//#define BT_SMP_IO_CAPABIRITY      SMP_IO_Capability_KeyboardOnly
//#define BT_SMP_IO_CAPABIRITY      SMP_IO_Capability_KeyboardDisplay

 組み合わせ表を自分の処理部分のみ書き出しました。これに従ってAPIが呼ばれます。JustWorksはAPは呼ばれません。APIでNGを返さなければペアリングOKとなり、 緑部分は認証もOKとなります。

 「表示のみ」は、パスキー値を決められますので固定値を返すようにしておけば、自動応答でも認証OKにさせる事が可能です。もちろん、相手にはその固定値を教えておく必要はあります。
*1 BLUETOOTH SPECIFICATION Version 4.0「Volume 3 Core System Package [Host volume], Part H Security Manager Specification」Table 2.4: Mapping of IO Capabilities to STK Generation Method

プログラムインタフェース(API)

プログラムインタフェース(API)

 取り交わされたキー情報は、デバイスの電源が切れたら消失しますので、再度、ペアリング操作を要します。ペアリングデータを保存したい場合は、「デバイス管理テーブル」をバックアップして下さい。

Copyright©2013-2019 Toyohiko TOGASHI


投稿日

カテゴリー:

投稿者:

タグ:

コメント

コメントを残す

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