わふうの人が書いてます。

iOSアプリケーション開発、BLEのファームウェアとハードウェア試作のフリーランスエンジニア。

nrf5 sdk12 のブートローダの作り方

2017年1月時点で、nRF52のファームウェア開発は、S132 SDK 12.2.0 を使っています。その開発環境でのブートローダの作り方のメモです。NordicのDFUは、SDKのバージョンごとに変更が多く入ります。

ドキュメント: BLE Secure DFU Bootloader

SDK12でのブートローダの作り

まず、ブートローダをセキュアにするために、バイナリ転送に公開鍵暗号方式が使われます。
これまでのDFUには、書き込もうとしているファームウェアが正規の開発元が提供するものかどうかを確認する機能はありませんでした。ですから、DFUに入る方法さえ調べてしまえば、改造したファームウェアを正規のDFU手順で書き込むことができました。
セキュアになったDFUは、転送されたバイナリが開発元しか知らない秘密鍵で署名されているかどうかを確認します。この機能で、開発元しか、ファームウェア更新ができなくなります。

ブートローダの実装では、DFUを開始するinit packetに、protocol buffersが採用されています。
Protocol buffersは、Googleの社内で使われているデータ構造定義ファイルから、シリアライザ/デシリアライザおよびRPCサーバのコードを生成するツールです。
拡張子( .proto )のファイルに、C言語に似たProtocol buffersの記法で列挙型や構造体のデータ構造を表記すれば、そこからC++を始めとする各種言語用のシリアライザ/デシリアライザのコードを生成します。
Protocol buffersのシリアライザには、プリミティブな値型のバイナリを単に並べて詰めていくだけではなく、連続する0は短い表現にしてデータ長を短くする簡単な圧縮機能もあります。

ブートローダの実装では、DFUに入るかどうかを判定するbooleanの値を返す関数が、weak宣言されています。ですから、ユーザがその関数を宣言すればユーザが宣言した関数がリンクされ、もしも宣言していなければデフォルトの関数としてSDK内部にあるweakなその関数がリンクされます。
SDK10のDFUでは、DFUに入るかどうかの判定関数がSDKのソースの一部に入っていました。そのため、そのDFUに入るかどうかの処理を独自に作りたい部分は、SDKから該当のソースコードを取り出して、直接ソースを変更する手間がかかっていました。

ブートローダのサンプルコード

ブートローダのテンプレートプロジェクトは、 /examples/dfu/bootloader_secure/ にあります。pca10xxxという開発ボードの名前ごとに、プロジェクトファイルがあります。

このブートローダに対応する、ファームウェア側でDFUに入るためのコードサンプルは、Experimental: Buttonless DFU Template Application にあるように、 /examples/ble_peripheral/experimental_ble_app_buttonless_dfu のボタンレスDFUのファームウェアです。これはDFU専用のサービスとキャラクタリスティクスを作り、そこに0x01が書き込まれればDFUに入ります。

DFUに入るフラグの保存方法

SDK12のDFUは、デフォルトの実装では、DFUに入るフラグをフラッシュに書き込み、そして再起動してブートローダがそのフラッシュのフラグを読み出して、DFUに入る動作をします。
フラッシュへの書き込みは、専用のライブラリがSDKに用意されています。 experimental_ble_app_buttonless_dfu のソースコードを追っていけば、その関数などが見つかります。

SDK10では、リセットされても初期化されないユーザが任意に使えるレジスタにDFUモードに入るフラグを書き込む実装を使っていました。フラッシュを使おうとすると、BLEの接続切断の完了を待ってから、フラッシュの書き込み処理を行い、それが完了するのを待って再起動をする処理が必要になり、手間だと思ったのでSDK10と同じレジスタに書き込むだけの簡易処理を今回は使いました。

ブートローダの作成メモ

makeコマンドやPythonが必要になります。開発環境はMacで、その上で仮想化したWindowsを使いKeilを使っています。Keilは単なるビルドとデバッグのIDEとなっています。
Windowsにmakeコマンドを入れたりPythonの環境を作るのは手間だったので、はじめからそれらの環境があるMacで、以下の手順を行っています。

まず、テンプレートプロジェクトをコピーしてきます。プロトコルバッファの定義ファイルからのファイル生成は、既存の生成ファイル(dfu-cc.pb.h, dfu-cc.pb.c)を、そのまま使います。

次に、秘密鍵/公開鍵の生成や、ファームウェアのイメージを作る nrfutil をインストールします。nrfutilのバージョンは以下のように、SDK12に対応するバージョンは1.5.0以降です。2017年1月時点では2.1.0になっています。

  • Version 0.5.2 generates legacy firmware packages compatible with nRF SDK 11.0 and older
  • Versions 1.5.0 and later generate modern firmware packages compatible with nRF SDK 12.0 and newer

nrfutilは、https://github.com/NordicSemiconductor/pc-nrfutil/ にインストール手順がありますが、pythonのパッケージ管理システムを使うと便利です。

pip install nrfutil

次に、秘密鍵/公開鍵を生成します。公開鍵は、dfu_public_key.c にバイナリ配列で定義されます。
デバッグ用プロジェクトでは、デバッグ用の鍵が予め入っているのですが、開発時に今のブートローダがデバッグ版なのかプロダクト版なのか区別するのは面倒なので、最初から鍵を生成して指定しておきます。

鍵の生成手順は、 Working with keysにあります。
秘密鍵を作りそれをpriv.pemというファイルに保存します。その秘密鍵から公開鍵を作りpublic_key.cに、ソースコードとして出力します。このファイルの内容を dfu_public_key.c にコピーします。

# Generate a private key in c:\vault\priv.pem
nrfutil keys generate c:\vault\priv.pem
# Write the public key that corresponds to the generated private key
# to the file public_key.c (in code format)
nrfutil keys display --key pk --format code c:\vault\priv.pem --out_file public_key.c

次に、ファームウェアで使うための、micro-eccというオープンソースの暗号処理ライブラリをインストールします。このライブラリのライセンス条項で、ビルドしたライブラリのファイルは提供されていないので、自分でビルドしなければなりません。
その手順はWorking with keysの次の節に書いてあります。

Mac では、gccコンパイラツールチェインを、リンクにあるARMのサイトからダウンロードしてインストールします。次に、ソースコードをコピーして、ビルドします。
Makefileは、インストールされたgccツールチェインのバージョン入りのフォルダ名を直接指定して、gccを使うようになっています。指定されたバージョンのツールチェインを入れていれば、make一発です。

コマンドで書くと、こんな感じです。

cd <InstallFolder>\external\micro-ecc
git clone https://github.com/kmackay/micro-ecc 
cd nrf52_keil/armgcc
make

できたライブラリ \external\micro-ecc\nrf52_keil\armgcc\micro_ecc_lib_nrf52.lib は、Keilのプロジェクトに、リンク対象として追加しておきます。

最後に、ファームウェアのパッケージを作ります。コマンドにすればこのような感じです:

nrfutil pkg generate dfu.zip –key-file private.pem –hw-version 52 –sd-req 0x00,0x91 –application nrf52832_xxaa.hex –application-version 0x0105

–sd-req には、ソフトウェアデバイスのバージョンを指定します。このソフトウェアデバイスのバージョンは、Windowsで、nRFgo Studioでソフトウェアデバイスを書き込んだとき、ソフトウェアデバイスのバージョンの下辺りに表示される2バイトの値を指定します。
–hw-versionは、51または52を指定します。あとは、見た目のとおりです。

この設定を間違えていると、nRF Toolboxからファームウェアの書き込みを開始したと思ったら、すぐに書き込みが終了して書き込めません。その時の原因は、いくつも設定項目があるので、どこに原因があるのかわかりにくいです。その場合は、ブートローダをデバッグ版のものを入れておけば、デバッグメッセージが出力されます。デバッグメッセージの出力先は、シリアルもしくはSegger RTTをsdk_config.hで指定します。

テンプレートからのファイル変更

main.c の変更箇所のメモです。BSP_BOARD_LED_0 にLEDを接続したピンを定義しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
50) /**@brief Function for initialization of LEDs.
51) */
52) static void leds_init(void)
53) {
54) bsp_board_leds_init();
55) bsp_board_led_on(BSP_BOARD_LED_0);
56) }

61) static void buttons_init(void)
62) {
63) // dfu/nrf_dfu.hで BSP_BUTTON_3は、BOOTLOADER_BUTTONに定義されている。
64) nrf_gpio_cfg_sense_input(BOOTLOADER_BUTTON,
65) NRF_GPIO_PIN_PULLUP,
66) NRF_GPIO_PIN_SENSE_LOW);
67) }

nrf_dfu_enter_check(void)は、DFUに入るかどうかを判定する関数です。デフォルトでは、ピンが押されている(値が0)、もしくはフラッシュにDFUモードのフラグが立っている場合に、DFUに入ります。めんどくさかったので、SDK10で使っていたレジスタを使う実装にしています。デフォルトの関数はweakなので、実際に使われる nrf_dfu_enter_check() は、ここで宣言した関数です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
34) #define BOOTLOADER_DFU_START 0xB1
70) __WEAK bool nrf_dfu_enter_check(void)
071) {
072) if (nrf_gpio_pin_read(BOOTLOADER_BUTTON) == 0)
073) {
074) return true;
075) }
076)
077) if (s_dfu_settings.enter_buttonless_dfu == 1)
078) {
079) s_dfu_settings.enter_buttonless_dfu = 0;
080) APP_ERROR_CHECK(nrf_dfu_settings_write(NULL));
081) return true;
082) }
083) return false;
084) }
085) */
086) bool nrf_dfu_enter_check(void)
087) {
088) return (NRF_POWER->GPREGRET == BOOTLOADER_DFU_START);
089) }

フリーランスあれこれ

これは フリーランス残酷物語 Advent Calendar 2016 9日目の記事です。

年末の金曜日に何を書いているのだろうとは思いますが

クリスマスもお祭りならば、ネットのお祭りに参加するのも、また祭りです。見ているだけより踊ったほうがより楽しいので、書いてみます。

能ある鷹は爪を隠す、あるいは沈黙は金と言います。しかし、ネット経由でお仕事を募集している身で、またネットに露出しなければ存在すら認識されないため、定期的に書くことは生命線でもあります。

格言なのかどうかはわかりませんが、俺の屍を越えてゆけ、と言います。しかし屍になるようなイベントがあるところに、わざわざ自分から出向いても、死体が1つ増えるだけで意味がないのではと、よく思います。

俺の屍を避けてゆけ、が正しいのではないかと思います。しかしまた、その1つの事例をみただけで、その先には何もないと思ってしまうのも、検討が浅い正しくはないふるまいなのかもしれません。行動するのも、また行動をしないことも、何事も自分の頭で考えて決めていく、あるいは思考を停止して決めることすらやめてしまうなど、自分で決められるのは、それは自由なのかなと思います。

まともなことは昔書いているので、今回はパスで

2009年10月から個人事業主を始めたので、もう満7年が経過しました。どこか相手先で作業をしたりではなく、在宅で作業をして成果物を納品する、完全リモートというか、委託などで業務をしています。いままで運良くいままで生き続けられています。

これまでに行ってきている業務は:

  • iOSアプリ開発
  • iPhoneとイヤホンで連携するハードウェアとファームウエアの試作
  • BLEのiOSアプリとファームウエアおよびハードウェアの試作
  • 著作、講演、技術サポート
  • 技術的な講師やハッカソン主催のお手伝い

などです。

真面目なことは2011年から2012年に、その頃使っていたブログサービスで書いています。退職するときは退職前にクレカを作っておいたほうがいいとか、任意保険や健康保険税とのつきあいかた、あるいは家から一歩も出ずに法人を作る方法とか、そういう自分で調べてやってみた記録です。

フリーランスって、歩兵のイメージがあるけど、じつは重装備で訓練が必要な言葉なのかも

今回のは、お祭りなので、こういうのじゃないことを書けなのかなと、空気を読んでみます。

フリーランスとよく言いますが、自分でランスという槍を持って、自分の身一つと才覚とを携えて、戦場に出向き仕事を探す個人で活動するイメージかと思います。

ランスといえば、騎兵が用いるものです。なんとなくフリーランスは歩兵のイメージがありますが、ランスですから歩兵ではなく騎兵、つまり馬と馬具そして防護の鎧などの道具を一揃い持っていて、かつ馬に乗る訓練など時間がかかる教育訓練の投資もしているわけですから、昨日今日はじめました、と言ってなれるものでもないのかなと思います。

会社をやめた話

会社勤務は10年間していました。奈良先端で修士を取得して、2年間キヤノン株式会社にいました。この時の業務は半導体設計部門でデジタルカメラの映像エンジンのなかの画像コーデックのファームウェア作りです。その後、愛知県蒲郡市に本社がある、日本電産ではない方の、株式会社ニデックに8年間いました。この時の業務は人工視覚デバイスの研究開発でした。

ある5月の朝のことです。起きると目の覚めるような青空でした。愛知県蒲郡のあたりはミカンの産地でもあるように、年間を通じて雨も雪も少なく、冬ですら青空が広がっています。この気持ちよさは、冬は重たい雲と雪に閉ざされる山陰地方出身の人にしか通じないと思いますが。

辞めよう、そう思ったので9月いっぱいで会社をやめて、さて何をしよう?と思ったとき、たまたまiPhoneが話題になっていました。個人でアプリを販売できるストアもあり開発情報はネットですべて入手ができるらしいので、それで食っていけるのかな?と思ったのが、始まりです。

キャリアプランがとか、世界を変えるとか、かっこいい言葉で外向きに飾ることもできるのかもしれませんが、そんな重たい言葉で毎日それをやり続けるのは、しんどい話だろうと思います。飽きた、きっかけは、そんなものです。

最初の1年

2009年10月から個人事業主をはじめました。手続きは、市役所に行って国民健康保険に切り替え、税務署に行って青色申告の申請まですませるだけです。

それまでの業務でFreeBSDで遊んでいたり、C# でWinFormそしてWPFでアプリを作り、組み込みLinuxボードでWinアプリにカメラをストリーミング表示する実験装置を作っていたりしていました。ですがMacは全く触れたこともありませんでした。

iOSアプリの開発を始めるのにObjective-Cを使うのですが、新しい言語を覚えるのを体が拒否してくれて、難儀しました。そのころ、MonoTouchというC# でiOSアプリが開発できる環境がでていたので、それを使って3ヶ月ほどアプリ開発の自習をしていました。3ヶ月後、Obj-Cを覚えちゃっていました。なんじゃそれ。

アプリを作ろうとして気がついたのですが、私には音楽や絵を作る才能が壊滅的にない。かといって、その頃出ていた白い画面を表示するだけのアプリを、”照明アプリです”といってストアに出す、ひらめきキラメキの才覚にあふれてもいなかった。

アプリ作って売るつもりだったけど、作れないやん。どうしよう。

一人でアプリを作るのを、すっぱり、あきらめました。でも他にやれることもないわけです。会社辞めただけで人脈があるわけもなく当然、仕事のつてもありません。

とりあえず、アプリを作るのは無理なので、iPhoneのイヤホン端子で連携する外部ハードウェアを作りました。いままで実験装置を作るのに組み込みの開発をやっていたので、そういうのはできるわけです。とはいえ、iPhoneでハードでといっても売れるとは思えないので、iPhoneで操作できるロボットを作ろう、としました。

その費用をどうしようかと探すと、蒲郡市のサイトに創造的事業育成プログラムがあり応募しました。市役所の方も、なんか正体不明の中年男性が書類を持って、国の流れで特に応募もないだろうものに応募者がふらりと来たのに、親切丁寧に対応してもらって、こういう仕事もあるんだなと思います。

100万円の助成金をうけてiPhoneで操作できるロボットを開発して、メイカーフェアーに出てたりしてました。市役所は定期的にプレス発表会を開催していて、そこで成果として発表して新聞掲載されたりしたのも、もはや懐かしい思い出です。

市役所の方と冗談で、フリーでやっているけど食えなくなったら生活保護のお世話になりに来ることになるかも、と言ったら、あなたは大丈夫でしょう…、と言われたときの顔が、けっこう記憶に残っています。世の中には自分が接したことがない部分があるんだなと、ちょっと感じました。

環境しだいでどうにもなるのだなと

会社を辞めて地元でもないところで一人住まいをしていると、人と接する機会が文字通りゼロになります。集団でいることがあまり好きではない性格ですが、この環境に3ヶ月ほどいると、例えばお風呂に服で飛び込んだり、微妙に面白い行動をしていました。

アマゾンでダンボールの小さな家が4000円くらいで売っていたので、部屋の中でリラックスゾーンを作ってみようとしたら、小さすぎたとか、そんなことをちょくちょくやっている感じです。

奇声を発するような、周りのご迷惑になるようなことはしていなくて、理性はあるのですが、何か思いつきで普通はしないようなことをしてた気がします。環境で人ってどうにでもなるのかなと。

いまはネタにするのですが、市役所からロボットをテーマにイベントをするから、開発したロボットの展示を依頼されました。行ってみると、小学校の入学説明会のあとに同時開催されていて、子供さんがたくさん来て、けっこう楽しそうに遊んでくれて、出した方の自分としてもかなり嬉しかった。

その次の年もイベントがあるからと出展を依頼されて、また子供が来るのだろうと思い、そもそも、この時点で何かおかしいスイッチが入っている気もしますが、今度はキグルミで行ってみようかと思いつきます。一生のうち一回くらい、キグルミしてみたいよねと。

2回めのイベントに出てみると、みなさんスーツの大人ばかりのイベントでした。昨年のイベントは市政55周年くらいの記念で子供も来てたけど、今回は通常イベントで大人しか来ないよと。先にそれ言っといてよ。連絡って、だいじですね。

せっかくなので、スーツで来場される方々相手に、こちらは特製スーツだしと思って、そのまま説明対応していました。でも、熱暴走でもたないんです。やってみると。2月の冷える日で屋内なのでそれなりに暖房が入っていますが、それでも低温な室温ですが、キグルミって15分で中が熱暴走するんだなと、体験しました。

その時の記念撮影:

このキグルミの話には後日談があって、5年後くらいに古巣の奈良先端から、IoTと起業をテーマに講演を依頼されました。

聞くと起業する予定の学生がいるとのことで、起業ってまず失敗するし、うまくいくものも最初の資本政策ちゃんとやらないと、美味しいところ持ってかれるだけで、頭おかしいことやることになる場合が多いよなと思って。これから起業というバカなことをするという人の前で講演するなら、ちゃんとバカだよねって伝えるために、まず自分がバカをやってみようかな、バカやってもいいよね(古巣だし)、と。

着たかっただけです。理由はなんでもいいんです。自宅でレンタルで一人で着てるとか、最高に神経を病むので、それはできない。イベントと聴衆が必要なんです。あと、講演で仕事だと、レンタル費用を経費にできますから。

キグルミで講演を提案したら、先生方、すんなり受け入れてくださいました。ノリノリです。やっぱ、頭おかしいです(褒め言葉)。 たまに、なんとなく、着たくなるんですよね。

ネットのフォームでお仕事の話

お仕事は、おもしろい、そう思います。

iOSアプリを作りますというウェブページを作って、問い合わせフォームを作ってました。そんなフォームでも、まだiOSアプリ開発が今のように会社があったりしない黎明期だったので、いくつか案件が来たりしました。

アプリ改修で2週間くらいの仕事だと思ったので、15万円で見積もりを出して着手、その後、機能追加を要望されたのですが、ちょっと技術的に無理かなと思って、それはできないとお断りしてたら、なぜできない?みたいな雰囲気になって、とか。そんな感じです。

最初に、これはできる、これはできない、これを作るのが今回の見積もりの仕事範囲という書類を、けっこうがっつり作って着手前に送っておいてよかったなって思います。あれがなかったら、泥沼だっただろうなって。

こういう話は、自分は自分にとっての絶対正義なので、自分を悪くいうことも、そもそも自分が悪いと認識することも困難ですから、相手を悪者にしがちなのですが、相手の目線からすれば相手の都合と考えと理由があり、そういうものなんだろうなと思います。

人づてでお仕事の話

メイカーフェアに出展していたときに、iPhoneでこんなものを作りたいのだがという方が、ある人からあなたならできると聞いたのだがとブースに来られました。この時も15万円で見積もりだしてました。2週間くらいなら、30万円くらいの月給の半分で15万円という計算です。バカですよね。いまなら200万円くらいスタートで出すのに。

この時作ったのは、プロ用のステージ機材で、いろんな音を出すというシンプルな機能のアプリでした。ただ画面タッチで動くだけではなくて、外部に接続したスイッチで動くもの、です。iPhoneと連携するハードをやっていので、紹介を受けたのでした。

この方とは、その後は相応の開発費用をもらうようになり、今もアプリの機能拡張や改修でお付き合いしています。普通出会わないところで、自分が全く知らない業界での話なので、やっぱりおもしろいのです。そして相手の方が回路やアプリそして技術者というものを理解して接してもらえているので、それでお付き合いが続いているのだろうと思います。こういう出会いがあるのは、ほんとうに感謝しかないです。

またデブサミというイベントで話してみないかとお話をもらったりもしました。お仕事って、あたりまえのことなのですが、人づてなんだなって。

また紹介をもらって、会社の立ち上げで必要になるという、スマホでクレジットカードの磁気情報を読み出す回路とライブラリの試作をしたりもしていました。こういう道具それ自体には価値はないのですが、それをクレジットカード決済などのサービスとして事業にするうえでは、道具が必要になるというのは、自分が何をやり何を得るのか、技術それ自体の価値とはどの視点から言えるのかとか、ちょっと考えちゃう材料です。

英語でお仕事の話

ネットにしか存在してないようなものなので、こんなことをやっているよというブログ記事を、英語でも書いて出したりしてました。英語圏のお仕事は、日本語圏の10倍くらいは確実にあるのかなっていう感じでした。

Githubに、AudioUnitという低レベルなオーディオのフレームワークのMonoTouchのラッパを公開してたりしたのですが、それを見た方が、ちょっとアプリに使いたいので少し手を入れたのがほしいとメールをもらったりしました。ユーゴスラビアかどっかそのあたりの方。

半日でできるものでした。5万円の金額でした。メールをやり取りしていると、そっちの物価であれば5万円は小さな金額だがこっちだと結構大変でとかあって、たいへんだなーっておもいました。

たしかクロアチアの、アプリ開発会社を経営されている方がいて、コードを作ってもらって納品を受けて、送金しようとしたら、送金手段がネットとかではなくて、結局はスイフトコードを教えてもらって郵便局で送金をしたとか、けっこうおもしろ体験でした。郵便局の方が、はじめてやる業務らしくて、手順書を見て1つ1つ確認しながら、めちゃくちゃ慎重に処理してはりました。

受託にはいたりませんでしたが、ブログとGithubのコードを見て、金融系の開発を依頼したいけど、みたいな話がありました。シンガポールからSkypeでインタビューを受けたのですが、私も微妙な英語でもここまで応対できるのかすごいなって、相手の英語力に感心しました。そこかよ。

ツイッターでお仕事の話

実は、この7年間のお仕事は、ツイッターで紹介をもらったり、あるいは問い合わせをもらっています。 もともとは、孤独に耐えかねて作ったアカウントでしたが、いまはネットに存在しているものとして存在の根拠となっている状態です。

https://twitter.com/u_akihiro めっちゃ2次元アイコンですが、お仕事のやりとりも、このアイコンです。問い合わせしてくる方々は勇気があるなと、正直自分で思います。

ではどんな仕事かといえば、いろいろです。

岐阜県大垣市はIT、2010年の頃はiOSやモバイルのベンチャー育成に力を入れていたのですが、その頃ツイッターで面白いからと、トリガーデバイスの佐藤さんに、プレゼンの機会をもらいました。

これがつながりで、その後の大垣でのiOSアプリ開発の講師や、iOSアプリ開発のお仕事など、ずっとお世話になっています。

また大学の研究成果をiOSアプリにする、という仕事もありました。めちゃくちゃ作るのが遅くて、発注を受けて10ヶ月くらいでやっと納品、納期を何ヶ月遅らせているんだと、怒られるなとビクビクして出したのを思い出します。プロジェクト提案の相手に見せるには、なんとか間に合ったよみたいなお話をもらって、その場はホッとしたのですが、2回めのお仕事は来ないので、そうだよなーって、思ったりしています。

iPhoneで動くロボットつながりで、ニューヨークのメイカーフェアの前に開催されるオープンソースハードウェアサミット、その第2回に出展したりもしました。その時は、スイッチサイエンスさんから、10万円(もしからしたら15万円だったかも)の援助資金をいただいたりしました。これもツイッターでのつながり経由です。

スイッチサイエンスさんからは、iPhone+ロボットの接続部分の基板を設計製造していただいたり、めちゃくちゃ助けていただいたのですが、私本人がプロジェクトを盛り上げるでもなく活動をせず、しょぼしょぼのままに、自然消滅状態です。事業をするのは腰を据えないとだめですけど、ふにゃふにゃです。

あとは、本を何冊か出しているのも、ツイッターがきっかけだったりします。iPhoneで電子工作という本は、こんな本を出したいなーというつぶやきを、出版社の方が見て、たまたまその日は東京出張だったので、その日のうちに打ち合わせで、あれこれあって出版になりました。

原稿を出す出すといいながら半年以上遅らせてしまい、さらには、てにをはのレベルでおかしな文章を修正してもらったりと、今思い出しても迷惑このうえないと冷や汗しかでないのですが。

その後、アップル、グーグルが神になる日 ハードウェアはなぜゴミなのか?、が光文社から出ています。iOS8をもみたときの感想をブログに書いたのですが、それを見た山路さんに出版を勧めていただき、でも文を書くのはと思っていたのですが、インタビューを受けて本文は山路さんが書いていただいて、出版されました。

自分のキャラを冷静に文章に書いてみると、どんだけご迷惑なんだろうと思うのですが、それでも見てくれる人がいて、その御蔭で人のつながりの中にいるんだなと、今書いてて実感します。

ときどき怒ることがあるけど、なにもいいことはない

2年に一回くらい、めちゃくちゃ怒り状態になることがあります。だいたい原因はつまらないことなのですが、何か自分でも知らない逆さ鱗があるのだろうとは思っています。

最近だと、パートナーのiOSアプリ開発で、ちょっとしたバグというかコードのミスがあり、そういうのを見てカッチーンと来て激怒状態になっていました。今思い返せば、そんなのちょちょっと直してコミットすればいいだけ、1時間もかからない仕事なのですが。

アホですね。相手は迷惑だっただろうなって思います。こういうのをやると、あとでお互いに、ああいうこともあったねと話し合えるようになっても、相手には怒っている私の記憶が残っているわけで、人間関係を潰すだけだなと、つくづく実感します。

立場が違うときついよなと

ある有名な会社のiOSアプリ開発の案件での話です。アプリのコードがほぼ出来上がり、もう納品できるかなと思ったあたりで、あるところで画面遷移を200回ほど繰り返すとメモリ不足でアプリが落ちるというのが、バグとして上がってきました。そこまでテスト作業をしているとか、どんだけやねん。

これが、おそらくiOSのフレームワークの深いところでメモリを解放していないんだろうなとは思えるけど、もしかしたら自分のコードか、あるいは相手会社のライブラリかどこかなのか、ぱっと見では原因箇所がわからない、ちょっといやんなやつです。

会社で技術者やっていたら、技術に向き合っていたら、こういう”バグ”はバグなので、直そうとするのはそりゃそうだと思います。一方で、外部から関わっている人間としては、1アプリいくらの予算金額で仕事を受けているので、関わる時間が短い方がいい、ぱっぱと納品して終わりにしたい立場です。

こんなのでアプリが落ちても気にする人いないだろう、そもそも何百回も画面遷移を繰り返す人なんかまずいないぞ、とアプリ体験への影響度つまりバグ修正する重要度は限りなくゼロに近いと、私には思えるので、べつにいいじゃんと思うのですが。でも仕事は仕事。バグというならバグなんですよね。

結局、実行時のプロファイルをとって、2週間くらいかけてメモリ解放忘れらしい挙動の箇所を特定して、それの再現確認用のテストアプリを1つ作って、iOSのフレームワークが原因だと証明して、それで事なきをえた感じでした。

で、私はAppleにこれをバグレポートしたかといえば、しなかったんです。ですから、全く誰にとってもうれしいことがない、フィードバックもしない、自分で自分の仕事を無駄というか、ゴミにしたわけです。

今こうやって書いて思い出してみると、バグレポくらい投げとけばいいのにと思うのですが、たぶん仕事を憎んだのかもですね。もう見たくない、さっさと終わらせたい、そんな感じだったのかもしれません。こういう状態って、誰も幸せにしないですね。

とりとめないですけど

とりとめないですけど、冷静に考えて一人でいるって危険しかないです。自然界を見渡しても、個体数の多い動物は群れを作る動物です。昆虫で大繁栄しているのは、極端に社会性を発達させている蟻です。

それはそうです。例えば病気で誰かが倒れたとします。それが一人でやっているところであれば、即倒産でしょう。でも10人のところであれば、周りが0.1余分にがんばれば、なんとかなります。これが1万、10万の数なら、もはや確率と統計学で扱えるでしょう。

自分が在宅で成果物納品で仕事ができるのは、時代がそうなっているラッキーさがあります。

Githubで実力の程はわかります。SNSで、なんとなくどんな人物かもわかるでしょう。正体不明の何者かに問い合わせるなんて、無謀で危険なことでしかないですが、問い合わせるまでもなくある程度の情報が得られるならば、問い合わせもするかもしれません。

ネット側に情報があったのも大きいです。i-modeの頃などは、技術情報がネットで公開されていなくて、東京の会社に日参して情報をもらって仕事をしていたという、ネットの仕事なのに、リアル世界にいないと仕事すらできない過去の話を聞くと、本当にiOSはネットにある世界でラッキーだったと、今も思います。

またiOSアプリ開発の黎明期だったのも、ラッキーです。まだ始まったばかりで、事業としての開発専業会社が確立していない頃はまだ、個人でも開発の仕事はありましたし。

こういうのを書いていると思うのは、フリーランスってそういう生き方があっているか、そういう生き方しかできないだけの話で、そういうやつもいるだけの話でしかないです。残酷物語とか言いましても、生きてることが残酷なら、そりゃ生きていることに付随する仕事も残酷になる人もいるのかもしれません。

と、ここまでカッコつけて書いてきましたけど、仕事場がこれですからね。自分でも、どうしてこうなったと思いますけど、今までいろんなことがあった、その蓄積からくる、すでに私にとっては日常なので、その経歴は語れるが、だからといって、それがどうした、でしかない話です。

この環境が日常になっちゃうと、そりゃ普通の会社に務めるかとか、検討しないわな。

傘をささずに雨の降るところにいたら濡れる。いままで傘があることに気づかなかった。雨がふるような場所に出たことがなかった。そこにいたら、そうなる。これまで延々と書いてきたのは、そういう話です。

俺の屍は避けてゆけ。他人のことなど、知ったことか。

では。

Swift3でのBLEアプリ開発-その2-

Swift3でBLEアプリを作ってみる

前回 https://blog.reinforce-lab.com/2016/12/01/ble-with-swit3-1/ は、Swift3でBLEアプリを作るときによく使う、バイナリ配列と値型との変換処理を書きました。今回はCoreBluetoothフレームワークとその使い方をまとめます。

ソースコードは https://github.com/ubi-naist/SenStick/tree/master/SenStickSDK にあります。

クラス構成

SDKの役割は、複数のBLEデバイスのセンサーデータの取得、そしてその蓄積データの読み出し機能の提供です。そこで、複数のBLEデバイスの発見と接続処理を SenStickDeviceManagerクラスに、BLEデバイスそれぞれを SenStickDevice で表しています。

これはCoreBluetoothの2つのクラスのラッパーになっています。

  • CBCentralManager - SenStickDeviceManager
  • CBPeripheral - SenStickDevice

SenStickDeviceManager, DispatchQueueの割当とスキャン処理

SenStickDeviceManagerクラスは:

  • Key-Value Observation(KVO)準拠にする
  • 通信処理はディスパッチキューを使う
  • シングルトンにする
    になっています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
open class SenStickDeviceManager : NSObject, CBCentralManagerDelegate {
let queue: DispatchQueue
var manager: CBCentralManager?

// Properties, KVO
dynamic open fileprivate(set) var devices:[SenStickDevice] = []
dynamic open fileprivate(set) var isScanning: Bool = false

// Initializer, Singleton design pattern.
open static let sharedInstance: SenStickDeviceManager = SenStickDeviceManager()

fileprivate override init() {
queue = DispatchQueue(label: "senstick.ble-queue", attributes: nil)
super.init()
manager = CBCentralManager.init(delegate: self, queue: queue)
}

スキャン中かどうかなどのステートは、読み出し専用プロパティで外部に見せています。SwiftのクラスのプロパティをKVO準拠とするには:

  1. NSObjectを継承すること
  2. 宣言に”dynamic”をつけること

が必要です。

通信処理はディスパッチキューを使います。CBCentralManagerはキューにnilを指定すれば、メインスレッドで処理をします。心拍計のように、さほどデータのやり取りがないデバイスが相手であれば、メインスレッドで処理をしてもよいのですが、今回のデバイスは、ある程度量のあるログ・データを読み出すため、メインスレッドで処理をしては画面表示の処理を止めてしまいます。

変数queueはletで宣言しているので、継承しているNSObject.init()を呼び出す前に、初期化する必要があります。通信の処理は受信した順番どおりに処理しなければならないので、シリアルキューを使います。

DISPATCH_QUEUE_SERIAL (or NULL) to create a serial queue or specify DISPATCH_QUEUE_CONCURRENT to create a concurrent queue.

変数managerは、delegateにselfを渡します。selfを参照するにはイニシャライザの処理が終わっていなければなりません。letで宣言すると、その変数を初期化するのに、それがselfを参照するから、その変数が初期化できなくなります。しかたないのでvar managerと宣言しています。

CBCentralManagerのインスタンスは、アプリケーションで1つだけにすることが推奨されています。そこでCBCentralManagerをもつSenStickDeviceManagerをシングルトンにしています。

実際には、アプリがCBCentralManagerを複数持っていても動作するのですが、スキャンやデバイスの発見処理を考えれば、たしかに1つだけにまとめておいたほうが後々トラブルにならずにすみそうです。

Swiftのシングルとnは、letでインスタンスを宣言して、そこにインスタンスを作って返す無記名関数を使って、インスタンスを入れます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 1秒毎にコールバックします。0になれば終了です。
open func scan(_ duration:TimeInterval = 5.0, callback:((_ remaining: TimeInterval) -> Void)?)
{
// デバイスリストをクリアする
DispatchQueue.main.async(execute: {
self.devices = []
})

// スキャン中、もしくはBTの電源がオフであれば、直ちに終了。
if manager!.isScanning || manager!.state != .poweredOn {
callback?(0)
return
}

// スキャン時間は1秒以上、30秒以下に制約
let scanDuration = max(1, min(30, duration))
scanCallback = callback

// 接続済のペリフェラルを取得する
for peripheral in (manager!.retrieveConnectedPeripherals(withServices: [SenStickUUIDs.advertisingServiceUUID])) {
addPeripheral(peripheral, name:nil)
}

// スキャンを開始する。
manager!.scanForPeripherals(withServices: [SenStickUUIDs.advertisingServiceUUID], options: nil)
isScanning = true

var remaining = scanDuration
scanTimer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: DispatchQueue.main)
scanTimer?.scheduleRepeating(deadline: DispatchTime.now(), interval: 1.0) // 1秒ごとのタイマー
scanTimer?.setEventHandler {
// 時間を-1秒。
remaining = max(0, remaining - 1)
if remaining <= 0 {
self.cancelScan()
}
// 継続ならばシグナリング
self.scanCallback?(remaining)
}
scanTimer!.resume()
}

scanメソッドは、BTの電源が入っていれば、すでに接続中しているデバイスのリストを取得してから、スキャンを開始します。受信回路を動かすスキャンは電力を消費し続けるので、タイマーを使い5秒で停止させています。

SenStickDevice, サービスの発見と受信処理

サービスの発見、そしてキャラクタリスティクスの発見の処理は、CoreBluetoothで手間のかかるコードです。発見したサービスごとに、そのサービスで発見すべきキャラクタリスティクスを指定するコードは、そのまま書くと読みにくくなるので、サービスおよびキャラクタリスティクスの配列と、その処理に分けて、見た目を良くしています。

サービスおよびキャラクタリスティクスのUUIDはSenStickUUIDsの構造体にまとめています。

1
2
3
4
5
6
7
8
9
10
public static let ControlServiceUUID:CBUUID        = {return SenStickUUIDs.createSenstickUUID(0x2000)}()
public static let StatusCharUUID:CBUUID = {return SenStickUUIDs.createSenstickUUID(0x7000)}()

public static let SenStickServiceUUIDs: [CBUUID: [CBUUID]] = [

DeviceInformationServiceUUID : [ManufacturerNameStringCharUUID, HardwareRevisionStringCharUUID, FirmwareRevisionStringCharUUID, SerialNumberStringCharUUID],

BatteryServiceUUID: [BatteryLevelCharUUID],

ControlServiceUUID : [StatusCharUUID, AvailableLogCountCharUUID, StorageStatusCharUUID, DateTimeCharUUID, AbstractCharUUID, DeviceNameCharUUID],

カスタムサービスのUUIDを、CBUUIDのイニシャライザそのままを呼び出して作ると、128ビットのUUIDを書かねばなりません。カスタムサービスそしてキャラクタリスティクスのUUIDは、自分が勝手に決めた128ビットのUUIDの、そのうち16ビットだけを変化させるように仕様を決めています。

そこで、その勝手なカスタムUUIDを作るメソッドを作り、それを無記名関数で呼び出してインスタンスにして、staticなletな変数に実行開始時に割り当てています。こうすれば、カスタムUUIDでも、16ビットの本当に指定する部分だけをコードに書けば済みます。

また、サービスとキャラクタリスティクスの構成は、サービスのUUIDをキーに、キャラクタリスティクスのUUIDの配列を値に持つハッシュで表現しています。

これを使って、SenStickDeviceのサービス発見では、UUIDをキーにして発見すべきキャラクタリスティクスのUUIDの配列を取り出し、キャラクタリスティクス発見の処理を進める、としています。

複雑なインスタンスでも、そのインスタンスを返す無記名関数を利用すれば、static letで宣言できるのでよいのですが、ちょっとテクニックに走り過ぎというか、無記名関数が入るのがあまりきれいでもないようにも思えるので、普通に構造体としてイニシャライザで初期化すればよかったかもしれません。

サービスそれぞれのクラスのインスタンス

BLEのサービスを発見するたび、SDKでの機能を表すクラスのインスタンスを作ります。BLEのサービスはハードウェアとしての機能単位で、アプリケーションからみた機能単位とBLEのサービスとが同じとは限らないのですが、今回は一致しているのでBLEのサービスそれぞれにクラスを割り当てています。

SenStickDeviceでサービスに対応するクラスのインスタンスを作るのですが、どのような条件ならインスタンスができるかをSenStickDevice側で処理させると、コードがめちゃくちゃ読みにくくなります。

そこで、例えばデバイス情報サービスを表すDeviceInformationSeviceクラスのイニシャライザ:

1
2
3
4
5
6
// イニシャライザ
required public init?(device:SenStickDevice)
{
self.device = device

guard let service = device.findService(SenStickUUIDs.DeviceInformationServiceUUID) else { return nil }

のように、サービスとそのキャラクタリスティクスをすべて呼び出したならば、インスタンスを作って返すようにしています。必要なキャラクタリスティクスが発見できていないなど、それぞれのサービスごとの都合は、guard文でインスタンスを作る条件を満たしていなければnullを返すことで、表現しています。

センサーログデータの読み出し処理

ログデータの読み出しでは、コネクション・インターバル20ミリ秒ごとに6パケット、つまり6回の受信イベントが起きます。

1
2
3
4
5
6
7
8
9
// 過度な呼び出しにならないように、メインスレッドでの処理が終わったら次の処理を入れるようにする。
var flag:Bool // obj_sync_enter()/obj_sync_exit()を使い排他処理したフラグ

if(self.flag == false) {
self.flag = true
DispatchQueue.main.async(execute: {
self.delegate?.didUpdateLogData(self)
self.flag = false
})

値を読み出すたびにUIスレッドにそれを通知するのですが、受信するたびに素直にそれをキューに入れるとUIが遅くなるので、かんたんにフラグを立てて、UIスレッドの表示処理が終わったら次の通知を入れるようにしています。(セマフォを使えばいいだけなのに、なぜか自分でフラグを実装しています。)

ジェネリックを使うとKVOが使えなくなって、ちょっと困った

センサそれぞれのデータ読み出しサービスは、読み出し手順はおなじだけれども、範囲指定の設定値とセンサデータのバイト配列表現だけが違うものです。

手順が同じでデータの表現だけが違うものとくればジェネリックを使う場面だと思い、こんな感じで書いています。

1
2
3
open class SenStickSensorService<T: SensorDataPackableType, S: RawRepresentable> where S.RawValue == UInt16, T.RangeType == S

open class AccelerationSensorService: SenStickSensorService<CMAcceleration, AccelerationRange>

加速度のセンサデータは、独自に定義してもいいのですが、CoreMotionにあるデータ構造体を拝借して、使っています。センサの測定派に設定値をenumでUInt16で定義しています。センサデータはバイナリ配列から戻せるようにプロトコルを定義して、CoreMotionから拝借している構造体を拡張しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 加速度センサーの範囲設定値。列挙側の値は、BLEでの設定値に合わせている。
public enum AccelerationRange : UInt16, CustomStringConvertible
{
case accelerationRange2G = 0x00 // +- 2g
case accelerationRange4G = 0x01 // +- 4g
case accelerationRange8G = 0x02 // +- 8g
case accelerationRange16G = 0x03 // +- 16g

public var description : String
{
switch self {
case .accelerationRange2G: return "2G"
case .accelerationRange4G: return "4G"
case .accelerationRange8G: return "8G"
case .accelerationRange16G:return "16G"
}
}
}

// センサーからの生データ。16ビット 符号付き数値。フルスケールは設定レンジ値による。2, 4, 8, 16G。
struct AccelerationRawData
{
var xRawValue : Int16
var yRawValue : Int16
var zRawValue : Int16

init(xRawValue:Int16, yRawValue:Int16, zRawValue:Int16)
{
self.xRawValue = xRawValue
self.yRawValue = yRawValue
self.zRawValue = zRawValue
}

// 物理センサーの1GあたりのLBSの値
static func getLSBperG(_ range: AccelerationRange) -> Double
{
switch range {
case .accelerationRange2G: return 16384
case .accelerationRange4G: return 8192
case .accelerationRange8G: return 4096
case .accelerationRange16G:return 2048
}
}

static func unpack(_ data: [Byte]) -> AccelerationRawData
{
let x = Int16.unpack(data[0..<2])
let y = Int16.unpack(data[2..<4])
let z = Int16.unpack(data[4..<6])

return AccelerationRawData(xRawValue: x!, yRawValue: y!, zRawValue: z!)
}
}

extension CMAcceleration : SensorDataPackableType
{
public typealias RangeType = AccelerationRange

public static func unpack(_ range:AccelerationRange, value: [UInt8]) -> CMAcceleration?
{
guard value.count >= 6 else {
return nil
}

let rawData = AccelerationRawData.unpack(value)
let lsbPerG = AccelerationRawData.getLSBperG(range)

// FIXME 右手系/左手系などの座標変換など確認すること。

return CMAcceleration(x: Double(rawData.xRawValue) / Double(lsbPerG), y: Double(rawData.yRawValue) / Double(lsbPerG), z: Double(rawData.zRawValue) / Double(lsbPerG))
}
}

このやり方はコードがすっきりするのですが、ジェネリックを使うクラスはNSObjectを継承できないので、KVOにできません。ですから、このクラスからなにか値の変更を通知させたいなら、プロトコルでデリゲートを宣言して、デリゲート経由で通知するほかなくなります。

プロパティのset文でデリゲートを呼び出せばいいだけなので、あっちこっちで値を変更するたび忘れずdelegate呼び出しをその都度書くような、ミスしやすい書き方は避けられるのですが、なんとなくKVOに慣れすぎていて、これでいいのかなと中途半端な感じがします。

まとめ

Swift3でBLEデバイスのSDKを作ってみると、ジェネリックや構造体の拡張を使うと、驚くほどすっきりしたコードが書けると思いました。その一方で、この書き方は初めてコードを読む人、あるいはコードのメンテを任された人がみて、ぱっと見で理解しやすいのだろうか? と疑問もあります。

あまり凝りすぎても仕方なく、また無闇矢鱈に拡張を使いすぎて、ファイルのあちこちに処理が分散して、ぱっと見てわからなくなったりしないように注意しないとと思いました。

2016年のBLEを振り返る

これは Bluetooth Low Energy Advent Calendar 2016 の12月7日の記事です。

2016年のBLEがどうだったのかを、FacebookのWF-BTLEグループ https://www.facebook.com/groups/563064710384459/ に投稿していたものを素材にして、振り返ってみます。

規格の流れ

2015年に4.2が登場して、2月に名古屋で勉強会をしていたりしました。2016年はBT5のリリース予定が発表されています。

Bluetoothは、物理的に同じハードウェアでソフトウェア的に対応可能な変更は、4.xとマイナー番号を増やしていく、電波を出す物理層に機能が追加される大きな変更がある場合は、メジャー番号を増やしていきます。

Bluetooth5では、BLEの物理層に2Mbpsが出せる変調方式が追加されます。また、パケットにエラー訂正機能を持たせて、通信距離の延長を提供します。電波環境の良いところでは2Mbpsの通信速度を活用した通信の高速化、またエラー訂正の活用で通信速度は出せなくても、より遠くに安定した通信を提供する方向のようです。

通信の高速化と、より遠距離に届くものは、両立するものではなく、場合によって使い分けるものです。BLEはスマートホンに採用されたことで急速に普及しています。ファームウエア更新のように、ある程度まとまったデータをやり取りする場合には、通信速度の高速化がユーザ体験を良いものとするでしょう。また家電に使うような、データ量は小さいBLE本来の使いみちでは、中継装置がなくても家全体どこにいても通信ができるようになるでしょう。

またメッシュネットワークの登場も発表されています。

BLEのメッシュネットワークの仕組みは、4.1で提供されているセントラルかつペリフェラルになれる機能を利用して、ルータの役割をもたせるものです。そのルーティングなどのプロトコルはソフトウェア依存で、任意のものが載せられます。

その仕組の上で、いくつか独自のメッシュネットワークが発表されていますが、独自に実装をしても自社製品でしか使えないとなるといまいちな場合は、決め手にかけるのでしょうか。

本当にメッシュネットワークが必要なところは、さっさと使えるものを独自に実装して自社製品で字固めをしていると思うので、よく開発者がメッシュネットワークとかいうのは、大手の企業で技術動向を調査すれば仕事になるサラリーマンくらいじゃないかなと、さほど意味あるようには思えないのですが、とにかく話題の材料にはなるメッシュネットワークです。

ですからメッシュネットワークはBleutooth5の一部というわけではなく、Bluetooth5の登場タイミングで発表されるBluetoothお墨付きのプロトコル実装、というくらいに見ておけばいいのでしょう。これから発表されるメッシュネットワークは4.2のデバイスでも動作すると発表されています。

メッシュネットワークよりも、IoT系というかIPプロトコルへの対応のほうが重要だとは思うのですが、こちらはパケットさえ通ればIPはRFCか何かで実装すればよいような、Bluetoothが認証するようなものにはならないでしょうから、Bluetoothの仕組みの上で実装が色々出てくればいいだけの話なのかもしれません。

あとは、最近はスマートホンの入力がこれまでのテキスト入力から、急速に音声入力、音声操作になってきています。クラウド側で手軽に音声認識ができるようになっているので、この流れは更に強くなるでしょう。

そうなると、BLEデバイスでも、操作のために音声のコーデックが欲しくなるのですが、こちらはいつワーキンググループが仕様を固めるのか、わかりません。2~3年はかかるのかもです。

自社サービスを作りたいのならば、そんな都合は関係ないので、独自実装でさっさと作ってしまえばいいですが、iPhoneやAndroidのスマホ側に音声データを渡したい場合は、OSがサポートしてくれないと作れない、OS側も独自方式で実装するよりBluetoothの公式が出てくるなら、それを待ってしまうのかもしれないとなると、にらみ合いでロックしてしまいそうです。

企業の流れ

BLEのシングルモードの半導体を出している会社は、こんな感じです。2015年から無線の半導体会社の買収が進み、Silicon LabsはBlueGigaを、CypressはBroadcomを買収しました。またNordicは、LTEの次のIoT向け技術の半導体開発を発表しています。

  • Nordic
  • TI
  • Silicon Labs
  • Cypress (Broadcom)
  • Dialog

流れとして、家庭内での家電のようなデータ量が少ないデバイスの接続には、スマートホンとも接続できる利点だけが利点である、2.4GHzのBLEが使われていくでしょう。

ネットワークにつながることで製品となりうるような製品では、今はスマートホンをハブにする他にネットワークとつながる方法がないので、しばらくはBLEが使われるでしょう。しかしNB-IoTなどが普及すればそちらも採用していくでしょう。

また今年はSigfoxあるいはLoRaなど、半径500メートルをアンライセンスドバンドで1つの基地局でカバーできる広域の無線ネットワーク技術のデモンストレーションがニュースになっていました。どの事業者がどの役割を担うのか、しばらくは力関係の上下をつける争いなのかもしれません。

Facebookの投稿まとめ

https://www.facebook.com/groups/563064710384459/ に投稿していたものです。

1月21日

BLEのモジュール選択は、いろいろな要素を見て、最後はどれかに決めなければならないと思います。
太陽誘電のnRF51822のBLEモジュールには、9.6 x 12.9の普通のサイズと、5.1x11.3というとても小さいもの、2種類があります。
チップワンストップという日本の半導体のオンラインショップで、太陽誘電のBLEモジュールが100個単位であれば800円代になっています。
http://www.chip1stop.com/search.do…
BLEモジュールの価格の目安は、1つ5ドル、日本円で800円位が目安です。1個単位で買う場合は、個別に包装するなどの手間のぶん高くなり、何十万個も購入するなら、その分より安くなると思います。
100個単位は、そのちょうど中間、数量による価格の下げの恩恵がまずないところなので、800円台というのは、目を引きました。mouserをみても、なかなかこの価格帯はありません。

1月26日

Arduino Genuine 101 がRSコンポーネントに商品登録されていました。海外から4営業日で届くそうです。1つ¥4,748。
このボードは、フィジカルコンピューティングの分野で広く使われているArudinoでもありますが、ハードウェアそれ自体は、
-Intel® Curie™マイクロコントローラ搭載
-32ビットIntel® Quark™マイクロコントローラ
-384 kBのフラッシュメモリ, 80 kB SRAM
-DSPセンサハブ, 6軸コンボセンサ
と、ウエアラブルデバイスを作ることもできそうな、とてもリッチなハードウェアです。
このボードが、日本の技適を取得しているかはわかりませんが、BLEが搭載されています。また、このチップのOSは3月にはオープンソース化される予定とのことです。
スマートホンと連携するハードウェア、アプセサリのキーテクノロジーであるBLEは、IPv6あるいは6LowPANなどで、IoTのキーテクノロジーへとその勢力範囲を広げつつあります。実験用としても、手軽な価格で1枚単位で購入できるボードが登場することは、その盛り上がりを支える道具として、意味があるなと思います。

4月6日

LoRaとBLEに対応した無線モジュールがあったので、日本の技適は取得されていませんが、リンクをはりました。
http://www.lairdtech.com/products/rm1xx-lora-modules
LoRaは低データレートの無線通信規格で、仕様は https://www.lora-alliance.org サイトから入手ができます。いまのver1.0の仕様は1GHz以下のサブギガのいくつかの周波数が記述されているようです。
電波が届く範囲は周波数により決まります。BLEが使う2.4GHzでは家程度の範囲ですが、サブGHzではモジュール次第ですが通信範囲をkm単位に広げることも可能です。
スマートホンとの無線通信として強力なブランド力をもとに急速に普及しているBLEも、さらなる普及のためにIoT系の技術を取り込むなどの、次の大きな変化が来るようです。
また開発案件では、スマートホンとの連携にはBLEを使うが、製品それ自体の魅力を作るためにはBLEという規格の範囲では作れないために、2.4GHz帯の独自の無線通信機能を入れていくものも、出てくるようになりました。
無線通信は技術手段にすぎませんが、その手段の変化の速さを見ていると、BLEのアプせサリまたIoTという単語が、単なる流行語ではなく、実際に需要がありお金が動く分野になってきているのかなと思います。

4月29日

CypressがBroadcomのワイヤレス IoTビジネス事業と関連資産を5.5億ドル、約600億円で取得するそうです。
これには、BroadcomのWi-FI, BluetoothそしてZigbeeの製品と知財そしてWICEDブランドと開発者のエコシステムが含まれます。

5月7日

ATMEL社のBluetoothのチップおよびモジュールのプロダクトページです。
BTLC1000は、Cortex-M0、26MHz、ROM 128kB(うちユーザが利用できるのは96kB)、RAM 128kB、RX/TX 3mA(3.6V時)、電源電圧 1.8 to 4.3Vです。
リチウムイオンポリマー電池の満充電電圧に耐えそうなので、直結ができるのでしょうか。
http://www.atmel.com/produc…/wireless/bluetooth/default.aspx
モジュールは、MOUSERを見ると http://www.mouser.jp/Search/Refine.aspx… 1つ1400円くらいです。データシートをみるとTELECも取得しているようです。
Siliconlab、Cypressなど、小さなマイコンを提供している各社からBLE SoCが次々に発表されていますが、その時にあわせて半導体会社がモジュールも提供するようになっているので、便利な時代だなと思います。

6月17日

Nordicのサイトで、Segger SystemViewというツールが紹介されていました。
これは、SEGGER’s Real Time Transfer (RTT) https://www.segger.com/jlink-rtt.html の技術を使い、ファームウェアの処理内容をマイクロ秒単位でグラフィカルに表示する無償ツールだそうです。
Nordicの開発キットに同梱されているJ-Link LITE CortexMを使っていれば、フラッシュの書き込みには Serial Wire Debug interface (SWD) http://infocenter.arm.com/help/index.jsp… を使うと思います。RTTはSWD経由でprintfデバッグのメッセージを外部に取り出せてデバッグ用のシリアル端子が不要にできるので、私は開発によく使います。
マイクロ秒単位でイベントをグラフィカルに統合できるprintfデバッグの進化系と見ておけばいいのかなと思います。ユーザによるイベント定義もできるようですから、BLE系でよくある、フラッシュの読み書きや、I2Cでバスを専有してしまうセンサーを使っている時の、処理時間が間に合っているかの確認など、便利そうです。

9月15日

TI社からBLEとサブGHzの通信機能を1チップ化したCC1350が提供されています。価格は4.6ドルくらいです。
CC1350は 315-, 433-, 470-, 500-, 779-, 868-, 915-, 920-MHz および 2.4-GHz の ISM バンドに対応しています。
日本では、BLEと920MHz帯がワンチップ化されたものとみなしておけば、よいと思います。
Wi-SUNの物理層であるIEEE 802.15.4g、IP-Enabled Smart Objects
(6LoWPAN)、 Wireless M-Bus、 KNX Systems, Wi-SUN™ そしてTI独自のプロプライエタリなシステムに対応するそうです。
電波を出すものは電波法の技術認証が必要でモジュールが登場しなければ、なかなか試すことも一手間ですが、TI社のセンサータグを、この半導体を使い提供されるようです http://www.tij.co.jp/tool/jp/cc1350stk
これまでBLEはスマートホンと連携したアプセサリの分野を切り開いてきました。ただ2.4GHz帯を使うBLEでは、電波が届く距離がもっと長くなければ実用的ではない分野は、物理的に苦手としていました。またスマートメータなど、そもそも2.4GHz帯ではなく、サブGHzを使うシステムとの連携もできません。
いろいろと、使いみちがありそうです。
http://www.tij.co.jp/product/jp/CC1350

10月5日

Bluetooth Developer Studio 1.1 がリリースされました。
これは、BLEの開発を容易にするために、サービスやキャラクタリスティクスの定義と、プラグインを通じたソースコードの自動生成機能がある無償のツールです。
今回のリリースで、ATTレイヤのプロトコルアナライザ機能がついたそうです。Bluetooth Developer Stuidoと対象デバイスの間の通信を記録して、ATTレイヤでのやり取りを可視化してくれます。
この機能は、デバイスとBluetooth Developer Studioの間での通信の解析であって、スニファーではないことに注意が必要です。スニファーは、通信をしている2つのデバイスがやり取りしている無線通信を傍受して、その通信を解析するもので、BLEデバイスの開発では、デバッッグに使います。
今後はIPv6対応も含めた汎用のプロトコルアナライザとして機能を充実させていくのだろうと期待ができます。

https://blog.bluetooth.com/bluetooth-developer-studio-adds-packet-capture/

10月19日

NXPから、BLEと802.15.4(プロトコルはThread)両対応のチップが出ています。
2.4 GHzトランシーバーは、 BLEの FSK/GFSK と802.15.4の O-QPSK をサポート、ARM® Cortex®-M0+ CPU, up to 512 KB Flash and up to 128 KB SRAM。チップの価格は3ドル程度です。

http://www.nxp.com/products/microcontrollers-and-processors/arm-processors/kinetis-cortex-m-mcus/w-series-wireless-m0-plus-m4/kinetis-kw41z-2.4-ghz-dual-mode-ble-and-802.15.4-wireless-radio-microcontroller-mcu-based-on-arm-cortex-m0-plus-core:KW41Z

10月20日

シリコンラボからBLE及びサブGHzのSoCを乗せた評価ボードを、クラウド接続を売り文句に出しています。価格は36ドルです。
モバイルアプリ(ソースコードはGithubにあるとありますが、リンクは不明です)、データはFirebaseに保存、ReactJSのウェブフロント(ソースコードも公開とありますが、特にリンクはありません)があるそうです。
センサーは、加速度センサー、温度、UV、室内の空気の質センサーなどが使われています。電波法の技術適合はないようですから、日本国内で利用するには適切な環境で使う必要があると思います。
BLEのSoCでは各社がオープンな姿勢を打ち出していますが、BLEのデモンストレーションでは、スマートホンのアプリケーションが多く、ウェブフロントエンドの公開例はありません。
このシリコンラボのリソースは、バズワードとなったIoTのデモンストレーションのスタートに良いリソースかもしれません。

11/11

シリコンラボからBT4.2のSiPモジュールが登場しています。サイズは6.5mm角、Digikeyをみると価格は6ドルくらいのようです。
Cortex-M4、RAM/ROM 32K/256K。3.3VでDCDC有効がでもピーク電流は10mA程度と大きめです。
データシートを斜め読みすると、認証はこれから取得するようで、データシートにはCE/IC/FCC/TELECなどが空欄になっています。
アンテナ周囲のGNDを置かない制約だけではなく、GNDの面積がアンテナの利得に影響するため、モジュール自体が小さくても、制約に従い実装すると、基板の大きさは微妙になるかもしれません。

11/25

DigikeyにSamsungのBLEモおよびZigbee/Threadのモジュール 、それぞれARTIK 020/030が登場しています。
Cortex-M4 40MHz, 256kB フラッシュROM。モジュールには、Telecマークが印字されています。価格は5ドルくらいです。
これの開発ボードにシリコンラボのマークがあります。モジュールのスペックを見ても、シリコンラボのモジュールそのものなので、発売元をすげ替えたものなのかと思います。サムスン電子は、英CSRの事業を買収していたと思いますが。

nRF51からnRF52へのポーティング作業メモ

nRF51からnRF52へのファーム移植

SenStickというBLEデバイスのファームウェア開発をしています https://github.com/ubi-naist/SenStick 。このデバイスは加速度や照度などの複数のセンサデータを取得し蓄積するものです。

初代はnRF51822(RAM 16kB/ROM256kB)を採用しています。次のデバイスでは、nRF52832(RAM 64kB/ROM 256kB)が採用されます。その次のデバイスへのファームウェア移植で盛大につまづいたところをメモしておこうかなと思います。

ハードの変更

移植で対応するものは、nRF51とnRF52とのハードウェアの違い由来と、使用するソフトデバイスおよびソフトウェア開発キットのバージョンの違い由来の、2種類があります。

今回は、nRF51822(rev3) + S110 + SDK10 を、nRF52(rev1) + S132 + SDK12.1 に移植します。機能は同じであり、またnRF51版とnRF52版とは、基本機能は並行して提供していくので、メンテをやりやすくなるようコードは可能な部分は共有する方針です。

nRF51からnRF52へのハード由来の移植ではまったのは:

  • デジタル・アナログ変換が逐次変換型(Successive approximation ADC, SAADC)。
  • P0.9とP0.10はデフォルトでNFCに割り当てられている。
    です。

nRF52はnRF51とは異なる種類のSAADCが採用されました。そして、nRF51のDACとnRF52のSAADCとでは、ハードウェア抽象層(Hardware Abstraction Layer, HAL)とドライバのソースコードが異なります。

ですからADC周りは、自分でラッパーを作ってドライバの違いを吸収するか、コード自体を分離するかします。SDK側で、ADCの種類が違っても、ドライバでHALをラップしてnRF51とnRF52どっちでも同じコードで動くようにしといてくれたらいいのにって思いますが。

P0.9とP0.10は、デフォルトでNFCに割当てられています。ですから何も設定をしないと、GPIOとして設定していても、GPIOとして機能しません。この2つのピンを通常のGPIOとして使うには、レジスタ設定が必須です。

出てくるはずの信号が全く出てこないで、はまるとけっこう精神的にきついです。データシートには、その旨が書いてあるのですが、よく読まずに回路を組むと、信号が出てこないと頭を抱えることになるので、やっかいです。

C/C++のプリプロセッサシンボルで、CONFIG_NFCT_PINS_AS_GPIOS を定義しておけば、P0.9とP0.10はGPIOとして使えるようになります。この処理は system_nrf52.c にあって、こんなコードです:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Configure NFCT pins as GPIOs if NFCT is not to be used in your code. If CONFIG_NFCT_PINS_AS_GPIOS is not defined,
two GPIOs (see Product Specification to see which ones) will be reserved for NFC and will not be available as
normal GPIOs. */
#if defined (CONFIG_NFCT_PINS_AS_GPIOS)
if ((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)){
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
NRF_UICR->NFCPINS &= ~UICR_NFCPINS_PROTECT_Msk;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
NVIC_SystemReset();
}
#endif

不揮発の設定レジスタをみて、NFCにピンを割り当てる設定になっていたら、そのフラグをクリアする、というコードです。ほぼおまじないなので、プリプロセッサに CONFIG_NFCT_PINS_AS_GPIOS を定義して忘れましょう。

あとで出てきますが、SDKの設定が sdk_config.h に集約されているなら、この定義もそこに集約してくれればいいものをと思うのですが、system_nrf52.c とそのinlucde先のファイルをみても、どこも sdk_config.h をincludeしていないので、Keil MDKのツールの設定側でC/C++の定義をするほかないという、定義する場所が2つに分裂状態になるのですが、そういうものなので、仕方ないと諦めます。

SDKの変更

S110+SDK10から、S132+SDK12に切り替えます。ソフトデバイスとソフトウェア開発キットの切り替えで気をつけるのは:

  • Keil MDKのSoftware Packsの利用をやめている。SDK配布はZIPファイル。
  • SDKの設定は sdk_config.h に集約している。

まずSDKの配布が、Keil MDKのSoftware Packsの仕組みで提供するものから、以前のようなZIPファイル配布に戻りました。Software Packsは、モジュールそれぞれのバージョンと依存性とを管理してくれていて、あるモジュールを選択したときに、それが依存するモジュールが読み込まれてなければ自動的に読み込み設定をしてくれるなど便利さはあったのですが、よほどツール依存が嫌われたのか、トラブルがあったのかしりませんが、SDK自体をZIPファイルで配布するようになっています。

SDKがSoftware Packsで管理されている場合は、MDK Keilの設定ファイルだけあれば、環境やビルドマシンが変わっても自動的に必要なソースコードをネットからダウンロードしてきます。ですが、その仕組がなくなったので、SDK12ではファームウェアのgitレポジトリにSDKそれ自体を置きました。

正直、SDKは1つのZIPファイルで依存性が閉じているので、SoftwarePacksよりも、gitで一元管理できるこちらのほうが便利だと思います。

SDKを通じて利用する機能の設定は、sdk_config.h に集約されています。

sdk_config.h は、いろんな設定がごっちゃーと全て1つのファイルにまとまっていて、かなり長いです。

USE_APP_CONFIG をC/C++をで定義しておくと、sdk_config.hは、”app_config.h”というファイルをインクルードします。sdk_config.h の定義は ifdef を使って、もしも定義されていなかったら定義する、という作りなので、sdk_config.h をデフォルト設定そのままとして、sdk_config.h は変更せずに、変更部分だけを app_config.h に書くようにすると、とてもスッキリ見通しが良くなります。

ちょっとはまったのが、BLEのサービスを利用する場合、そのソースコードをプロジェクトに追加するだけではだめで、このsdk_config.hで利用設定しないとだめな作りになっています。例えばBattery serviceを組み込むなら、ble_bas.c をプロジェクトに追加して、さらに BLE_BAS_ENABLED を定義します。

ble_bas.cは、先頭で

1
2
#include "sdk_config.h"
#if BLE_BAS_ENABLED

としているので、定義がなければCソースコード全体が無視されます。おそらく、テンプレートプロジェクトには、BLEサービスの全ソースコードをプロジェクトに追加しておいて、どのBLEサービスを組み込むかは、sdk_config.hに一元管理する作りなのかなと思います。ふつー、ソースコードをプロジェクトに追加したらその機能が有効になると思うような気もしますが、SDKの作りがそうなので、そういうものなのでしょう。

RTTでログの出力

デバッグ時のログ出力には、Segger RTT Viewerを使っています。これはファーム書き込みにも使うシリアルワイヤインタフェース経由でデバッグログとデバッグメッセージのやり取りができるSegger独自のプロトコルです。

SDK12ではデフォルトでSDKに組み込まれています。RTTが使えるようになるまで、ちょっとひっかかりました。

使えるようになるまでの手順は、まず、sdk_config.h で設定をします。
設定差分でいうと、こんな感じで、ログを有効にして、バックエンドをRTTに、UARTは今回は使わないので無効化しているので、そっちがバックエンドにならないようにしました。

1
2
3
4
5
6
7
8
9
10
11
// ===
// ログ
#define NRF_LOG_ENABLED 1
#define NRF_LOG_BACKEND_SERIAL_USES_UART 0
#define NRF_LOG_BACKEND_SERIAL_USES_RTT 1
// <0=> Off
// <1=> Error
// <2=> Warning
// <3=> Info
// <4=> Debug
#define NRF_LOG_DEFAULT_LEVEL 4

プロジェクトに追加するソースコードは、ログ関係:

  • nrf_log_backend_serial.c
  • nrf_log_frontend.c

RTT関係:

  • RTT_Syscalls_KEIL.c
  • SEGGER_RTT.c
  • SEGGER_RTT_printf

です。

NRF_LOG_DEFAULT_LEVEL の設定以下のメッセージは出力されません。DEBUGで出力しているメッセージがRTT Viewerに出てこなくて、なぜだろうと思ったら NRF_LOG_DEFAULT_LEVEL が3(Info)になっていた、なんていうつまらないオチをやらかしてました。

あと、RTT Viewer で最初に出てくるダイアログに、チップの種類選択があります。ここがnRF51のままで、nRF52になっていなくて、ログが出てこなくて出なくて困りました。

ログの出力はデフォルトでバッファリング(sdk_config.h をみると NRF_LOG_DEFERREDうんたらとあるあたり)されています。デバッグ用ビルドとリリースビルドで、バッファ処理で処理時間が違い振る舞いが変わると困る、なんてことが少なくなるのかもしれません。ぶっちゃけどっちでもいいのですが。

ログをバッファリングしている場合、 NRF_LOG_FLUSH(); を呼び出して初めてログが出力されます。BLEのイベントループにでも NRF_LOG_FLUSH(); を追加しておけばいいです。最初、これを入れていなくて、たしかにログが出てくるはずなのに何も出てこない、なんていう、よくある失敗をしていました。

pstorageが廃止、fstorageに

nRF5のフラッシュのROMへのデータ保存と読み出しのライブラリは pstorageが長く使われてきましたが、SDK12ではこれは廃止されていてfstorageというライブラリを使うようになっています。

pstorageもfstorageも、nRF5のフラッシュ読み書きのレジスタを使うライブラリでしかないので、pstorageのソースコードを持ってくればSDK12でもpstorageは使えますし、pstorageとfstorageの共存も可能だそうですが、ややこしいので、fstorageで書き換えました。

fstorageはページ単位のフラッシュ消去と書き込みのメソッドを提供します。フラッシュメモリは、書き込める値は0だけで、値が0のビットを1に戻すことはできません。0xffを書き込むには、フラッシュの消去をします。この消去はページ単位でのみできます。

fstorageには読み出しのメソッドがありません。フラッシュなので、構造体から書き込み対象のページ領域先頭アドレスを取得して、普通のメモリとして読み出せばいいです。また書き込みが完了したときに、その書き込みポインタが返されるので、それを覚えておくかします。

読み書きのこの辺りが手間だと思うならば、次のflash data storageを使うのが良いです。ただし、適切なタイミングでのガーベージコレクションを入れておくようにします。

ボンディングを使うときは、SDK10でdevice_managerがありましたが、これがpeer_managerに置換されているので、そちらを使います。peer_managerは内部でfstorageを使いke-valueで値を保存するFlash data storage(fds)ライブラリを使っています。

fdsの内部実装は単純なリストです。値を追加あるいはアップデートするたびにリストが伸びていくので、フラッシュの割当容量末尾までリストが届かないうちに、適当なタイミングで、ガベージコレクションのメソッドを呼び出す必要があります。

peer_managerのコードを

1
% grep -R fds_ .//nRF5_SDK_12/components/ble/peer_manager/*

とかしてfdsを使っている部分を見ても、ガベージコレクションはしていないので、BLE接続が切れたときにでも、自分でそれを呼び出すようにします。peer_manager内部で、fdsの容量使い勝ったかのチェックとエラー通知でも入れてくれればいいのに、きっとボンディングの追加や削除を何度も繰り返すと、変な振る舞いになる嫌なバグ的な症状でそうで、なんかいやんな感じです。

ソフトウェアデバイスの変更

S110(ver8.0)からS132(ver3)への切り替えは、MTU EXCHANGEリクエストの対応と、GATTのwrite with authorizationの構造体フィールド追加への対応の2つです。

S132ではMTU Exchangeが飛んできます。これを処理してリプライを返さないと、エラーになって切断されます。MTUデフォルトでいいので、内部でよろしく処理してくれればいいのでめんどくさいのですが、処理コードを追加します。

1
2
3
4
5
6
7
8
//nRF52, S132v3
#if (NRF_SD_BLE_API_VERSION == 3)
case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
// S132では、ble_gatts.hで、GATT_MTU_SIZE_DEFAULTは、23に定義されている。
err_code = sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle, GATT_MTU_SIZE_DEFAULT);
APP_ERROR_CHECK(err_code);
break; // BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST
#endif

GATT_MTU_SIZE_DEFAULT はble_gatts.hで、BLEの仕様にある最小値23に設定されています。今回は通信相手がiPhoneなので、もっと大きい値にしてもいいのですが、S110+SDK10のnRF51版と同じふるまいにしたかったので、MTUは23にしています。

次に、write with authenticationの振る舞いが異なる部分に対応します。write with authenticationは、GATTSへの値の書き込みイベントをファーム側で受け取る場合に用います。

S110のタイミングチャートは、書き込みタイミングを取り出すだけのシンプルなものでした。sd_ble_gatts_rw_authorize_reply()に渡すreplyの構造体は、 reply_params.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; のように、gatt_statusだけを設定して返していました。

S110 SoftDevice v8.0.0 API, GATTS Write Request with Authorization
http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s110.api.v8.0.0%2Findex.html

このタイミングチャートが、S132ではこうなります。
S132 SoftDevice v3.0.0 API,GATTS Write Request with Authorization
http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s110.api.v8.0.0%2Findex.html

S132では、この書き込みへのリプライ時に、peerの値をそのままGATTSに反映させる従来通りの振る舞いと、peerの値ではなくファーム側から値を指定してGATTSの値を更新する2つのフローがあります。そのため、値へのポインタとその値のバイトサイズを指定するフィールドそしてupdateという1ビット幅のフィールドが追加されています。

updateフィールドは、ファーム側からGATTSの値更新を示すフラグかと思いきや、このフィールドが1でなければ、sd_ble_gatts_rw_authorize_reply()がinvalid parameterのエラーを返してくるので、盲目的にupdateは1を設定ます。なんのこっちゃ。

ドキュメントに、その旨ちゃんと書いてあったので、よく読めということですねorz
http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s110.api.v8.0.0%2Findex.html

uint8_t ble_gatts_authorize_params_t::update
If set, data supplied in p_data will be used to update the attribute value.
Please note that for BLE_GATTS_AUTHORIZE_TYPE_WRITE operations this bit must always be set, as the data to be written needs to be stored and later provided by the application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
} else if(p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) {
bool result = true;
if( p_auth_req->request.write.handle == p_context->sensor_setting_char_handle.value_handle){
result = senstickSensorControllerWriteSetting(p_context->device_type, p_auth_req->request.write.data, p_auth_req->request.write.len);
} else if( p_auth_req->request.write.handle == p_context->sensor_logid_char_handle.value_handle) {
senstickSensorControllerWriteLogID(p_context->device_type, p_auth_req->request.write.data, p_auth_req->request.write.len);
} else {
// 一致しないハンドラ
return;
}
// リプライ
// nRf51ではリプライのフィールドには、gatt_statusのみがある。SDK12 S132v3では、gatt_status, update, offset, len, p_dataの5フィールド。
reply_params.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
// iOSアプリ側でBLEのアクセスでAUTH_ERRORがあると、一連の処理が破棄されるのか?、みたいな振る舞い。読み出せるべき値が読み出せないとか。なので、ここはスルー。
reply_params.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
#ifdef NRF52
// このパラメータが1になっていなければ、invalid parameterでsd_ble_gatts_rw_authorize_reply()が落ちる。
reply_params.params.write.update = 1;
#endif
// reply_params.params.read.gatt_status = result ? BLE_GATT_STATUS_SUCCESS : BLE_GATT_STATUS_ATTERR_WRITE_NOT_PERMITTED;
err_code = sd_ble_gatts_rw_authorize_reply(p_context->connection_handle, &reply_params);
APP_ERROR_CHECK(err_code);

プロジェクトの作り直し

nRF52のテンプレートプロジェクトが .//nRF5_SDK_12/examples/ble_peripheral/ble_app_template/ にあります。今回は、この下にある、nRF52のテストボードpca10040でs132を使う pca10040/s132 なんとかのプロジェクトをコピーして使っています。

Keil MDKを使っているので、それでプロジェクトを開いて、必要なソースコードを登録します。メモリ配置を設定して、C/C++定義をいじっていきます。

C/C++の定義は、エラッタ対応のコードを呼び出すための定義がいくつかあります。その処理は system_nrf52.c にまとまっています。nRF52(rev1)のエラッタをみると、スリープから復帰するときRAMの内容が吹き飛ぶとか、GPIOTEとTIMERを同時に動かすとスタティックに消費電流が増えるとか、起きると困る、電池を使っているときは結構困るエラッタがあります。それらのエラッタへの対応が入っているので、このC/C++定義は必ず入っているか、確認します。

nRF52 rev1のエラッタを見ていると、nRF51のrev3で修正解決されたようなエラッタが再現しているようにみえるので、nRF51 rev2から設計分岐させて、nRF52にエラッタがそのまま引き継がれた状態なのでしょうか。なかのことしらないですけど。

まとめ

そんな感じです。

Swift3でのBLEアプリ開発-その1-

Swift3でBLEアプリを作ってみる

これは Bluetooth Low Energy Advent Calendar 2016 の12月1日の記事です。

Swift3が急速に広がっていますが

Objective-Cに続く新しい言語としてApple社からSwiftが公開されて、はやバージョン3になりました。

Swift3ではApplication Binary Interface (ABI)の大きな変更が入り、そのためSwift2でビルドされたライブラリをSwift3から呼び出せないなど、Swift2とSwift3との混在ができません。この破壊的な変更が強制力になり、ライブラリやアプリケーションのSwift3対応が加速されています。

自分がアプリケーションやライブラリをSwift3で書き直さねばならないときは、どのような言語で書いても同じ振る舞いをするものを言語の都合で書き直す手間が無駄に思えて、恨み言を言いながら作業をします。これって、ほんっと作業なんですよね…

ですが、Swift2からSwift3への移行はかなりスムースにいきます。それはXcodeの変換ツールのおかげだけではありません。アプリ開発は、いろいろなライブラリを取り込んで作りますが、それらのプロジェクトが生きているライブラリが軒並み、Swift3での破壊的な変更のお陰で、一気にSwift3対応を勧めていたおかげです。

自分が作業をするときは恨み言を言いますが、他人の仕事だと、べんりだなーって思うので、いい加減なものです。ですが、ライブラリがSwift3に移行して逆にデフォルトがSwift3となり、アプリをSwift3へ移行する圧力も高まっているんだろうなと思ったりします。

Swift2版はgithubで切られたブランチで参照して取ってこないとないよとか、おもしろいことになるのと、バグ修正があっても、Swift3に移行完了していたら、わざわざSwift2もメンテするかなとか思いますしね。

iOSでBLEを使うアプリは、CoreBluetoothフレームワークを使います。ですからSwift3であっても、これまでのアプリづくりと何ら変わるところはありません。

しかしSwiftならではの話もあるので、ともあれ、Swift3でBLEのライブラリあるいはアプリを作る話でもしてみます。

SenStickのソースコードをもとにして

BLE系の開発コードは、アプリケーションを作ってもらうためのSDKをバイナリ、場合によってはソースコードで公開することはよくあります。ですが、BLEデバイスの製造販売が利益源ですから、そのBLEデバイス側のソースを公開することはありません。

BLEのセンサロガー https://github.com/ubi-naist/SenStick は全てがオープンソースで公開されているので、これをサンプルにします。これのコードは私が書いています。

お仕事のコードはたいてい納品したら相手のものですから、誰かの目に触れることは滅多とないのですが、これは大学のプロジェクトでもあり、またハードウェアはゴミでしかない、という話が通じる人たちなので、公開されたっぽいです。

アプリでBLEデバイスを使うSDK tree/master/SenStickSDK これのコードを見ていきます。

バイト配列と値型の変換

BLEのキャラクタリスティクスを通じてやり取りするのはバイト配列です。キャラクタリスから読み出したバイト配列は、値や構造体に変換しなければなりません。また、キャラクタリスに書き込む場合は、その逆に値や構造体をバイト配列に変換します。

このバイト列と値との変換は、extensionで作っています。 PackableType.swift にまとめています。

なんとなくByte型
1
public typealias Byte = UInt8

まず、UInt8にByteという別名を与えます。単なる好みで、どうでもいいことだし、UInt8でいいじゃないと思うのですが、このコードを書いていたときの気の迷いです。あとで、UInt8で置換しときます…

1
2
3
4
public protocol PackableType {
func pack(byteOrder: ByteOrder) -> [Byte]
static func unpack(_ data: Array<Byte>, byteOrder: ByteOrder) -> Self?
}

そしてバイト配列と値型それぞれの変換メソッド名を与えます。これはシリアライズ/デシリアライズなのですが、アルファベットにするとserialize/deserializeと文字数が長いです。そのわりに、’de’の部分しか違わないので、まずありえないのですがなんとなーくタイプミスしそうだな(賢い補完があるからそんなことあるわけないのですが)と、なんとなーくで、pack/unpackという名前にしてみました。

気の迷いです。素直にserialize/deserializeでよかったなって、今書いてて思います。

1
2
3
4
5
6
7
8
9
10
11
public enum ByteOrder : CustomStringConvertible {
case littleEndian
case bigEndian

public var description: String {
switch self {
case .littleEndian: return "LittleEndian"
case .bigEndian: return "BigEndian"
}
}
}

エンディアンを指定するためにenum型で定義しておきます。デバッグ時に文字列で表示できると便利(かな)と思うので、enum側は必ず文字列に変換できるようにコーディングしています。

1
2
enum SomEnumType : String {
case littleEndian = "littleEndian"

列挙型を文字列に自動変換したいならば、文字列で列挙型を定義してもいいのですが、littleEndianはlittleEndianである、みたいな区別するための識別子を列挙しているものを、わざわざ人間が読み出しやすくするためだけに、文字列を割り当てて列挙するのは何か違う気がするので、そういう書き方はしていません。

これもコンパイラが.toString()みたいなことをしたら自動的にソースコードのenumの名前を表示してくれればいいので、人間がコードを書くようなものじゃない気もするのですが。

CustomStringConvertible および CustomDebugStringConvertible https://developer.apple.com/reference/swift/customdebugstringconvertible カスタムな文字列変換、デバッグ用の文字列変換のプロトコルが解説されています。

これは、Objective-CのNSObjectにある、descriptionのオーバーロードに相当します。Swift2では、 Printable および DebugPrintable がありましたが、それらの名前が変更されてたものです。

これを使ってわざわざ文字列変換を書いているわけです。

解説に、通常は String(reflecting:) で debugPrint(_:) を呼び出すと書いてあります。正直、なんのこっちゃなのですがSwiftはリフレクション、クラスや列挙型などの型情報を読み出す機能、があるので、Swift3だとprint(“\(列挙型な変数)”)とすれば、ソースコードにある列挙型の名前そのものが出てきます。

わざわざ列挙型と同じ名前を文字列に明示的に変換するだけの CustomStringConvertible なんて、いらんないですね。削っときます…

16ビット幅の整数の変換
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension UInt16 : PackableType {
public func pack(byteOrder: ByteOrder = .littleEndian) -> [Byte]
{
var buf = [UInt8](repeating: 0, count: 2)

switch byteOrder {
case .littleEndian:
buf[0] = UInt8(UInt16(0x00ff) & self)
buf[1] = UInt8(UInt16(0x00ff) & (self >> 8))
case .bigEndian:
buf[1] = UInt8(UInt16(0x00ff) & self)
buf[0] = UInt8(UInt16(0x00ff) & (self >> 8))
}

return buf
}

そして実装していきます。宣言したPackableTypeプロトコルでUInt16を拡張することにして、pack()を実装していきます。バイトオーダは、Bluetoothのエンディアンは通常はリトルエンディアンなので、それをデフォルトの引数にしています。

UInt8の長さ2の配列を作り、UInt16の下位1バイトを取り出してUInt8にして代入していく、とてもシンプルなコードです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public static func unpack<C: Collection>(_ data: C, byteOrder: ByteOrder = .littleEndian) -> UInt16? where C.Iterator.Element == Byte ,C.Index == Int
{
guard data.count >= 2 else {
assert(false, #function)
return nil
}
let bytes = Array(data)
switch byteOrder {
case .littleEndian:
return UInt16(bytes[0]) + UInt16(bytes[1]) << 8
case .bigEndian:
return UInt16(bytes[0]) << 8 + UInt16(bytes[1])
}
}
}

ではバイト配列からUInt16への変換は、こんな感じ。Arrayを作って、1バイトづつUInt16に変換して、エンディアンに合わせて桁をあわせて足し算しています。

変換のソースは、<C: Collection>のジェネリックで指定しています。要素はByte型、配列のインデックスはInt型のコレクションであれば、なんでも使えるはず。

なんでArray(data)で変換しているんだ、コレクション型でdataを直接使えばいいじゃないか、というのがありますが、これが結構泣ける話があります。

Array型の変数の部分配列を取り出すと、ArraySlice型が出てきます。このArraySlice型はもとのArrayの指定された範囲を参照するものですが、インデックスの開始が0ではなく、参照した配列の中での位置が、配列の先頭要素になります。

1
2
3
4
let arr = [1, 2, 3, 4, 5]
let sliced = arr[2..<4] // slicedは、[3, 4]
sliced[0..<1] // fatal error: Negative ArraySlice index is out of range
sliced[(0+sliced.startIndex)..<(1+sliced.startIndex)] // [3]

何を言ってるかわからないので、コードにすると、こんな感じです。

5つ要素がある配列があり、その部分配列2..<4(インデックス2,3の要素の部分配列)を取り出します。このときArray型はArraySlice型を返します。このArraySliceで要素を参照するときは、部分要素のインデックスは、元の配列のインデックスを使います。0から始まらないのです。

もしも、インデックス0の要素を取り出そうと、0..<1(いや、0でいいですけど)、と指定すると、元の配列の0番目の要素にアクセスしにいって、しかしそれはこのArraySliceの範囲外なので、例外が飛んできます。

つまり配列でwhereで要素とインデックスに制約をかけても、ArrayとArraySliceどちらも受け入れてしまう、ArraySliceもちゃんと扱えるように、要素参照のインデックスで必ず、スタートのインデックスを加算すればいいけど、めんどくさい。たぶん、ArraySliceをインデックス参照でstartIndex基準にするような何かをジェネリック的に拡張でかければいいんだろうけど、自分で何言ってるのかわかんないくらい、どうやって書くのそれって思うし。

だから、とりあえず何も考えずにArray()にして、見なかったことにしているわkです。

1
2
3
4
5
6
extension Int16 : PackableType {
public func pack(byteOrder: ByteOrder = .littleEndian) -> [Byte]
{
let v = UInt16(bitPattern: self)
return v.pack(byteOrder: byteOrder)
}

符号なしはわかった、では符号ありはどうしているのかといえば、UInt16(bitPattern:)で、Int16をビットパターンとしてUInt16に変換して、UInt16のpack()を使っています。符号ありで0x00ffとの論理積やシフトを扱うのがこわかった(確認するのもめんどくさい)のです。

1
2
3
4
5
6
7
8
9
    public static func unpack<C: Collection>(_ data: C, byteOrder: ByteOrder = .littleEndian) -> Int16? where C.Iterator.Element == Byte ,C.Index == Int
{
guard let value = UInt16.unpack(data, byteOrder: byteOrder) else {
assert(false, #function)
return nil
}
return Int16(bitPattern: value)
}
}

バイト列からの変換も、一度UInt16にして、それをbitPatternとしてInt16にしています。符号が絡むシフト演算とか、怖さしかないですから。(ユニットテスト入れろ、という話です。)

1
2
3
4
5
func hoge_unpack(_ data: [Byte]) -> UInt16
{
let value = UInt16.unpack(data[0..<2])
return value
}

使うのはこんな感じで。考えたら、UInt16なら配列の長さが2以上あればいいのだから、別にわざわざ部分配列を作って渡さなくても、配列をそのまま渡せばいいだけに思えます。Swift1の頃は、メソッドに渡したインスタンスを逐一copyして渡していたみたいですが、let と var をちゃんと見る今時なら、たぶん関数呼び出しのたびに不要なコピーはしないと思いますし(たぶん)。

バイト配列から文字列への変換

バイト配列をよく使うので、デバッグ時にそれらを見やすい文字列、(0x00, 0x01、みたいな)、表示になるように、[UInt8]を拡張しています。

1
2
3
4
5
6
7
8
9
10
11
12
protocol _UInt8Type { }
extension UInt8: _UInt8Type {}
extension Array where Element : _UInt8Type {
func toHexString() -> String
{
var s = String()
for (_, value) in self.enumerated() {
s += String(format:"0x%02x,", value as! CVarArg)
}
return s
}
}

Arrayの要素がUInt8のものを拡張しているのですが、これElement: UInt8とすると、 type ‘Element’ constrained to non-protocol type ‘UInt8’ とコンパイルエラーになります。ジェネリックタイプがうんたらとか解説がありますが、理解できないのでスルー。

しかたないので、UInt8に空のプロトコルをくっつけて、そのプロトコルで制約をかけて拡張しています。

これがSwift3.1だと https://github.com/apple/swift/blob/master/CHANGELOG.md

SR-1009:

Constrained extensions allow same-type constraints between generic parameters and concrete types. This enables you to create extensions, for example, on Array with Int elements:

extension Array where Element == Int { }

と書けるそうです。でも、Xcode8.1 は Swift3.0.1 なので、これが使えるのはまだ先です。

まとめ

Swift3でバイト配列と値型の相互変換のコード部分を解説しました。よくわからない? 私もです。

iPhone7とiOS10のモバイル決済

代わり映えのしないハードウェア

iPhone7とiOS10が発表されました。ハードウェアには、より処理能力の高いプロセッサとメモリの大容量化という定番の進歩と、イヤホン端子の廃止や防塵防滴化など、他社のスマートフォンであれば以前からある付加価値が追加されました。

また、新色 ジェットブラックが追加されました。iPhone3GSのプラスチックの艶やかな黒、そしてiPhone4シリーズのガラスで覆われた艶やかな黒以降、iPhone5およびiPhone6シリーズでは失われていた、艶やかな黒色の再登場です。

ApplePayの日本でのサービス開始

これらのハードウェアの進歩は、もはや毎年繰り返される定番の話題で見るべきところはありません。ですが、ApplePayの日本でのサービス開始がアナウンスされました。このApplePayのサービス開始の一部として、SuicaおよびiDとQUICPayの非接触型決済サービスへの対応がアナウンスされました。

これらの非接触型決済サービスは、非接触型ICカードにFelicaを採用しています。FeliCaは、ソニーが開発した非接触型ICカードの技術方式の名称です。Felicaは日本で広く、しかし日本でのみ広く使われている技術ですが、iPhone7およびApple Watch2のハードウェアがFelicaに対応しました。

これでできることは:

  • ハードウェア: Felicaに対応。
  • サービス: プリペイド方式のSuicaに対応。
    • 国際ブランドの登録クレジットカードからチャージ可能。
  • サービス: ポストペイ方式の非接触決済 iD、QUICPay が利用可能。
    • 国際ブランドの登録クレジットカードと連携させる。

ApplePayを使うと、自分が持っているクレジットカードを登録して、アプリ内およびウェブでの支払いに、その登録情報を使って支払いができます。またハードウェアがFelicaに対応したiPhone7以降であれば、店舗で非接触型決済もできます。

非接触型決済の、日本での現実的な対応

決済には、ブランド会社とカード発行会社そして加盟店管理業者の3つの役割があります。JCBのように、これら3つの役割を兼ね備えた会社もありますが、たいていはそれぞれを担う会社は別です。

ブランド会社はVISAやMasterCardのように決済の仕組みを提供します。カード発行会社はブランドからライセンスを取得してクレジットカードを発行します。加盟店管理業者は、加盟店を開拓して、売上データの受け取り、カード発行会社からの売上代金回収そして加盟店への入金業務を行います。参照: 【第4回】[基礎編] 「イシュアー」と「アクワイアラ」 http://pointi.jp/credit_card/credit_about04.php

日本ではすでに広く非接触型決済のサービスが普及しています。ここでApplePayの加盟店を広げようとしても、時間がかかります。すでにあるSuicaなどが使えるようにしたのは、現実的な対応です。

おサイフケータイとちょっと違うっぽい

AndroidのおサイフケータイやモバイルSuicaのサービスがあるけど、使ったことがないので、同じようなものかなと思ってググッて使い方のページとかを見ていた。結構違うんだなって印象。

iDは、ドコモが運営する決済プラットホームでブランドだから、VISAやMasterCardのような国際ブランドと同じように、そのブランドをクレジットカード発行会社がライセンスして、カードを発行して、iDの加盟店管理業者がカードリーダを店舗に設置する流れのものなんだろう。

そのiDの非接触型決済だと、クレジットカード発行会社にiDの利用を申し込んで、そのカードをAndroidに登録するみたい。だからおサイフケータイは、iDカードというハードウェアの機能をAndroidのハードウェアに転写するイメージ。iDって、あとから追加サービスとして申し込むとiD専用のFelicaカードが送られてきたりするから、そういうイメージで、追加サービスのカードのハードウェア部分が、たまたまAndroidのハードウェアになっているだけなんだろう。

ApplePayの説明だと、MasterCardとかJCBとか(VISAはない)の国際ブランドが並んでいるから、ぱっと見ると、iDを申し込んでなくてもいいように見える。なのでiDという仕組みがたまたま使える、ApplePayっていうイメージ。

ただややこしいことに、MasterCardは、MasterCardコンタクトレスというFelicaではないNFCを使う非接触型決済サービスを出してて、これがなかなか広まらなかったのだけど、iDでこのサービスが使えるようになってたりするから、表向きiDにみえて中はMasterCardのサービスとかの流れとかあるのかもしれないから、そういうことができる国際ブランドが並んでいるのかもしれない。業界のことなんか知らないから、わかんない。

とはいえ、ぱっと見では、クレジットカード会社にiDの申し込みをしてそれを登録っていう感じではなく、国際ブランドの手元のカードをiD支払いに紐付ければ、あとはAppleが適当によろしく処理してくれるのかな? と思える。これは大きな違いだと思う。おサイフケータイは、カードというハードウェアをAndroidというハードウェアに置換するだけ。ApplePayは、決済インフラにiDが選べるだけ、みたいな。

座組を想像すると

ApplePayは、どんな座組になっているのだろうか。クレジットカード決済は、ブランド-カード発行会社-加盟店管理業者、の3つの役割がある。iPhoneがカードの役割を果たすとすれば、Appleは決済の事業を持たない製造販売の会社だから、取りうる組み方は:

  • (ApplePay)-加盟店管理業者
  • ブランド-(ApplePay)-加盟店管理業者
  • ブランド-(ApplePay)

の3つの組み方になる。実際にはカード発行会社のカードを登録して利用するので、ApplePayの裏側にカード発行会社がずらっと並ぶ形になるから、それを乱暴にApplePayとまとめるのは間違いなのだろう。だから、これを書き直すと:

  • 座組としては: ブランド-カード発行会社-(ApplePay)-加盟店管理業者
  • 利用者から見ると: ブランド-ApplePay-加盟店管理業者

と視点ごとに区別して書けばいいのだろう。

Androidのおサイフケータイでは、iDのブランドで発行されたカードを登録する、Androidがカードエミュレータになるハードウェアであるだけ。それならばAndroid端末を製造する会社は、iDカードをエミュレートするハードウェアを製造すればいいだけだから、何も考える必要はない。だからカードがAndroidになるだけで、ユーザ体験も何も変わらない。

ApplePayの組み方は、カード発行会社と利用者の間に、強引に割り込んだ感じに見える。だから、カード発行会社であれば手にできる手数料は取れない。

カード発行会社とiDとの間の支払いをつなぐために、支払い期間のズレの吸収などで、それなりのお金をプールしなければならないだろうから、そのプールしなければならないお金を運用すれば得られただろう最低限の利益が確保できる程度に、とても薄い手数料を受け取る契約を、カード発行会社と結ぶくらいだろう。

支払い手数料を受け取れるカード発行会社は、顧客確保のためにポイントをつけるなどで、利用者を囲い込む。しかしApple自体は、薄い手数料しかとれないから、ポイントをつける原資がない。だがApplePay自体がiPhoneというハードウェアと結びついているから、iPhoneが売れることで利益が得られる。

これまで金融のなかで、3つの役割が成り立っていたところに、その役割を食う形で参入するのは、既存勢力から一斉に攻撃されておよそ無理に思えるけれども、この無理な割り込みの形は、そもそも利益源と顧客確保の方法が異なる4つ目の立場として、成り立っちゃったのでしょうか?

インバウンドとか狙うならiDと国際ブランド連携っていいと思う

SuicaもiDも、日本では便利かもしれないけれども、日本でしか便利ではない。そのうえ東京にいくと駅自体がSuicaに最適化されすぎて改札にICカード専用が増えている。東京に住んでいる人には便利だが、短期間滞在だと不便というのは、海外から来た人を排除する力だろう。

ApplePayで、ちょっと滞在したときに、滞在国のローカルサービスをちょこっと使えるのは、インバウンド、つまり、海外から来た人が消費をする、国内で消費しているけど輸出相当の消費、のために不可欠なピースだろう。

Suica程度なら使い捨てでカードを買うのはありだけど、あれはJR東のViewカードからしかチャージができない。クレジットカードからチャージする方法がない。だから日本円を手にして駅にある端末でチャージとか、およそ原始的な方法しかない。あるいはiD使いたくても、日本ローカルなカード発行会社からiDの付帯で個別カード発行とか、短期滞在でありえないでしょう。どっちも、ありえない不便さだろう。

SuicaにしろiDなんて日本どローカルなサービスを、カード発行しないと使えませんとか、東京オリンピックの招致のアピールで、”お・も・て・な・し”を口にしていながら、お金を使っていただく方々に対して、この国のルールに従え、なんて上から目線は、ありえないと思うわけで。

なので、手元カードとiDを紐付けられるのであれば、とても良い感じだと思う。というかApplePayの立ち位置こそ自然で、その国で事業をしているカード発行会社が絡む形のほうが、不自然に見える。

まだ発売前で、じつは海外から来た人は使えません、かもしれないし、その辺りが自然かどうかはわからないけど、インバウンドをテーマにするなら、Androidのおサイフケータイには絶望しかないけど、ApplePayにはこの先への希望があると思える。

Suicaのカード取り込みはすごいと思う

ApplePayは、手持ちのSuicaカードを残金情報を引き継いてそのままiPhoneに取り込めるらしい。しかもカードのデポジット代金500円がその時に返却されるらしい。さらに定期券などの情報も引き込める。まるでモバイルSuicaだけど、モバイルSuicaをiPhoneに持ってきたのじゃないっぽい。モバイルSuicaのだと年会費1000円(JR東の子会社のブランド、ビューカードと連携させていたら今のところ年会費不要だけど)がかかるけど、ApplePayにはそういうのはないらしい。

もともとJRのSuicaは、将来どのような異種のシステムが接続するかも予測ができない、ことを前提に作られている。異種統合型情報サービスシステムにおける自律分散アシュアランス技術、というやつ。

カード単体で情報を保持する、しかも駅を超高速でパスできるFelicaは、そのシステムの要の技術になる。カードの偽造や情報の書き換えが、カードそれ自体のハードと通信技術で厳重に確保されているから、もしも課金データを撮りそこねた場合は、Felicaカードの情報をマザーとする、というシンプルな方針が取れる。

その要なカードをiPhoneに取り込める、というのだから、驚く。それはiPhoneそれ自体がFelicaカードと同じセキュリティなどの技術で守られているとJR東が認めた、ということなのだろう。

しかも、iPhoneを落としても、リモートでiPhoneのひも付けを削除すれば、その端末に紐付いていたSuicaの情報を、新しい端末に繋ぎ変えられるらしい。Suicaのカードは常に1枚しかない、という前提があるが、iCloudはその常に1枚しかない状態を確実に保証できるほどJR東に認められているのだろう。いろいろ、大したものだなとおもう。

こんなの、JR東と協業しないと絶対に実現できないけど、ちゃんと協業しているのだなと、その取組はすごいなと思う。でもiPhoneからすれば、今まで自然にやってきたことを、今回もちゃんとやっているだけかもしれない。

この取り組みはしかし、携帯業界でも同じことをしてきた

もともと電話機を手がけていないAppleがiPhoneを出した時の座組を想像すると、決済は ブランド-カード発行会社-加盟店管理業者 だけど、携帯業界も、通信インフラ-SIMカード発行と代金回収-代理店 と3つの役割がある。

通信技術それ自体が3GそしてLTEとどんどん進歩していた。どんどん進歩する技術とちゃんと相互運用ができる携帯端末をちゃんと販売しなければならないから、キャリアが販売する端末を決められた。だから端末の販売代金を手にできた。技術が進歩している時は、技術開発と基地局の整備に莫大な投資が必要で、それを回収するためにも、端末販売代金は大切な収益源になる。

i-modeの時代は、ドコモが最初から課金と料金回収の決済システムであり、サービスを提供する奇妙なカスタムが入った独自HTMLライク技術を端末横串で一斉に搭載させるために、東京で閉じこもって、直接やりあって、すりあわせる。通信インフラと決済そしてサービスに端末のアプリの基盤技術仕様決定、全て握れていたのだから、文字通り、王様。

でも、技術それ自体が成熟すると。変な囲い込みをしかける。新規性にかける。陳腐化、ガラケー化。

そこにiPhoneという海外からの端末が登場した。普通なら割り込む余地がないけれども、顧客数を伸ばしたいソフトバンクは独占の形でiPhoneを取り入れ、急速に成長した。iPhoneはアプリやアプリ内課金のプラットホームでもあるから、i-modeから課金プラットホームでもあったドコモは最後までiPhone販売をしなかったが、そのドコモも今はiPhoneを売っている。

もともと割り込む余地が無いような業界のがっちりした枠組みの中で、うまく4つ目の役割で入り込み、課金という重要なプラットホームとしての立場すら手に入れたのが今のiPhone。

いまの3大キャリアは、売上自体は右肩上がりをしないと会社の評価が下がるから、パケット代金はそのままに、でも高額な月額を、iPhoneを販売してそこに割引をつけて、実質の月額で補っている。形のないパケットの量で商売をするための、さじ加減をつける材料にiPhoneの販売金額が使われている。

莫大な初期投資が必要な時期ならそれは社会に必要だったのだろうけれども、投資が一段落したならば、それで儲け続けてもらっても、社会はあまりうれしくない。そのお金が別の分野に使われて、違う分野が隆盛してほしい。それに電波資源という公共の財産を使いながら、事業的に独占の立場を占めるのは許されないことだから、回線部分だけを切り出してMVNOとかが隆盛してくるようにする。これでやっと、本来の 通信インフラ-SIMカード発行と代金回収-代理店 の3つの役割を、今まで前2つが1つの会社で牛耳っていたのが、3つの個別の会社に担わせることができる。

カードというハードウェアとのさようなら

クレジットカードのカード番号や有効期限あるいはセキュリティコードまで、全部の情報が磁気テープに入っているから、スキミングをしてコピーをするのは容易で、セキュリティなんてどこにもない。

でも、アニメ調の柄のクレジットカードだと、海外だと怪しんで受け取ってくれないとか、通信回線がない奥地にいくと、クレジットカードの上にカーボン紙を置いてローラーをがーっと走らせて転写する方式で、最近のNFCとIC化されたクレジットカードは回路を壊すからエンボス加工がなくて受け付けてくれないとか、けっこう末端の人だよりでセキュリティ的な対応をしている。

考えてみれば、クレジットカードは番号とかのアカウント情報を記載しているただのカードで、署名をして初めて支払いが成立するものなのに、怪しい柄だからとか、ローラーで転写できないから受け付けられないとか、逆にありえない。ローラー転写できなくても、手書きで書き写せば同じことだと思うし、今時エンボス加工のカードは3Dスキャナとプリンタで即座に物理コピーできそうだけど、そういうものでもないらしい。

クレジットの信頼を付与されているのは自分のはずなのに、紙と鉛筆の世界では、クレジットカードという形あるものでクレジットを表現しちゃっていたから、いつのまにか、クレジットカード=クレジットと、思い込みがあるような気がする。

最近のクレジットカードは、ICカード化とか顔写真を入れられるとか、いろいろとセキュリティを高められますとか提案してくるけど、紛失しても補償されるから、カード発行会社には利得があるのだろうけど、利用者にとってのメリットはない。

出張をしていると、クレジットカードをもしもなくしたらと思うと、けっこう怖い。なくして不正利用されても、紛失をカード発行会社に届けた日から遡って60日以内は補償されるから、その意味での怖さはまったくないけど、出張先で支払手段をなくすとか命の危機を感じるので、それが怖い。

iPhoneにクレカ登録だと、クレカとかなくしても、どこかでiPhoneを手に入れて自分のアカウントを入れれば、クレカが復活するのだから、そういう怖さから開放されそうで、それはいいなと思う。カードというハードウェアを離れて、本当に自分が持っている信用、クレジットで支払いができるようになると。

だから、出張先でもしもiPhoneをなくしても、どこかでiPhoneを買ってアカウントを登録すれば、支払い能力が復活する、カードという物理が、iCloudの私のアカウントにひも付けされて形ない情報に変換できる、そこがいいなと思う。

いまのATMは、カードを差し込んで使うから、iPhoneの中にあるクレジットだと現金を手にすることはできないけど、スマートフォンだけでATMからお金を引き出す、クレジットカードのハードが消えて情報化する流れがあるから、落として困るクレジットカードは、少なくとも都市部では、急速に消えるだろう。

うだうだ書きましたが

長々書きましたが、クレジットカードというハードウェアを正しくゴミにした、って気がします。本来、私に付与されているクレジット、信用をネットのアカウントに紐付ける、その紐付いた信用はiPhoneを通じて支払い相手に示せる。

iOS10雑感

はじめに

WWDC2016の動画から、iOS10への雑感をまとめます。

WWDC2016のここを見る

  • ロック画面のスライドバーがなくなり、ホームボタンの押し込みに変更された。
  • WatchOS3の登場、iCloudへのアクセスやジャイロなどのセンサーへのアクセスなど、単体での動作がほぼできるように。
  • SiriKitを通じて、タクシーを呼ぶ、メッセージを送る等の用途を限定して、アプリにSiriが開放。
  • CarKitのプレゼンがあった。
  • VoIPアプリに電話の発呼を開放するCallKit。

豊かなロック画面、アプリケーションのサービスの集約点

ロック画面のスライドバーがなくなり、ホームボタンの押し込みに変更されています。ホーム画面の左右スクロールは、通知画面やカメラ画面への移動となっています。またSiriKitを通じて、タクシーを呼ぶ、メッセージを送るなどの限定された範囲でSiriの機能がアプリに公開されました。またよりリッチな通知表示が可能となりました。

これを見ると、もうアプリを探して起動するすることは、手間で嫌なことになり、ホーム画面で日常の要件はほぼ完了するのが当然になると思います。この流れは、操作にアプリ画面を見る必要が無いならば、Watchに自然に体験が広がってもいくでしょう。

今までの電子手帳のイメージ、画像やテキストが表示されて、それを見て、タッチやテキスト入力の操作をする、そういった前提が変わります。

SiriKitの提供

iOS10では、SiriKitを通じて、タクシーを呼ぶ、メッセージを送るなど用途は限定されていますが、アプリケーションにSiriが開放されました。音声でサービスを呼び出す体験は、周囲に人がいて奇異に見られないならば、便利な体験です。アプリケーションを使う場合とSiriを使う場合では:

  • ホームボタンを押す → アプリケーションを探して起動する → アプリに条件を入力して検索する → 結果を目で見て選択する
  • Hey, Siri、タクシーを呼んでという → いくつかのサービス候補を選んでタッチ

と、手間と頭をつかう回数が少なく、操作完了までの時間も短く、今までのモバイル体験とは別の体験になります。

iOS9までは、アプリケーションの豊かさが、iPhoneをより便利により豊かな体験にしてきました。より多くの人が使うプラットホームが、より多くのアプリケーション販売収益をもたらし、それがより多くのアプリケーション開発をよびよせる、正のフィードバックをもたらしていました。

アプリからサービスのプラットホームへ

いままでのエコシステムはアプリケーションの豊かさでしたが、もうモバイル機器があることが当然になると、ユーザが求めるのはサービスです。そしてサービス事業者は、必ずiOSとAndroidの常に両方にアプリケーションを提供します。社会の様々なサービスがモバイルを通じて提供される時代には、サービス1stの時代には、モバイルのプラットホーム事業者の立場と役割が変化します。

これまで写真や音楽といった自社サービスと豊かなアプリケーションの世界は次の時代に切り替わろうとしています。例えば、音楽というコンテンツを販売するiTunesは、ユーザが楽曲を買えば買うほど、楽曲を楽しむためにiTunesを使い続ける強い理由になりました。しかし音楽は月額定額で配信で楽しむ時代になってしまうと、購入したコンテンツを理由にしたユーザの囲い込みは、その根底から崩れます。

モバイルのプラットホームはAppleとGoogleのほぼ2強ですが、より便利で自然なサービス体験を提供する方にユーザは流れます。音楽や写真がたまっているから、iPhoneを使い続ける、という囲い込みの理由がなくなれば、ユーザは携帯電話を買い換えるたびに、iOSとAndroidを自由に比較して、気軽に切り替えるかもしれません。

ハードウエアの販売が大きな売上と利益をもたらすAppleにとっては、死活問題にもつながります。

より豊かで便利なサービスを提供するプラットホームへと、大きく鍵を切り替えていかざるをえないのだろうと思います。Siriのアプリケーションへの開放は、ユーザがサービスを利用する手間を極限まで小さくする1つの仕組みとなるかもしれません。また、サービス事業者はアプリのSiri対応をしなければならないでしょう。

サービスと会社の協業

CarKitは車載AV機器との連携を提供します。その仕組はiPhone側から画像を表示する仕組みと、車載機器側からはタッチされた(x,y)座標をiPhoneに返す程度の、とてもシンプルな仕組みです。実装には手間はかからないでしょうし、車載AV機器それ自体の価値を強制的に横取りするようなものでもありません。

CallKitはVoIPアプリにiPhoneの通常の電話体験を開放するものです。VoIPでかかってきた電話が、CallKitを通じて、通常のiPhoneの電話UIへとつながります。LINEなどのサービスはもちろん、内線電話アプリなどのビジネス向けのサービス開発に役立ちそうです。

アプリケーションによるエコシステムであれば、Appleが新たな機能を提供することで、新たなアプリが開発販売されて、それがiPhoneの魅力となり、アプリそしてiPhoneの売上という収益源になりました。しかしVoIPアプリで、サービス利用をアプリ内決済に限定することは困難でしょう。CallKitを提供しても、Appleはこれまでのエコシステムのような収益が得られるとは、限りません。

しかし、例えばビジネス向けのサービスに採用されれば、iPhoneという端末は売れ続けます。逆に、Androidがビジネス向けで他社が使いやすい何かを提供してそのような立場を占めてしまえば、一旦採用された分野に後から潜りこむことはビジネス用途ではかなり大変ですから、売上のチャンスを手にできなくなるでしょう。

BLE4.2勉強会に参加して

BLE4.2勉強会でのプレゼンテーション

BLE4.2勉強会 Maker Lab Nagoya に呼んでいただき、以下の資料でプレゼンをしていました。

参加された方々は、初めての方や、2013年にあったBLEの講演や2014年に大垣で開催されたiBeaconハッカソンに参加されたお久しぶりの方々で、
また名古屋以外から多く来られていました。

iOSと家電を統合するHomeKitというものがあるのですが、ラズペリーパイやESP8266などにホームキットを実装して、
Siriに”リビングは何度?”と温度を聞いたり、”照明をつけて”と指示を出したり、もう会話が成り立つレベルで使えること、
またその作ったものを見せていただくなどしました。

本音では、あんたらプロやん、むっちゃプロやん、っていうか知識もうあるやん勉強会とか必要ないやん、と心のなかではツッコミまくりです。

プレゼンの構成と内容

プレゼンの構成と内容は、すでにBLEをよく知っている方々と、基礎から知りたい方々のどちらにも役に立つものをと考えました。
前半はBLEを、後半はBLEに限らず今バズワードとなっているIoT系も含めて広く、ハードウェアが持つ意味をどう変化させていくのか、
その自分なりの考え方をまとめました。

BLE4.2の使い方、使われ方

BLEの内容は、2013年での講演のようにBLEの技術内容を物理層からボトムアップで詳細に語るのではなく、
その技術がどのような使われ方につながるのか、またその技術が世の中で使われるとどのような意味を帯びるのか、から構成しました。

ネットワーク・トポロジは、つながりかた、つまり通信で誰と誰が関われるか、その形を決めます。
4.0での単純なスター型でよい場面、また使えない場面。そして4.1で入った更新がもたらした変化を話しました。

パケットをやり取りするリンク層は、電池交換や商品価格や外形デザインに大きく影響する消費電力などに影響します。
ブロードキャスト、リンクした時の通信の特性を話しました。

Bluetoothは無線通信を通じてハードウェアを使うための技術と言ってもいいでしょう。通信を通じて、ハードウェアと関わるために
ATTおよびGATT層が持つ役割を、20世紀ではなく21世紀でのオブジェクト指向という視点で話しました。

セキュリティ・マネージメントは、表には見えにくい技術ですが、ハードウェアがネットワークに繋がる時代には、
そのセキュリティ・マネージメントこそがハードウェアの所有や利用の権利を管理する重要な核となります。
通信技術の視点でのセキュリティ・マネージメントは、利用者の大切なプライバシーやセキュリティを守るための技術ですが、
そこにハードウェアが関わることで、権利のための基盤技術となり、大きな意味を持ちます。

L2CAP connection oriented channel
は4.1で導入されたBLEでもL2CAPを直接ストリーミングとして使える仕組みです。IP系の技術などGATT/ATTとは全く生まれの違う技術でも、
GATT/ATTと共存させることができます。

無線通信は通信相手も同時に普及しなければならないために、通常は普及までに長い時間がかかります。BLEは、すでにほぼすべてのスマートホン
に採用されているBluetoothの規格一部であるため、Bluetoothブランドのもとで、またiPhoneにより短期間のうちに普及しました。

直接電波をやりとする物理層は、金物でしか実現できませんから、このチャンネルのような仕組みがあると、
電波をやり取りする部分は、スマートホンから直接使える利点もあり大量に製造されることでコストも下がりますから、BLEでよいのではないかとも思えます。

IoT系の話

インターネットを振り返りIoTを見通す

IoTという単語が話題になってきています。その話題の単語について語るのは、例えば2000年にインターネットという単語で、これから生まれていくる
新しい企業やサービスを議論するような、いくつもの分野にまたがるとても広い範囲の話となり、何時間でも話が尽きないことになります。

しかしインターネットという単語は、たしかに新しいサービスを生み出していますが、世界の価値生産からみてそれなりの割合を占める
新しい産業を生み出したのかといえば、自分の生活を見る限りは疑問です。
しかし、新しい技術で市場に参入することで、大きなシェアを占めることは、いろいろなところで起きそうです。

例えば、私は、商品購入や移動あるいは宿泊は全てネットを経由して提供されるサービスを利用しています。
ネットがなかった2000年以前とネット経由でサービスを利用している2016年では、利用している会社あるいは業者は全く違っていますが、
しかし消費活動それ自体は同じものです。
しかし、インターネットあるいはITという新しい単語で、ネット経由の旅行業を始めた会社は大きく成長しています。
アマゾンなどの通信販売会社は年々売上を伸ばしています。

IoTという単語で新しい消費は確かに生まれているはずですが、しかし個人の消費行動が大きく変わらないなら、
世界の価値生産の大きな部分を占めることはありません。
産業としてみれば人の消費活動それ自体は新しい物はなく、しかし新しいものとしてラベリングされることで、新しい物を提供する会社が
既存の需要/シェアを飲み込み、急速に成長する気がします。

見せかけのオープン

IoTという単語で、いくつかの企業が集まり新しい規格や団体を作り出しています。あまりに多くありすぎて、もはや、どれがどれかを認識することすら困難です。

今の時代的には、それが実用に足るのかを判断できる程度に、1つの規格を見ていくのも時間と手間がかかります。さらに、通信技術が使われるためには
既存分野の枠を超えた広がりなど、周囲の盛り上がりが注目をあつめるために必要でもあります。

技術仕様がオープンであれば、オープンソースプロジェクトなどで、使いやすいものが出てくる可能性もあります。
また、規格団体がオープンソースでプロジェクトを運用することもできます。もしもオープンでなければ、
規格団体がプロジェクトを運用するときに自身のオープンにはしない方針に縛られて、バイナリファイルの形でしか提供できない、あるいは
個別問い合わせでライセンスを締結しなければならないなど、活動の活発さを削ぎ落とすことにしかなりません。

また、仕様がオープンであっても、収益源として確保する方法が、今はいくらもあります。
事業をするならばライセンスが必要になる法的なユーザライセンスにしておいてもいいでしょうし、
事業規模で実施するには認証コードや暗号鍵のようなものの払い出しをうけなければならないような技術的な方法もあるでしょう。

もしも規格が、本当にオープンなのか、それとも今時の時代の流れに合わせて横並びになるためにオープンにしているだけなのかを判別するならば、
個人あるいは従業員数名の会社でも、規格に対して提案ができる仕組みがあるのか、また提案が取り込まれる可能性があるのか、を見てみるのがいいかもしれません。

もしも、規格は公開されてオープンになっているが、規格を決める手順や運用は公開されていないならば、形はオープンではあるが、その意味は、
我々の軍門にくだれ、これは軍門にくだるための手順書だ、という斜めからの見方も、できるのかもしれません。

オープンの意味

2000年にインターネットという単語が話題になり、ウェブ制作会社など今までなかった会社が生まれる一方、インターネットを活用した今では大きな会社がいくつか生まれています。
そのような会社は、システムは会社の根幹ですから、会社内部でシステムを作っています。

一方で、2010年以降は、iPhoneのアプリ市場の盛り上がりなど、ハードウェアとSDKそしてアプリ配信と課金システムがプラットホームとなり、その上で多種多様なサービスが
展開されています。いわゆる、エコシステムと呼ばれるものですが、日本でも1つのアプリで何千億円もの売上をあげるソーシャルゲームが誕生しています。

そう見ると、IoTでのオープンというのも、プラットホームとしてのオープンさ、エコシステムになるための必然としてのオープンさなど、いろいろ違うオープンがあるのかなと思います。
これからIoTで事業をする会社が外注することは、ありえないでしょう。IT系の投資は、初期はせいぜい人件費、1桁億円でしょう。
金額のちいささと事業の将来を決めるものととらえる重要性をちゃんと理解するなら、内部で閉じたチームでお仕事をされるだろうな、そう思います。

IoT系の本当に社会で運用されるニュースが表に出てくるのは、おそらく8年後くらい、例えばアマゾンのシステムを利用しているところが、アマゾンの宣伝のために講演に出てくださいと
促されて表にでてくるなど、そういう感じかなと思います。

どんづまりの悪あがき

ニュースでIoTという単語を目にした時は、それをニュースに出すことが利益になるものがいないか、を考えてしまいます。

前節の旅行業界の変化の振り返りから、IoTという単語は便利な道具ではなく、消費や決算あるいは流通経路が今までとは違うものにしうる何かとも言えます。
誰しもがそういった可能性を現実に起きうるものと感じるからこそ、IoTという単語は話題を集めるのでしょうが。

既存の大企業が、5年間の中期経営計画を立てるとき、IoTという単語を使うのは、流通などの経路の変化とはお金の流れの変化であり、それは大変化ですから、それはそうなる気がします。
しかし、ビジネスモデルそれ自体は全く変えずにネットを活用していますと外部にアピールするようなものであれば、
それはIoTという単語をただの便利な道具と誤解しているだけか、未来的な単語を取り入れることで株価に期待値を織り込みたいだけか、
なのでしょう。

自分が変化する気がなくて、現状からさらなる成長を探す努力に疲れて、
一振りすれば業績が10倍になる魔法の道具の渇望からIoTという単語が口から出るなら、もうその会社は寿命を迎えているのでしょう。

雑談のなかから

ウェブとハードウェアのエンジニアの、混じらなさ

BLEの開発でも経験しますが、アプリはアプリの会社、ハード(とBLEのファームウェア)はハードの会社と発注先が別れることが、
よくあります。そうすると、アプリとハードのエンジニアの所属会社は別ですから、勤務場所も別となり、アプリとハードの連携部分で、
うまく動かない、こういうものではないと、トラブルしか起きません。

IoT系の分野でも、プロジェクトをちゃんとマネージメントできる人がいなければ、そういう事態になるしかないだろうなと思います。

受託案件をしていて思うのは、3という数字が大事だと思います。発注と受注という1対1の関係性では、意識して対等の関係であろうとしても、
どうしても納期あるいは仕様などで、対等になりにくい部分が無意識に意識されて、それが見えないトラブルの原因となりかねません。
そのようなとき、発注-受注の関係ではなく、第3者的に、普段は別に仕事をしているわけではないが、客観的に流れを見ていて、
ときどき関与する人が、いるといいのかなと思います。

MQTTとかCoAPとか

IoT系のデモンストレーションとして、MQTTを使って、1つのハードウェアのボタンを押すと、ネットワークで繋がった別のハードウェアのLEDが点灯するデモンストレーションが、
よく行われます。

2000年前半にIPv6振興で公的機関が動いていた時期があります。その当時はブラウザから機器を操作する、いわば回りくどいリモコンがデモンストレーションとしてよく見かけました。
現代にブラウザから機器をオン・オフしても、それはそうだよねとしか思えません。ハードウェアの連携という姿を表わすのに、このようなデモンストレーションになるのかと思います。

その時によく使われているのがMQTTというプロトコルです。ブラウザではない何かのデモンストレーションをするときに、ツボにはまるのだろうと思います。

ただ、デモンストレーションの都合というしょうもない発端を一度忘れてみると、MQTTの仕組みをデータによるシステムの疎結合の結合点としてみると、いわゆるIoT的なものを構築するときの
1つのパターンと見てもいいかもしれません。

例えば、Suicaのシステムを構築した方が博士論文を出されています。
異種統合型情報サービスシステムにおける自律分散アシュアランス技術の研究

Suicaといえば、駅だけでなく、いまやバスや売店など広い範囲で使われている、まさに支払い手段のインフラといっていいまで普及したブランドです。
このSuicaを支えるシステムは、毎日大量の利用/決済データを全国各地で処理するだけではなく、システムの一部が故障や停止をしても全体として停止しないタフさをもたせる、
また異種のシステムの接続を受け入れていく必要がでてくるだろうなど、設計時点では想定できない事態に対処できる柔軟さをもたせるなどの、
実用から必要であり、まただからおもしろいと見える、発想から設計されいる、っぽいです。

このシステムの技術的な根っこは、
システム要素がバスに流れるデータに反応するデータ駆動の疎結合、
データの矛盾が発生した場合はSuicaの電子カードの記録をオリジナルデータとする矛盾発生の対処方針、にあると私は思いました。

そう思うとMQTTは、システムが時間とともに成長し変化していく柔軟さを持たせるために不可欠な、構成要素間の疎結合と要素間の連携動作の連結点、電気回路でいえばバスのようなイメージのなにかを、
IPの世界で提供する何かと見ておくと、いいのかもしれません。

その他

オフレコでしか話せない内容を話せて、個人的には満足しました。

なぜきぐるみを着たのかという話、集団から異種な存在である立場の持つ意味、
ニュースで見かける東京というか秋葉原発のハードウェア・スタートアップの内容が個人的にむちゃくちゃ嫌いな理由、
IoT系にも通じるボケとツッコミの大切さ、ハードウェアそれ自体に価値がなくなっていく時代の流れなど、
リアル世界で肉声で話してみて、自分にとって大きな収穫を得られた気がします。

また今後共。

nRF52の印象

モジュールの種類と選定ポイント

各社からnRF52を搭載した無線モジュールが、評価版が1月から2月、量産が3月から4月に開始される予定です。nRF52はnRF51とピンコンパチブルなので、モジュール利用者から見れば、nRF51の無線モジュールをそのまま置換できるイメージでよいでしょう。無線モジュール開発会社にとっては、バランがオンチップになったりと高周波周りは再設計で手間がかかるとは思いますが、それが仕事ですから、作ってくるだけの話だと思います。

モジュールは、大きさで2種類に分類できます。一つは10x13mmくらいのもの、もう1つは11x5mmあるいは更に薄く小さくしたものです。nRF52は、6×6mm QFN と小型の3.0×3.2mm CSPの2種類のパッケージで提供されます。小さいモジュールはCSPを使っていたりします。

10mm角程度のモジュールは、汎用的に作られているものです。nRF52のすべてのIO端子が外部に取り出されている、たいていの場合ではDC/DCの外付け部品と32kHzの発振素子がモジュール内部にあります。小さいモジュールは、ものによりますが、すべてのIO端子は取り出されていない、DC/DCおよび32kHzの部品が省略されていることがあります。

モジュールを選ぶときには、極端に消費電力を気にするのか、IO端子はどれだけ必要か、の2点で決めればよいです。大きさが制約条件になる場合は、小さなモジュールでならば実装できるとしても、技術的には、独自に実装してしまったほうが早いかもしれません。ただし開発費用が1000万円ほど上乗せになりますから、そのあたりは技術ではない選択になるでしょう。

BLEで電池を使うものは、使い捨てのCR2032などのコイン型電池あるいは充電可能な40mAh程度のリチウムイオン電池を使うでしょう。コイン型電池で直径16mmか20mm、リチウムイオン電池でもその程度の大きさなので、腕に巻く細いバンドでもない限り、どちらの大きさのモジュールでも使えるくらいです。

消費電力を気にする場合は、DC/DCは必須です。(32kHzの発振素子もあったほうが、たぶんよい。nRF51の場合では、32kHzの発振素子がない場合は平均4マイクロアンペア程度消費電流が増えます。nRF52でそれがどうなるかは未確認ですが、増えるんじゃないかしらん?)

nRF51からnRF52へ移行すべきか

nRF51を使っている既存製品の次モデルをnRF52にしておくとメリットがあるのか、あるいは新規開発でnRF51とnRF52を選ぶとすれば、何を基準に判断すればよいのか、いろいろあると思います。

nRF51それ自体は今後も提供されていくでしょう。ですが特にnRF51を選ぶ理由がないならnRF52を選んでおくのがいいと思います。

nRF52はnRF51と内部構造は同じです。またパッケージはピンコンパチブルです。ファームウェアやSDKあるいはソフトウェア・スタックなどのソフトウェアのバージョンの違いは出てきますが、nRF52はnRF51の上位版として使えます。

極端に消費電力を気にする場面では、採用する価値が大いにあります。

これは無線回路の低消費電力化もさることながら、周辺回路のPPIおよびEasyDMAの活用が可能になったことがあります。逆に、それらの機能をファームウェアが活用しないならば、nRF52の価値はないです。機能を達成するためには、ハードウェアの選定時には、その旨を共有するために、ファームウェア開発者も同席させておくべきです。

nRF52から見えるファーム開発手法

nRF51でもそうでしたが、nRF52ではさらに、ハードウェアっぽい開発のやり方が適していると思います。1つはRAMの使い方、もう1つは周辺回路のリソースです。

ヒープ領域つまり動的に確保解放するメモリ領域は、BLEでは使う必要はないと思います。nRF51のSDKを見ると、例えばタイマーなどは、必要なタイマーの数をコンパイル時に指定してしてしまいます。1つ1つの機能が必要とするRAM領域を静的に確保し、使い続ける感じです。

これはアプリケーションが使えるRAMサイズが8kBもしくは24kBのnRF51だったから、そういうコードを書いていただけで、RAMが64kBになったnRF52では、ヒープ領域を活用すればいいだけかもしれません。

ヒープは、処理を時間軸で見た時に、処理が開始/終了する場合に、メモリ領域の確保と解放によって、小さなメモリでも処理を収めることができます。ある処理について、処理時に必要なメモリ量がかなりあるとします。しかし、処理が終わればメモリは解放できるとします。そんな処理を、1つ1つ処理していくならば、ヒープ領域を使うべきです。

ですが、BLEのハードウェアは、電源が入ってからずっと、それぞれの機能自体が最初からずっと動き続けるものがほとんどです。ソフトウェア処理であっても、とてもハードウェア的です。このようなものは静的メモリ確保がよいです。メモリが不足して処理が停止することがないか、というワースト・ケースの検証がむちゃくちゃやりやすくなります。

この方法では、メモリ不足の例外が生じた時には、動的に確保されるのはスタックを見るだけでよいです。スタックは処理順番と処理過程の情報を全て含みますし、静的に機能しているものは静的なメモリ領域を見ればよいので、例外発生時のデバッグが楽です。

極端な話をすれば、動的なメモリ確保では、メモリダンプをしても、どこにどんな値があるのかを読み取るのは、とても大変です。静的な割当であれば、シンボルテーブルを見ながら人力ですら、目視デバッグ可能です。

nRF52は周辺回路すべてにEasyDMAが入りました。nRF51でのPPIは、タイマーやGPIOを組み合わせて一定周波数のパルス波形を出す程度にしか、活用できませんでした。しかしメモリ転送が組み合わせられるので、TWI経由でのデータ収集がPPIとEasyDMAでできます。

if then elseが入らないかぎり、また演算処理をしないかぎり、プロセッサを起動する必要はないでしょう。またデータをためてから一気に処理をすることで、プロセッサのスタートアップ時間40マイクロ秒で消費する電力を最小化できるでしょう。

通常であれば、プロセッサが制御を行うもので、周辺回路は割り込みでプロセッサに処理を委譲するファームウェアを書くと思います。nRF52でも、電力を気にしないならば、普通のそのようなファームウェアは書けますし、それで機能します。

nRF52であればできること

nRF51ではできなかったがnRF52であればできることは、デジタル信号処理が必須になる利用場面です。音声のやり取り、音の送受信、また万歩計などライフログ的な機能などがあります。

例えば万歩計であれば、3軸加速度データを1000サンプル/秒で収集、合計3kB程度のデータを貯めて1秒ごとに一気に演算処理する、なんてことができます。プロセッサがスリープから起動する40マイクロ秒よりも、演算処理時間が十分に長く取れる程度に、IOをバッファリングさせることがnRF52なら、可能です。

IoTの話題

IoT系の話題に乗りたいなら、nRF52を採用すべきです。今後、IoTに向けた規格拡張がなされていきますが、nRF52はそれらに最適な対応ができるハードウェアに、たぶん、なっています。

IoT系の話題が盛り上がっています。BLEでも、IoT系で必要になるメッシュネットワークに必要な技術を提供しています。また今後のBluetooth規格には、メッシュネットワークが規格もしくは実装推奨など何らかの形で、SIGからの提供があるでしょう。

IoT系でのBLEの立ち位置は、2.4GHzで電池持ちがやたらといい超低消費電力無線通信技術で、スマートフォンやパソコンが通信相手になれる普及した無線通信技術です。

920MHzのように100メートルを超える距離の通信や、Zigbeeなどのメッシュネットワークを始めとした工業用途での実績はありませんが、スマフォが対応している1点だけでも、一般消費者に使いやすさを提供できる技術です。

隣のBLEデバイスとの通信の確保、つながったデバイスがパケットを中継して、遠く離れたデバイスにパケットを届けるくらいまでは、技術が提供されるかもしれません。しかし、WiFiなど他のIoT系と同じく、通信スタックをどうするか、デバイスの管理機構やそのための通信仕様、あるいはデバイスのデータ表現の方式などは、標準規格が乱立していきます。

このあたりは、BLEとしては、上層には何を採用してもプロセッサにその処理を実装すれば作れます。何をどうするかは、ハードウェアの製造会社が決めれば良いだけの話です。

雑感:BLEだけでよいのだろうか?

nRF52に限らずnRF51でも成り立つ話なのですが、複数のBLEデバイスが身の回りにあふれる状況で、BLEだけでよいのだろうか?、と思うことがあります。IoTやスマートホームの話題にかぎらず、身の回りにわんさとデバイスがある場面であれば、1台ごとにユーザが管理するのは、ユーザ体験として無理があります。

そのときに、開発としては、メッシュネットワークやIoTの標準技術になるだろうIPv6などの通信技術や規格を求めたくなるかもしれません。しかし、通信技術とは通信相手があって初めて意味がある技術です。身の回りにあふれるデバイスとの通信相手は、なんでしょうか? 必ず、WiFiに接続する何らかのデバイスあるいはスマートフォンがルータになります。このルータと複数のデバイス間はBLEしかありえません。しかし、デバイス間がBLEである必然性はあるのでしょうか?

またIPv6が規格としてかたまり利用可能になったとしても、デバイス管理システムは、おそらく自社で構築する他ないでしょう。これはIPという技術は通信を提供するだけで、その上のアプリケーション、管理システムにはまったく関わらないからです。おそらく、会社ごとの管理システムが乱立して、誰も使わない暗闇の時代になるでしょう。

IPv6でBLEデバイスが直接インターネットに繋がることは、デバイス管理上、ありえないと思います。家庭用のWiFiを見ていても、防壁がなければいろいろなところからちょっかいをかけてくるのがわかります。BLEデバイスであれば、通信はすなわち電池の消費に繋がる場面もあります。セキュリティの意味でも、BLEを直接外部の任意の通信にさらしてもしかたないです。インターネットとBLEの間にあるルータには、フィルタリング機能が入るでしょう。

では、デバイス間では? BLEをベースにしたメッシュネットワークなど、これから実現される技術を使っても良いのですが、自社製品だけで固めるならば、独自の技術でも良いはずです。nRF51およびnRF52には、Time Slot APIという、BLEが無線回路を使っていない間、無線回路を使うAPIがあり、任意の通信プロトコルが実装可能です。そういったAPIを活用して、BLEに準拠しない自社で閉じた通信で、デバイス間連携と管理システムを作ってしまうのでよいのでは、そう思います。

また、技術が標準化されたならば、ファームウェアを更新することで、その技術への対応がnRF51/52であれば、できます。そういう進め方で、いいようなきがするのです。