以前、こんな記事を書いた。
hiro99ma blog: [ble]ATT_MTU
S110のGATT ServerではATTのMTUサイズが23byteになっている、というお話だ。
ATTとしては510byteとか512byteとかまでいけるのだけど、MTUは23byte。
Opecodeが1byteで、残りが22byte。
Authentication Signatureというデータがあるときはそれが12byte固定だから、残りが10bytre。
ない場合は22byteが使える。
(あってるかなぁ)
2byteのWriteをしたときのパケットは、こうだった。
残り22byteのうち、AttHandleが2byte取っているので、書込みに使えるデータは20byteになる。
20byteまでは1回で済むから速いけど、それを過ぎると簡単にいかない。
Readの場合、nRF51822にデータを書き込んでおくと、SoftDeviceとClientがうまいことやってくれるので、あまり気にしなくて良い。
が、Writeはそうは行かないようだ。
Core_V4.2.pdfの、[Vol 3, Part G]"4.9 Characteristic Value Write"が書込む動作なのだが、1回で通信できないものは"4.9.4 Write Long Characteristic Values"か"4.9.5 Reliable Writes"のシーケンスになる。
どちらも、Clientが"Prepare Write Request"でデータを投げ、Serverが"Prepare Write Response"を返すようになっている。
データの終わりには、Execute Write Request/Responseを投げ合っておしまい。
これがnRF51822では、Queued Writesというシーケンスになっている。
GATT Serverのシーケンス一覧では、6つのシーケンスがある。
- GATTS Queued Writes: Stack handled, no attributes require authorization
- GATTS Queued Writes: Stack handled, one or more attributes require authorization
- GATTS Queued Writes: App handled, no attributes require authorization
- GATTS Queued Writes: App handled, one or more attributes require authorization
- GATTS Queued Writes: Prepare Queue Full
- GATTS Queued Writes: Execute Write without Prepare Write
下2つはちょっと毛色が違うので、省こう。
ハンドルするのがStackかAppか、認証があるかないかで分かれている。
ハンドルがStackかAppかというのは、データを持っているのがSoftDeviceかAppかということのようだ。
いつもは値がどう管理されているかを気にしていなかったのだが、Characteristicを追加するときには「vloc」という設定でできるようだ。
設定値はBLE_GATTS_VLOC_STACKとかBLE_GATTS_VLOC_USERとかで、BDSを使っているとデフォルトではStackが選択されている(is_value_userが0になるので)。
ただ、Queued Writsでは、sd_ble_user_mem_reply()というAPIでの返し方を指すみたい。
ここでNULLを渡すシーケンスが、Appハンドルとなっている。
じゃあStackハンドルの場合はどうかというと、メモリをアプリ側で用意してアドレスを渡してやってる。
つまり、ATT用のメモリとは別に、キューに貯めるメモリがいるということなのか。
だったら先ほどのvlocとは関係がないな。
以下、Stackハンドルだけ見ていく。
まず、ClientからPrepare Write Requestが来る。
そしたら、イベントとしてBLE_EVT_USER_MEM_REQUESTが通知される。
SoftDeviceとしては、ユーザのメモリが必要なイベントの1つと見なされているのだろう。
ここでsd_ble_user_mem_reply()でメモリを返すと、ClientにPrepare Write Responseを返す。
それ以降の流れがシーケンスを見てもよくわからない。
Responseを返した次にやってくるのが、handle_2のPrepare Write Requestだからだ。
同じhandleであれば考えやすいのだけど。。
Core_V4.2のWrite Long Characteristicシーケンスを見てみよう。
たぶん、引数の1番目がハンドル値、2番目がオフセット値、3番目がデータだろう。
となると、普通は同じハンドルに対して何度もReqとResが往復するはず。
でも、nRF51のシーケンスを見ると、オフセットは1回目と2回目で違っているようだ。
となると、Reliabile Writesのシーケンスの方が近いのかもしれない。
これはこれで、2番目の引数が0だ。
うーん・・・。
面倒なのでやってみると、こうなった。
ClientはNexus5で、アプリとしてNordicのnRF Master Controlとかいう名前のを使っている。
Opcodeの0x16がPrepare Request、0x17がPrepare Response、0x18がExecute Request、0x19がExecute Responseだ。
同じハンドルに対してRequestとResponseが繰り返されているし、オフセットも更新されている。
だから、Write Long Characteristicのシーケンスと同じになっているようだ。
(Core_V4.2シーケンスの引数は、パケットデータの順番そのままっぽい。)
まあ、これはこれでいいのかな?
さて、ちょっとした問題があるのだ。
最後のデータを投げたあとにExecute Write RequestをClientが送るのだが、そこにはハンドル値が載ってない。
だから、アプリとしてはどのCharacteristicの書き込みが終わったかわからないのだ。
もちろんNordicのシーケンスでも同じだ。
ClientからExecute Write Requestを受けとると、まずSoftDeviceが管理しているATTテーブルを更新し、それからBLE_GATTS_EVT_WRITEというイベントで通知している。
これは、通常の書き込みを行ったときと同じイベント名だ。
書き込みを行ったときのイベントは、BDSではどのCharacteristicのハンドル値と一致するかで比較して、処理を進めている。
だから、ハンドル値が無しにされると、誰から書き込まれたかがわからない。
ble_app_hrs---LongWrite
これはNordicのサンプルなのだけど、書込まれたときにどうこうする処理がないためか、特に何もしていないように見える。
そうやって考えると、Prepare Writeをしている間はどのCharacteristicに書込みをしてもよくて、好きなだけ書込み終わったらExecute Writeする、みたいな考え方なのだろうか。
DBのトランザクション中、みたいな。
まあ、ATTテーブルは更新してしまうからロールバックはできないのだが。
0 件のコメント:
コメントを投稿
コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。
注: コメントを投稿できるのは、このブログのメンバーだけです。