2013/12/22

[nfc]HCEのサンプルを作った

ようやく、HCEのサンプルを作った。

https://github.com/hirokuma/HceSample

URI(スマートポスターじゃなく)のつもり。
動作は、Windows8.1 + RC-S380 + FeliCaランチャーで確認。
なんでかわからないけど、FeliCaランチャーが起動していないとだめなのだ。
NFPで動くんだったら関係なさそうなのだけど、なんだろうね?

NDEFのシーケンスで、NFC HACKSに載っている範囲でしか見てないから、Type 4A全般のエミュレーションにはなっていない。
最初は、こちらを見ながら作ろうとしていたのだが、途中でNFC Forumの仕様書を見始めたので、たぶん別物になったんじゃないだろうか。
http://www.slideshare.net/hieroadgjmptw/hcetype4

最初のSelectで、NDEF applicationの名前を行うので、そこで一致したら「NDEFアクセス中」みたいなフラグを立てるようにした。
ここらへんが、まだType 4Aのしくみをわかっていないので、そういう捌き方で良いのかはっきりしてない。
まあ、動くからいいか・・・。

うちではこれ以上の動作確認ができないので、ここまでかな。


あと、月刊NDEFも久々に作りました。
カードエミュレーション特集。
http://www.slideshare.net/hiro99ma/ndef-2013-12

記憶をたどりながら書いたので、あまり説明にこだわってなく、こざっぱりした感じになったんじゃないかな。
まあ、大半は昨日酒を飲みながら書いているので、ちょっと心配ではあるが・・・まあ大丈夫だろう。

2013/12/19

[nfc]HCEの動作確認をしたい (5)

実は・・・今週のHCE記事だが、日曜日に書きためたものを4日に分けてスケジュール投稿する、ということをしてみた。
5日目の今日は、12/18の2時半くらいに書いている。
目がしょぼしょぼだ・・・。


さて、4回目では、Windows8.1 + PaSoRi RC-S380を使って、Type4のまねをしているNexus7のHCE動作を見てもらおうとしたが失敗したところであった。

何がいかんかというと、よくわからんバイナリを受信して、よくわからんバイナリを返している、という部分だ。
バイナリデータがある限り、我々は解析をしなくてはならない。
ましてや、NFC HACKSという本があるのに、解析しないでいることができようか(いや、解析せずにはいられない)?

そんなわけで、HACK#17「NFC Forum Type 4 Tag」を開いた。
ふむ、よく聞く「T=CL」というのは通称だったんだ、とか、EFファイルってSIMカードの構造もそうだったよな、などと思い返しつつ読んでいると、まねしようとしていたNDEFメッセージの読み込みシーケンスがそのまま書いてあった。

ふむふむ、スライドに書かれているバイナリ値は、直接やりとりしている値ではなく、意味があるところだけ抜き出しているのだな。
ならば、そのまま送るだけではダメなのは当然だ。

プログラムはスライドを参考に、データはNFC HACKSを参考にして、こんなプログラムにした。
(これはこれで省略もあるがね。)

import android.nfc.cardemulation.HostApduService;
import android.os.Bundle;
import android.util.Log;
public class HostSampleService extends HostApduService {
    static enum CardStatus {
        INIT,
        CCFILE,
        REQ_DATA,
        OPEN,
        LEN,
        RET_DATA
    }
    final static byte[] SUCCESS = { (byte)0x90, (byte)0x00 };
    final static byte[] REQ_DATA = {
        (byte)0x00, (byte)0x0f,    //CCLEN
        (byte)0x20,          //Mapping Version
        (byte)0x00, (byte)0x40,    //MLe
        (byte)0x00, (byte)0x40,    //MLc
        //TLV(NDEF File Control)
        (byte)0x04,         //Tag
        (byte)0x06,          //LEN
        (byte)0xe1, (byte)0x04,    //signature
        (byte)0x00, (byte)0x32,    //max ndef size
        (byte)0x00,         //read access permission
        (byte)0x00,          //write access permission
        
        //success
        (byte)0x90, (byte)0x00
    };
    final static byte[] LEN = { (byte)0x00, (byte)0x03 };
    final static byte[] RET_DATA = {
        (byte)0x00, (byte)0x03,    //LEN
        //message
        (byte)0xd0, (byte)0x00, (byte)0x00,    //empty
        
        //success
        (byte)0x90, (byte)0x00
    };
    private final static String TAG = "HostSampleService";
    private CardStatus mCardStatus = CardStatus.INIT;
    @Override
    public void onDeactivated(int reason) {
        mCardStatus = CardStatus.INIT;
    }
    @Override
    public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
        for (int i = 0; i < commandApdu.length; i++) {
            Log.d(TAG, Integer.toHexString(commandApdu[i] & 0xff));
        }
        switch (mCardStatus) {
            case INIT:
                Log.d(TAG, "init");
                mCardStatus = CardStatus.CCFILE;
                return SUCCESS;
            case CCFILE:
                Log.d(TAG, "ccfile");
                mCardStatus = CardStatus.REQ_DATA;
                return SUCCESS;
            case REQ_DATA:
                Log.d(TAG, "reqdata");
                mCardStatus = CardStatus.OPEN;
                return REQ_DATA;
            case OPEN:
                Log.d(TAG, "open");
                mCardStatus = CardStatus.LEN;
                return SUCCESS;
            case LEN:
                Log.d(TAG, "len");
                mCardStatus = CardStatus.RET_DATA;
                return LEN;
            case RET_DATA:
                Log.d(TAG, "retdata");
                mCardStatus = CardStatus.INIT;
                return RET_DATA;
            default:
                return SUCCESS;
        }
    }
}

 

これをNexus7に焼いて、PaSoRiに近づけると、Windows8.1からあのNDEFタグ検知したときに聞こえるあの音がしているではないか!

NDEFの中身がEmptyなので動作はしないけれども、ここにURIとか書いておけばトースト表示してくれるんじゃないだろうか。
興味はあるが、今回はやめておこう。
もっと気力があるときにやらないとな。

2013/12/18

[nfc]HCEの動作確認をしたい (4)

さて、ちゃんとHCEの動作確認をするには、受け側がちゃんと動かないといかんだろうということはわかってきた。
めんどうだが、Starter KitとNFC HACKSを読みながらがんばるしかないか・・・。

 

が、ふと考えると、うちにはWindows8.1があった。
この子は、NFPをサポートしている(PaSoRi RC-S380持ってるし)。
ならば、かざせば動いてくれるんじゃないか??

気をよくしてつなげてみたのだが、Nexus7をかざしても、あのタグをかざしたときのような音が出ない。
うーむ・・・。

あ、NFPって、たしかNDEFしか反応せんやん。。。
HCEもカードエミュレーションだから、ソフト的にNDEF値を返さないと反応しないような気がする。


http://www.slideshare.net/hieroadgjmptw/hcetype4

こちらを参考にしながら、同じシーケンスでアクセスしに来るという前提で、決め決めのデータを返すアプリに仕立てた。
スライドのp.24で端末が返しているデータね。

やっぱり・・・そういうのは無謀よね。
要求するデータサイズなんかも違うだろうし、受信パケットを見ずにやるってのは、さすがにだめなようだ。

12-16 00:54:18.171: D/dalvikvm(1205): GC_CONCURRENT freed 377K, 20% free 10581K/13168K, paused 3ms+3ms, total 62ms
12-16 00:54:18.171: D/dalvikvm(1205): WAIT_FOR_CONCURRENT_GC blocked 28ms
12-16 00:54:20.443: D/BrcmNfcJni(1098): RoutingManager::stackCallback: event=0x18
12-16 00:54:20.443: D/HostEmulationManager(1098): notifyHostEmulationActivated
12-16 00:54:20.453: D/BrcmNfcJni(1098): RoutingManager::stackCallback: event=0x17
12-16 00:54:20.453: D/BrcmNfcJni(1098): RoutingManager::stackCallback: NFA_CE_DATA_EVT; h=0x302; data len=13
12-16 00:54:20.453: D/HostEmulationManager(1098): notifyHostEmulationData
12-16 00:54:20.453: D/HostEmulationManager(1098): Binding to service ComponentInfo{com.blogpost.hiro99ma.hcesample/com.blogpost.hiro99ma.hcesample.HostSampleService}
12-16 00:54:20.473: D/HostEmulationManager(1098): Waiting for new service.
12-16 00:54:20.473: D/HostEmulationManager(1098): Service bound
12-16 00:54:20.483: D/HostSampleService(19005): init
12-16 00:54:20.483: D/HostEmulationManager(1098): Sending data
12-16 00:54:20.483: D/HostEmulationManager(1098): notifyHostEmulationData
12-16 00:54:20.493: D/HostSampleService(19005): ccfile
12-16 00:54:20.493: D/HostEmulationManager(1098): Sending data
12-16 00:54:20.493: D/BrcmNfcJni(1098): RoutingManager::stackCallback: event=0x17
12-16 00:54:20.493: D/BrcmNfcJni(1098): RoutingManager::stackCallback: NFA_CE_DATA_EVT; h=0x302; data len=5
12-16 00:54:20.503: D/HostEmulationManager(1098): notifyHostEmulationData
12-16 00:54:20.503: D/HostSampleService(19005): reqdata
12-16 00:54:20.503: D/HostEmulationManager(1098): Sending data
12-16 00:54:25.047: D/BrcmNfcJni(1098): RoutingManager::stackCallback: event=0x19
12-16 00:54:25.047: D/HostEmulationManager(1098): notifyHostEmulationDeactivated
12-16 00:54:25.047: D/HostEmulationManager(1098): Unbinding from service ComponentInfo{com.blogpost.hiro99ma.hcesample/com.blogpost.hiro99ma.hcesample.HostSampleService}
12-16 00:54:25.057: E/BrcmNfcNfa(1098): UICC[0x0] is not activated

と思って、受信したバイト列をログに出すようにしたのだけど、シーケンスと同じだった。
シーケンス表の上から6行目の「(はい、これ~)」を送信しているところで、AndroidがDeactivateしている。

うーん、やっぱり、意味がわかってデータを送信していないので、なぜだめなのかがわからん。

2013/12/17

[nfc]HCEの動作確認をしたい (3)

さて、前回はNexus7でHCEが動いているので動作が少し違うという確認をした。
まだHCEっぽい動きはさせていないのだが、HCEサンプルの有無でPaSoRiの取得するSAK(SEL_RES)が変わったので、Android BeamではなくHCEが動いていると考えてよいだろう。

少し気になるのは、画面消灯時は動作せず、ロック画面だと動作するという動きについてだ。
Android Developerに説明がある。

  • スクリーンがオフになったら、アプリのNFC制御は止める。HCEも同じ。
  • HCEサービスは、ロックスクリーンでも動作できる。
    android:requireDeviceUnlockという属性で制御できる。
    デフォルトでは、デバイスのアンロックは不要でサービスは動く。
  • requireDeviceUnlockをtrueにすると、アンロックさせないとかざしてもHCEサービスは動かず、Androidがユーザにプロンプトを出す。

ということのようだ。
この動作は、私の確認した動作と一致している。
画面が消灯していると、HCEサンプルを動かしていても反応しないのだ。

HCEサンプルでは、requireDeviceUnlockはfalse(デフォルト値と同じ)になっている。
でも、trueだったとしても、動きは同じになるだろう。
なぜなら、Androidがプロンプトを出すかどうかは、AIDが動いているHCEアプリたちと一致するかどうかを判断してからになるからだ。

 

まだ、その部分まで受け側のアプリを作り込んでいないから、ダイアログが出るところを確認できない。
Android4.4 + enableReaderMode()で試すのが手っ取り早いのだろうが、手元に端末がない。
それに、せっかくならPaSoRiを使っておきたいのだよ、私は。
(RC-S390のSDKが一般向けに出ないかな、ぶつぶつ)。

2013/12/16

[nfc]HCEの動作確認をしたい (2)

前回、PaSoRi + NFC Starter KitでNFC-Aの応答部分で捉まえるようにして、HCEサンプルを動かしたNexus7(2013)をかざしてみたが、HCEサンプルがあってもなくてもブレークポイントに止まる、という話をした。

でも、ね。
実は違いがあるのだ。
それは、ロック画面での動作である。

ご存じの通り、Androidではロック画面中はSNEPなどをしないようになっている。
HCEサンプルを入れない状態だと、Nexus7をPaSoRiにかざしても何もならない。
が、HCEサンプルを入れた状態だと、かざすとブレークポイントに止まるのだ!

そのときのSEL_RESは、0x20。
NFC-DEP/LLCP/SNEPしてるときは、0x60。
b7~b0という表現をすれば、SAKのb6=1になってるかどうかの違いみたいだ。

SAKのビット割り当てがどうなっているかというと、これはNXPの資料がよいだろう。
AN10833[pdf]のp.10に、SAKのビットについて記載がある。

image

こちらの表は、b8~b1の割り当てだが、見る箇所はわかるだろう。
b7=1、b3=0であれば、ISO 18092なのだ。

すっきりした。

2013/12/15

[nfc]HCEの動作確認をしたい (1)

AndroidのHCEだが、サンプル通りに作ったり、ネットで見た通りに作ったりしているが、とにかく動作確認ができない。
ブライテクノBlogさんの記事を見ると、もう1台Android4.4の端末を用意して、Android Beamを無効にするAPIがあるので、それを使ってR/Wにすればよいよ、とある。
hiero_adgjmptwさんのスライドもあり、これを見ればAndroid側は間違えずに済むだろう。
ではやってみよう、と思ったのだが、今月は私の手元にNexus7(2013)が1台だけしかないことを忘れていた・・・。

あきらめるのもなんだし、そもそもうちにはPaSoRiという立派なR/Wがあるのだ。
これを使わない手はなかろう。


PaSoRiを使うので、Windows + SDK for NFC Starter Kitでやることにした。
Type4Aがどんなのかわからないにしても、ライブラリがうまいことやってくれるだろう。

ベースは、以前作ったライブラリで良いだろう。
https://github.com/hirokuma/NfcStarterKitWrap
いくらType4Aといえども、元はNFC-Aだ。
ポーリング動作については変わらないはず。

C#で新規プロジェクトを作り、追加で既存のプロジェクトを選び、NfcStarterKitWrapを選択。
そして、参照させよう。
フォームにボタンを1つつけ、イベントハンドラを用意する。

namespace Type4Rw
{
    public partial class Type4Rw : Form
    {
        private NfcStarterKitWrap.nfc mFNS = new NfcStarterKitWrap.nfc();
        public Type4Rw()
        {
            if (!mFNS.init(this))
            {
                MessageBox.Show("SDK for NFC Starter Kit fail");
                Environment.Exit(0);
                return;
            }
            InitializeComponent();
        }
        private void buttonPolling_Click(object sender, EventArgs e)
        {
            bool b = mFNS.pollingA();
            if (b)
            {
                MessageBox.Show("OK!");
            }
        }
    }
}

そして、NfcStarterKitWrap.csのdetectNfcA()という関数があるので、ブレークポイントを張る。
最初にエラーチェックがあるので、そのあとがよいだろう。

止めようとしているのは、NFC-Aを検知したときのコールバック関数。
さて、Nexus7をかざすと止まるだろうか?・・・・止まった。

NFCID1は4byte。値はランダムっぽい。
ATSサイズは5バイトらしいのだけど、ATSって配列は1byte分しかない。値は0x05。
SEL_RESは0x60、SENS_RESは0x0400。

SENS_RESは、NFCID1のサイズなんかを表している。
SEL_RES(SAK)はかざしたタグの特徴。
昔整理した表だと、こうなっていた。

image

b5=1で、他が0なので、ISO 14443-4(RATS+PSS)、のようだ。
RATSは、Request Answer To Select Request、のことらしい。
PPSは、Protocol and Parameter Selection、のことらしい。
おっ、いったか?

いや、念のために、HCEサービスを止めて試しておかねば。
さて、かざしてみると・・・止まった(;_;)。

Android Beamを止めたとしても、LLCPとかは動いてるんだろう。
Nexus7(2012)だとNFC-FでしかLLCPなどをしていないように思っていたのだが、手元にないので自信がなくなってきた。

ともかく、R/W側ももうちょっとちゃんとType4Aとしてのシーケンスを流してやらないと、HCEが動いているかどうかの確認はできなさそうだ。
先は長いなぁ。

2013/12/08

[nfc]nfc.scサービス

最初に書いておきますが・・・1時間くらいかけて調べながら書いた後、ネットに上げようとしたら「保存できません」と言われて、泣く泣く再起動しました・・・。


nfc.scは、ブリリアントサービスさんのサービス。
ベータ版だからか、ブリリアントサービスさんのページからは飛べなかった。
NFCの開発者ブログもそうだけど、こういうのって難しいですな。
すぐに仕事と結びつくなら経費割り当てて更新するけど、会社ブログの1つとなると、効果が見えにくいし

現場の人は忙しくて更新が怠りがちになる、という。
しかも、更新する前に(きっと)文面のチェックとかもあるだろうから、その文面をチェックする人も忙しくて・・とか、なんかいろいろと想像してしまう。

でも、新しいサービスを始めたのにホームページに載ってなかったら、「あら、中で何があってるんだろう」なんてあらぬ想像をしてしまったり。

まあ、そういうのは置いておくとしよう。


さて、nfc.scのページだが・・・。
何ができるページか、まとまったところがわかりづらい。
たぶん、「開発したい」のところだろう。

FAQのページもあるのだが、NFCでできることとnfc.scでできることが混ざっている。
これは、大項目を分けた方がよいだろう。
あと、規約的なページは、それぞれ別個にしないと、利用者に嫌がられると思う。
更新日もわからないと、利用する側としては使いづらいか(利用する側は、サービス提供側のルール変更が困ると思う)。

メニューの順番も「使いたい」と「開発したい」がわかりづらいか。
だって、nfc.scは「開発者向け」と書いているのに、先に「使いたい」と書かれると誰向けなのかわからん。
βだろうとなんだろうと、使う人へはサービスを作ったサイトでやるだろうから、このページを経由するのは考えにくい。
そう思えば、「使いたい」メニューは不要なんじゃないかと思う。「開発したい」の下にぶら下げるか、メニューの順番を右にずらすか。

とにかく、nfc.scでできること、という内容がどこに書かれているかわからなかった。


大きく、提供してくれるのは2つのサービスだ。

  • アプリで使えるライブラリ(今のところAndroidのみ)
  • クラウド環境

私みたいに、アプリの実装は何とかできなくもないけど・・・という人には非常にありがたい。
NFCを使う利点は、基本的にユーザが「かざす」という操作をしたことを受け取れることだ。
画面をタップしても同じことはできるんだけど、それを「タグを読む」という動作でやることで、いくつかの制約が生まれてくる。

  • タップした端末の情報がわかる
  • 端末の位置情報がわからなくても、タグ側に位置情報があれば伝えられる

制約というか、端末の情報取得ができるって方になってしまったな。
まあ、情報抜き取りは嫌われるし、情報取得の制限はOSでできそうだけど、タグに書いてある情報を伝えるというくらいはできるだろう。
そこに、なるべく個人を絡めて、個人の情報というか、傾向的な情報を得たいところだろう。
性別、年齢、最寄り駅・・・いろいろ範囲はあるので、説明して、範囲を選んでもらえればよいのではなかろうかね。

情報の提供範囲が狭くなると、サービスの提供ができなかったり、他の人と同じサービスを受けられなかったりするだろうけど、もう、どうしようもないのではなかろうか。
自分の情報がどのくらいの価値になっているかもわかるだろうし。
「無料」ってのが、何を提供して無料になってるかは、知っておいた方がよさそうだ。


便利そうではあるが、どうしたらよいのか、というのが今のところの印象だ。
クラウドとかに詳しければ使ってみるかもしれんが、使う側としてはライブラリがあったとしても、その前にドキュメントがないと使うのが怖いのかも。

やはり、1つ実例を挙げた方が良いのかも。

[ble]iPad mini oldとNexus7 2013のCONNECT_REQを比較

前回、周波数ホッピングの計算について調べた。
Nexus7でキャプチャできなかったのは、このホッピングがアルゴリズム通りになっていないからではないか?という推測があるんだけど、どうなんだろう。

まずは、チャンネルマップを取得するCONNECT_REQを見比べよう。

iPad mini old
image_thumb[5]

Nexus7 2013 (WiFiあり)
image

Nexus7 2013 (WiFiなし)
image

WiFiの有無でHopが違う!と思ったが、どうもランダムっぽい。
Core_V4.0 p.2207によると、5~16のランダム値とのこと。

とりあえず、チャンネルマップは違うようだ。
これはAPIレベルなのか、OSレベルなのか・・・。
あまりアプリで意識するものでもなさそうだから、OSというかドライバというか、その辺でやってるんじゃなかろうかね(調べてない)。

あと、タイムアウトってのも違うみたいだ。
iOSは720msで、Androidは7000ms。
これがconnSupervisionTimeoutで、教えてもらったところでは「切断と判断する時間」らしい。


Androidがunusedにしているチャネルも調べておこう。
9~18chに相当するはずなので、2422~2438MHz(2426MHzはAdvertisingの38ch)。
さて、ここに何があるのか・・・。

いろんなものとぶつかっているので、今さらそこだけ避ける具体的な理由が思い当たらん。
通常のBluetoothとかかなぁ・・・。

http://www.ekouhou.net/%E5%91%A8%E6%B3%A2%E6%95%B0%E3%82%92%E9%81%B8%E6%8A%9E%E3%81%99%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E5%8F%8A%E3%81%B3%E6%96%B9%E6%B3%95/disp-A,2012-530455.html

 

なんにせよ、キャプチャできないんだから、アルゴリズム通りになってないかどうかって判断できないんだな・・・。

[ble]BLEのホッピング

Bluetoothは、1つの周波数を使って通信するのではなく、ある程度の範囲の周波数をチャネルに区切り、動的にチャネルを切り替えながら通信を行うらしい。
これを「ホッピング」などと呼ぶそうだ。
あまりわかっていないので、調べておこう。

Core_V4.1[pdf]が出てるけど・・・まだCore_V4.0[pdf]の資料で見ていきます。


BLEは、チャネルが40(0~39)ある。
そのうち、37, 38, 39chはAdvertising専用。
残りの0~36chがデータ通信用。

image

(Core_V4.0[pdf] p.2199)

RFのチャネル番号と、Data/Advertisingのチャネル番号は異なるようだ。
以下は、Data/Advertisingのチャネル番号で表現しよう。

 

PDUの種類も、Advertising channel用とData channel用の2種類に分かれている。
今回調べたいものは通信中だから、Data channel用のPDUになる。


channel selection algorithm

image

(Core_V4.0 p.2239)

 

image

(Core_V4.0 p.2212)

 

Data channelは37個あるが、それは状態を持っているようだ。
状態のテーブルがあって、"used"とか"unused"とかが割り当てられている感じがする。
次に使うチャネル番号を決めるときは"unused"となっているものの中から選んでいくけど、37回やると全部"used"になってしまうから、計算式で計算されたチャネル番号の状態を"unused"に戻していく。
それを「channel selection algorithm」と呼んでいるのだろう。

channelについては、「4.5.8.1 Channel Classification」に説明があった。
Data channelは「used channels」(used for the connection)と「unused channels」(not used for the connection)があり、これを「channel map」と呼んでいるそうだ。
used=使ってよい、unused=使うな、という意味か。
そして、used channelsの最小数は2、とある。つまり、最低でも2ch分は空いていないといかんということか。

このmap情報は、Link層が管理していて、masterが用意し、slaveはそれを受けとって使うそうだ。
最初は、CONNECT_REQ PDUに載せ、変更するときはLL_CHANNEL_MAP_REQ PDUを使うみたい。

 

数式としては以下の2つだけだ。

unmappedChannel = (lastUnmappedChannel + hopIncrement) % 37  ... (1)
remappingIndex = unmappedChannel % numUsedChannels ... (2)

"unmappedChannel"は、現在のところ未割り当てになってるチャネル番号。
上記の計算で、次に未割り当てにするチャネル番号を決める。
"lastUnmappedChannel"は、文字通り、最後に「unmapped」にしたチャネル番号で、最初は0。

1イベントが終わったら、式(1)でunmappedChannelを計算する。
unmappedChannelの状態が"used"だったら、そのチャネル番号をData channelとして使う。
状態が"unused"だったら、式(2)でremappingIndexを計算。

この"remappingIndex"は状態のテーブルとは別で、"used"になってるチャネル番号のものを集めたテーブル用の番号だ。
そのテーブルからremappingIndexのところにあるチャネル番号をData channelとして使うことになる。

 

訳してみたが、あってるだろうか?
実際に計算してみよう。
37チャネルでやると大変だから、5チャネルにしてやってみるか。
hopIncrement=1にしておこう。

mapped 0 1 2 3 4
state used used unused unused used

まず、最初はlastUnmappedChannelが0なので、0chを使う。

mapped 0 1 2 3 4
state used used unused unused used

0chが終わったので、計算。

unmappedChannel = (0 + 1) % 5 = 1

次は1chを使う。

mapped 0 1 2 3 4
state used used unused unused used

1chが終わったので、計算。

unmappedChannel = (1 + 1) % 5 = 2

あら大変、2chは「unused」だ。
このときは式(2)を使う。

remappingIndex = 2 % 3 = 2

remap 0 1 2
ch 0ch 1ch 4ch

remappingテーブルの2番目は4chなので、次は4chを使う。

mapped 0 1 2 3 4
state used used unused unused used

4chが終わったので、計算。

unmappedChannel = (4 + 1) % 5 = 0

また0chに戻る。


そんなわけで、ホッピングするチャネルを知るためには、masterからslaveに渡されるチャネルマップとhopIncrementを取得すれば良いことになる。

CONNECT_REQ PDUなるものがあるらしいので、見ておこう。
これは接続時のものなので、Advertising用のPDUだ。

image

(Core_V4.0 p.2206)

image

(Core_V4.0 p.2207)

ここの、ChMとHopがそうだ。
5octで40bitだから、それぞれに1/0を立てていくんだろう。

では、SensorTagで実際にキャプチャしてみよう(iPad mini old)。


image

最初が1Fだから、3bit分抜かれている。Advertising用だろうな。
37~39chなので一番最後だが、Bluetoothは並びがLittle Endianだから、先頭のb7~5ビットに0が入っているのだろう。
hopIncrementは8。

image

8 --> 16 --> 24 --> 32、ときて、次は40だけど37でmoduloして余りが3だから、3ch。

うん、解釈は間違ってないようだ。

[ble]WiFiを切ったらキャプチャできたが、納得できん

前回の続き。
SensorTagとNexus7間の通信をうまくキャプチャできない件について。
結果として、できるようになったみたいだ。

 

構成

 

現象

  • SensorTagとNexus7間は、うまくいってるようだ
  • BLE Snifferのキャプチャが、途中で止まる
    • Advertisingは取れている
    • connectすると、しばらく取れている
    • 数秒以内に正常なキャプチャができなくなる。取れるのはエラーのときだけ。

 

というわけだ。
iPad mini oldのときは問題ないので???だ。

 

どうにもわからんかったので、FacebookのWF-BTLEというグループに聞いた。
いくつか推測してもらって、その中に「AndroidのWiFiが邪魔してるのでは?」というのがあった。
まさかそんなことがねぇ、などと疑いつつもやってみると・・・取れるじゃないの奥さん

詳しい人だなあ、と思って見てみると、http://reinforce-lab.github.io/の人だった。
なるほど。


じゃあ、iPad mini oldでうまくキャプチャできているところにNexus7を近づけたら同じ現象が起きるはず、とやってみたが、こいつはうまくいっている。
うーん、これはこれで納得がいかない。

教えてもらったところでは、L2CAPで使えないチャネルをスキップするという情報交換ができるらしい。
それが機能していて、CC2540Dongleもそれを使っているのかいな。
Androidではそれを無視して送るとか・・・でもアプリは取れてるし・・・。

スニファがBLEで使うチャネルを同時にすべてスキャンできてキャプチャするなら関係ないんだろうけど、たぶんそんなことはできないだろうから、そういう推測になるかなあ。
もしできるんなら、Advertisingだけ取れて、connectしたら3分の1の確率でしか取得できない、なんてことはないはずだ。

 

では、次に調べるのは、BLEがどうやってホッピングしているか、というところだろう。
次はこのチャネル使います、という指定をしないと、うまく取得できないはずだ。

2013/12/05

[ble]うまくキャプチャできてない

眠たいので、画像だけ。

iOS(iPad mini old)とSensorTag

image

image

image

ちょっと乱れてるけど、だいたい取れてそうだ。

 

Android(Nexus7 2013)とSensorTag

image

image

image

まともそうに見えるが、最後はぐちゃぐちゃだ。
画面左下にパケット数が出ているが、AndroidはぐちゃぐちゃになってからはCRCエラー時のパケットしか出てこず、パケット数はあまり増えていかない。
iOSだと、順調に増えている。

うーん、この違いは何だ?

ちなみに、Android/iOSで動かしているアプリは、どちらもTIから取ってきたものだ。
だから、作りに違いがあるとはあまり考えにくい。

パケットの取りこぼしも考えたが、Advertisingのときは普通に取れてるから、考えづらいと思う。
まあ、ドングル側の作りがあまりよくない、とかもあるかもしれないけど、それならiOSだって同じ現象が起きてよさそうだ。

ってことは、何か別の理由があるのだろうけど、それっぽい説明が思いつかない。

2013/12/01

[ble]Androidでアプリを動かすとsnifferがうまくいかない

わからんシリーズだ。

TIのBLE SmartRF Sniffer(名前はうろ覚え)を使ってパケットキャプチャをしようとしている。
Androidの方が慣れているから、Androidにアプリを入れて、SensorTagとの通信をキャプチャしようとしていたのだが、うまくいかない。

  • Advertisingまでは取れるが、connectすると止まる。
  • connectしても続いているが、少しするとホッピングするチャネルがぐちゃぐちゃになって、止まる。

だいたいこのどちらかだ。
iPad miniと通信した場合は、そんなことはなさそうな感じがする。
SensorTagは違いが無いから、OSの差なのか?

とはいえ、どちらもアプリとしてはちゃんと動いているのだ。
うーん、訳がわからん。。。

これで「PCの性能があまりよくないから」とかだと、嫌だなぁ。

[ble][st]SensorTagのデータの見方

SensorTagのデータをBLE Snifferで見ると、こんな感じで出てくる。

image

Data Typeが「Empty PDU」なものの中に、ときどきそれ以外のものが出てくる。
「L2CAP-S」となっているものが、だいたいデータっぽい。

前半のデータは、あんまり気にしなくてよさそうだ。
DirectionはSlaveからMasterへ。つまりSensorTagが送ってきているデータだ。
image

真ん中が、本体。
image

OpCode 0x1Bは、Notificationとして送信された値という意味。image

0x004Bは、ハンドル。
これは、SensorTAgのGATT一覧表[pdf]に載っている。
image

温度=0xf615、気圧=0x8d49、でよいのかな?
センサの値をそのまま送ってるだけだろうから、各センサのデータシートを読まないと変換できん。
ちなみに、パケットの生データはこんな感じだ。

2F AD 9A AF 0A 0B 07 00 04 00 1B 4B 00 15 F6 49 8D 56 DC 8E


では、Androidのソースを少し見ておこうかね。
ここに一覧があるけど・・・なんでWindows版とかLinux版にわかれてるんだ?
あ、インストーラだ・・・。
Windows版をダウンロードしたのに、中にはexeとtarが入ってて、tarを解凍するとLinux版のインストーラらしきものが入っていた。
ナンダコレハ。。。

Windowsのインストーラを起動すると、勝手にフォルダ掘ってファイルコピーしやがった。
しかも、Androidのプロジェクトzipだし。
インストーラじゃなくていいやん!
腹が立ったので、zipとpdfだけコピーしてアンインストールした(pdfはライセンスの説明だけみたい)。

[ble]TIのSmartRF Packet Sniffer BLEはトリガとしてAdvertisingがいる?

BLE Device Monitorはあきらめ、素直にSnifferアプリを使うことにした。

http://processors.wiki.ti.com/index.php/BLE_sniffer_guide

SmartTagがiOSアプリと通信している様子を見てみようとしたのだが、何も出てこない。
アプリには刻々とデータが表示されているので、通信していないはずはない。
なのにー、なーぜー・・・。

SmartTagの横が電源ONみたいなものなのだが、押すと出てくる。
では、とモニタを止めて、また再開させると、やはり出てこない。
ADV_CONNECT_REQに書いてあるInitAを打ち込んでみたが、それでも出てこない。

ということは、モニタの開始トリガとしてAdvertisingを捉まえる必要があると言うことかな?

Advertising Channelを38にしてやってみたが、やはりボタンを押す必要があった。
ボタンさえ押せば、Channelの設定には関係なくパケットが見えるようである。
ってことは、Radio Configurationはあまり気にしなくてもよさそうだ(いまのところ)。

 

・・・と思ったけど、ときどきボタンを押してもすぐに終わってしまうことがある。
うーん、よくわからん。

CRCエラーが結構発生しているが、これはスニファが拾いきれなかったとかかな。

[ble]TI SensorTagの情報を集めよう

集めよう。
SensorTagに紙が入ってて、そこにURLが書いてあるんだけど、打ってもつながらない・・・。

とりあえず、ここから。
http://www.tij.co.jp/ww/wireless_connectivity/sensortag/index.shtml?DCMP=advertorial201309&HQS=SensorTag-bs-tech-adv-jp

iOSとかAndroidとかのソースがダウンロードできる。
AppStoreからインストールしたんだけど、OADファイルでFWアップデートできるってでてきた。
なんだ、それは?
http://www.ti.com/tool/sensortag-sw

 

BLE Device Monitorなるものがあるらしい。
USBドングルがいるって書いてあるが、今回買ったやつでいいのかな。
http://processors.wiki.ti.com/index.php/BLE_Device_Monitor_User_Guide

と思ってやってみたが、COMポートで接続するらしい。
説明を見ると、ドングルを挿すとCOMポートが見えるはずだから・・・とあるんだけど、私のところはCebal controlled devicesとして見えている。
うーむ。

http://e2e.ti.com/support/low_power_rf/f/538/t/163044.aspx
どうも、デフォルトではスニファ用のファームが焼いてあるから、焼き変えろってことらしい。
で、その方法は・・・?
CC-Debuggerなるものがいるんか・・・。
開発キットで買うとついてきたんだけど、そうしなかったからな。

 

知識が乏しい私としては、BLE Device Monitorの方が楽そうなのだが、仕方ない。
新しく買われる方は、ファームウェア開発するしないだけじゃなく、BLE Monitor用のファームを使いたいかどうかも考えてから買った方がよいでしょうな。

[ble]CC2541 SensorTagとCC2540EMK-USBが届いた

BLEをRC-S390だけで調べるのは難しいと思ったので、仕様がわかるキットを買うことにした。
TIのCC2541 SensorTagが安いということだったので、それを1つ。
あと、BLEのパケットを読むのに使えるというCC2540EMK-USBを1つ。

DigiKeyで28日に注文したのに、30日にはもう届いていた!
そ、そんなに早いのか??
1ヶ月くらいかかるイメージだったので、びっくりだ。

image

image

上の写真がSensorTagだが、同封の紙に「技適に通ってない」と書かれている。
が、心配しなくてもよい。
これについては、こちらが詳しい(というよりは、これを読んだので買った)。
http://xiangcai.at.webry.info/201311/article_8.html

念のため、基板に書いてある番号で検索した。
適合した機器の検索サイトに出てきたんだから、大丈夫だ。
http://www.tele.soumu.go.jp/giteki/SearchServlet?pageID=jg01_01&PC=007&TC=N&PK=1&FN=349ul&SN=%94%46%8F%D8&LN=6&R1=*****&R2=*****

電脳羊さんのサイトで、基板をケースに入れるときに「爪を折らないように」と書いてあるが、やってみて、確かに折りそうになった。
透明なふたがあるんだけど、なんか引っかかるようにできていて、かぱっとはいかないのだ。


CC2540EMK-USBは、TIの開発キットを買うとそっちにもついてくる。
なのに入っていない方を買ったのは、開発キットの方はチップ側のソフトを焼き込まないといかんらしく、それにはIARの開発環境しかない、ということだった。
30日までは無料なんだけど、そっから先は買わんといかんらしい。
制限無く無料の方はリンクサイズに制限がかかるらしく、それではBLEのライブラリが載らないんだとか。
http://vibrosoft.net/archives/56

ソフトは、こちらからダウンロード。
http://www.ti.com/tool/packet-sniffer

インストールして、EMKをPCに挿して、アプリ起動。
選択ダイアログが出てきた。

image

 

Startを押すと、取得画面らしきものが開く。
三角の再生ボタンっぽいものをクリックすると、おお、なんか取得してるみたいだ。

image

SensorTagの電池を抜いても何か取得してるのであせったが、RC-S390の電源が入っていたようだ。
RC-S390のボタンを2秒押しして、LEDが1秒点灯したら電源OFFだ。
そうやって電源を全部落とすと、ログを取得しなくなった。

さて、ようやくデータを見ながらの勉強ができそうだ。

オライリーさんからNFC HACKSが出た

http://www.oreilly.co.jp/books/9784873116242/

オライリーさんから、NFCの本が出た。
私が持っている「HACKS」シリーズは、BINARY HACKSだけだった。
が、今回、ちょっとだけレビューさせていただいたので、NFC HACKSを寄贈いただいたのだ!
わーい。
「hiro99ma」が初めて活字化された本としてプレミアが出るに違いない(出ません)。
まあ、私が書いたものは何もないんですけどね。。。

書いた人の一人(Atelier NODOKA)
http://www.atelier-nodoka.net/2013/11/oreilly-nfc-hacks/

レビュー記事(明日の鍵)
http://blog.tomorrowkey.jp/2013-11-27/review-of-nfc-hacks/


うちのサイトをNFC観点で見ていくとわかるのかもしれないが、基本的に興味があるのは「作り方」の部分。
仕様書から仕様を把握して、それを実装に落とす、というところまでが好きなようだ。

そんな私が興味を持ったのは、第2章。
特に、Type4とかISO-DEPには注目せざるを得ない。
そう、HCEがあるからだ。

HCEでできるのは、Type1でも2でも3でもなく、4らしいではないか。
Type2と3は日本でもよく使われるから解説記事があるけど、Type1と4はあまり例がないためか情報を見たことがない。
それが日本語で読めるのは非常にありがたい。
いや、NFC Forumからドキュメントは出てるんだけど、知ってる言語で書かれてる方が何かと、ねぇ。

 

また、DEP/LLCP/SNEPが載っているのも見逃せない。
DEPも、LLCPで使うNFC-DEPだけじゃなく、Type4で使う(と思ってるけど)ISO-DEPのことも書かれている。
NFC-DEPはISO/IEC-18092に載っていて、それはECMAからダウンロードもできるので無料で情報が得られるけど、ISO-DEPはISO/IEC-14443で、これはまだダウンロードできる仕様書を見つけられていない(あやしいのはあったけど)。
Androidでアプリを作るだけであれば意識しなくてもよいかもしれないが、とはいえAndroid APIにはIsoDepもあるので知っておいて損はないんじゃなかろうか。

 

まあ「アプリ作るだけならそこまで知らなくても」というのはあるかもしれないが、技術屋さんとしては訳がわからないものは気持ちがわるいではないか。
そこら辺がすっきりできるであろう(個人の感想です)。


AndroidとWindows NFP, PC/SCのことも書かれている。

まだAndroid 4.4が出る前なのでHCEのことは書かれていない。
が、それを待っていたらキクニさんの雨宿りしている人のマンガじゃないけど、いつまで経っても出せなくなるからな。

ただ、Secure Elementについては実装から追っていった情報が載っている(HCEはSecure Elementを使わない)。
SEは私は操作できないから興味が無いのだけど、仕事でやらんといかん人もおるかもしれん。
FeliCaもSIMベースに移行する予定だったと思うので、「Androidで国内端末だから関係ないや」という人もぼちぼち読んでいった方がよいかもしれませんな。

Androidアプリに詳しくはないが、NDEFの読み書きやAndroid Beamなどの基本機能の使い方(=実装の仕方)が書かれている。
NFCといえばAndroid端末、って気がするので一番ページ数が多いと思うかもしれないが、実は一番ページが多いのは2章の規格説明だったりする。

 

WindowsはNFPがWindows8から使えるので情報がよく出ているけど、PC/SCは縁遠かった。
が、AndroidでHCEが出てきてType4の振る舞いをする(らしい)となったとき、APDUなるものでアクセスする関係から、PC/SCの方も調べることがあった。
まあ、まだうまくはできてないんだけど、この本に例があるので、足がかりに読んでみようと思う。


ページ数は少ないが、5章のNFC応用サービスは興味深い。
私なんかは自分で作って満足するだけなんだけど、サービスに昇華させろ、といわれたら非常に困る。
考慮する点がわからんからだ。

この章は「セキュリティに配慮した」というタイトルにもあるように、NFCが持つ情報として考慮すべき事が書かれている。
これだけ押さえてれば大丈夫、というのはサービス内容によって変わるため無理だけど、書かれていることと照らし合わせながら、やりたいこととやれること/やらない方がいいことを洗い出していくといいんじゃなかろうか。


私は仕事でNFCしてるわけじゃないのだが、2年くらい細々と調べていたのでおおよそのことは知ってるつもりだったけど、いやあ、まだまだですわ。
でも、よくわからないときはこの本を見ればいいや、という使い方ができそうだ。

 

NFCって、それなりに特殊な分野だと思う。
通信して、メモリ持ってて、超近距離で、セキュリティも持ってて、とサービスにマッチしなければ使い道がないってくらい特殊だ。 
なので、「NFC使って何かやろう!」ってよりは「サービスを考えた結果、NFCってのが使えそうだ」というパターンが多いんじゃなかろうか。
そう考えると、NFC技術者って必要になってから生み出されることになりそう。
そういう人が「仕事でNFCってのをやらんといかんのだけど、何したらいいんだ?」という状況になったときには最適だろう。

また、今のうちにNFC技術者になっておけば、『お客様の中にNFC技術者の方は…』という状況の時に手を挙げられることになる。
なるのか・・・?
まあ、NFCやろうっていうときに、やったことある人とない人だったら、ある人を選ぶな。
そういう人になりたいときにも、よいかな。

2013/11/24

[ble]master/slave, initiator/responder, server/client

用語が、用語が・・・。

似たような感じの言葉がよく出てくるのだが、使い分けがわかっていない。
特に、「master/slave」「initiator/responder」「server/client」は、どれも同じような感じがしつつ、やっぱり違うんだろうけど、曖昧なままごまかしてきた。

そろそろ、はっきりさせたい。


Central/Peripheral

まずは、これか。
いわゆる「デバイス側」がPeripheralで、その持ち主が「Central」というイメージだ。

Core_V4.0のp.200に「BLEのGAPは4つのroleを定義する:Broadcaster, Observer, Peripheral, Central」とある。
つまり、GAP(Generic Access Profile)の機能ということだろう。

 

Master/Slave

Core_V4.0のp.1639-1640にGAPの4roleが対応する機能一覧がある。
そのLink Layer functionalityのConnection Stateに「Slave Role/Master Role」がある。
CentralはMaster Role、PeripheralはSlave Role。
つまり、LL(Link Layer)の機能ということだろう。

AdvertisingするのがSlaveで、それを受け入れるのがMaster、と思っている。
なので、PeripheralがAdvertisingし、Centralが受け入れる、ということになる。

 

Initiator/Responder

これはまだ私の調べごとには出てこないのだが、Interface誌にでていた。
「マスタ(イニシエータ)」「スレーブ(レスポンダ)」と書いてある。

p.2130に出てきている。
これは「Conditions」の説明なので、状態というか、そういうものなのか?
Physical Link Createコマンドを受けとるとInitiatorになり、Physical Link Acceptコマンドを受けとるとResponderになるのかな。
説明が「physical link initiator/responder」だから、PHYの機能ということだろう。

PHYが一番下の階層だ。
LL(Master/Slave)はその上。
GAP(Central/Peripheral)はもっと上。

検索したけど、あんまりそれっぽいことが書かれていない。
忘れてしまうか。。。

 

Server/Client

自分で書いておいて何だが、これは「Attribute Server/Client」のことかな?
ATT serverとATT clientみたいだ。
p.141には、ATT clientがコマンド、要求、確認をATT serverに対して送信する、と書いてある。
ATT serverはそれに対して、応答、通知、指示をATT clientに送信する。

Attribute Protocolの説明なので、これはGAPと同じ階層のようだ。
また、p.141にGATT(Generic ATTribute profile)の説明もあり、ATT serverの機能を実現しているのがGATTだ、とある(optionallyでATT clientも)。
p.1637にプロファイルスタックの図があったのだけど、GAPがベースのようになっていて、そこにGATTがいるのか。

image

で、この辺でPushとかPullとかの話が出てくると思ったのだが、Core_V4.0を「Push」で検索しても引っかからない。
なんだ?
それに、CharacteristicとAttributeの区別もついていない。
「Characteristic」という属性がついたAttributeがCharacteristicってことかいな?
Characteristicのアクセスは、ReadとかWriteとかNotifyとか、そんなのだよなぁ。。。
うぅぅ。。。

[ble]BLEを試してみたい (5)

iOSはまあ何とかなりそうなので、Androidを進めていこう。
いや、うちのMac miniは遅くてイライラしてしまうのだ。
SSDにすべきかなぁ。


そうそう、スキャンして目的のデバイスを見つけることについて考えないと。
今は、デバイス名だけを見比べて、一致したらやめている。
でも、同じBLEデバイスが何台も置いてあるという状況もあるだろう。
そう考えると、デバイス名だけってのは乱暴そうだ。

ならば、デバイス一覧を表示させて、ユーザに選んでもらうしかない。
ペアリングの入力無しで、デバイスに対しても個別の設定が不要なのであれば、見つけたものをどんどん処理していけばよいだろう。
そのときは、デバイスのハードウェアアドレスで見分けるしかないのかもしれないが、外見ではどれがどのデバイスかを見分けるのは難しいだろう。
なるべく安く作りたいとしても、LEDくらいはつけておいて、自分の状態を表現できた方がよさそうに思う。

あ、デバイスを作るときのことね。


さて、スキャンした結果がコールバックされたとき、引数が3つついてくる。

onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)

deviceとrssiはいいのだが、scanRecordはなんだ?
「The content of the advertisement record offered by the remote device.」とあるので、Advertisingのときに送られてきたデータなのだろう。

RC-S390の場合、37byte返ってきているみたいだ(配列要素は62あるけど、37byteまでデータが詰まってて、残りは0x00だった)。
バイナリ値があれば解析をする・・・それが刑事の哀しい性(?)だ。

Core_V4.0の仕様書を「37」で探したところ、p.2202がそれっぽい。
「 The valid range of the Length field shall be 6 to 37 octets.」

image

image

ここのLengthだろう。
scanRecordが37byteで、それはFigure 2.2のPayloadにあたるのであれば、Figure 2.3は関係ないな。

スキャンした結果が返ってきたのならば、p.2206のSCAN_RSPになるのか。

image

AdvAはランダムだかなんだかのデバイスアドレス、ScanRspDataはAdvertiseしたホストからのデータ。
ということは、RC-S390が私に送ってくれた何かということになる。

scanRecordを眺めてみたが・・・さっぱりわからん。
最後の6byteが「PaSoRi」という文字になっているということはわかった。

SCAN_RSPでネットを調べると、私が思い違いをしているようだ。
http://reinforce-lab.github.io/blog/2013/02/07/ble-linklayer/
AdvertisingとScanは別の動作らしい。
p.2203によると、PDUの種類は7つ。

image

このうち、_INDがAdvertising PDUだそうな。
Payloadは、AdvAはどれもあり、違いはAdvDataがあったり別だったりするくらいだ。

image

で、どれもホストからのデータというくらいで、詳細は無い。
先ほどのリンクを読むと、LengthとDataが並んでいるらしい(Dataは、AD TypeとAD Dataから成る)。

Core_V4.0をAD Typeで検索すると、p.1735が出てきた。
1octのLengthと、そのLength長のData。Dataはn octのAD TypeとLength-n octのAD Data。

image

AD Typeはこちらの一覧を見るらしい。
いやあ、ここまで書かれてあると非常に助かりますわい。

で、これを見ながら解析していったのだが・・・あわない。
AdvAが無いものとして解析するとちょうどよさそうなのだ。
どうも、引数のscanRecordは、AdvertisingのパラメータとScanの結果が一緒に入ってきてるような気がする。
サイズも62byte分ということで合うし。
ということは・・・「37」って数字でそれっぽいところが引っかかったのは、単なる偶然だったのか・・・。
おそろしや・・・。

 

入っていたのは、こんな情報。

  • BR/EDR Not Supported
  • Incomplete List of 128-bit Service Class UUIDs
  • Slave Connection Interval Range : 20ms~300ms
  • Tx Power Level : 0 dBm
  • Complete Local Name : PaSoRi

BluetoothDevice.getName()に入ってるのは、このLocal Nameなのかな。
このデータをアプリがどのくらい管理しないといかんのかはわからないが、あまり気にしなくてもよさそうな気はする。

 

さて、次はConnectionだ。
Androidのサンプルはなんか難しいので、こちらを見てやっていくことにする。
http://blog.fenrir-inc.com/jp/2013/10/bluetooth-le-android.html

[java]コールバックしてToastを出したい

半分Java、半分Androidだ。

まず、コールバックしたい。
こちらはJavaだ。

コールバックする側interfaceを作っておき、コールバックしてほしい人はそのインスタンスを作り、中身を作っておく。
そしてコールバックする側に渡す。
コールバックする側は、そのメソッドを呼ぶだけ。

 

// コールバックする側がinterfaceを作っておき、
public interface callback {
    public void onResult(int result);
}

--------------

    //コールバックしてほしい人はそのインスタンスを作り、
    //中身を作っておく
    private xxClass.callback mCallback = new xxx.callback() {
        @Override
        public void onResult(int result) {
            Log.d(TAG, "callbacked");
        }
    };

//そしてコールバックする側に渡す
{
    xxClass xx = new xxClass();
    xx.function(mCallback);
}

--------------

//コールバックする側は、そのメソッドを呼ぶだけ
void function(callback cb) {
  cb.onResult(0);
}

--------------

interfaceじゃなくてabstractってのでやろうとしたけど、なんかダメだった。
気が向いたらちゃんと調べよう。


コールバックした先でToastを表示したい。
こっちはAndroidだ。

やったんだけど、コンテキストが違うせいか、コンパイルで怒られた。

The method makeText(Context, CharSequence, int) in the type Toast is not applicable for the arguments (new BleUtils.scanResultCallback(){}, String, int)

BleUtils.scanResultCallbackは、上でいうところのxxClass.callbackだ。
Log.d()は使えたんだけど、Toastはだめだって。
まあ、画面に何か出すんだから、UIコンテキストじゃないとダメなのかな(追求してない)。

これは確か、BLEのサンプルで何かやっていた。

    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
      runOnUiThread(new Runnable() {
        @Override
        public void run() {
          mLeDeviceListAdapter.addDevice(device);
          mLeDeviceListAdapter.notifyDataSetChanged();
        }
      });
    }

このrunOnUiThreadってのがそれっぽいようだ。
が、同じようにしてみたけど、やはり怒られる。
うーむ。

あれだ、コンテキストが違うので、第1引数をthisってしてもだめなようだ。
getApplicationContext()とすると、エラーが消えた(参照:モバイル開発系(K))。
そうかそうか。

もう1つ。
コールバックの引数で文言を変えたい。
このときは、仮引数の宣言をfinalにするとよいようだ。
スタック渡しだからダメなんだろうと思っていたんだけど、Javaだから「まだ使います」って言っておけばよいということなのかね。

 

これでようやく、BLEの検索結果を戻せるところまで来た。
https://github.com/hirokuma/BleTest

[ble]RC-S390は定期的にAdvertisingしているようだ

試していて気付いたのだが、RC-S390をAdvertisingにしなくても、ペアリングをしていなくても、スキャンはできているような気がする。
いや、そもそもRC-S390は自分で作ってるわけでもないので、どこでAdvertisingになっているかなんてわかるはずもない。

と思ったのだけど・・・やっぱり、AdvertisingじゃないとCentralはスキャンできないんじゃないかと思う。
だって、他に方法がなさそうだから。
iPad miniにインストールしているPaSoRiアプリも、RC-S390のボタンを長押ししなくてもペアリングしにいこうとする。
じゃあ、そのときNexus7の設定アプリから見えてるかというと、見えない。
RC-S390の青LEDが点滅するモードになると、設定アプリから見える。
うーむ・・・。

 

まず、RC-S390の使い方を把握しておこう。
ボタンを2秒長押しすると、電源のON/OFFができる。
見分け方は、

  • 電源がONになった・・・通信状態ランプ(青LED)が3回点滅
  • 電源がOFFになった・・・通信状態ランプ(青LED)とバッテリー残量ランプ(橙LED)が1秒点灯

だ。
では、2秒長押ししてみよう。
うん、両LEDが点灯したから、電源OFFだ。
では再度2秒長押ししたら・・・あれ、また両LEDが点灯したぞ・・・。
さらに長く押したら、青LEDが点滅し始め、LightBlueが検出した。

つまり、一度ペアリングさせないと電源がONにできないという仕様なんだな。
まあ、製品としてはそうか。

LightBlueが反応したので情報を見てみると、ペアリングを要求された。
要求はされたのだが、サービス一覧は取得できたみたいだ。
このとき、Nexus7の昨日作ったスキャンアプリは検出できなくなっている(PaSoRiは青LED点滅中)。
ペアリング要求をキャンセルしても、Nexus7からは検出できない。
BlueLightで「PaSoRi」をタップしてDisconnectすると、Nexus7からも見えるようになった。
Nexus7からペアリングすると、青LED点滅は消えた。
この状態でボタンを2秒長押しすると、両LEDが点灯。つまり電源OFF。
もう一度2秒長押しすると、青LEDが3回点滅。つまり電源ON。
BlueLightでスキャンすると、PaSoRiが見えている。

 

わかったことをまとめよう

  • PaSoRiが誰ともペアリングしていない場合、電源のONだけをすることはできない
  • ペアリングしていなくて青LED点滅しているとき、誰もConnectしていなければAdvertisingしている
  • 誰かがConnectすると、Advertisingしない(当たり前)
  • Connectionするとペアリングの要求を行う。ペアリングしなくてもサービスとキャラクタリスティックの一覧は取得できるみたいだ(まだBLE仕様をわかってない)
  • ペアリングすると、ボタンで電源のON/OFFができるようになる
  • 電源OFFのときは、スキャンしても検出できない(当たり前)
  • 電源ONにすると、スキャンして検出できる。

ってことは、ペアリングして電源ONなら、ConnectionしていないときはAdvertisingしてるってことか。
それはUndirectedでやっているので、ペアリングしていない人でもスキャンできてしまうのかな。
ペアリングしている人にだけAdvertisingする方がよさそうではあるが、別の端末で使いたいときに面倒になるのかな?
いや、それなら5秒長押ししたときにだけUndirectedなAdvertisingにすればよさそうなものだが。
まあ、ここはまだ私が仕様をよくわかっていないせいかもしれんので、わかってから考えよう。

 

いつだかの推測で、相手がいなくてもConnection状態を維持しているのでは?と書いていたが、そうではなく、Connectionしてない間はAdvertisingしているので、それをアプリが捉まえている、という動作なのだろう。

image

2013/11/23

[ble]BLEを試してみたい (4)

さて、Androidでデバイスのスキャンまでできたので、iOSで同じところまでやってみよう。

https://github.com/hirokuma/BleTest_ios

こっちは、あれこれ参考にさせていただいた。
というのも、iOSアプリのしくみがよくわからんかったからだ。
プロジェクト管理に回るっていうのはこういうことなのね・・・という一抹の寂しさを感じる瞬間である。

それはいいとして、今回はまったのはここだ。

mCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

みんな「self」って書いてるけど、型は「id<CBCentralManagerDelegate>」なのだ。 ってことは、selfと言ってはいるけれども、なんか継承しているはずだ。

結果としては、ここを参考にした。 
https://github.com/reinforce-lab/CoreBluetooth_samples/blob/master/BSHSBTPT01_samples/first_sample/KeyFobSample/KeyFobController.m

mファイルに@interfaceを書き、 @interface xxx() <CBCentralManagerDelegate>

のような形にして、selfに持たせた。
まあ、selfじゃなくてもいいんだけど、さっき書いたAndroid版と同じようにしたかったので、なるべく閉じたファイルにしたかったのだな。


しくみがわからんかっただけで、今のところ実装量はiOSの方が少ない。
と思ったが、まだiOS版はスキャンの停止を組み込んでなかった。
これからですな。。。

[ble]BLEを試してみたい (3)

Androidのサンプルを見ながら作っていたのだが、私が知らない小技があって(というよりも、私が知らなさすぎるだけだが)、もうちょっとシンプルな作りにしたいと思った。

で、結局こちらを参考にさせていただいた。
http://blog.fenrir-inc.com/jp/2013/10/bluetooth-le-android.html

Activityに突っ込んでしまうと、他のアプリを作るときに持ってくるのが面倒になるので、別のクラスを作ってそこにBLE操作関係を置くようにした。
https://github.com/hirokuma/BleTest/blob/master/src/com/blogpost/hiro99ma/bleutils/BleUtils.java

動かすと、アプリ起動などと同時にスキャンを行う。
スキャンしたデバイス名は、logcatに出てくる。
うちにはRC-S390しかないのだが、ちゃんと「PaSoRi」が出てきた。

 

サンプルを見ていてよくわからなかったのが、BluetoothAdapter.isEnabled()を2回呼んでいるところ。
なんだろう?
絶妙なタイミングで呼び出された場合を警戒しているのか?

これで、基本的なスキャンのところまではできたんだけど、スキャン結果をコールバックするところが無い。
理由は簡単で、やり方を調べていないからだ。
まあ、これからだな。

[ble]BLEを試してみたい (2)

Android4.4のUSBデバッグアイコンが、いつも気になる。
キットカットなんだろうとは思うけど、なんか、なかなか、ねぇ。

image

  • チョーク4本
  • いかだ
  • お風呂のふた
  • 本の背表紙

4.4も、最初はKがつくパイの名前だったと思う。KがつくパイといえばKidney Pieが思いつくんだけど、あれはお菓子じゃなくて料理なのかな?


では、サンプルソースもあることだし、まねをしながら同じようなアプリを作ってみよう。
作りながらメモを書いていくだけなので、途中で間違ってしまう可能性有りだ。
ちゃんとした説明は、以下のページがよさそうだった。
【連載】Bluetooth LE (5) Android 4.3 で Bluetooth LE 機器を使う

じゃあ書かんでいいやん、と思われるだろうが、勉強の一環だ。

 

AndroidManifest

まずは、Android Manifestだろう。
uses-featureとuses-permissionを追加するようだ。

image

android.hardware.bluetooth_le

 

image

android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN

それぞれ、通信許可と検索許可らしい。
ペアリングが済んでいたらADMINはいらないのかな? まあ後で試そう。

いつも思うのだが、uses-permissionはコンボボックスで選択できるのに、uses-featureはテキスト入力なので、うろ覚えで選択ができない。
こっちもコンボボックスにしてくれないものか。

サンプルではServiceがあるので、そのための記述があるけど、まあ後で考えよう。

 

BLE peripheralのスキャン(の下調べ)

まず、対象の機器を探そう。
RC-S390はボタンを長押しすると「私はここよ!」みたいなモードになるらしい。
たぶん、これが「Advertising」という状態なんだろう。
Interface誌の説明では、Advertisingとかはリンク層の動作らしい。
まだGATTとかのアプリは絡んでこない。

Advertising→Connecting、という流れだそうな。
Core_V4.0[zip]を見ると、p.2263に状態の一覧らしきものがあった。

  • Standby
  • Advertising
  • Scanning
  • Initiating
  • Connection

まず最初はStandby。
Initial Setup→Random Device Address→White Lists、という過程を経ないとadvertising/scanning/initiatingにできなさそうだ。
PDFでは、Host A/LL Aだけがやってるけど、これはHost B/LL Bでも同じなのかな?
どちらかがCentralで、どちらかがPeripheralになると思うのだが。。。

Advertisingは「by enabling advertising」とあるので、Standby状態から「advertisingになりたい」という要求を受けないと遷移しないのだろう。
RC-S390では、そのトリガがボタンの長押しということか。
UndirectedとDirectedの2種類があるらしい。PDFのシーケンス図を見ると、Undirectedでは"Advert"が飛んでいるが、Directedだと"LL_DIRECT_IND"が飛んでいる。。。何を見たらいいんだ。。。

 

PDFのp.1639にGAPと機能の必須/オプションみたいな表がある。
GAPは、Generic Access Profileの略らしいが、もうこの際細かいことはどうでもよい。
PeripheralとかCentralという文字が出てきてるので、この表を見ると何かわかりそうな気がする。

  • StandbyはCentralもPeripheralも必須
  • AdvertisingはPeripheral必須
  • ScanningはCentral必須
  • InitiatingもCentral必須
  • Connectionは、CentralはMaster必須、PeripheralはSlave必須

ということらしい(MはMandatoryだろうけど、Eはなんだろう。。。)。
Advertisingに2種類あると書いたが、Undirectedが必須で、Directedはオプショナル(なんかタイプが44つあるみたいで、Connectable undirected eventってのが必須)。
後で出てくるScannningも、Activeは必須(正確にはC1)で、Passiveはオプショナル。
(でも「オプショナル」っていいつつ、普通載せるけど、ってことあるしなぁ。。)

 

さて、基礎知識を身につけたところで、再挑戦。
Standbyは、CentralもPeripheralも実施する。
そしてPeripheralはAdvertisingを開始し、CentralはScanningを開始。
CentralがScanningしてPeripheralを見つけたら、Initiatingする。
Initiatingは、PeripheralとしてはAdvertに対してCONNECT_REQが返ってくる動作に見えるようだ。そこまで含めて"Advertising"ということか。
Initiatingが終わると、接続完了ってことで、たぶんConnection状態になるんだと思う。

調べるのが面倒なので推測だが、Connection状態になると、「切断」ってやらない限りは接続が切れないようになってるんじゃなかろうか。
そうじゃないと、RC-S390をつなげたiPad miniが電源とか切ってても、アプリを起動したらAdvertisingしなくても使えるようになることの説明がつかない。
まあ、相手がいなかったら切断するってしくみにすることもできるんだろうけど、プロトコルとして制約がないってことなんじゃなかろうか。
ほら、NFCのLLCPだと一定時間内に応答がなかったら切断シーケンスに入るから、それとは違うということをいいたいのだよ。

 

脱線したが、スキャン動作は、ペアリングとは別として「必要」ということだ。
じゃあペアリングはなんなんだ? なくてもよいのか??
これもInterface誌にあった。
リンク層の接続はAdvertisingとかでやるが、ペアリングはもう少し上の層での認証接続とのことだ。通信する相手が正しいかどうかの確認を行うということか。
だから、ペアリングしなくてもRC-S390は見えたが、サービス取得などができなかったということのは、まだ接続してよい相手として認められていなかったからだろう。
なお、Interface誌の説明ではペアリングにも4種類あり、自動で承認するタイプもあるんだって。
へー。

 

また脱線してしまった。。。
長くなりすぎたので、今回はここまで。