2014/07/13

[ble][nrf51]S110をひもとこう (4) - nAN36 - (5)

以前、Excelで作っていた図があったので貼っておこう。
たぶん、BLEの大ざっぱな構成だと思う。
image
3つのブロックのうち、実装がいるのが一番上のブロックなのだろう。
つまり、今回のサンプルであるLED Buttonアプリが、Profileって書いている部分(ちょっとはみ出すんだろうが)辺りなのだろう。
Profileは、私の解釈では、プロトコルを含む機能、ということにしている。
1つのProfileは、1つまたは複数のServiceで構成されている。
なんでか知らないけど、Bluetoothフォーラムの上記リンクでは、ProfileとServiceが同じ表になっている。
でも、普通はProfileの話しかしないよな・・・
例えばANPはこんな構成。
image
ANP_SPEC_V10より
BLPは、こう。
image
BLP_V10r00より
数えると、Profileは10個、Serviceは14個あった。
S110のヘッダを見ると、提供しているのはProfileじゃなくてServiceのようだ。
ではBLEでやるのはこの14サービスから選ばないといけないかというと、そうでもない。
今回のLED Buttonも、Serviceを作るところからやる。
Serviceを作るということは、Profileを作るということにもなるだろう。


4.3 Set up

4.3.3 Setting up the service

Serviceのテンプレートは無いが、Battery Serviceがシンプルな構造なので、それをカスタマイズするのがよいだろう。
(githubの方は修正後の結果だけど、nAN36ではこれを作る過程から書かれている。 )
  • Source/ble/ble_services/ble_bas.c
  • Include/ble/ble_services/ble_bas.h
これを自分のところにコピーして、リネームする。
今回はble_lbs。

4.4 Implementing the service

Serviceは他のアプリで再利用できるように、汎用的な実装にする。
 

4.4.1 Designing the API

以下を書き換える。
  • uint32_t ble_bas_init(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init);
  • void ble_bas_on_ble_evt(ble_bas_t * p_bas, ble_evt_t * p_ble_evt);
  • uint32_t ble_bas_battery_level_update(ble_bas_t * p_bas, uint8_t battery_level);
これが、こうなる。
  • uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init);
  • void ble_lbs_on_ble_evt(ble_lbs_t * p_lbs, ble_evt_t * p_ble_evt);
  • uint32_t ble_lbs_on_button_change(ble_lbs_t * p_lbs, uint8_t button_state);
ble_evt_tはS110の構造体だが、それ以外は自由だ。
戻り値を取るタイプは、NRF_SUCCESSなどを返す。s110/nrf_error.hに定義があるので、見ておくとよいだろう。

4.4.2 Implementing data structures

ble_lbs_tやble_lbs_init_tのことについて。
BAS(Battery Service)の初期化構造体は、こう。
typedef struct {
    ble_bas_evt_handler_t         evt_handler;
    bool                          support_notification;
    ble_srv_report_ref_t *        p_report_ref;
    uint8_t                       initial_batt_level;
    ble_srv_cccd_security_mode_t  battery_level_char_attr_md;
    ble_gap_conn_sec_mode_t       battery_level_report_read_perm;
} ble_bas_init_t;
イベントハンドラ、オプションのパラメータ、初期値、セキュリティモードなど。
BASのService構造体は、こう。
typedef struct ble_bas_s {
    ble_bas_evt_handler_t         evt_handler;
    uint16_t                      service_handle;
    ble_gatts_char_handles_t      battery_level_handles;
    uint16_t                      report_ref_handle;
    uint8_t                       battery_level_last;
    uint16_t                      conn_handle;
    bool                          is_notification_supported;
} ble_bas_t;
イベントハンドラ、Serviceの状態、現在のバッテリーレベル通知ができるかどうかなど。
BASはServiceの開始から終了までの間、定期的にバッテリーレベルを通知するServiceだが、LED Button Serviceは期間ではなく単発だ。
LBSの初期化構造体は、こう。
typedef struct {
    ble_lbs_led_write_handler_t led_write_handler;
} ble_lbs_init_t;
LED状態を書き込むイベントハンドラがあればよい。
ハンドラの型というかシグネイチャも、特に規定は無く決められるようだ。
BASは
  typedef void (*ble_bas_evt_handler_t) (ble_bas_t * p_bas, ble_bas_evt_t * p_evt);で、LBSは
  typedef void (*ble_lbs_led_write_handler_t) (ble_lbs_t * p_lbs, uint8_t new_state);だ。
次のパラメータは「keep track of the state」のために必要。
えーっと・・・状態を追跡するため?
  • Serviceのハンドル
  • Characteristicのハンドル
  • Connectionのハンドル
  • UUIDタイプ
  • LED書き込みのハンドラ
これらをService構造体に持つことになる。
typedef struct ble_lbs_s {
    uint16_t                    service_handle;
    ble_gatts_char_handles_t    led_char_handles;
    ble_gatts_char_handles_t    button_char_handles;
    uint8_t                     uuid_type;
    uint16_t                    conn_handle;
    ble_lbs_led_write_handler_t led_write_handler;
} ble_lbs_t;

ここで休憩だ。
というのも、なぜ上記のパラメータを持っていないといけないかがわかっていないからだ。
ようやく私も、ここらへんでBLEってものを学ばないといけないと気付いた次第である。
適当にやっていた2章を見直そう。

2 Introduction to Bluetooth low energy

ここの図を見て、今回の最初に描いていた絵の意味がわかった。
あれは、BLEのプロトコルスタック構造なんだ。
上から順に、Profiles、Host、Controllerというレイヤーみたいだ。

2.1 Generic Access Profile (GAP)

GAPはアプリとのインターフェースになる最下層のBluetoothスタックだ。
パラメータとして、govern advertisingとconnection among other thingsを含んでいる。
うーん、伝わらない・・・。
詳細はBluetooth Core SpecificationのVolume 3 - Part Cに書いてあるらしいから、覗いてみよう。
このProfileは、以下に関する一般的な手順を定義する。
  • Bluetoothデバイスの検索(idle mode procedures)
  • Bluetoothデバイスの接続管理(connectiong mode procedures)
わかるような、わからんような。
とにかく、Generic Access、だから、一般的なアクセス、ってことか。
全部理解しようと思ったら、日が暮れるどころか今年が終わってしまいそうなので、このくらいにとどめておこう。

2.1.1 Roles

BLE接続(BLE link)を行ったり管理したりするのに、roleを決めないといけない。
接続に先だって、CentralかPeripheralかを決定しておく。Centralは接続しに行く側で、Peripheralは接続される側。
これはLink Layerだと、MasterとSlaveという用語になる。
うん、前に調べたことがある
そもそもというか、S110はPeripheral用のSoftDeviceなので、今回はPeripheralということになる。
GAPのRoleとしてはこれ以外に、BroadcasterとObserverというものがある。
BroadcasterはAdvertisingイベントを出すデバイスで、ObserverはAdvertisingイベントを受信するデバイス。
接続は確立させないと書いてあるので、これがBeaconってことかな。
個人まとめ
  • 接続を確立させるなら、Central-Peripheralの関係
  • 接続を確立させないなら、Observer-Broadcasterの関係

2.1.2 Advertising

PeripheralはCentralとの接続のために、20ms~10.24sec間隔でAdvertisingパケットを送信する。
接続前のCentralはAdvertisingパケットを受信するだけだし、接続前のPeripheralはAdvertisingパケット送信後の短時間のみ接続要求の受信確認(listen)を行う。
Advertisingパケットは31バイトまでのデータを含めることができる。
CentralはAdvertisingパケットを受信すると、Scan要求という、「まだAdvertisingデータがあるなら送れ」要求を送信する。
これは「Active Scanner」になっているときだ、と書いてある。
Core_v4.1のp.1930では、ScanningタイプとしてPassiveとActiveが書いてあり、Centralとしては「PassiveをサポートしているならActiveの方はオプショナル、PassiveをサポートしていないならActiveは必須」と書いてある。
p.1255では、LE_Scan_Typeの定義があり、デフォルトは0x00でPassive、0x01がActiveで、SCAN_REQを送信する、と書いてある。
p.2506にはAdvertising PDUの種類が4つある。
  • ADV_IND
  • ADV_DIRECT_IND
  • ADV_NONCONN_IND
  • ADV_SCAN_IND
このADV_SCAN_INDを受けとったときにSCAN_REQする・・・のかと思ったが、p.2527に表があった。
image
ADV_INDかADV_SCAN_INDのときにSCAN_REQできる。ADV_SCAN_INDは接続できないタイプなんだな。

2.1.3 Scanning

Scanningは、CentralがAdvertisingパケットを確認することとScan要求を送信すること。
「Scan Window]と「Scan Interval]という時間のパラメータがある。
Centralの動作だから、今回は気にしなくてよいかな。
Interval間隔でWindow時間だけ確認する、という動作じゃなかろうかね。

2.1.4 Initiating

Centralが接続しに行くのは、Advertisingパケットを確認するためにScanningするのは同じ手順だ。
Centralが、PeripheralからのAdvertisingに対して接続要求を返す。
NFCだと、R/WをInitiatorって呼ぶのと似たような用語だな。
あ、ここら辺のタイトルになっているScanningとかInitiatingとかは、Core_v4.1のp.2499にある図のStateと一致している。
image
Centralは、Standby→Scanning →Standby→Initiating→Connection、という流れ。
Peripheralは、Standby→Advertising→Connection、という流れ。
だから、次の節は・・・

2.1.5 Connection

接続だ。
定義としては、CentralとPeripheralが最初のデータ交換をしてからがConnectionになる。
Connection状態になると、CentralはPeripheralからの要求に対してデータを要求する。要求するのだが、それは「Connection Interval」と呼ばれる間隔で行われる。この間隔はCentralが決めるのだけど、PeripheralはCentralに対してConnection Parameter Update Requestを送信することができる。
接続間隔は、7.5ms~4sec。
Peripheralが「Connection Supervision Timeout」と呼ばれる時間以内に応答しなかったら、リンクは失われる。
「Slave Latency」の説明もあるけど、よくわからん。Core_v4.1のp.1295からすると、0~499の値を取る。
p.2550の図にタイミングが書いてあるんだけど、見てもわからんし、latencyが載ってない。
image
p.2549では、Connectionのパラメータとして
  • connInterval
  • connSlaveLatency
  • connSupervisionTimeout
が上げられている。LL_CONNECTION_UPDATE_REQをCentral→Peripheralに送る(Link Layerの話なので用語としてはMaster→Slaveになる)。その時間では動けない、とPeripheralが判断したら、CentralにConnection Parameter Update Requestを送信する、ということなのかね。
Connection Parameter Update Requestは「新しい接続パラメータを送ってくれ」と要求するもののようだ(Core_v4.1 p.1718、2000)。L2CAPのコマンドみたい。CentralやPeripheralがConnection Parameter Request Link Layer Control procedureをサポートしてないときに送るものらしいが、送るのはPeripheralからみたいだ(BroadcasterやObserverはサポートしない)。
image
シーケンス図がほしい・・・。



長くなったので、ここで2.1章は切ろう。
次はGATTなんだけど、ここも理解できるか心配だ。。。












0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。