Arduino用RC-S620/Sサンプルスケッチの改造
先日実行したRC-S620/Sライブラリ付属のサンプルスケッチでは、RC-S620/Sにおサイフケータイ(モバイルFeliCa搭載端末)をかざすと@SDK4FeliCaのURLがPushされるようになっている。
FeliCa Pushとは?
ここでPush(FeliCa Push)というのは、おサイフケータイに対して別のおサイフケータイやリーダ・ライタからURL(ブラウザ起動)や、メールアドレス(メーラ起動)、AndroidであればアプリのIntent情報を投げる機能のことである。私の身近で使われている例では、カラオケ館受付のケータイ会員証表示端末(PitTouch端末と言うらしい)やほっともっとのほっとポイントくらぶなどがある。
サンプルスケッチの改造策
さて、既に動くサンプルスケッチに手を入れることを考えたとき、最初に思いつくのがPushするURLを変えてみることである。デフォルトのまま展示したのでは、なんだかSonyの回し者っぽい。
幸いにも、Pushに関してはFeliCaNetworksのサイトで仕様が公開されている(技術情報→その他→リーダライタ端末からの携帯ブラウザ起動等(三者間push)に関する情報→『モバイルFeliCa IC チップ機能解説書(外部リーダ/ライタからの携帯端末内特定機能の起動)』、『外部リーダ/ライタから携帯端末内の特定機能を起動するデータフォーマット仕様書』の二種類)。
ただし、Arduino用RC-S620/SライブラリではPush機能をカプセル化してくれているため、『外部リーダ/ライタから携帯端末内の特定機能を起動するデータフォーマット仕様書』のみを参照すればよい(ライブラリ使用者は個別部数、個別部、チェックサムのみのデータを作ればよく、Push先のIDmとかは気にしなくて良い)。
Pushデータの解読
……と、今でこそそれっぽく書くことができるが、実際に試行錯誤する段階ではそのような情報が落ちていることを知らなかった。そのため、私が一番はじめに着目した部分は、push関数に渡す以下のデータ配列だった。
uint8_t data[] = {
0x01, 0x02, 0x2a, 0x00, 0x1d, 0x00, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x74, 0x77, 0x69,
0x74, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x53, 0x44, 0x4b, 0x34, 0x46, 0x65, 0x6c,
0x69, 0x43, 0x61, 0x46, 0x6f, 0x6c, 0x6c, 0x6f,
0x77, 0x20, 0x4d, 0x65, 0x21, 0x21, 0xf1, 0xee,
};
わけわかめである。が、ピンと閃いて各要素でASCII文字にできるところは置き換えてみた。それが以下。
uint8_t data[] = {
0x01, 0x02, 0x2a, 0x00, 0x1d, 0x00, 'h', 't',
't', 'p', ':', '/', '/', 't', 'w', 'i',
't', 't', 'e', 'r', '.', 'c', 'o', 'm',
'/', 'S', 'D', 'K', '4', 'F', 'e', 'l',
'i', 'C', 'a', 'F', 'o', 'l', 'l', 'o',
'w', ' ', 'M', 'e', '!', '!', 0xf1, 0xee,
};
文字を並べると、「http://twitter.com/SDK4FeliCaFollow Me!!」となる。なんだか暗号解読みたいで面白くなってきた。
それでは、この文字の部分を任意のアドレス、たとえば「http://www.atelier-nodoka.net/」などに変えてみてはどうかとやってみた。ダメだった。
ふと思い立ち、NFC猛者さんのSDK for NFC Starter Kit サンプルプログラムのまとめを参照してみる。困ったときの猛者さんである。サンプルプログラムの中には、FeliCa Pushを実現したものもある。そこでgithub上のコードをカンニングすると、なんとなくデータの生成方法が分かる。
すなわち、先ほどASCII文字化できなかった部分に関して以下の法則が認められた。1バイト目は0x01固定、2バイト目は0x02固定であること。3バイト目4バイト目は総データ長(個別部パラメータ長)をリトルエンディアンで格納、5バイト目6バイト目は総データ長のうちURLの長さをリトルエンディアンで格納する(サンプルスケッチのデータにはFollow Me!!という文字列が含まれるが、ちゃんと動くのはこの部分を適切に指定しているからだ)ということである。また、最後の2バイトにはそれ以外の各バイトを加算していった値の負数(つまり2の補数)をビッグエンディアンで指定する形式のチェックサムとなっている。
コードはおおよそ、『外部リーダ/ライタから携帯端末内の特定機能を起動するデータフォーマット仕様書』に書いてあるとおりにデータを生成していたのだが、この時点では知らなかった。
Arduino用に移植
猛者さんのコードを私なりにスケッチに移植したのが次のget_push_data関数である。この関数は、pushしたいURLとデータ配列、さらにデータ長を格納するための変数へのポインタを渡す(書いててふと思ったが、data_lengthを戻り値として返しても良かったかもしれない)。
void get_push_data(char url[], uint8_t* data, byte *data_length)
{
word chksum = 0;
word cnt = 0;
word str_len = (word) (strlen(url) + 2);
// 個別部数 = 1
chksum += data[cnt] = 0x01;
cnt++;
// ブラウザ起動 = 2
chksum += data[cnt] = 0x02;
cnt++;
chksum += data[cnt] = (byte)(str_len & 0x00ff);
cnt++;
chksum += data[cnt] = (byte)((str_len & 0xff00) >> 8);
cnt++;
str_len -= 2;
// URLの生成
chksum += data[cnt] = (byte)(str_len & 0x00ff);
cnt++;
chksum += data[cnt] = (byte)((str_len & 0xff00) >> 8);
cnt++;
for(int i = 0; i < str_len; i++) {
chksum += data[cnt] = url[i];
cnt++;
}
// チェックサムの追加
word sum = (word)-chksum;
data[cnt] = (byte)((sum & 0xff00) >> 8);
cnt++;
data[cnt] = (byte)(sum & 0x00ff);
cnt++;
*data_length = cnt;
}
それで最初のデータ配列はおおよそこんな感じになる。
char url[] = "http://www.atelier-nodoka.net/";
byte len;
uint8_t data[192];
get_push_data(url, data, &len);
こうして、任意のURLをPushできるようになった。
変数dataについて、はじめはuint8_tへのポインタ(uint8_t *data)という形で定義していた(書いてて気づいたがこれ、実はとても危険なことをしている)。JavaやC#、Rubyなど最近の言語を使っているとつい忘れがちであるが、メモリの確保をちゃんと意識した上でコードを書くべきだと思った。