2015/09/29

[nrf52]NFCのところを見ていく (2)

nRF52832ですが、今回もBLEは出てこない。
いいのだろうか、これで・・・。
ともかく、前回に続き、NFCを見ていく。


とっかかりはここだろう。
Experimental: NFC URL Record Application
NDEFのURIレコードを実現するアプリだ。

main.cだけしかなく、コメントを含めても100行程度だ。
呼んでいる関数は、

  • nfcSetup()
  • nfc_uri_msg_create()
  • nfcSetPayload()
  • nfcStartEmulation()

だけ。

そしてですな、nfc_uri_msg_create()以外はライブラリになっていて、SDKにしては珍しくlibファイルでの提供になっているのだ。
普段はだいたいソースが置いてあって、ないのはSoftDeviceのソースくらい(擬似コード?は置いてあるが)なので、珍しいのだ。
stringsコマンドで見てみるとarmccとか出てきたのだが、GCCでも使えるのかな?
でも、その近所にあるhal_nfc.cはソースがある。
状況からすると、nfc_lib.libにある処理がhal_nfc.cの処理を呼び、hal_nfc.cがハードをたたいているのだろう。
まあ、細かいことはいいや。

ただ、hal_nfc.cに定義してある値が、Type 2 Tagのブロック0~2までの内容になっているようだ。
つまり、NFCID1に相当する部分ですな。
ようなのだけど、ソースコメントは「TODO」って書いてあり、FICRからNFCIDを作るんですよ、みたいに書いてあるので、実はPreview DKだからソースと同じ値のFICRが書込まれているだけかもしれん。
じゃないと・・・ちょっとね・・・・。

でも、FICRの説明を読むと「Default header for NFC Tag」としか書かれていない。
なので、デフォルトではない値を使ってもよい、というように読めるが、それでよいのかな?
なんとなく、NFC-AはNFCIDを動的に更新させたり、更新できても全桁はさせないだろうと思い込んでいたのだ。
まあ、FeliCaのIDmと同じようなものだから、それを気にするようなシステムを作っちゃいかんのだが。


唯一、読んで意味がわかりやすいnfc_uri_msg_create()を見ておこう。

staticで25byte(ヘッダ4byte + メッセージ長1byte + メッセージ20byte)確保し、引数で与えられたデータをNDEFのURI形式にしていくだけだ。
つまり、単にNDEFのデータを作るだけの関数なので、自分でやってしまってもよい。
自分のstaticで確保したバッファアドレスを上位に渡すのはあまり行儀よくないが、まあ、全体的にお試しで作った、という感じがするから気にしても仕方なかろう。

まだこの辺の整備をどうするか、決めかねてるのですかね。


で、これだけ見るとデータ長も少ないのだが、実際に動作させて読込むと、256ブロック×4byteで1KBも読めることになる。
これが時間かかるんだな・・・。
もともとNFC-Aは106kbpsと遅いので、処理がもたついているのか通信が遅いのかはわからないが、3~4秒くらいかかっているようだ(心の中で数えたので、かなり曖昧だが)。

どこで制御するのか知らないが、製品になるときには全長の指定もできるようになってほしいものだ。

2015/09/28

[nrf52]NFCのところを見ていく (1)

nRF52832ですが、BLEは出てきません。
今回は、NFCのところを見ていきます。
うちのサイトの特徴はNFCのことが比較的多いことだろうと思ってるので、たまにはアピールしておかないと。


NFCT — Near field communication tag
これが、今のところ見えているnRF52832のNFC仕様だ。

NFC-Aで、NXP TagInfoアプリで読んだときにはType 2 Tagと出ていた。
タグのメモリは大きく、4byteのブロックが256分あった。


で、ここの説明がかなり長い。
図表を見るだけでも、かなり詳しく説明をしてくれている。

が、今回はそこまで深入りしない。
MIFARE Ultralightなどと同じType 2 Tagだ、というだけで満足だ。

しかし、NFC-Aというだけなら、MIFARE DESFireみたいなType 4A Tagでもよいし、TopazみたいなType 1 Tagでもできると思う。
その中でType 2 Tagたらしめているのは、やはりフレームデータのフォーマットとか交換方法だとかだろうか。
あまりその違いはわかっていなかった・・・。

無念な気もするが、調べていけば何かわかる日も来るだろう。
今日はこれくらいにして、次回はアプリを見ていこう。

2015/09/27

[ai]認識が甘かった

人工知能ってのがなんだかよくわからないけど、とりあえず動かしてみてから考えよう。

と思ったが、非常に甘かった。
なんか、人工知能にもいろいろあるようだ。
正直なところ「しまった」と思ったが、調べると書いてしまったので、せめて9月の間くらいは調べよう。


検索して、上の方にあったのがこちら。

What's AI

 

いつ頃のサイトかわからないけど、2015年の本が紹介されているので今年にも更新はされているようだ。
(リンク先を画像にしているのは、トップページにURLと画像が紹介されていたからで、他意はない。)

分野がいろいろとあることはわかったが、研究がしたいわけじゃなくて、使ってみたいだけだ。
研究をないがしろにするつもりはないけど、私はそっち側ではないのだ。


では、実装できそうなもの、ということで探したのだが、これはこれでなんかよくわからん。
「ディープラーニング(deep learning)」というのがよく出てくるので、そっちで調べることにした。

ぼくの実装した最弱のディープラーニング - きしだのはてな

本を読んで実装できるらしい。
ほかにも、なんやらかんやらと見つかったのだが、とりあえずわかったのは「学習に使うデータがいる」ということだ。
学習に使えるデータが足りない分を人工知能で補おうなどと思っていたのだが・・・。
そもそも、ディープラーニングってのは、データを使って学習させる、というやり方のようだ。
そりゃ、データがいるわな。

 

そういうわけで、あまりネットにデータが無さそうなものをやった方が新鮮みがあってよさそうだけど、それには自分でデータをある程度使えるようになるまではためないといかんよ、という結論に至った。
こういうのは、個人向けじゃなくて、企業みたいなお金と信頼があるところのやることだろうな。

 

ディープラーニング - Wikipedia
あまりまじめに読んではないが、「データを食べさせていけば、評価式を自動的にうまいことやってくれる」みたい。
たとえば、パラメータがx, y, zの3つだったとして、「x+y+z=1という条件で(x+y) / zが最大になる」という式と「x+y+z=1という条件で(2x + y) / zが最大になる」という式では、x, y, zの重みが変わってくる。
で、画像だったら、丸っこいだとか、細長いとか、そんな特徴もあるだろうし、鼻が赤いとか、耳が大きいとか、そんな特徴もあるだろうから、これを式にしようと思うとなかなか難しい。
「この式を満たせばよい」という黄金の式があればいいんだろうけど、ではそれをどうやって求めたらいいのか、という問題が出てきて、そっちの方が大変だったりする。

なので、今回思いついた臭いがどうのこうのは、どっちかというと式が求まりやすいタイプだと思う。
臭いの成分ってのがなんなのか知らないけど、1次元のデータが複数ある感じじゃなかろうか。
推測するだけではあんまりなので、少し検索した。
におい成分の閾値|化学・工業製品・原材料・化粧品|住化分析センター

うん、人工知能についてだけじゃなく、臭いってものについても認識が甘かったですな。
むかし課長が「飛行機の操縦ができない人に飛行機のプログラムを書かせられるか?」というような話をされてた。
興味を持つのはよいけど、ちゃんとやるなら、ちゃんと取り組まないといかんですな。

臭いのことは諦めるとして、人工知能のことはもう少し調べますかね。

そこで人工知能

前の記事で、消臭剤うんぬんと書いた。
まあ、これは今のうちの玄関がキッチンと近いため、なにか料理するとすぐ玄関に臭いがこもってしまう現状を表している。

では、たとえばここに臭いセンサーを付けたとしよう。
どういうことができるのかは知らないけど、ガスクラマトグラフィーみたいな分析はできないと思うので、どちらかというと「○○センサー」みたいな特化したものしかないのではなかろうか。
でも、そういうのはひとまず置いておこう。
すごい技術で臭いを分析するセンサーができたり、複数のセンサーを組み合わせて臭いがありそうな成分だけ分析することができたとする。

では、その次どうしたらよいか。
まず、センサーの部分は安価にしたいので、臭いを分析してどこかのサーバにアップする、くらいのことしかしない。
センサーがすごく賢くて解析までできるならいいけど、値段が高くなりそうだし、場所も食う。

そのデータをどこにアップするかというと、話の流れからするとクラウド上に置くのがよいのかな。
うちだと、私がいる間はだいたいパソコンがついてるのだが、外出しているときは全部電源を落としている。
まあ、WiFiルータくらいは電源を付けてもよいかな。
そうなると、センサーの部分にはWiFi通信の機能がある方がよい。
でも、最近はIoTルータみたいなものもあるようだし、Raspberry Piみたいな小さい人にBLE持たせて、そこで通知してもらうのもよいだろう。センサーの電力消費が抑えられそうだし。
データを集めるRaspberry Piがデータ解析してもよいけど、ちょっとパワー不足か。
それに、クラウド上にデータを置くんだったら、全部クラウド上で解決した方が楽な気もする。
BLEと直接つなげられるルータもありそうだし。

とにかく、そうやっていくと、あとはクラウド上で解析する方法と、解析した結果を通知する方法が残る。
解析した結果は、まあスマホに送るなり、なんかメールで通知するなり、なんでもいいや。
問題は解析する方法だろう。
ぱっと考えて「臭いのデータを送ったんでよろしく!」というのを、どうやってさばいていいのかわからない。
言葉だったら検索させてしまうというのもいいのだろうけど、臭いなんて数字のパラメータでしかない。
あるのは、その数字をどう評価するか、という手法だけだ。

いくらインターネットで検索が便利になったとしても、どう検索してよいかわからないのであれば使いようが無い。
そうなると、自分で情報を作るしかないのではなかろうか。
一人で情報を作ってもたかが知れているので、フォーマットを公開して、いろんな人にそのフォーマットでデータをアップしてもらって、辞書を作る。
今回やりたいのは「臭いの情報から適した消臭剤を紹介する」なので、インプットのデータは臭いのパラメータで、アウトプットのデータは消臭剤の名前、ということになろうか。

センサーを無料で配るなどして、うまいことやれば、比較的データは集められるかもしれない。
でも、単純な力業だけでは足りないような気もする。


そこで、足りないところは人工知能で埋める、というのはどうかな、と思った。
いや、人工知能ってのがどんなもんかわからんけど、学生の頃にニューラルネットとかを課題でやったときは「ほほー」と思うくらいには面白かった記憶がある。
あれから何十年も経っているので、技術はかなり進んだんじゃなかろうか。

そんなわけで、10月はあまり忙しくない予定だから、最近の人工知能のことで私にもわかりそうなものを調べてみよう。
ほら、BLEとかNFCだけだと、どうしても頭がそっち側だけになってしまっていかんのでね。

IoTとは何だろうか

急に、IoTってなんだろう?という疑問が出てきた。
NFCとかBLEとか調べているけど、私としてはIoTは意識していない。
このブログで、インターネットのことが出てきたことはほとんどないんじゃないかね。

とはいえ、用語としてはよく聞くので、自分なりに考えておこう。


何の略か

Internet of Things。
「モノのインターネット」と呼ぶようだ。

で?

インターネットは、いまのインターネットでよいとしよう。
では「モノ」は?
いまのところインターネットにつながっていないものを「モノ」と呼んでいるようだ。

うちには、タブレットとかパソコンがあるけど、それはインターネットにつながっているから「モノ」じゃないのだろう。
そういうのと縁がなさそうな、扇風機とか電気ポットとか、あるいは枕とか本棚とか。
カーテンレールとか筆立てとか、そういうのを「モノ」と呼ぶような感じがする。

 

モノがインターネットにつながってどうするのか?

どうするのがいいのだろうね。

たとえば、扇風機に何かつけて、インターネットに接続したとしよう。
ネット経由で電源の入り切りができたとしても、使わないだろう。
風量の調節だってそうだ。
だから「既存の機能がネットで使えるようにする」というのは、たぶんIoTの目指すものとは違うような気がする。

「部屋の温度を監視したりエアコンの動作とかを判断して、扇風機を入り切りする」だとそれっぽい。
が、そういうのは別にインターネットにつながってなくても、宅内で集中管理するしくみがあればできるだろう。
わざわざインターネットにする必然性がなさそうだ。
まあ、新しくネットワークを作るよりはインターネットの方が楽、ということはあるかもしれない。

私にはちょっと思いつかないのだが、この辺がアイデアというか、売り物になっていくことなのだろう。
あまり技術に入れ込んでない人の方が得意かもしれない。
特に私のように、あまり出歩かなかったり、生活に不便を感じない人には思いつきにくい気がする。

ただ、この距離感というのは場合によりけりだろう。
私はまだいろいろ歩けるので、買いものに行ったり、玄関を開け閉めしたりしても苦にならない。
これが、腰を痛めるなどして歩けなくなってしまうと、玄関がものすごく遠くなってしまうだろう。
そうなると、また新しい見え方をするんじゃなかろうか。
つまりまあ、想像力ってことでしょうかね。

 

まとめ

もう、まとめになってしまった。   

  • IoTは、あまりインターネットと縁がなさそうなものをインターネットにつなげること。
  • せっかくインターネットにつなぐなら、その意味がないとIoTと呼んでもらえなさそう。
  • 行動範囲が狭い人には、あまり縁がないかも。
  • 想像力が大切だろう。


うん、あまりよくわからなかったよ。
私があまりスマートフォンとか使わないし、そもそも外出しないし、そういうのがピンとこない原因かもしれん。

なんとなく、

  • 何もないモノだから、センサーつけないと情報が取れない!
    • センサー部品を売りこめ~
  • インターネットにつながってないから、ネットにつなげる手段用意しないと!
    • 無線でつなぐ部品を売りこめ~
  • インターネットといえば、やっぱりクラウドでしょう!
    • クラウドを売りこめ~

みたいな感じがしてしまった。
まあ、検索で出てきた上位の方だから、そういう売り込み系が目立っただけかもしれんが。

 

そうねぇ、私が期待するようなのは、

  • 近所の人で朝出勤する人のうち、何%がコートを着始めたかによって「そろそろコートを着てもいいよ」と思わせてくれるデバイス
    • 自分一人が先に着ると、負けた気がするから
    • まあ、寒ければ着るだけなので、ちょっと知りたいかな、という程度
  • その逆で、春先になってコートを着なくなった人の割合もわかるとか
  • 玄関に空気の成分を分析するようなデバイスで、臭いが変だったら「この臭いにはこんな消臭剤がよさそうですよ」とか言ってくれる。
    • 消臭メーカーとつるんでいそうな感じがするが、そこはうまいことやってくれる

みたいな、生活に近いところかな。
流行に乗ったりはしないけど、どういう候補があるのかよくわからないような、つまり「詳しくない分野」に食い込んでくれるとうれしいのだ。
それがIoTなのかどうかってのはよくわからんが、ローカルで解決しにくいことに応用しないとインターネットを使ってもねぇってなりそうなので。
例に挙げた消臭剤だって、別に消臭剤がなくなってきたかどうかなどは知りたくないのだ。

自分にとって詳しくない分野はたくさんあるのだけど、それに気付かせてくれるようなモノがいいんじゃなかろうか。

2015/09/26

[nrf51]Bluetooth Developer Studioサンプル

nRF51822用のBluetooth Developer Studioサンプルをgithubに置いた。
https://github.com/hirokuma/nrf51822_bds_sample

内容は、以前作っていたサンプルと同じようなもののはず。
https://github.com/hirokuma/nrf51822_templete_v810

Bluetooth Developer Studio(以下、BDS)はまだβ版で、いま私が使っているのは1.0.1531.0。
今後変わるかもしれないけど、まあ、そういうの気にしてても仕方がない。

ビルドするときは、Nordicサイトから対応したSDKを取ってこよう。
今は、bdsplugin1.1.6__nRF51SDK_9.0.2-BDS.zip、という名前なので、たぶんSDK v9.0.2相当なのだろう。
単独のSDK最新はv9.0.1だが、そこにBDS用の修正が入っているだけのようだ。
https://developer.nordicsemi.com/nRF51_bluetooth_development_studio_plugin/

 

ブログを見ていたら、Nordicからツールの紹介がされていた。
https://devzone.nordicsemi.com/blogs/765/new-bluetooth-smart-tool-from-nordic/
今日は二日酔いで読む気がしないので、また後日。

2015/09/23

abstractとsummaryの違い

Bluetooth Developer Studioでは、説明欄がいくつもある。
ソースには現れないと思うので、メモ書きだったり、設定ファイル一式を公開したときに読んでもらえるような内容を書いておくのがよいのだろう。
まあ、個人でやっている分にはそれほど気を遣うつもりはないのだが。

さて、その欄はこんな感じ。

image

"disclaimer"は、断り書きとか、免責条項とか、そんな意味らしい。
個人だと「変更してもいいけど責任は持たんよ」みたいなことを書けばよいのかな。

さて、問題は次だ。
"summary"と"abstract"。
どちらも「概要」みたいな意味だと思っていたのだが、枠が違うということは意味も違うのだろう。

検索したら、さらっと出てくるかと思ったのだが、そういうものでもなかった。
summary abstract - Google 検索

"executive summary"という表現も出ていて、そっちは言いたいことを整理したもの、みたいな感じか。
じゃあabstractは整理しないで書けばよいか、ということでもないみたいで、よくわからない。

ネットで検索すると、学術論文のようなものの話が多い。
これもBDSもdoxygenみたいに「brief」と「detail」くらいだったら気にもしなかったのだが。。。
だいたい、abstractは抽象化とかじゃなかったか(JavaかC#かわからんが)。

なので、abstractは中身に踏み込むよりも「こういう用途を意図してます」みたいなことを書いて、summaryは「これはこんなことするよ」みたいな概略を書こうかね。

[bds]Full UUIDは出力できた

昨日、Bluetooth Developer StudioでnRF51のソースを出力させたが、Base UUIDが128bitになってなかった。
http://hiro99ma.blogspot.com/2015/09/bluetooth-developer-studio-beta-program.html

ソース生成はプラグインの役目なので、ちょっとだけ眺めてみた。
そうすると「if (!_.isNull(FullUuid))」みたいなコードが入っていて、Full UUIDを出力する準備はできていそうなのだ。

<%=~%>の区間を変数で置換しているのは、なんとなくわかる。
<%~%>の区間は、たぶん中の式を評価して、エラーが発生しないとか、条件が真の時とかで出力するんじゃなかろうか。
Full UUIDは、<%~%>の区間だから、後者。
じゃあ、使い方が間違っていたというだけかな?


サービスを編集するときの、これ。

image

FULLにチェックすると、こうなる。

image

これが、上記のFullUuidに相当しそうだ。

が、ここからがちょっとよくわからない動きをしていて・・・。

まず、UUIDの一番右に「-」がついている。
これは、最初からそうだったのか、私が間違って入力してしまったのかが今となってはわからない。
とりあえず邪魔だから、削除してSAVE。

image

もう一度開いて、FULLのチェックを外すと、また0x3B86になる。
なるのだが、SAVEボタンが有効にならない。
とりあえず何か変更させよう、と、「TRANSPORT CONFIGURATIONS」のLOW ENERGYにチェックして(意味はわからんが)、SAVEが有効になったので押す。
んで、LOW ENERGYのチェックをはずしてSAVE、とすると、なぜかUUIDが変わる・・・。
何度かやってみたけど、チェックを外したときにUUIDが変わるようだ。

その状態でコード生成すると、Base UUIDが入っていた。
一件落着。。。

うーん、あまり納得がいかない動きだ。
このFULLチェックボックスにチェックが入っていようといまいと、コード生成したときにはBase UUIDが入るようになったからだ。
つまり、このチェックボックスが原因ではなく、おそらく末尾に「-」がついていた方が問題だったんじゃなかろうか。

ならば打ち間違えが原因か、となりそうだが、やってみても文字数制限のためか、手で「-」を打てなかった。
ということはツールが出力したということなのか。
しかし、別のServiceを作成しても、新たにProfileを作成しても、同じことが起きなかった。
うーーーーーん・・・・

納得は行かんが、正しいUUIDが書いてあれば反映される、ということにしておこう。
バグ報告するチャンスだったんだが、残念だ。

 

Base UUIDまで出力できたときのプロジェクトを置いた。
といいつつ、ビルドまでで動作確認はしてないのだけどね。
https://sites.google.com/site/hiro99ma/ble/files/bds_test.zip?attredirects=0&d=1


もう一度プロジェクトを作ったら、また同じことが起きた。
英語は苦手ではあるが、せっかくなのでIssueとして挙げてみた。
が、ログインしないと読めないみたい・・・。

image

こんな感じ。
変な英語かもしれないが、伝わってくれればいいなー、くらいの気持ちで書いた。

いま、Bluetooth SIGからメールが来てビクッとしたが、単なる自動メールだった。
やりとりが発生すると英語についていけない気がするから、するっと終わってくれんかのう。

2015/09/22

Bluetooth Developer Studio Beta Programを試す

Bluetoothのツールとして気になっていたものに、Bluetooth Developer Studioがある。
なんなのかはわからないけど、アプリを作ったりデバッグするのによさそうなことが書いてある。

以前、ダウンロードしてみようとDeveloper Potalにサインインはしたのだが、どうもそのアカウントでは無理そうだった。
なんとなく、SIGのアカウントがいるようなんだけど、たぶん有料だろうし、企業じゃないと取れない気がする。

そのまま忘れていたのだが、nRF51 SDK v9.0.0のリリースノートに、実験的にBluetooth Developer Studioのテンプレートを付けてみた、とある。
もしかすると、Bluetooth SIGはもっと気楽なものなのか?と思ってサイトを見ると、It's free!と書いてあった。
間違ってたらあやまろう、という精神で、SIGへの入会はさておき、Beta Programへの参加をしてみることにした。


Bluetooth Developer Studioのページに行くと、右側に入力欄があった。
適当に打ち込むと、メールアドレス宛にダウンロード先が書かれているメールが来た。

インストーラは、こんな感じ。
なお、今回はBluetooth-Developer-Studio-Beta-2r1531.zipという名前のファイルだった。

image

インストールは、ウィザードを進めていくだけで終わる。
アプリを起動すると、こんな画面が出てきた。

image

よくわからんので、Nordicの説明通りにやっていこう。
と思ったのだが、いきなり詰まる。
プロファイルの作成をするように書かれているのだが、メニューにはプロジェクトの作成しかない。
とりあえず進めていこう。

image

OKすると、画面に「New Profile >」と出てきた。
それをクリックすると、プロファイルの作成画面になる。
プロジェクト名は「TestProject」にすればよかった、と思いつつも、次にいこう。

image

image

これでYesとすると、プロファイル名がさっき入力したものになった。
ここまでで、nRF51サイトの1番目だ。

image

次に、サービス。
右上にあるCUSTOM SERVICEを押すと、こんな画面が出てくる。

image

サービス名の変更はよいとして、NordicサイトではCharacteristicの作成になっているけど「+」ボタンがない。
わからんので、SAVEしてみよう。

image

ここで、「+」じゃなくて、その左にあるボタンを押すと、こういうのが出てきた。
(この長い名前はCharacteristic名で、「+」を押すと新しいCharacteristicのボタンが増える。)

image

 

左下のADD FIELDSを押すと、こういう感じ。
名前はvalueで、FORMATは_VARIABLE。
image

プロファイル作成の手順3まで終わると、こう。

image

手順4まで終わらせると、CharacteristicにRXとTXが加わる。
image

で、ここまでやってからコード生成をするようなのだが、プラグインの設定がいるようだ。
説明では「NORDIC SEMICONDUCTOR NRF51 PLUGIN」を選ぶように書かれているけど、出てこないので。

image


プラグインは、こちら。
https://developer.nordicsemi.com/nRF51_bluetooth_development_studio_plugin/

解凍して、こんな感じでコピーすると、
image

さっきの選択肢が増えた。
image

生成すると、こういうダイアログが出てきた。
image

生成されたのは、これら。
image

プロジェクトは、TestProfile.bds。
フォルダは管理用っぽい。
生成したソースは、ここに見えている4ファイルだけのようだ。


Bluetooth Developer Studioの役目はここまで。
あとは、生成されたソースを編集するなりして組み込むだけだ。

プラグインがnRF51で使えるようなコードを生成しているので、テンプレートに組み込んでやればいけそうな気がする。
以前、入出力するだけのサービスを作って、あとは必要に応じて書き換えればいいようにしていたのだが、Bluetooth Developer Studioがあれば手で書き換えるところがかなり少なくて済みそうだ。
サービスが直接ドライバなどをたたいたりしないように作りさえすれば、ほとんど修正無しでいいんじゃなかろうか。

BLEで何か作りたいときは、「サービス作りたい」とか「キャラクタリスティック作りたい」がメインじゃなくて、それよりも上側にある機能を作る方がメインだ。
だから、多少コードが大きくなったとしても自動生成してくれるのはありがたい。


nRF51822に組み込んで、動かしてみた。
nRF51 SDK v9って、nRF51422のMakefileしかないので、変更が面倒なのよね。。

こんな感じのサービスが見えた。
image

追加したCharacteristicは2つだし、Propertiesも指示通りになっている。
CCCDはStudioで追加してないのだけど、自動的に有効になっているようだ。

が、だ。
UUIDが納得いってない。
0xB5E3と0x3453はひっついているし、サービスも0x3B36ということでついているのだが、Test Profileを作ったときのBASE UUID「4465d45c-f065-4d99-b2de-5ff339d3d9c2」がどこにもないのだ。
どこに消えたのだ?

前作ったソースを見ると、BASE UUIDはsd_ble_uuid_vs_add()を使って指定するようだ。
しかし、自動生成されたソースにはない。
そもそも、BASE UUIDの値自体が入っていないように見える。
ただ、BLE_UUID_BLE_ASSIGN()を使って16bitのUUIDは指定している。
これはプラグインがうまいこと生成できてないのか、あるいは何か私が考え違いしてるのか。。。

あとですな、Bluetooth Developer Studioのプラグインをダウンロードすると、nRF51 SDKも一式入っているのだが、これはBDS用にカスタマイズされているようなのだ。
確かに、includeが足りないとかでコンパイルエラーが発生して、私も手で修正していたのだ。
なので、BDSを使うなら、SDKごと使い分けるのがおすすめだと思う。

 

2015/09/23
UUIDが出力できたので、一式置きました。
GCCでビルドしたので、そのセットしか入れてないです。
https://sites.google.com/site/hiro99ma/ble/files/bds_test.zip?attredirects=0&d=1

[nrf51/52]Device Managerを少しだけ見る

nRF51やnRF52のSDKには、BLEアプリを作るテンプレートプロジェクトが入っている。
BLE周りの初期設定して、Advertisingして、イベントが発生したらハンドラを呼んで、というような一通りのものが入っている。
サービスは標準のものを組み込むなり、自分で作るなりしないといけないし、I2Cみたいな周辺機能を使いたいなら初期化の追加が必要が、それ以外のものはだいたい入っているように思う。

私もnRF51822を購入してテンプレートの作りを調べていたのだが、SDKのバージョンが上がり、作りが変わったようだ。
SoftDeviceのバージョンが上がったためか、Coreの規格バージョンが上がったためかはわからんけど、nRF51822ではSDK v9から変わっている。

ざっと見たところだが、Device Managerというモジュールと、Advertisingのモジュールが増えているのかな。
セキュリティ関係の設定がDevice Managerに入っているようだ。
せっかくペアリングとかして端末と紐付けるのなら、暗号化はしたいところだ。

そういうわけで、Device Managerを見ておこう。


Device ManagerはnRF51もnRF52もあまり変わらないようなので、nRF51で見ていこう。

http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk51.v9.0.0%2Flib_device_manager.html&cp=4_1_0_3_1_2

準備は、dm_init()とdm_register()だけ。
dm_register()でハンドル値を取得している(URL先に載っているサンプルはパラメータが1つになっているが、実際は2つ)。

で、そのハンドルを何に使うのかというと・・・?
DFUのときに使っているくらいか。

イベントが呼ばれたときにDevice Managerのハンドラを呼んでやる、くらいのことしかしていないようだ。
これでペアリングなどもうまいことやってくれているようである。

あとは、実際に使ってみた方が、理解するのは早そうだ。
サービスを作るところだけやってやればいいんだけど、ちょっと気力がいりますな。

2015/09/19

[nrf52][nfc]NFCペアリング問題はこれで終わろう

nRF52832のNFCペアリングサンプルを動かしてUARTログを見ると、こういうアドレスを探しに行っていた。

[DM]: Searching for device 0x87 0x6B 0xE7 0xDA 0x8E 0x68.

このアドレスは、ペアリングしようとしているNexus5のアドレスではなかった。
それに、毎回変わっているのだ。
そして比較しているアドレスは、保持しているNexus5のアドレスのようなのだ。
それは探してもペアリングに失敗するわな・・・。

では、このアドレスはどこから湧いて出たものだろうか?
スニファで見ていると、これのようだった。

image

CONNECT_REQは、仕様書Core_V4.2 Vol.6 Part B 2.3.3.1 CONNECT_REQだろう。
そのInitAのアドレス値をPreview DKは探そうとしている。
しかし、Preview DKが保持しているのは、Nexus5のアドレスとNexus5で表示されている値のようだ。

こんがらがってきたので、少し整理しよう。

AdvAは、ランダムではなく、毎回同じ値だ。
InitAは、ランダムな値だ(ルール性の有無ではなく、とりあえず毎回違う値になっているように見えるという意味で)。
そして、AdvAはNexus5のアドレスと呼んでいる値とは異なる。

まず、Preview DKはどこからNexus5のアドレスを取得したのか?
CONNECT_REQにもAdvertisingにも出てこないから、どっちかだろう。

  • 初回にNexus5とPairingできたときには取得できた
  • NFC経由で取得できた

なぜなら、Advertisingのアドレス(AdvA)は固定だし、InitAはランダムだからだ(そのまんまな理由ですが・・・)。
といいつつも、後者しかないだろうな。

・・・・・えっ、NDEFのデータにはAdvAしか出てないの??
NDEFタグから読み込んだ内容を保持していると思い込んでいただけに、私としてショックだ。


「InitAってなんじゃ?」というQAがあった。
what is initiator address in CONNECT_REQ? - Nordic Developer Zone

忘れていたが、BLE(だけかどうか知らんが)のアドレスにはpublicとrandomがあるのだよ、というお話だ。
上のパケットを見ると、RxAddもTxAddも1なので、AdvertisingアドレスもInitiatorアドレスもランダムなのだ。
(今さら気づいたが、AddってアドレスのAddなのね。付加情報のaddかと思ってた。)
CONNECT_REQはMaster側のデバイスが送信するので「このAdvAアドレスに対して、私InitAアドレスが接続要求しますぞ」という意味になろう。

 

NDEFには、AdvAのアドレスしか載っていなかった(Random Address)。
Randomといっても毎回同じ値なので、staticなのだろう・・・か?
hiro99ma blog: [ble]Device Addressのpublicとrandom
下位2bitでわかるとのこと。
randomアドレスの特徴は、これ。

  • staticアドレス
    • 下位2bitが1
    • 残りのビットが全部0じゃないこと(0x000000000003はダメ)
    • 残りのビットが全部1じゃないこと(0xFFFFFFFFFFFFはダメ)
  • privateアドレス(non-resolvable)
    • 下位2bitが0
    • 残りのビットが全部0じゃないこと(0x000000000000はダメ)
    • 残りのビットが全部1じゃないこと(0xFFFFFFFFFFFCはダメ)
  • privateアドレス(resolvable)
    • 下位2bitがb10
    • 残りのビットが全部0じゃないこと(0x000000000002はダメ)
    • 残りのビットが全部1じゃないこと(0xFFFFFFFFFFFEはダメ)

AdvAは、0xCD256AB87D9Aだから・・・下位2bitは10じゃないか。
ということで、これはresolvableなprivateアドレスということになる。

なにが「resolvable」かっていうと、なんか、Local Identity Resolving Key(IRK)とかPeer Identity Resolving Key(IRK)とかいう言葉が出てきたので、Pairingとも関係しそうだ(どっちも略がIRKなのはどうなんだ、とは思うが)。
ただ単なるでたらめな数字じゃなくて、Hash式が書いてあるから、それなりに認証するときに解決できるような値なのだろう。そこが「resolvable」たるゆえんか。

難しいことはさておき、CONNECT_REQではアドレスがランダムでもよさそうなことはわかった。
でもまあ、念のため原典を当たっておこう。

Core_V4.2の[Vol 6, Part B] 2.3.3.1 CONNECT_REQだ。
InitAは、TxAddが0ならpublic、1ならrandomということだ。


念のためにAD-BTSSP v1.1を読むと、NDEFには「3.3.1 LE Bluetooth Device Address」を持つことになっている。
ここには、Randomアドレスであれば[Vol 3, Part C]10.8を参照することになっていた。

10.8に書いてあることは、上の方に書いたアドレスの種類だけでなく、bonded deviceについても少し書いてあった。
resolvable privateアドレスを使う、ということのようなので、さっきの推測は違ってなかったようだ。
が「or」で、10.3章に定義した手順で認証した、とある。

この「or」が英文のどこにかかるのかがすぐわからない・・・。
間違っていても訳さないことには進まないので、解釈してみよう。

bonded deviceは、10.8.2.3章に定義したresolvable private addressか、connectionを確立して10.3章に定義した認証手順で処理を進める。
resolvable private addressを使うdeviceは、常に配信要求を以下のように行う。

  • 両側ともbond可能な場合:[Vol 3, Part H]3.6.4章で定義したIRK値
  • 上記以外:あらかじめ配信用の鍵を用意しておく

IRK値配信要求が失敗した場合、10.3章で定義する手順で再接続・認証するか、現在の接続を切断する。

うーん・・・。
まずは、わからないものを調べていこう。

10.8.2.3章: Resolvable Private Address Resolution Procedure
Resolvable Private Addressの解決手順は[Vol.6 Part B]1.3.2.3章に書いてありますよ、という章。
Hostは、対向デバイスから取得したIRK値か、ローカルデバイスのIRK値を使ってresolvable private addressを解決できますよ、ということらしい。

10.3章: Authentication Procedure
認証手順、だろう。
フローチャートにParingとか出てくるので、読んでおいて損はないのだろうけど、ちょっと気力が・・・。

[Vol 3, Part H]3.6.4章: Identity Information
Identity情報。
128bit(16 oct)のIRK。
この情報は「Transport Specific Key Distribution」フェーズで使われる。

あまりわからなかったが、とにかくIRKを使えばResolvable Private Addressを解決できる、ということがわかっていればよいんじゃないかな。


この辺を手がかりにネットで検索していこう。

What mechanism randomizes the BLE address that gets hashed with the IRK? - Nordic Developer Zone
S110のv7.0.0 alphaまではResolvable Private Addressに対応していない、ということのようだ。
念のためBLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLEで検索すると、device_manager_peripheral.cでは何箇所か使っていた。
何に使っているかはわからないが、書いてあるということは使っていると思っててよいんじゃないか。

bluetooth - Can you uniquely identify a BLE MAC address in Android 5.0? - Stack Overflow
Android5.0からは、static addressが使えなくなった、とある。
これは何か関係しているかもしれない。
nRF52 SDK v0.9.1のNFCペアリングは、Galaxy S6でしかやってない、とリリースノートに書かれている。
ただ、ネットで見ると、今年の5月くらいに5.1.1配信が海外で行われた、という記事があったので、同じバージョンかもしれん。
Nordicさんも、端末じゃなくてOSバージョンまで特定してくれないと困るなあ。

Bluetooth Low Energy SMP Pairing | Freescale Community
ここはシンプルにまとまっている。
まとまっているのだけど、私がTKとかSTKとか出てくるたびに拒否反応を起こしてしまい、読みたくない。。。

 

よし、私が実データ派だということを思いだしたので、もう一度nRF52のFLASHを消去してペアリングし直そう。


初回は、CONNECT_REQ後、SM_Pairing_Req/Rsp、SM_Pairing_Confirm、SM_Pairing_Randomが走るようだ。
2回目も同じ。
同じなのだけど、コンソールログがちょっと違う。
初回はFLASHに値を保持していないので、アドレスが0xFFFFFFFFFFFFになっているのだが、2回目はNexus5のアドレスが入っていた。

スニファのデータを眺めた感じでは、Nexus5のアドレスを直接やりとりしているようには見えない。
ということは、InitAで渡しているアドレス値を変換するとNexus5のアドレスになるっていうことかい?
それが「Resolvable」という意味なのだろうか??

ならば、こういう仮説が立てられそうだ。

  • 1回ペアリングが成功すると、何か相手の情報を保持する。アドレスと、暗号化の鍵か何かのようなもの(IRK?)。
  • それ以降は、鍵の交換を省略し、保持している鍵でInitAのアドレスを解決し、保持している相手のアドレスを比較し、一致したら接続する。
  • Nexus5側でペアリング情報を削除すると、その鍵情報がnRF52側と変わってしまうのだが、nRF52側はそれを知らない。
    • なので、nRF52は不一致と判断して接続をやめてしまう

ここの最初で「検索しに行こうとしているアドレスが違う」と書いたが、コンソールに出力しているのはInitAそのもので、まだ解決する前のアドレス値だ。
だから、そこは心配しなくてもいいんだろう。

 

ひとまず、NFCペアリングできなかった件は、これで終わりにしよう。
仮説を検証するのが筋なのだろうけど、そこは私の手に余るし、もうちょっとお遊びで使いたいのだ。
FLASHを消せばペアリングもそこそこうまくいくようなので(Android側は、LocalNameじゃなくてAdvAアドレスが表示されるような中途半端なペアリングになっているが)、DeviceManagerからデータを消すルートさえ用意しておけばいいんじゃなかろうかね。

おっと、サンプルのmain()を見ると、ボタン2を押しながら起動するとBondingデータをクリアするようになっていた。
やるな、Nordic。

2015/09/18

[nrf52][nfc]ようやく1つめのブレークポイントに当たる

nRF52でNFCペアリングをしようとして、assertが発生していると言うことはわかった。
問題は、その次だ。
どこが失敗してassertを発生させているのだろう?

これが、経過した地点のUARTなり何なりのログが出ればわかるのだけど、assertしたら何も出ていない今ではそれができない。
J-Linkのブレークポイントも、同時に4つくらいまでしか設定ができない(ハードウェアブレーク、の制約かな)。

なので、少ない労力で、これは!というところにブレークポイントを設定して、当ててやりたい。
そうしたい気持ちの半分は「めんどくさいから、さっさと当たってほしい」で、もう半分は「動物的勘であててやる!」というものだ。
後者の方が半分以上あるとは思う。

 

今回選んだのは、device_manager_peripheral.cのdevice_instance_find()。
「Searching for device」というログがUARTに出ていたからだ。
ここでm_peer_table[]というテーブルのアドレス情報と、引数のp_addrのアドレス情報を比較しているようだ。
そして一致すればよいのだが、一致しなければエラーで抜ける。
じゃあ、エラーになってるんじゃないの?ということで、条件分岐を追加してブレークポイントを設定した。
ソースが提供されてるって、こういうときいいよね。

そして、そのブレークポイントに止まった。
nRF52 Preview DKが保持していると思われるアドレス情報と、引数でもらってきたアドレス情報が一致していないのだ。
それは、だめだろうという気がする。

しかし、だ。
nRF52 Preview DKが保持していると思われるm_peer_table[]には、Nexus5の設定画面で表示されるBluetoothのアドレスと一致している。
一致していないのは、引数で渡されたアドレスの方だ。
引数で渡されるということは、接続時などに相手から渡されるアドレス情報とかじゃないんだろうか?
そのときに、Nexus5に固有のアドレスを使うのでは無く、ランダムなアドレスを使っているとか、そんなことではなかろうか。

そんなわけで、次はBLEの無線を捕捉して、引数で渡されるアドレス情報との関連を見つけていくことになろうか。
いやあ、長いなぁ。

2015/09/16

[arm]引数はr0~r3

関数呼び出しの時に引数があるが、引数はスタックに積まずにレジスタで渡すことが多い。
引数が少ないときは、だいたいそうだ。
多くなるとスタックを使わないとどうしようもなくなるので、効率が落ちる。
だから「引数はなるべく少なめに」ということになる。

では、ARMはどんな感じになるのか。
やりたいのは昨日の続きで、引数を見たいだけなのだ。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0205ij/Chdichhg.html

これを見ると、RealViewというかARM社のコンパイラの仕様なのかになるのだろうが、r0~r3が入力パラメータとして使われる、とある。
なお、戻り値はr0になるみたいだ。

覚えておいて、損はあるまい。


実は、nRF52のapp_uart_log()を使って引数を出力したかったのだけど、app_error_handler()でやろうとしてもだめだったのだ。
WFIしてもWFEしても、はたまたapp_uart_flush()をしても、吐いてくれない。
その前にどこかで終わってしまっているのだろうか・・・。

場所がわかっているからブレークポイントで止めてみたのだが、有効な値じゃない感じがしている。
r0は0x16で、error_codeに当たるはずなのだが、nrf_error.hを見ても該当しない。
p_file_nameに一番の期待をしたのだが、r02には0xffが。あまり期待せずにアドレス0xffを見に行ったが、ASCIIっぽいものは入っていなかった。
うーーーん・・・。

[nrf52][nfc]やはりassertしていた

nRF52のNFCペアリングをデバッグ中。
まだMakefileを仕立てるほどの気力がないので、Keilでやっている。
最適化しさえすれば、32KBに収まっているので大丈夫だ(95%使用、と出てくるが)。

シリアルログを出してみたが、どうもペアリングできないときはnRF52が再起動しているようだ。
自分のmain.cにapp_error_handler()を再定義してブレークポイントを張ったところ、止まったのだ。
ではコールスタックを見てみよう、というところで、Keilのコールスタックに見慣れておらず、ここにメモを残しておこうと思ったのだ。


コールスタックは、こんなウィンドウに出てくる。
下から順に呼んでいて、一番上が最新のものになっている。

image

それは普通なのだが、ツリーを展開するとこうなった。
スタックの中身も含めて表示してくれるようなのだ。

image

例えば、app_error_handler()は引数を3つ取る。

void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t *p_file_name)

変数名の左に付いているアイコンのうち、矢印が付いているものが引数のようだ。

また、関数名をダブルクリックしてもジャンプはしてくれず、ツリーが開閉するだけ。
飛びたいときは、右クリックする。

image

「Caller」か「Callee」の選択となる。

「Callee]を選ぶと、コンテキストを表示した関数に飛ぶ。
ブレークした関数であれば、ブレーク位置。
誰かを呼び出したのであれば、呼び出した直後の位置。

「Caller」を選ぶと、自分が呼ばれる前の関数に飛ぶ。
「私はこの人から呼ばれました」という意味のCallerか。

今回を例に取ろう。
ble_evt_dispatch()で右クリックし、「Caller」を選択すると、その呼び元であるintern_softdevice_events_execute()に飛んだし、「Callee」を選択すると、次に呼び出す関数dm_ble_evt_handler()の次にカーソルが当たった。


これで追っていこうとしたのだが、最適化がしっかりかかっているためか、未使用の引数はかなり省略しているようだ。
おかげで、assertしたらLEDを光るようにしていたから飛んだことまではわかるものの、どういうエラーだったかなどは取って来れなかったのだ。

せめて、UARTのログに出すくらいはしておかないといかんな。

2015/09/13

[nrf52]ペアリングできてたっぽい

さっきの記事を書いた後、もううまくいかーーん、と放置してご飯とか食べてたのだが、さっきAndroidの設定画面を見るとNordic_HRMがペアリングしたデバイス欄に出ていた。
よくわからんけど、ペアリングできていたようだ。

わからん・・・わからなさすぎる・・・。

じゃあ、もう一度ログを取り直そうと動かしているのだけど、初回に取ったログと出方が違う。
AndroidかnRF52か(あるいは両方)わからないけど、ペアリングというか、ボンディングというか、そういうのをやったと覚えているような感じを受けている。

一度Android側を切断させたのだけど、やはり動きが違う。
ということは、nRF52も覚えていると言うことか。
ペアリングすると、Nordic_HRMじゃなくて、デバイスのアドレスがそのまま表示されるようになってしまった。

08 1b 9a 7d b8 6a 25 cd 01 : LE Bluetooth Device Address

「CD:25:6A:B8:7D:9A」という表示だ。
だから、アドレスはわかってるのだろう。しかし、Local Nameとの整合ができていないのかな。

 

nRF52が保持しているのなら、とりあえず別のAndroid端末で試してみよう、とNexus7を相手にしたのだが、こちらは再起動がかかってしまうようだ。
デバイスアドレスがログに出ているのだが、Nexus7ではなくてNexus5のアドレスだった。
「保持しているのと同じアドレスかどうか比較して、違ったから再起動した」ということかしら。

ミドル層が勝手に再起動するとは考えづらいので、アプリに通知してアプリがリセットさせていると考えるのが妥当だろう。
device_manager_evt_handler()が呼ばれて、APP_ERROR_CHECK()マクロでapp_error_handler()が呼ばれている、ということだろうか。


デバッガでその辺を追おうとしたけど、なんかうまくいかない。
最適化が効き過ぎているのか、と-O0にすると、32KBの壁を越してしまい、リンクできない。
うぅ・・・こんなサンプルくらいで壁を越すとなると、Keil MDK評価版ではつらいな。
かといって、本物を買うってのもなぁ。

http://news.mynavi.jp/column/sopinion/412/
3年前の記事だが、値段が書いてあった。
nRF52832は512KBのROMがあるなら、フルで使いたいなら$4,895か。

リンク先のLPC Toolsを見てみよう。
http://www.lpctools.com/mdk-cortexmnode-lockedlicenses.aspx
Cortex-Mのみでノードロックライセンス(たぶんPC固定)だと$3,640。

うん、仕事で買ってもらうことでもない限りは、やめとこう。
GCCを検討するときだ。

 

STM32のF0とL0は、MDKを無償で使えるライセンスになっているようだ。いいなぁ。
うちのはF1とF4なので、対象外だな。
http://www2.keil.com/stmicroelectronics-stm32
もしかしたらNordicにも・・・
http://www2.keil.com/nordicsemi
ないかー。

[nfc/ble]NFCペアリングがまだまだだめだ

nRF52でNFCペアリングがうまくいかないシリーズ。

[nrf52][nfc]NFC Pairingサンプルを動かす (1)
[nrf52][nfc]NFC Pairingサンプルを動かす (2)
[nrf52]NFCペアリングサンプルがまだうまくいかない
[nrf52]まだNFCペアリングできないのだ

  • Peripheral
    • nRF52 Preview DK
    • SoftDevice S132 v1.0.0-3alpha
    • nRF52 SDK v0.9.1
  • Central
    • Nexus5
    • Android 5.1.1 (Build : LMY48l)

やりたいことは、「Nexus5でnRF52 Preview DKにNFCタッチしてペアリングしたい」だ。

  • できていること
    • nRF52 Preview DKがNFCタグで動く
    • NFC ForumのSimple Secure Paringに書いてあるタグの内容が読める
    • タグの内容をNexus5で読んで「ペアリングしますか?」と聞いてくる
    • nRF52 Preview DKでAdvertisingが開始する
  • できていないこと
    • AndroidのBluetooth設定画面で「ペアリングされたデバイス」に出てこない

nRF52 Preview DK側の動き

  1. NFCタッチ待ち
  2. タッチされてタグが読まれるとAdvertisingを開始する
  3. Connectしたら、HRSサンプルと同じ動きをする

Androidに載っているHRSアプリとしては、Connectしてしまえば使うことができる。
nRF52 Preview DKのアプリも同じだ。
だから、ペアリングしようとすまいと、HRS自体は動くのだと思う。
実際に動いてるし。

そもそも、ペアリングって何だ?


Interface誌2015年8月号のBLE特集を読むと、暗号通信をするときには鍵交換する必要があり、その鍵交換の手順をペアリング(Bonding)と呼ぶ、とある。
なるほど、では、暗号化されたデータを使わないならペアリングは不要ということか。
ただ、「暗号化しない」という情報を交換するかもしれない。

このNFCペアリングサンプルがどういうやりかたをしているのか、調べる必要がありそうだ。
ソースを読むしかないかと思ったが、Note欄の下に書いてあった。

このアプリはデフォルトではJust Worksペアリングを行う(Android端末でテストを行う準備をするため)。

"prepared for testing with Android device"は、「Android端末でテストする準備」でよい?
ペアリングする準備とかじゃ無くて、テストする準備?
もう少し先を読んでみよう。

OOBペアリングを行うようにするには、main.cに適切なTK値を定義し、NFC_BLE_PAIRING_TYPEとOOB_AUTH_KEYを使うようにする。

ソースを見ると、NFC_BLE_JUST_WORKS_PAIRINGとNFC_BLE_OOB_PAIRINGがコンパイルオプションとしてあり、JUST_WORKSの方はTK値が常に0、OOB_PAIRINGはTK値が設定できる、とコメントにある。
OOB_PAIRING有効時の設定を見ると、SEC_PARAM_OOBが有効になって、OOB_AUTH_KEYが定義されるようになっていた。

じゃあ、とコンパイルオプションを変更したが、結果としては同じだった。
そうですか。

ただ、NFCタグの中身は増えていた。
キーを渡しているのだろうね。

 

ちゃんと、ペアリングしよう、という意思はあるようなのだ。
こんなパケットが出てる。

image

ただ、ここから先、MasterがRETRYを繰り返しているだけで終わってしまう。
MasterはNexus5のはずだから、nRF52 Preview DKが返事をしていないっぽい。
これって、Nordicのシーケンスでも一番最初のところですな。

main.cを見ても、sd_ble_gap_sec_params_reply()を使っているところが無いのだけど、それがよくないのかい?
SECなイベントとしては、BLE_GAP_EVT_AUTH_KEY_REQUESTだけをさばいているようなのだけど、シーケンスに書いてあるBLE_GAP_EVT_SEC_PARAMS_REQUESTはやってない。

main.cではやってないのだけど、device_manager_peripheral.cではやっていそうだ。
dm_ble_evt_handler()にcase文としてあるし、main.cはble_evt_dispatch()でdm_ble_evt_handler()を呼んでいる。
じゃあ、ここがうまく動いていないということか?

 

device_manager_peripheral.cはログを出すようなので、見てみよう。
(これでやっていたのが「hiro99ma blog: [nrf52]app_uart_fifo.cが2つある」だ。)

[DM]: >> BLE_GAP_EVT_SEC_PARAMS_REQUEST

このログは出た。
出たなら、sd_ble_gap_sec_params_reply()も実行しているはずなのだ。

[DM]:[CI 0x00]:[DI 0x00]: Bonded!

こんなログも出てるし。
わからん、わからんのだぁぁぁぁ

[nrf52]app_uart_fifo.cが2つある

NFCペアリングがうまくいかないので、ログを出そうとした。
UARTログだ。

サンプルをそのままログ有効にするだけだと、文字が化け化けだった。
結構悩んだのだが、UARTのみのサンプルを見るとapp_uart.cではなく、FIFOを使うapp_uart_fifo.cを使っていた。
ではまねしよう、とdrivers_nrf/uart/app_uart_fifo.cをプロジェクトに組み込んだのだが、なんだかえらくビルドが通らない。

UARTサンプルを細かく見ると、drivers_nrfではなくてlibraries/uart/app_uart_fifo.cを使っていた。
プロジェクトでnRF_Driversのツリーに入っていたから、だまされた・・・。

そっちに差し替えると、ビルドも通って、文字化けもしなくなった。
もうFIFO版だけでいいんじゃないのかって思うけど、やっぱりバッファを食うからかねぇ。

 

なお、nRF51の方は、drivers_nrfの方にしかapp_uartがいない。
傾向からすると、nRF52もいずれはそうなっていくんだろうな。


なお、SEGGER経由でログを出す方法もあるらしい。いつか試したいものだ。
https://devzone.nordicsemi.com/tutorials/6/debugging-with-real-time-terminal/

[keil]Packを整理したらビルドできなくなった

KeilでnRF52のサンプルをビルドしようとした。

最近、Nordicからメールで更新通知が来ていて、Packとかが新しくなったようだった。
Packとか何とかはよくわからないけど、とりあえず更新しておけばいいんじゃなかろうか、くらいでやっておいた。

ふっと、KeilのPack Installerで見てみると、過去バージョンが残ったままになっているようだった。
いつインストールしたかとか、どういう管理していたかも記憶にないので、とりえあず最新版以外はPackごとRemoveした。

そしたらですな、以前ビルドできていたサンプルソースがビルドできなくなったのだ。
コンパイルエラーとかじゃ無くて、プロジェクト自体のエラーみたいなものだ。
CMSIS COREとDevice Startupが無い、とおっしゃる。

「Manage Run-Time Environment」で見てみると、確かに赤くなってエラーっぽい。

image

よくわからんので「Select Software Packs」のダイアログを出すと、ARM::CMSISとDeviceFamilyが赤くなっていた。
まあ、さっき消したから、ないんだろうな。
でも最新版は入っているはずと、画面の上にある「Use latest versions of all installed Software Packs」にチェックをした。
これで最新版になっているはず。

はずだったのだが・・・だめだった。
Manage Run-Time Environmentにも新しいのが出てこないので、どうしようもない。
該当するPackを入れればいいんだろうが、それは負けた気がする。

よくわからぬまま、もう一度Nordicからメールできていた更新ファイルをインストールし直した。
今日は8.0.4とかいうのが最新っぽい。

  • nRF5x_MDK_8_0_4_Keil4.msi
  • NordicSemiconductor.nRF_DeviceFamilyPack.8.0.4.pack

そうしたら、新しいのが使えるようになっていた。

image

インストール→消す、とやったのがまずかったのだろう。
消してからインストール、ですかね。


別のサンプルプロジェクトを立ち上げたところ、また同じことが起こった。
今度は、インストールし直さずとも、Select Software PacksでLatestにチェックを入れ、Run-Time Environmentで選び直せばうまくいった。

2015/09/12

[smc]イベント内容を定義していないとDefault

あたし一日中状態遷移のことばかり考えてる・・・と、いうわけでもないのだが、こういうのは勢いで調べておかないと。
というわけで、まだ状態遷移のsmcについてだ。

サンプルのsmファイルを見ると、各状態のイベントごとの動作が入っていないことがある。
たとえば、EX1はイベントで「Zero, One, Unknown, EOS」があるけど、OK状態のときにはどのイベントについても記述が無い。
さて、イベント内容を定義していないと何が起きるのだろう?

 

cygwinで実行させると、コアダンプした。
assertionログが出ているので、assert()が引っかかったようだ。
これは「Default」という特殊イベントが発生したのと同じことになるようである。

自動生成されたソースは、こうなっている。

static void AppClassState_Default(struct AppClassContext *const fsm)
{
    if (getDebugFlag(fsm) != 0) {
        TRACE("TRANSITION   : %s.%s\n", getName(getState(fsm)), getTransition(fsm));
    }
    State_Default(fsm);
}

そしてState_Default()は、statemap.hにあった。

#define State_Default(fsm) \
    assert(0)

あまり調べてないけど、たぶんこれが動いているのだろう。

 

では、自分で何も無いときの動作を定義したい場合はどうするかというと、2つあるようだ。
「Default状態」(Dは大文字)に遷移するか、「Defaultイベント」が発生するかだ。

じゃあ、両方書いた場合はどちらが優先されるのか?
やってみると、Default状態の方が実行された。
うーん、私のイメージでは、各状態に書かれたDefaultイベントの方が強い、だったのだが。

Zeros状態のZeroイベントを無くした状態で実行すると、こんな動きになっていた。

image

引数は"000"で、1番目の"0"でStart→Zerosに遷移後、次の"0"でZeroイベントを発行(AppClassContext_Zero())。
でも、そのときの遷移定義がないので、Map1_DefaultState_Zero()が実行されている(最後のAppClass_DefaultState()はActionとして実装した関数)。

Map1_DefaultState_Zero()はこんな感じで自動生成されていた。
赤文字が、Actionで実装した関数を呼んでいる箇所。

static void Map1_DefaultState_Zero(struct AppClassContext *const fsm)
{
    struct AppClass *ctxt = getOwner(fsm);
    const struct AppClassState* EndStateName = getState(fsm);
    if (getDebugFlag(fsm) != 0) {
        TRACE("LEAVING STATE   : Map1_DefaultState)\n");
    }
    if (getDebugFlag(fsm) != 0) {
        TRACE("ENTER TRANSITION: Map1_&Map1_DefaultState.Zero()\n");
    }
    clearState(fsm);
    AppClass_DefaultState(ctxt);
    if (getDebugFlag(fsm) != 0) {
        TRACE("EXIT TRANSITION : Map1_&Map1_DefaultState.Zero()\n");
    }
    setState(fsm, EndStateName);
}

2015/09/09

[smc]idはgetId(state)でとれる

プリプロセスを展開したらわかりやすいかと思ったのだけど、そもそもソース中でidを使っている箇所が無いためか、_idで展開されるものが無かった。
うう・・・あきらめてファイルを読むことにしよう。

lib/C/statemap.hの中に、getId(state)というマクロがある。
中身は、state->_id。
では、このstateはなんだよってなると、ここでプリプロセスを展開したものが役立つ。

struct AppClassState {
    void(*EOS)(struct AppClassContext *const fsm);
    void(*One)(struct AppClassContext *const fsm);
    void(*Unknown)(struct AppClassContext *const fsm);
    void(*Zero)(struct AppClassContext *const fsm);
    void(*Default)(struct AppClassContext *const fsm);
    int _id;
};

これは、AppClass_sm.hの中にいた。

struct AppClassState {
    void(*EOS)(struct AppClassContext *const fsm);
    void(*One)(struct AppClassContext *const fsm);
    void(*Unknown)(struct AppClassContext *const fsm);
    void(*Zero)(struct AppClassContext *const fsm);
    void(*Default)(struct AppClassContext *const fsm);
    STATE_MEMBERS
};

つまりまあ、STATE_MEMBERSがint _idだってことだ。

それはよいとして、AppClassStateの本体はどこにあるのだ?
ヘッダの中では、こうなっていた。

extern const struct AppClassState Map1_Start;
extern const struct AppClassState Map1_Zeros;
extern const struct AppClassState Map1_Ones;
extern const struct AppClassState Map1_OK;
extern const struct AppClassState Map1_Error;

これは、昨日見ていた「getState」マクロだ。
つまり、こういう使い方になるようだ。

 getId(getState(&this->_fsm))

サンプルEX1で引数を"00011"としたときは、こんな順番になっていた。

./checkstring 00011
getId : 0
getId : 1
getId : 1
getId : 1
getId : 2
The string "00011" is acceptable

"0"がStart、"1"がZeros、"2"がOnesなのだろう。最後のが出力されないのは、その前にwhile()を抜けるからだ。

 

最後は、この0とか1とかが#defineで定義されていればよいだけだ。
そういうのがないと、どれが何番目かわからないが・・・えっ、ないの??
ないのか・・・自分でJARを作ってやれば出力できなくも無いだろうが・・・。

では、せめてsmファイルに書いた順番になるのであれば、あきらめて自作もできるだろう。

0 : Start
1 : Zeros
2 : Ones
3 : OK
4 : Error

では、smファイルの並びを、OK --> Ones --> Error --> Start --> Zerosに変更。

0 : OK
1 : Ones
2 : Error
3 : Start
4 : Zeros

うん、間違いなかろう。
この定義値の生成も、前回のExcelファイルに組み込んでしまいたいところだが、後日だな。

[smc]Excelファイルで簡単なsmファイルを作る

まだ、もごもごやってるsmcだ。

昨日はアドレスで比較するような状態取得を考えていたけど、やっぱり格好が悪い。
定数になっていればswitch-caseでもいけるのに。

と思ったら、マクロの中に「_id」という変数を見つけた。
これは何か使えそうだ!
が、それにはマクロを展開して、どういう使われ方をしているか眺めておきたい。
吐き出すのはgcc -Eでできるけど、読むのに時間かかるなぁ。

 

というわけで、今回はsmcのフォーマットsmファイルをExcelで作った。
smファイルを作るExcelファイル
・・・と書くと数日で作ったように見えるが、2008年に作っていたものを今のsmcバージョンに合わせただけだ。
ガードとかは入れるところが無いのだけど、まあ簡単なsmファイルならこれでもよいだろう。
上書き防止のため、smファイルがあると作ってくれないのがうっとうしいかもしれない。

そんなに難しいことはしていないし、マクロもパスワードをかけているわけでは無いから、ガードを付けたりするのも簡単だろう。
ネットで検索すると、他にもExcelで作られた方もあるようだから、そちらの方が機能がよいかも(中身を見てないので)。

[smc]現状を知ろう

ふっと思ったのですよ。
「もしかして、なんか私の考え方が間違ってるんじゃ無かろうか?」と。

しかし、心配しても仕方ないので、あきらめて現在の状態を取得し、その状態によって条件を決める関数を作り、それを状態遷移のガードに使おう。


statemap.hに「getState」というマクロがあったので、printfで値を見てみた。

./checkstring 000
state : 40314c
state : 403134
state : 403134
The string "000" is acceptable

%xで表示させると、こんな感じ。
最初の0x40314cは、mapファイルではMap1_Startのアドレス、後はMap1_Zerosのアドレスだった。
つまり、今の状態がこれ、という変数を持ってるのでは無く、状態ごとのイベント関数群のポインタを持っているようだ。

たとえば、Map1_Startはこういう定義になっていた。

const struct AppClassState Map1_Start = {
    Map1_Start_EOS,
    Map1_Start_One,
    Map1_Start_Unknown,
    Map1_Start_Zero,
    Map1_Start_Default,
    0
};

なので、ここに独自関数を追加できるようになっていれば済むのだが・・・。


今日はここまで。

あと、前回のガードを追加すると、遷移条件を満たさなかったときにDefaultの遷移をするようだ。
定義していなくて実行時エラーになっていたから、たぶんそうだろう、というところだが。

2015/09/08

[smc]状態ごとに条件を変えて遷移できるか?

状態遷移のsmcは、使ってみればわかるだろう、と思っていたのだが、いきなり詰まった。

やりたいのは、A/D変換の結果によって遷移先を変えたいというもの。
ただ、その条件は状態によって違うのだ。
ありがちだと思うのだが、どこまでsmcでできるのだろうか?

状態に遷移するイベントを作って置いて、自分で状態ごとに値を判断してイベントを呼ぶ、というのは無しにしたい。
それだったら、普通に組んだ方がわかりやすいだろうし。

smcのページを見ていくと、タイトルとしてはこれが近いような気がする。
http://smc.sourceforge.net/SmcManSec2.htm#TransGuards

UMLのガードって、条件みたいなものではないか。
だから、条件による遷移、みたいな意味だったらよいと思ったのだ。
日本語だと「守る」って感じがしてしまうんだけどね。


ガードにはif文に書けるような条件を書いておけ、というように読める。
ただ、私がやりたいのは静的な条件では無く、状態によって変わる条件なのだ。
そういうのはどう表すのだろう?

コンテキストクラスのメソッドを呼びたいなら、ctxtを自分で付けろ、とある。
でも、Cだしなぁ。
というよりも、ctxtってなんだ?
thisみたいなものなら、引数にして渡してやれば同じことができるかもしれない。

 

どうやら、ctxtはコンテキストクラスを表す変数のようだ。
Cでのサンプルを見る限りでは、%classにつけた構造体名がそれに当たりそうだ。
では、試してみよう。

extern int AppClass_IsAcceptable(struct AppClass *this);

こんなヘッダをAppClass.hに書き、ソースにはこう。

int AppClass_IsAcceptable(struct AppClass *this)
{
    return this->isAcceptable;
}

特に意味はない関数なのだけど、サンプルソースで使えそうなのを入れてみただけだ。

そして、smファイルは

Zero    [AppClass_IsAcceptable(ctxt)]    Zeros    {}

遷移条件なので、遷移先の前に書くのだろう。
これは、Start状態のZeroイベントに書いたのだが、AppClass_sm.cにはこう反映されていた。

static void Map1_Start_Zero(struct AppClassContext *const fsm)
{
    struct AppClass *ctxt = getOwner(fsm);
    if (AppClass_IsAcceptable(ctxt)) {
        /* No actions. */
        setState(fsm, &Map1_Zeros);
    }
    else {
        Map1_DefaultState_Zero(fsm);
    }
}

AppClass_IsAcceptable()が真ならばZerosに遷移するし、そうでなければ・・・デフォルト状態?に遷移するようだ。

 

なので、Context Classを表す構造体のメンバに、判断する材料を全部突っ込んでおけばよいということになるか。


でも、C++みたいにvirtualで持つわけではないので、もうちょっとうまいことやらないと作業が楽にならないな。

状態を表すint型の0から始まる変数があって、それを添字にして関数を用意する、みたいなのが楽なんだけど、そういう感じでもなさそうだ。
そもそも、AppClassState構造体は状態ごとに別の実装を持てるようになっていそうだから、間借りさせてくれるとよいのだが。

2015/09/06

[nrf52]まだNFCペアリングできないのだ

タイトルには「できない」と書いたが、うまく行くときもある。
こういうときはだめ、というのがわからないのは、できないのと同じだ。

シーケンスは、これだろうと思う。
http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s132.api.v0.9.0%2Fgroup___b_l_e___g_a_p___p_e_r_i_p_h___b_o_n_d_i_n_g___p_k___c_e_n_t_r_a_l___o_o_b___m_s_c.html&resultof=%22oob%22

Connection Establishedとあるから、まず接続して、Centralから「ペアリングしよう」と言われて動き出すようだ。
が、Centralへペアリングの応答を返した後でOOBの入力をやって、それ以降はやりとりがなさそうなのだ。
まあ、OOBだから、Bluetoothのところでは何もしないといわれればそれまでなんだけど。

では、Androidで失敗しているのは、何ができなかったからダメだと思ったのだろう?
ペアリングに成功した端末は、Android設定画面のBluetoothで「ペアリングされたデバイス」に現れている。
ペアリングはしてないが、検出できた端末は「使用可能なデバイス」に出てくるようだ。
TIのSensorTagも、ここに出てくる。
しかし、どうも「使用可能なデバイス」は常に見ているわけじゃなく、設定画面に入ったり、メニューで「更新」としたときにやっているようなのだ。

ペアリングするかどうかのダイアログは、NFCタッチのタイミングで表示される。
サンプルプログラムではNFCタッチのタイミングでAdvertisingをし始めるのだけど、そこら辺にずれがあるんじゃなかろうか。


ならば、だ。

  1. AndroidのBluetooth設定画面を開く or 「更新」を実行する
  2. Androidが端末を探している間にPreview nRF52 DKのNFCアンテナに軽くタッチし、Advertisingさせる
  3. 設定画面の「使用可能なデバイス」にNordic_HRMが表示されたら、ちゃんとタッチしてペアリング確認ダイアログを表示させる
  4. OKする

だとどうだろう?

・・・やってみたが、だめだった。
ペアリングされたデバイスになってくれない。

そもそも、ペアリングされたデバイスになっていなくても、HRMサンプルは動くようだ。
元のHRMサンプルにペアリングのところだけ付けたものだから、接続さえできればよいのだろう。
あれ、じゃあこのサンプルのペアリングって何のためにやってるのだ?


NXP Tag Infoアプリで読んだデータだ。
XMLも出力してくれるのだが、解析した画面の方がわかりやすい。

image

仕様は、これだろう。
http://members.nfc-forum.org/apps/group_public/download.php/18688/NFCForum-AD-BTSSP_1_1.pdf

まずNDEFレコードが1つしかない。
これはSimplified Tag Format for a Single Bluetooth Carrierというやつだろう。
そのときは、Bluetooth OOB情報だけをNDEFメッセージに入れるようだ。
以下、順にデータを切り出していく。AD Typeは2番目だ。

02 1c 00 : LE Role - Only Peripheral Role supported
08 1b 9a 7d b8 6a 25 cd 01 : LE Bluetooth Device Address
03 19 41 03 : Appearance - Heart Rate Sensor: Heart Rate Belt
02 01 04 : Flags - BR/EDR Not Supported
0b 09 4e 6f 72 64 69 63 5f 48 52 4d : Complete Local Name
- "Nordic_HRM"

Advertisingしているデータは、こんなのだった。

image

LE Bluetooth Device Addressを確認しよう。
CSS_v5.pdfによると、LE Bluetooth Device Addressのサイズは7byteで、6byteはCore_V4.2のVol.3, Part B, Section1.3と同じ。残りは、bit0がRandom Device AddressかPublic Device Addressか。

まず、最後のデータは01なので、これはRandom Device Address。
では、残りのデバイスアドレスをどう並べるか。Section 1.3だけを見ても、エンディアンなどは書いてない。でも、48bitって書いてあるので、何も考えずにリトルエンディアンにすればいいんじゃないかな。
ならば「cd:25:6a:b8:7d:9a」。
AdvAと一致するな。

暗号化に関することなど書かれているかと思ったが、NFCのデータにはそんな要素はなさそうだ。


結局、今回もペアリングのことはわからないままだった。
AndroidでOOBのペアリングができないというわけでは無く(PaSoRi RC-S390で試した)、NFCでのペアリングができないだけのような気がする。
私が「動いている」と思っていたのも、単にNFCタッチによってAdvertising開始して、アプリがConnectしただけのようだし。

状態遷移smc

組込み系の人間でいるつもりなのだが、状態遷移が苦手だ。
苦手というか、状態遷移図とか表を作るだけ作って、実装はちまちまifとかswitch-caseで分けていたりする。
でも、状態でバーンと動きが変わるのであれば、状態ごとに実施するイベントハンドラを切り替えるようなやり方の方が、実装としてはさっぱりと切り分けられてよいこともある。
まあ、同じような実装をあちこちに書いてしまいがちになるというのはあるがね(下手なだけか?)。


そういった悩みを解決してくれるわけではないのだが、「状態遷移を作るのがめんどくさい」ということについては、多少手助けしてくれるツールがある。

SMC: The State Machine Compiler

これを最初に知ったのは、いまはなきC Magazine誌の記事だった。
株式会社エス・スリー・フォー » SMC(State Map Compiler) の拡張
1999年ってことは、16年前か・・・。
当時の私は、まだインターネットの環境を持ってなかったか、ダイアルアップでちまちまやってた時代だと思う。
ファイルはあるのだが、当時のsmcのバージョンなどはよくわからない。
ただ、bisonとかflexとか、そんなのを使っていたようだ。

確か、お仕事でアプリっぽいものを作っていた時期がある。
そのときにも思い出して、ちょっと調べていたのがこちらだ。
StateMap - YMHRS
7年前か。我ながら適当すぎる・・・が、今とあまり変わらない気がする。
何か変更していたようで、ローカルに持っていた。
が、何をしたかったのかがよくわからん。。。

さて、時代は2015年9月。
smcのバージョンは6.6.0になっている。
bison/flexだったものは、JARファイルで実行できるようになっていた。
そして、対応言語がかなり増えている。
私としては、C言語対応になったのがありがたい。

 

SMCがどういうツールかというと、ルールに沿って状態遷移をテキストに書いておくと、ソースに変換してくれるのだ。
そのくらい自分で書けばいいやん、という気になってしまうかもしれないが、自動で書けそうだ、と思ってしまうと、自動でやらせてしまいたくなるではないか。


C言語でどうやっているか見てみよう。
きっと、ファイルの形式は言語に因らず共通なのだろうけど、C言語でのサンプルだけ見る。

ファイルの先頭は、こう。

%class        AppClass
%header        AppClass.h
%start        Map1::Start
%map        Map1

classとなっているが、これが関数のプレフィクスになるようだ。
あとは、各状態ごとにイベントと遷移先とやることを書いていく。

Start
{
    Zero    Zeros   {}
    One     Ones    {}
    Unknown Error   {}
    EOS     OK      {Acceptable();}
}

"Zero"というイベントを起こすと、状態がZerosに遷移する、という感じだ。
やることは、そのまま関数になるようだ。クラス内の関数を呼ぶ、ということのようで、これにもクラス名のプレフィクスが付いていた。
そうそう、状態を管理する変数としてContext構造体というのを生成してくれるのだが、それも引数に取るようになっている。

void AppClass_Acceptable(struct AppClass *this);

もちろん、呼ぶ関数は自分で実装しないといけないし、プロトタイプ宣言も自分で用意する。
そういうのを書いたヘッダファイルを、%headerで指定するようだ。
ちょっと残念なのは、Windowsで実行しているためかもしれないが、変換した結果のソースファイルを見ると、%headerで指定したファイルを#includeで書いた行がCR/LFで改行されているのだ。
cygwinでやったからLFになると思ったけど、JARだからJavaがそう動いているのかな?

 

さて、呼ぶ方だが、名前にプレフィクスとして%classで指定した名前+"Context_"が付いている。
例えば、上記のZeroイベントであればAppClassContext_Zero()となる。

初期化用にInit()が自動で作られるので、まずはこれを呼ぶ。
のだが、その前に状態を管理する構造体を自作する。名前は%classで指定したもの。
状態として持つべきデータなどは、ここに全部入れておくイメージなのかな?
少なくとも、状態遷移する判断に使うデータはここに入れそうだ。

struct AppClass {
    int isAcceptable;
    struct AppClassContext _fsm;
};

この構造体のインスタンスをInit()の引数で渡す。

struct AppClass thisContext;
AppClassContext_Init(&thisContext._fsm, &thisContext);

「Contextのポインタを渡すんだから第1引数はなくてもいいんじゃないの?」と思ったが、AppClassContextをAppClassの中に入れなくてもよいということだろう。

あとは、イベントが発生したときに関数を呼ぶだけだ。
「関数」と書いたが、デフォルトはマクロで、NO_APPCLASS_SM_MACROマクロを定義すると関数にもできる。

状態遷移するとき、文字列を使っているようなので、状態名がバイナリのサイズに影響しそうだ。
まあ、目くじらを立てるほどではないだろうが、S02みたいな割り切った名前の方がよいかも。

 

gcc -O2で関数化したら、こんな感じだった。

0x00401390                AppClassContext_Init
0x004013c0                AppClassContext_EOS
0x00401400                AppClassContext_One
0x00401440                AppClassContext_Unknown
0x00401480                AppClassContext_Zero

うん、何とも言えんね。