2014/09/20

[nrf51]はじめてのBLEアプリ - (2)アプリを作る

前回:[nrf51]はじめてのBLEアプリ - (1)サービスをつくる
nRF51822のBLEアプリを作る練習。
前回はサービスだったので、今回はそのアプリ実装を行う。
今気付いたが、チュートリアル記事を書いているような感じに見えてしまうな。。。
すまん、この一連の記事は、実況中継なのだ。
もちろん動くようになるまでやるけれども、PDFに書いてある手順を見ながらやっているだけなので、今の段階では間違っていることもあるかもしれない(SoftDeviceのバージョンも上がってるし)。

サービスを呼び出すだけだから簡単だろうと思っていたが、いやいやどうして。
作ったサービスは、あくまで部品のようなもの。
AdvertisingやConnectなどはやらないので、そこはアプリがやる。

 

PDFのLBSサービスでは、クライアント(相手)からのLED制御と、サーバ(nRF51が搭載されたボード)のボタン押下通知ができるようになっている(LED / Buttonサービス)。
私は機能名を付けず、I/Oサービスとして、クライアントからのInputと、サーバからのOutputということにしている。
Output側は、Clientから値を読むこともできるし、変化があったときにNotifyで通知を出すように考えている。

前回サービスを作ってわかったのは、Bluetoothのプロファイルやサービスというのは、あくまでBluetoothの通信自体を指すと言うこと。
音楽関係のプロファイルだからと言って、再生したい音楽を送信する必要も無ければ、受信して音楽を再生するという必要も無い。
ただ、音楽を再生したい/音楽を再生させたい、という需要と供給があるので、そのプロファイルやサービスを使用しているだけなのだ。

その需要と供給を叶えるのがアプリの部分である。


アプリの作成

BLEではいろいろと想定があるかもしれないが、今回はほぼ素のnRF51822を使っていることもあるので、電池駆動を前提として考える。
携帯電話のような充電電池駆動でもなく、乾電池で駆動する想定。

前提がどこに影響するかというと、省電力だ。
スマートフォンの場合でも省電力は求められるだろうが、乾電池駆動の場合はさらに厳密だ。
製品にするときに「通常使用時には○年持ちます」みたいなことを書くことになるからだ。

と書いてはみたものの、nRF51822ではSoftDeviceが主な部分を握っているので、まず「無線の送受信」というところで省電力を考えることはできない。
Nordicのブログにある情報が一番信用がある。というよりも、それくらいしか情報がない。
How to minimize current consumption for BLE application on nRF51822 [closed]

情報がないんだけど、書いてあることが長い・・・。
ま、まずは動くことを確認してから読もう。

 

予定では、こんな感じの回路にする。
右側3つは、アプリのテンプレートで名前の定義があったので、そのまま使おう。
実際の製品だと、電池がもったいないから外すんだろうな。

左側が、今回のサービスで使う部品。
やっぱり、簡単なのってLEDとSWしかなくてね・・・。
これにとってかわるのって、相当難しいんじゃないか? 出力は圧電ブザーも簡単ではあるが、鳴りっぱなしになるとイライラするだろうし。

image
(2014/09/22修正:SWにプルダウン抵抗追加)

 

テンプレート

nRF51 SDKのアプリテンプレートは、こういう初期化が行われている。
全部はやらなくてよいように思う。

    // Initialize
    leds_init();
    timers_init();
    gpiote_init();
    buttons_init();
    ble_stack_init();
    scheduler_init();
    gap_params_init();
    advertising_init();
    services_init();
    conn_params_init();
    sec_params_init();

 

スケジューラの使用

SoftDeviceにはOSっぽい機能は無いのだけれども、その中でもOSっぽいといえるのはスケジューラだ。
何ができればOSなんだ?というのはあるけど、私のイメージとしては、何か少しでもリソースを管理してくれるなら、それだけでOSだ。厳密な定義には興味が無い。

前回スケジューラを調べたときは、wfeを実行している、というところまで見ていたようだ。
ARMは、WFEかWFIを使うと、省電力状態になるらしい。
http://www.aps-web.jp/academy/cortex-m/20/a.html

細かいことはともかく、スケジューラを使わないとがんがん無限ループするだけのようだから、使おう。
テンプレートでスケジューラの使用有無を設定しているのは、以下だった。

  • APP_TIMER_INIT()
    • app_timer_evt_schedule() : app_timer.c
  • SOFTDEVICE_HANDLER_INIT()
    • softdevice_evt_schedule() : softdevice_handler.c
  • APP_BUTTON_INIT()
    • app_button_evt_schedule() : app_button.c

大文字の方が初期化マクロで、小文字の方はスケジューラが使用する関数名だ。
この関数自体はSDKの中に入っているので、スケジューラを使用する場合にはMakefileで該当する関数を含んだファイルもビルドするように追加しておかないといかん。
追加してなかったらリンクエラーになるのだが、事情がわかってないと「このリンクエラーは何だ?」ということになってしまう。

テンプレートのMakefileはGCCのオプションで-ffunction-sectionsと--gc-sectionsが付いているので、未使用の関数は削除されるはず。
そう考えると、とりあえずMakefileにはソースファイルをあれこれ置いていてもそこまで心配はしなくてよさそうな気はする。
が、使ってないものが入っているのも気持ち悪い気がするので、最後に整理したらよい、くらいにしておくか。

 

 

サービス

#includeして、サービス構造体のインスタンスを用意して、services_init()で初期化を呼び出して、ble_evt_dispatch()でハンドラを呼び出す。

ということはわかったのだけど、複数のサービスを登録している場合にはどうなるのだ?
ble_evt_dispatch()は特定のサービス向けに登録するものではないし、呼ばれたときの引数を見てサービスのハンドラを呼び出しているわけでもない。

    ble_gatts_evt_write_t *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    if ((p_evt_write->handle == p_ios->char_handles_in.value_handle) &&
      (p_evt_write->len == 1) &&
      (p_ios->evt_handler_in != NULL)) {
        p_ios->evt_handler_in(p_ios, p_evt_write->data[0]);
    }

このhandleを比較している箇所だ。
これで、該当するキャラクタリスティックへの要求かどうかを判断している。

 

 

ボタン

ボタンは以前も使ったが、GPIOTEとAPP_BUTTONの両方がいる。
テンプレートでは、よくわからんが「WAKEUP_BUTTON」というものが定義されている。
BLE_GAP_EVT_TIMEOUTで使うようなんだが、うーむ。。。
GPIO_WAKEUP_BUTTON_CONFIG()というマクロもコメントアウトながら呼ぶようになっているが、SDKには定義がない。
テンプレートではそのマクロは使わず、普通のボタンの1つとして取り扱っているし。
わからないのでそのボタン定義は削り、今回のボタンだけにしておく。

ボタンは、初期化した段階では使用できなかったと思う
使うときにapp_button_enable()を呼び出し、使わないときにapp_button_disable()を呼び出す。
テンプレートでは、CONNECTしたときにenable、DISCONNECTしたときにdisableにしている(コメントアウトされてるが)。

 

Advertising

そしてAdvertisingだ。
ここでは、サービスのUUID(16bit値)と、登録したときのUUID typeを指定するようだ。
これが配列で指定できるので、複数設定できるのかもしれん。
そこまでは書かれていなかったので、動くようになったら試してみよう。

 

ここまででアプリの説明は終わりになっている。


動作確認

ビルドして、コンパイルエラーなどを取ったら、動作させてみよう。

コンパイルしてわかったが、これがwarningになる。

ble_uuid128_t   base_uuid = IOS_UUID_BASE;

なんでかというと、ble_uuid128_tは構造体だからだ。
構造体の中に、uint8_t uuid128[16]というメンバがいる。
だから、こうなる。

ble_uuid128_t   base_uuid = { IOS_UUID_BASE };

あと、テンプレートのボタンイベントハンドラは、引数の名前と使っている名前が違っている。
テンプレートに過ぎないとは言え、やはり自分用のテンプレートを一度作った方がよいだろう。

 

なお、「make」のデフォルト動作は「clean release」である。
gdbでソースデバッグするなら「make debug」とやる。

 

eclipse

今までテキストエディタとコマンドプロンプトでやっていたので、デバッグのためeclipseを使う。
nRF51822で使うための環境設定は省略だ(参考)。

 

デバッグ

まず、ble_advdata_set()でエラーが返ってきた。
値は7で、NRF_ERROR_INVALID_PARAM(nrf_error.h)。
adv_data_encode()でscanrspの方をさばいているときにエラーを出している。
うーむ・・・。

ん、UUID typeが0だな。。。
handlerもNULLになっている。。。
あ、services_init()がadvertising_init()より後で呼び出されてる!
テンプレートぉぉぉぉぉ!!!

順番を入れ替えると、無事Advertisingされました。
やれやれ・・・。

image

 

iPad miniからLightBlueで見ると、こんな感じだった。

image

うーん、0002はINPUTにしてるので、Writeになっててほしかったんだけど・・・。
ああ、char_mdの設定を間違えていた。

image

 

CONNECTまでは確認できたのだが、Writeができてない。
BLEモニタにも出てないので、LightBlueが出していないのか、モニタの設定を間違えたか。。。
ここは、後日だ。

0 件のコメント:

コメントを投稿

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