2015/05/17

汝の名はBlueZ - (11) : githubに置いた

BlueZ特集の最後だ。
初回が4月21日だから、1ヶ月近くやってたんだ・・・。

SensorTagに少し対応したgatttoolをgithubに置いた。
確認環境は、Raspberry Pi(2011.12) + BT-Micro4だ。
https://github.com/hirokuma/bluez-gatttool-sensortag

ソースはBlueZのattribフォルダがほぼそのまま。
違うのは、gatttool.cとinteractive.cくらい。
gatttool.cは、引数を打ち込むのが面倒なのでデフォルト値を付けただけ。
主にinteractive.cを変更している。

対応と言っても大したことはしておらず、以下を自動的にやるだけだ。

  • connect
  • primary
  • ボタンとIR TemperatureへのNotify許可
  • ボタンのNotificationを受けたら、ボタンがS2なのかS3なのかをコンソール出力

 

makeは、BlueZ 5.30で行っている。
BlueZのソースファイルからビルドしておかないと、makeできない。
というのも、BlueZのビルド時に生成される内部用ライブラリを2つほど使っているからだ。
makefile自体は、nRF51822で使われているものを流用した。
私はMakefile書くのが上手じゃないし、nRF51822のはクロスコンパイルもセルフコンパイルもどっちでもいけそうなので、テンプレートに仕立てたのだ。

makeすると、"gatttool"ではなく"gatool"ができる。
一応名前を分けておかないと、紛らわしいだろうと思ってね。
実行は、普通のgatttoolと同じようにできる。
引数を省略すると、gatttool.cのmain()に書いたデフォルト値が使われるので、書き換えると良いだろう。
インタラクティブモードで"st"というコマンドを追加していて、これを実行すると上記の自動でやることをやってくれる。

少々だったら、こんな感じで作っていけばCentral側として相手になってくれそうだ。
画面を出したいとか思ったら、やはりAndroidみたいなスマートフォンが手軽になりそうだけど、コンソールにいろいろ出せるとデバッグがしやすいので、これはこれでよいと思うのだよ。

汝の名はBlueZ - (10) : gatttoolをいじる

GLibのイベントループのことを調べたけど、gatttoolで状態を持ってるのは接続のことだけで、primaryとかの非同期コマンドの実行状態などは管理していない。
なので、これが終わったらこれしたい、というようなことをやりたいのであれば、実行状態を持つように書き換えがいる。
追加するのは、コマンドの開始時と、完了コールバックのところになるだろう。

それを全部に対してやるくらいだったら、コールバック関数のところに自分の関数を呼ぶように作り替えてしまえば、わざわざ何も無いときにポーリングするような作りにしなくても済みそうだ。
だが、処理は1箇所にまとめたいので、ポーリングするようにしようかとも思っている。


まずはサンプルとして、TIのSensorTag向けに作ってみよう。
SensorTagはいろいろとセンサーが付いているので、題材として使いやすい。

Characteristicを読んだり書いたりするだけだったら、別にコードを作り込まずともコマンドを入力すれば良い。
今回の目指すところは、こんなところにしよう。

  • ボタンを押したらNotifyを受け取り、押されたのがS2かS3かをコンソールに出力する
  • インタラクティブモードで起動し、開始コマンド(自作)を打ち込んだら、後は勝手にやってくれる

たったこれだけ?と思われそうだが、準備運動せずに海に飛び込んではだめなのだ。


connectやCharacteristicへの操作はコマンドを打ち込むが、Notificationはどうなってるのか知らなかった。
調べたところ、受けとったらコンソールに出力されるようだ。
SensorTagで試してみた。0x6CはボタンのNotificationを許可設定するCCCDで、01:00で許可になる。

[34:B1:F7:D4:FA:33][LE]> char-write-req 6c 0100
Characteristic value was written successfully
Notification handle = 0x006b value: 01
Notification handle = 0x006b value: 00

この辺を受け付けられるところがGLibを使った強みか。
受けとっているのは、interactive.cのevents_handler()。
コンソール出力からソースをgrepできるのがありがたい。

events_handler()は、connectが完了したコールバックで登録されている。
なので、connectの動作をgatttoolのルートでやってしまうなら、そのまま流用できる。
ただ、Characteristic操作と違って開始-終了がCentral側ではわからず通知だけになるので、ここはコールバック時に処理をした方がよいだろう。

2015/05/16

GLibのイベントループ

BlueZのgatttoolを変更して、Central側の動作確認ツールを作ろうとしている。
まずはconnectさせて、connectしたらprimaryを見るだけのところを作っているのだが、これがなかなかうまくいかない。

connect実行後に接続状態を監視するスレッドを作って、本体はjoinで待たせている。
しかし、接続状態が変化しないのだ。
Peripheralは接続が完了しているので、connect自体は動いている。
volatileつけてなかったから最適化されてしまったのかと思ったが、そもそもconnect完了のコールバックが呼ばれていない。

おそらく、joinで待たせているためGLibのイベントループが動かず、コールバックが呼ばれないのだと思っている。
なので、状態監視しているスレッドの中でイベントループを回す関数でも呼び出してやればよいと思うのだが、それに該当するAPIが見つけられていない。

軽く調べておこう。


GLibの本家資料は、たぶんここだろう。
GNUかGNOME。

今回やりたイベントループのことは、ここになるか。
https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#glib-The-Main-Event-Loop.description
イベントはファイルディスクリプタとタイムアウトとなっている。
Wikipediaの説明でもそう書いてあるので、そうなんだろう。

なので、今回やりたいような「この変数がこの値になったら」みたいなのはイベントにできなさそうだ。
そういうことをしたければ、自分でfdつくって値の変更タイミングで書込むイベントにするか、タイマで監視するようなしくみにするか・・・。

 

APIを見てると、g_idle_add()というのがあった。
優先度の高いイベントが無いときに呼ばれるそうだ。
試しにやってみると、確かにそういう動きをする。
何かイベントが発生し終わったときに呼ばれるのではなく、アイドル状態だったら呼び出され続けるようだ。
"add"なので、BlueZでアイドル状態を監視するようになっていたとしても追加できる。

g_idle_add()で気になったのは、g_idle_source_new()。
このAPIは呼び出していないのだが、g_idle_add()は使えた。
BlueZをgrepしても出てこないので、たぶん誰も呼び出していないのだが、それでよいのだろうか?

 

こちらを見て気付いたのだが、g_main_context_iteration()を呼ぶことでイベントループを1回だけ回せるようだ。
PyGTKで重い処理をしているときにGUIを固まらせないための手法をまとめる - 試験運用中なLinux備忘録


そういう視点でGLib Referenceを読むと、Descriptionにアイドルのこともシングルなイベントループ呼び出しのことも書いてあった。
英語を読む力が弱いのは課題だな・・・。

callとinvoke

英語の話になるが、callとinvokeの違いがよくわからない。
なんとなく、callだと直接呼び出し、invokeだと別コンテキストから呼び出すような感じを受ける。

invokeの意味 - 英和辞書 - 英語辞書 - goo辞書
たぶんラテン語が語源になってるのだろうが、「上に呼びかける」ということで、お祈りする意味があったようだ。
なので、OSとかフレームワークとかにお願いしておく、みたいなとらえ方で良いのかな。

2015/05/11

[bluez]gatttoolを切り離してビルド

直接BlueZとは関係が無いので、タイトルはいつものとは別にした。

pythonで書こうかと思っていたのだが、gatttoolのコンソール出力を使って判断する、というのがどうもしっくりこない。
ツールが出力するログが変わったら、ソースの修正が必要になるので、そうそう変わるものではないとわかってはいるのだが、やる気が出なくなってしまった。
お仕事だったらあきらめてやるのだけど、せっかく趣味でやってるだけなんだから、やりたくないことはやらないようにしよう。

gatttoolはinteractiveじゃないモードもあるから、それでやるならpythonでもよいかと思ったのだが、1回実行するごとにconnect→操作→disconnect、となるようなので、ちょっと期待と異なる。
connectしたら、しばらくはdisconnectしたくないのだ。
だからinteractiveモードを使うんだろうけど、今回はそれを使いたくない。
だったら、自分で作るしかない。

BlueZのtestディレクトリにpythonで書かれたものがあったが、あれはD-Busを使っているようだ。
D-Busがよくわかっていないから何とも言えないが、軽く調べた範囲ではC言語から使うのはめんどくさそうだ。
じゃあpythonでやろうかとも思ったのだが、Characteristicを操作するようなサンプルがない・・・。
ドキュメントにはgatt-api.txtというのがあり、API仕様っぽいものが書かれているのだけど、D-Busがわかっていないためか見てもさっぱりわからない。
ネットで検索しても、これを使っていそうなサンプルを見つけることが出来なかった。
「なら私が!」と思えれば良いのだけど、ちょっと厳しい。

じゃあ、やっぱりC言語でやるか。


どうせ個人用のツールなので、gatttoolを変更するだけで済ませようと思う。
まずは準備として、gatttoolをBlueZのmakeではなく自分のmakeで行えるようにする。

attribディレクトリを別の場所にコピーし、Makefileを自作して、makeするだけ。
Makefileを作るのに時間がかかったのと、gatt.cがbluetooth.hをうまくincludeしないようだったので明示的に書いた(書く場所を上の方にしておかないと、なぜか読み込んでいなかったようだ)。

ソースとbluetooth、readline、GLibのリンクだけで済むかと思ったら、BlueZビルド時にできるbluetool-internalとshared-glibもリンクしないといかんようだった。
できればBlueZをインストールするときにコピーされるライブラリだけで構成したいのだが、知識が足りないので今回はこのままにしておこう。