PushKitでデバイストークンが取れなくて、困っていました。原因は、バックグラウンドのオプション指定に、VoIPが含まれていないことです。

Xcode ver9.2では、アプリケーションターゲットのバックグラウンド・モードにVoIPがなくなっています。ですが、アプリケーション内部ではVoIPのオプションを見ています。Infoタグで、Info.plistの編集で手動で、バックグラウンドにVoIPを含めるようにします。

Capabilitiesタブのバックグラウンドを変更するたびに、この手動で追加したVoIPは、Xcodeにより消されます。なので、忘れずに手動で再度VoIPを、バックグラウンドに追加します。

Read More

何したの?

加速度の大きさで信号を出力するものを試作する案件に、micro:bitが使えるかと思い、試作に用いてみました。HEXファイル、iOSアプリのソースコード、アプリの動いている動画などのリソースは https://github.com/reinforce-lab/microbit_accs_siwtch にあります。

マイコンというハードウェアへのファームウェア書き込みは、書き込み装置が必要で、書き込み手順を覚えなければならず、誰でもできるものではありません。このマイコンボードであれば、誰でもファームウェア更新ができます。これなら、ファームウェアを書き込んだハードウェアを送付しなくても、HEXファイルを送るだけですむかもしれません。

どんな情報があるの?

自分で作るiOSアプリケーションと、micro:bitとの間の、任意の情報のやり取りの実装方法があります。iOSアプリケーションの表示画面は、このようになっています:

micro:bitで、文字列に組み立てた加速度や内部設定値、そしてIOの情報を、BLEを経由してiOSアプリに伝えています。iOSアプリで、それをグラフ表示しています。
iOSアプリから、設定値をBLEでmicro:bitに伝えて、micro:bitのプログラムで、その値を該当する変数に格納しています。

micro:bitに最初からあるIOサービスを使い、BLE経由でセンサやスイッチの情報を読み取るサンプルはたくさんありますが、自分のプログラムで処理した内容を外部に伝えたいときのスタートポイントになるでしょう。また、外部からmicro:bitへ情報を伝える方法の解説が、なぜか、見かけませんが、BLE UARTを使った情報の渡し方の解説になっているでしょう。

micro:bitって?

micro:bit http://microbit.org/ja/ は、プログラミング教育の教材で、Nordic Semiconductor社のnRF51822を使ったマイコンボードです。

グラフィカルにブロックを並べてプログラムを組み立てられるビジュアル・プログラミング環境があります。またBluetooth low energy(BLE)でスマートホンと通信できるので、スマートホンやタブレットで作ったプログラムをBLEで書き込んだり、スマートホンとマイコンボードとで通信ができます。

nRF51822は、RAM容量が16kバイトと32kバイトの2品種がありますが、micro:bitに使われているマイコンボードは16kバイトのものです。BLEをまったく使わない場合は、16kバイトのRAMを全てユーザのアプリケーションに使えます。BLEを有効にすると、BLEのスタックが12kバイトのRAMを使うので、ユーザのアプリケーションに使えるRAM容量は4kバイトになります。スタックに最低2kバイトが必要ですから、実際に使える容量は2kバイトです。

プログラミング環境

micro:bitの開発環境は、TypeScriptを用いるものと、Pythonを用いるものの2つがあります。いずれもビジュアルプログラミング環境が提供されていますが、テキストでソースコードを書くこともできます。Pythonの開発環境では、BLEは使えません。その代わりに、Nordic Semiconductor社の独自プロトコルの無線通信機能のみが使えます https://lancaster-university.github.io/microbit-docs/ubit/radio/ 。この独自プロトコルの無線通信は、micro:bitのボード間の通信はできますが、BLEではないので、スマートホンとは通信できません。

TypeScriptを用いるものは、Microsoft社のmake:codeで提供されています。下層は、arm mbedとNordic nrf51を使ったC/C++開発ランタイム https://lancaster-university.github.io/microbit-docs/ です。TypeScriptで書いたソースコードが、このランタイムのAPIを呼び出す形になります。

TypeScriptは、Microsoft社が開発している、JavaScriptに静的型付けとクラスベースのオブジェクト指向を加えたJavaScriptのスーパーセットになるよう定義された言語です。make:codeのプロジェクトは、TypeScriptのサブセットでプログラミングができます。TypeScriptで書いたソースコードは、ウェブブラウザの内部で構文解析されて、Arm thumbコードに変換されて、HEXファイルが生成されます。ローカルで、機械語とHEXファイルの生成まで行い、ユーザのコンパイルがローカルで完結してサーバーを必要としない作りになっています。

Pythonを使うものは、省略します。Pythonのバイトコードを実行するために、ある程度のRAM容量が必要になりますが、BLEを有効にすると必要なRAM容量が確保できないみたいです。BLEを有効にすると実行に必要なRAM容量が取れないため、Pythonのmicro:bit環境では、Nordic Semi.社の独自プロトコルの無線通信は提供されていますが、BLEは使えません。

foreverブロック

アプリケーションはforeverが繰り返し呼び出されます。

foreverブロックは図の通りです。加速度を取得して、しきい値を超えればP0/P1のピンにHIGHを出力にする、また閾値や加速度の値をBLEでスマートホンに伝えるメインルーチンを書いてみました。加速度の大きさを変数”accs”に取得して、それを”plot”ブロックで、micro:bitのLEDアレイに表示させています。文字列は”join”で連結して作っています。最後にBLE UARTで、スマートホンに文字列を伝えます。また、ルーチン呼び出し頻度を確認するために、ルーチン呼び出しごとにmicro:bitのP2をトグルさせます。

この時のP2の波形を示します。

BLEでスマートホンと接続していない時の波形:

BLEでスマートホンと接続した時の波形:

です。

BLEと接続していない時は、foreverが30ミリ秒ごとに呼び出されます。micro:bitはプログラミング教材ですから、ブロック崩しなど、micro:bit背面のLEDアレイとタクトスイッチだけで入出力が完結した、見た目に動作がわかりやすいゲーム作りが、教材として使われます。メインルーチンが30ミリ秒ごとに呼びされるので、スイッチの状態を都度画面に反映する関数をforeverにべた書きするだけで、そのようなゲームが作れます。もしもforeverが、while文で永久ループで繰り返し実行されるものならば、sleep()か何か、適当な遅延時間を自分で入れないと、画面の表示速度が速すぎて、ゲームにならないでしょう。プログラミング教材として、時間を気にしなくても良いように、適当な周期でこのルーチンを呼び出しているのでしょう。

BLEと接続すると、foreverが55ミリ秒ごとに呼び出されています。BLEの接続の有無で、ルーチン呼び出し周期が変わってしまうのは、ちょっと、なにそれ?と思います。下層はmbedなので、mbedのイベント呼び出しの作りを見れば、どうしてこうなるのかがわかると思いますが、例えばforeverのルーチンを呼び出して、30ミリ秒して次の呼び出しでBLE関連の処理を呼び出して、また30ミリ秒後にforeverルーチンを呼び出す作りなら、そうなるかもしれませんが、普通、タイマーイベントに登録して、どのルーチンも30ミリ秒ごとに呼び出すように作る気がしますけど、mbedだし、気にしないことにしましょう。

スマートホンからデバイスへのデータ書き込み

make:codeのブロックは、例えばカンマで文字列を分割するような、文字列を解析する処理ブロックがありません。BLE UARTには、デリミタまで文字列を読みだして、読みだした文字列を返すブロックがあります。

この”bluetooth uart read until”の振る舞いは、BLEで1つのパケットで受信した文字列で、文字列にデリミタが含まれていたら、発見した最後のデリミタの前の文字列を返します。

例えば、read untilで”:”をデリミタに指定したとします。スマートホンから、BLEの1つのパケットで、”123:456:”を送信した場合、このブロックは”123:456”を返します。

シリアル通信だから、BLEのパケットを意識しない、文字列ストリームで、デリミタの検出で文字列を分割して結果を返してくると思い込むと、まず”123”が返ってきて、次の呼び出しで”456”を返すように思いますが、違います。BLEの1回のパケットで書き込まれた文字列で、デリミタが含まれていたら、そのデリミタの前の文字列を返す、という振る舞いをします。

渡したいパラメータが複数種類ある場合は、パラメータのスタートを示すデリミタを送ってから、1つつづパラメータにデリミタをつけてパケットを送ります。図では、念のため、パラメータの種類ごとに違うデリミタを指定して、もしもパケットを読み落としてパラメータの読み出し順番がずれても、誤ってパラメータとして受信処理をしないようにしています。

BLEの書き込みは、write with response、write without responseのいずれでも使えます。ただ、write without responseで書き込むと、パラメータに0が設定されていたりして、詳しく見てないですが、パケットをボロボロ落としているような気がします。

グラフ表示

senstickというBLEセンサーデバイスのiOSアプリを流用して、作ってみました。iPadがあればplaygroundを使えば、簡単にグラフ表示できるのかもしれませんが。

micro:bitは受託開発に使えるのか?

試作のための動作確認をやりとりする場面で、頻繁にファームを変更して動作を確認しなければならいが、相手が遠隔にいるため、相手にファーム更新をしてもらわなければならない場面では、利用できると思います。

USBのマスストレージで、ファーム更新ができることに価値を見出すなら、その書き込み回路だけを使えばよいことです。micro:bitの開発環境を使わねばならない理由はありません。

BLEの接続状態でforeverルーチンの呼び出し周期が異なる、BLEを切断するとファームが固まりリセットしなければならない、ファームウェアを更新するたびにiOSデバイスとのボンディングをやり直さなければならないなど、不要な手間がいっぱいありすぎます。

micro:bitは不用意に他人のmicro:bitにつながらないように、まずボンディングをしてから、BLEが使えるようになる作りになっています。教育教材としては正しいのですが、試作ではとりあえずデバイスに接続したいだけなので、ボンディングが必須なのは、めんどくさいです。

また、BLEの接続や切断でファームが固まる、BLEの接続状態でforeverのルーチン呼び出し周期が変化するなど、変なところがあります。デバッグ手段はUARTでprintfデバッグになり、めんどくさいです。

make:codeの開発環境は素晴らしいのですが、その下層にあるプラットホームが微妙すぎます。

Read More

WWDC2017から、次のハードウェアを考えてみるというお題で話す機会をもらったので、そこで話した内容をメモ書き的に残してみる。

Apple社のWWDC世界開発者会議も、毎年年を重ねるにつれて、非常にオープンになってきている。そこから、今後のスマートフォンの流れをハードウェアの視点から見てみようと思う。

そもそもだけど、なぜ次を知りたがるのか。それは単に趣味で新しいものが好きだから。根拠の理由説明の必要がなくて身も蓋もないけど。だけど個人の立場からは、新しいものが出てくるって言う瞬間はとても大きなチャンスだから。誰もが同じスタート地点で、早くに学習すればするほど先行者利得でその後の数年間、強い立場あるいは利益を得られる立場になれる。だから新しいハードウェアが登場するのは、非常においしいタイミングでもある。

でも、ハードウェアの新しいものが来るだろうか。多分来ない。新しいものをもたらす新技術や部品の開発は、今のスマートフォン年間2億台を生産するレベルに到達しようとすれば、研究開発に10年、量産までに10年の長い期間がかかると思えばいい。だからiOSとして出てくるハードウェアは、サムスンのGalaxyの1年か2年は遅れたハードウェア要素が採用されたもの、だろう。

スマートホン以外のハードウェアはどうか。ヘッドヘッドマウントディスプレイとか、VRとかいろんなハードが新しく出てきている。それらは、スマートホンでトップシェアが取れないサードの会社が次の可能性を求めて挑戦している分野でもあるけれども、そういった分野にアップルが入ってくるのかどうか。つまり他社にすでにあるようなハードウェアをアップルが扱い始めるかどうか。次のハードウェアは来るのか? という問いかけは、実は、すでに挑戦者たちが挑んでいる新しいハードウェアがいろいろあるけれども、Apple社がそこに参入するだろうか?というのが、本当の問いかけるべきことだろう。

じゃ、新しいハードウェアがもたらすものは何か。新しいものを買って喜ぶ、という理由の説明の要らないものだけじゃない。結局、価値の流通経路の切り替えだということだ。ガラケーの時代からスマートフォンになったときに、その価値の経路が切り替わったために、例えば日本にある企業の中には、1年で売り上げが半分になったところがある。あるいは、ブラウザベースのソーシャルゲームでプラットフォームであった会社が、ネイティブアプリに切り替わった時に、それまでのような強いプラットフォームではいられなくなった瞬間があった。そして、人間との接点はいつもハードウェアだから、ハードウェアが変化したと言うのは、そういった価値の流れの切り替えが1年から2年のうちに起きる可能性がある、そういう意味でとても大きな関心を持つのだろう。

ただ価値を扱うのは人間である限り、20世紀にあったビジネスを21世紀のやり方で焼き直すのが、延々と繰り返されるだけだ。人間は、新しい価値をそんなにすばやく理解しない。人工知能がすごく話題になっているが、それで何ができるのかといえば、できること自体は人間を雇ってできる事と何も変わらない。ただ電力だけで長時間疲れもせずに働くと言う特性を使って、生身の人間では不可能であった何かを素早く事業立ち上げて、価値の経路の切り替えをやってくる人たち、新しいサービスを立ち上げる人たち、そういったのは出てくるだろう。人工知能により、それまでの人間の価値の消費量が2倍になるような、そんな新規性は、まずないだろう。当面は、目の前にある大きさの限られたパイの争奪戦だろう。

今の21世紀のハードウェア価値の切り替えと言う視点で見るときには、常にそこに印刷って言う要素が入ってくる。これはとても面白いことだと思う。20世紀の車の大量製造は、今も日本の大きな経済の原動力になっている。それはプレスあるいは射出という同じものを大量に複製する技術がベースになっている。

例えば高度な半導体と言うのは、これは印刷技術の塊だ。シリコンの半導体の上に複雑な回路パタンを印刷していくそれが今の集積回路クラウドの大量製造印刷の性質を持っている。アカウント思っていれば、そのアカウントの上で購入をすることで、いろんなコンテンツの利用権のアカウントに集約して紐付けられる。愛下名塩にアカウントが中止になる。われわれはリアルな人間だから、道に何も実態持っていない。ネット側の我々の存在に対応する実態はアカウントである。コンテンツはもはやコピーすることなくアカウント

こうして見ていると最初の5時にとってハードウェア新しいもの、に対する興味の価値とは何だろうかちょっと焼き直し必要な感じがする。結局は人が今消費しているのは何かしら形のないコンテンツであるつまりコンテンツが最強な時代なんだ。そういえばiOSの黎明期、開発者たちは今バーチャルリアリティーで新しい可能性に挑戦している人たちがいる。今のバーチャルリアリティーのハードウェアの市場規模は単体でApple Watchているんだけれども、そういったコンテンツに関わるところ2常に立場を変えていくというのはすごく大きな流れだろう。

例えば高度な半導体と言うのは、これは印刷技術の塊だ。シリコンの半導体の上に複雑な回路パターンを印刷していく。それが今の集積回路。また、デジタルデータは容易にコピーできてコストがかからない、まさに印刷の性質を持っている。クラウドであれば、アカウントの上で購入をして、コンテンツの利用権のアカウントに集約して紐付けられる。われわれはリアルな人間だから、ネット側に何も実体がない。ネット側の我々の存在に対応する実体はアカウントである。

こうして見ていると最初の、個人にとってハードウェア新しいもの、に対する興味の価値とは何だろうかちょっと焼き直し必要な感じがする。結局は人が今消費しているのは何かしら形のないコンテンツである。つまりコンテンツが最強な時代なんだ。そういえばiOSの黎明期に活躍した開発者たちの、ある人達は、今バーチャルリアリティーで新しい可能性に挑戦している。今のバーチャルリアリティーのハードウェアの市場規模はApple Watch単体規模と、まだ小さいのだけど、そういったコンテンツに関わるところに常に立場を変えていくというのは、個人ならではの攻め方だろう。

ハードの話をしよう。スプラトゥーン2と言うゲームが発売されたそうで、ソーシャルメディアを見ていると、ゲーム機が購入できないと言うツイートがたくさん並んでいた。

そのツイート見ていてなんとなく伝わってくる雰囲気が、前に新書で出した、ハードウェアはなぜゴミでしかないのか、っていう話とすごく近い感じがした。誰もゲーム機本体を見ていない。ゲームがしたいんだけれども、ゲームをするために本体が必須だから本体が欲しいと言っている。もしもこれが、iPadでゲームができるんだったら、みんなiPadを買ってゲームをダウンロードして遊んでるだろう。ハードウェアで環境が囲われている、しかもそのハードウェアが買うお金があるのに買えないから、みんな不満を表明している。もちろんゲームとして素晴らしく完成されたハードなのだけど、誰もハードを見ていない。

ちょっと、じゃあ昔のハードウェアはどうだったのか、昔を見直してみよう。このトヨタ式生産方式は、カンバン方式の生みの親が書いた本だ。読んでみると結構面白い。これから作れば売れる時代は終わり、低成長な社会が来ると言っている。まぁその低成長と言うのが、今の我々から見れば高度成長経済と呼ぶ時代なんだけど。

だから、これまでの大量生産ではなく、人々の趣味嗜好に合わせた、ものを少数多品種で売れるだけを都度作る、そのためにかんばん方式って言うものを考えたっていうのが。まぁ、その趣味嗜好に合わせた製造社会っていうのが、我々から見た高度成長社会なんだけど。

アメリカから購入した最新鋭のプレス機は、いかに大量に生産するかに特化している。だからカンバン方式には合わない。午前にある機種を製造、午後に違う品種の製造をしようと思えば、その間に金型の組み換えや調整をしないといけない。でも、アメリカから購入した最新鋭機だと、それに1日かかってしまったりする。だからカンバン方式にあうプレス機と言うと素早く違う金型に切り替えられないと。そういった加工装置を得るのに10年ぐらい時間をかけてやってた。

じゃ、iPhoneのハードウェアを見てみよう。この要素を削り落としたらiPhoneではなくなるっていう要素がいっぱいある。

でも面白いのはアップル自体は、電池にしろ液晶にしろ半導体製造にしろ、そういったキーテクノロジーを自社で持たない。カメラモジュールは例外的にソニーから買っているけれども、必ず複数の会社から調達する。量の確保と部品価格を競争での低減を狙ったものだろうけれども、

しかし、LLVMと言うコンパイラ、というかコンパイラ・インフラストラクチャだけど、があるけれどもこういったものは自社で早くから開発している。コンパイラと言うのは、開発言語をプロセッサで実行できる機械語に変換するものだけれども、実はそういった部品というわけではなくて、iOSの開発の全体に影響する大きな存在に今はなっている。

1文字1文字入力するたびにコンパイラが動いて、非常にスマートに次はこういう入力をするのというの提案してくる。それがあるから高度なアプリを短時間で開発できてる。もう人間単体の記憶力で出来るような世界じゃないですから。今のアプリはストア提出がVMのバイトコード、中間コードで提出する。だから今後例えばプロセッサのアーキテクチャが大きく変わって機械語ががまるで別のものになったとしても、アップルが中間言語を新しいターゲットにビルドし直すことで、どんな新しいハードウェアにでもアプリケーション動かすことができる、そういう仕組みに切り替わっている。今のハードウェアの可能性を引き出すかなめとなるものは自社でしっかり開発している。

ハードウェアを製造する部分は自社で持たないけど、集積回路の設計技能と言うのも社内で早くから持っている。ARMと言うプロセッサを独自に設計開発するチームを買収したり、そして今年はGPUも独自設計するんじゃないか、あるいは電力制御を統合してくるのではないかとやっている。今の半導体はとても大きな1つの集積回路に全ての機能を載せることができる。それがより電池を食わなく、高性能なもの作る必然でもあるのだけど。そういったところでも、ハードウェアの部品自体は外部調達だけど、その上に乗せるコンテンツとしての表現能力の要は、自社で抱えていると。

iOSの開発者って今どこにいるんだろう。

例えば今までiOSの新しいものが出るたびに、その機能を紹介した本が必ず出版されていた。今年はちょっと毛色が違う。iOS11の本が、クラウドファウンディングでプロジェクトが立ち上がった。今までは、出版されていたのに、今は最初にクラウドファウンディング。売れ数飲み込みが立たないのか。

今見てみると592人がそれに出資している。アプリ開発者の年収を考えたら本業の開発者が5,000円程度の出費を絞るわけがない。多分この600人と言うのは日本のアプリ開発者の目安、全数の3分の1とか4分の1とかはここにいるなら、日本の開発者数っていうのは1,000人から2000人の間位だろうか。

開発者はどこにいるのか。会社の中に入る。アプリの上で大きなお金が動いているのは、ソーシャルゲームであったり、資本を集めてやるものばかり。アンドロイドのプロジェクトも同じように立ち上がってるが、やっぱり600人には届かない程度。

ARKit

ARKitの機能は、特徴点の検出と3次元空間でのそのマッピング。マッピングした特徴点の集合から平面を推定する機能と、特徴点群と平面とのヒットテスト、そして周囲の照明を推定する機能の3つしかない。YouTubeで検索すると、色んな人のデモンストレーションが結構いい感じで出ている。コンテンツを作るためのキラー機能としてのとしての存在感を感じさせる。

次のハード?

じゃあ次のハードウェアってなんだろう。もうこんな問いかけをするまでもなく、家に常に置いてある端末で激しい競争が起きている。Google、アマゾン、アップル。スピーカを内蔵してネットワークに常につながっている何かと言うのを出している。でも会社によってその性質はまるで違う。Googleは家電を制御できるよと言うのをキラーアプリにしている。アマゾンだと間買い物ができるという機能、いろんなアプリケーションのハブになると言う機能。アップルのは、これからだからわからないけれども、見た目音楽を聞くための機能のスピーカーにしか見えない。Siriと連携したりホームキットと連携するのは当然の機能になるんだろうけど。

VRとか?

もう一つがVR、リアルの世界だけれども、今世界2017年の1Qで世界の販売台数は2,000,000台位。対してApple Watchがそれ単体で3,500,000台。だからとして市場の規模が小さい。

人は、今まで人が見たことがないようなものを、他人の目があるところで身につける事はありえない。だから、これらが普及するとしても常識になるのは10年先だろう。これらが使われるのは、仕事、もしくは家の中。そういった使わざるを得ない状況か、他人の目がない場所でのゲームあるいは会議のツールとか。

コンテンツが最強と言う視点からは当面はゲームが主体、動画を見るのが主体、の中にバーチャルリアリティーをビジネス利用みたいなベンチャー開発が出てくる。

半導体の面積

面白いのはiPhoneの半導体の面積で見ていくと言うことだ。

例えばiPhone 4からiPhone 7までのチップ面積のプロセスを並べてみる。iPhone 7に至るとチップの面積はiMac 27インチMacに搭載されているCore i5のプロセッサの面積より大きい。インテルのはプロセッサしか入ってないけれども、iPhoneのものはプロセッサ以外の全ての機能が1つのチップに集約されているから、面積を見るだけではしょうがないのだけど。

で、パソコンの世界出荷台数2016年が2.7億、アップルの出荷台数が2.2億台位だから、ものすごく荒い言い方をすれば、アップル1社で世界中のパソコンに使われている半導体と同じだけの面積を、iPhoneで使っている。VRにしても、機械学習にしてもそれには莫大な計算能力が必要になる。世界中の計算機と言うリソースをプラットフォームとして、アプリ開発者に提供していると考えれば。またアプリ利用者が増えると、利用者が増えた分だけ自然にスケールする。そのハードウェアは非常に高額だけど、ユーザが自分のお金で購入してくれる。しかもiOSのサポート期間が切れていくから、市場の最低処理能力は自動的に更新される。

とても強いビジネスだと思う。これがGoogleだったらどうだろう。結構クラウドが主体の会社だから、世界中のパソコンに使われてるのと同じ半導体位の面積を、自社のクラウドサービスのデータセンターに使うかもしれない。莫大な投資だ。莫大な投資に見合うサービスでないと、提供できなくなるのかも。人のデータを使うのであればアップルの立場と言うのは強い。ただ利用者全てのデータを集約して初めてできる何かがあるのならば、クラウドファーストって言うやり方を取るしかないから、それはそういうやり方の方が強いのかもしれん。でも2億台からもれなく全てデータを取る必要、母集団からのサンプリング定理を思い出すまでもなく、まずなさそうだけど。

Read More

あまりに執筆が遅いのではと思ったので、遅いと思うか早いと思うかは別として、作業量の記録をしてみようと思いました。
やったことは:

  • 執筆ファイルのバイト量をファイルに1行追記するシェル・スクリプトを作成する。
  • launchctrlで、そのスクリプトを毎日午前4時に動かす。
    です。
    できたデータファイルは、適当な表計算ソフトでグラフ化することにします。

シェルスクリプトの内容は以下のものです。
適当なデータファイル(progress_data.txt)に、日時と、執筆ファイルのバイト数を1行記録していきます。
執筆ファイル名は、”ch”+章番号+拡張子”adoc”としています。

1
2
3
4
5
#!/bin/sh
cd $WORKING_DIRECTORY(ファイルがあるディレクトリ名を設定する)
date '+%F' | tr -d "\n" >> progress_data.txt
echo ' ' | tr -d "\n" >> progress_data.txt
wc ch*adoc | tail -n 1 | tr -s ' ' | cut -d ' ' -f4 >> progress_data.txt

このシェルスクリプトを、”watching_progress.sh”として保存。

1
2
% chmod +x watching_progress.sh , 実行権限を与える
% cp /dev/null progress_data.txt , 空の出力ファイルを用意する

次にこれを毎日午前4時に実行させます。Macではlaunchctrlというコマンドが使えます。

com.reinforce-lab.com.watch-progress.plist という次のような内容のファイルを作ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.reinforce-lab.com.watch-progress.plist</string>
<key>Program</key>
<string>/(作業ディレクトリのフルパス、自分の環境に合わせて)/watching_progress.sh</string>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Minute</key>
<integer>0</integer>
<key>Hour</key>
<integer>4</integer>
</dict>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

このファイルをロードして、登録できていることを確認します。plistを編集して再読込するときは、unloadしてからloadし直します。
ロード時にシェルスクリプトが動くはずなので、データファイルに出力があるかを確認します。

1
2
3
% launchctl load com.reinforce-lab.com.watch-progress.plist
% launchctl list | grep com.reinforce-lab.com.watch-progress
% ls -a watching_progress.sh

アンロードするときのコマンド。

1
% launchctl unload com.reinforce-lab.com.watch-progress.plist

再起動したときに、plistが読み込まれるように、”~/Library/LaunchAgents/“で先程のplistファイルが見えるようにシンボリック・リンクを作成します。

1
2
% cd ~/Library/LaunchAgents/
% ln -s $(作業ディレクトリ)/com.reinforce-lab.com.watch-progress.plist ./

グラフ化してみる

CSVデータをグラフ化するのにNumbersの別ファイルを用意した。そこに日々更新されるCSVデータをコピペするのに、apple scriptでこのようにした。
この処理は、csvファイル名は、progress_data.csv。このファイルをnumbersで開くとカラムA:Bにデータがある。それをグラフ化するファイル progress_data.numbersを事前に用意しておく。そのファイルのカラムA:Bにコピーしたデータをペーストして、ファイルを閉じる。

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
(*
CSVデータをnumbersのファイルに貼り付けるスクリプト。
2017年4月26日 上原 昭宏
*)
tell application "Numbers"
activate
-- CSVのデータをコピー。
open "Your Working Directory/progress_data.csv"
tell table 1 of sheet 1 of document 1
set selection range to range "a:b"
delay 2.0
tell application "System Events" to keystroke "c" using command down -- copy
end tell
close document 1
-- 対象ファイルにペースト。
open "Your Working Directory/progress_data.numbers"
tell table 1 of sheet 1 of document 1
set selection range to range "a1"
tell application "System Events" to keystroke "v" using command down -- paste
end tell
close document 1 saving yes
end tell

CSVを吐き出したあとに、シェル・スクリプトで、このapple scriptを実行するコマンドを追加。

1
osascript progress_data.scpt

Read More

ポエムです

nRF5で使えるリアルタイムオペレティングシステム(RTOS)を見てみました。RTOSとか、全然知識ないです。なので、ポエムです。

SenStick http://senstick.com を開発していて、イベント駆動の開発をC言語で自分で関数を組み合わせて作るのが辛くなってきたので、今時の開発ライブラリあるいはRTOSを使えば先人の苦労の結晶の恩恵で、楽ができるのかなと思ってRTOSを探してみました。

今時のRTOSは、IPv6なTCP/IPスタックやHTTPSやCoAPなどのプロトコルを提供しているから、RTOSをベースにしておけば、将来的にIP系の技術を使うときに安心だとも思います。こういう、今使わないけど将来使うかもしれないっていうのは、将来もずっと使うことはない定番ですから、多分意味ないですけど。

nRF52対応なRTOS

オープンソースなnRF5対応RTOSをぐぐってみると:

があります。

BLEスタックを独自で持っているか、持たないか

nRF5対応RTOSは、BLEスタックを独自で持っているか、Nordicのソフトデバイスを使っているかの2つに分類できます。

nRF5のソフトデバイスは、ユーザのファームウェアの処理にかかわらず、BLEの通信を扱うために、BLEの通信処理が必ず最優先で実行される作りになっていて、またユーザのアプリケーションが暴走してもBLEのの通信スタックには影響しないよう、割り込みやメモリ領域のアクセスレベルを設定します。

RTOSは、タスクのプリエンプション(実行の横取り)など、割り込みを利用するものは、特権モードが使えること前提のコードもあります。そういった仕組みだと、特権モードはソフトデバイスが持っているので、RTOSをそこに入れることができません。

やるとすれば、BLEスタック自体を独自開発してソフトデバイスの役割を自前で持つか、特権モードを使わないで作るかになります。特権モードがないと、メモリアクセス保護は使えませんが、ソフトウェア割り込みはソフトウェアデバイスがユーザアプリに開放していますから、プリエンプションの実装などはそれらを使えば実装も可能でしょう。

nRF5はBLEのパケットをマイコンで処理するため、マイクロ秒単位の応答速度が必要です。そして、1チップでBLEの通信とユーザのアプリケーションが実行される構成で、スタックがBLEの認証を取るためには、ユーザのコードがどれほど馬鹿なことをしても、BLEの通信に影響しない作りが求められます。また通信スタックが使っているメモリ領域を外部から変更して破壊されないためには、メモリ領域の保護が必要です。

Nordicのソフトデバイスは、nRF5が採用しているCortex-Mの実行モードを利用して実装しています。Nordicのソフトデバイスは、ハンドラモードで実行され、ユーザのファームウェアはスレッドモードで実行されます。ハンドラモードは全ての特権モードへのフルアクセスができ、メモリアクセス権限も設定できます。スレッドモードで実行されるユーザのファームウェアは、許されていない割り込みへのアクセスや、ソフトデバイスが使うメモリ領域へのアクセスなどができないようになっています。 Cortex-Mの動作及び実行モード http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203hj/Cihhfga3.html

BLEスタックを独自に持つのが、zephyr と mynewt 。それ以外はBLEスタックにソフトデバイスを使います。ですから前者であれば割り込みやメモリ保護など特権モードを活用した実装ができます。後者では特権モードは使えず、スレッドモードの範囲で実装したものになります。

zephyr

インテルが主導してLinuxファウンデーションの活動を通じてコミュニティで作られているRTOSです。ライセンスは、Apache 2.0 license。

ターゲットはIntel Edisonなどがあるので、x86やIntel Curieの中にある独自32ビットプロセッサARC、それにnRF52やTI社のWiFiモジュールCC3200があります。

カーネルの割り込みや特権モードの機能は、それぞれのプロセッサの種類ごとarchごとに、アセンブラコードとC言語のソースファイルがあります。Cortex-MのソースコードはWindRiverの名義になっていますが、WindRiverはIntelに買収されているので、その資産がここにあるのでしょう。

ドキュメントもしっかりしていますし、ソースコードを読んでもきれいなコードだと思います。BLEのスタック部分は、無線通信のハードウェアに直接関わる部分はNordicの中の人が参加してソースコードを書いているようです。L2CAPよりも上のレイヤはインテル名義になっています。nRF52以外でBLEを使えるように、HCIでシリアル通信でコントローラを接続しても使えるようになっています。スタックのQIDは取得しているっぽい?

プロジェクトを見る限り、RTOSを作りました、それ以上でもそれ以下でもないです。Linuxで同じことができるけれども、Linuxが動くメモリも豊富なリッチなものでも、メモリが64kBのものでも、1つのOSのAPIで開発を統合できる、そんな感じに見えます。

Intelは、Intel EdisonやCurieなどを出してIoTの時代に向けた自社半導体製品の販売に力を入れています。IoT系は、Linuxを走らせられないメモリの小さな数多くのデバイスと、それらを統合してイン−ネット側との橋渡しをするLinuxも走らせられるほどリッチなハブデバイスの2つに別れるでしょう。その2つの開発基盤として、これまで買収したRTOSのリソースを公開、また発展させていくコミュニティとして位置づけているのかしら?

でもnRF51のI2Cがサポートされていないっぽい? 移植してBLE通信ができましただけしか確認していないのじゃないかな。

本当にRTOSでしかないので、Intelが組込みやっぱり辞めましたになると、プロジェクトが止まるのかなと思えます。

mynewt

ApacheのインキュベーションにあるRTOSです。nRF51/nRF52/RISC-Vなどをサポートしています。

NimBLEという独自のBLEスタックを持っていて、Nordicのソフトウェアデバイスよりも2倍同時接続数が多いなど、その特性の高さをアピールしています。BLEの規格を決めている中心メンバーのNordicが作っている半導体を使って、そのBLEスタックであるソフトウェアデバイスに対して、何も真正面から喧嘩売るようなアピールしなくてもと思うのですが、そうらしい。

米国の runtime.io という会社が主体になって開発しているようです。デバッグどうするのかな?と思ったら、社内ではEclipseを使ってデバッグしているそうですが、その環境構築手順はまだ解説を書いていない、そのうち書くかもと、メーリングリストで2017年1月にやり取りされていました。めちゃくちゃうちわっぽい。

このプロジェクトの立ち上げのきっかけが、ネットで制御できるIoTな街灯をATMELのマイコンで開発したら、その制御ハードウェアの種類がいくつもできたり、ファームウェアのバージョン管理の手間が爆発したりと、管理面で散々なことになったことらしいです。

ソースコードを見ると、徹底してモジュールにして管理していて、しかもモジュールごとにGitのレポジトリを参照させている。テストコードがけっこう多い。そしてファームウェアの更新にブートローダがあり、署名したファームウェアを受取り更新する。デバイスの管理ツールもある。ネットに繋がったデバイスを管理するところからスタートしたのだろうなと思わせるコードです。

Cortex-Mのスレッド/ハンドラモードは使っていないっぽい。32-bitマイコンで動きますというのは、優先権限がない場合でも移植できるよという意味なのかも。Segger SystemView対応が入っている。開発環境はDockerで提供されている。実機のUSBデバッグは、gdbとかのツールがDockerで動くからVirtualBoxの拡張パックでUSBポートを追加して、仮想化したのがUSBにアクセスできるようにして、という手順。ビルド環境はすぐ手に入るけど、デバッグ環境がめんどくさい。

バイナリのビルドで、ブートローダ、カーネル、それとアプリケーションをそれぞれ分割してバイナリにして配置できる。ブートローダがカーネルもアプリケーションも更新できる仕組み。今時のネットの環境だと、ファーム更新は必ず入れておきたいから、こういうのが根っこで提供されているのは、よいと感じる。自分で作ったら、もしも何かあっても文鎮化しないかのテストって、めちゃくちゃしんどいだろうし。自分のアプリケーションは自分しか使わないから自分が開発する他ないけど、ブートローダみたいなものは、誰かが1回開発したら全員使うものは同じものなのだから、根っこで提供されていればいい。

ARMはソフトバンクグループになりましたが、このプロジェクトはRISC−Vをサポートしているので、そういう面での勢力にもなれるのかしら。

contiki-os

スイス?の大学のセンサネットワークの開発で20年近く使われているみたいです。とにかくメモリを使わない、書き間違いをしにくい平易なコードが書ける工夫がされている感じのコードだなと思います。シンプルですが、見ていると、いい感じです。

特徴的なのは、プロトスレッドという仕組みです。通常のスレッドは実行中のレジスタとメソッド呼び出しのスタックを保持するメモリ領域を確保します。これだとスレッド(あるいは処理単位、タスク)の数だけスタック割り当て(たいてい200バイト程度?)が必要になります。

ContikiOSのプロトスレッドは、そのスレッドの実行場所がどこかを記録する1ワード(実体はポインタです)を保持するだけで、スタックなどは確保しません。すべての実行は1つのスタックで行われます。プロトスレッドは、それぞれのタスクが実行した丁度いい頃合いにリターンして、CPU処理時間を気を利かせて渡し合う、協調的実行をスレッドとしてコードで表現できる仕組みとみると、スッキリします。

組込みでのC言語のソースコードには、データ処理の流れと、時間軸での処理の流れの2つが入ります。例えば、センサーからデータを取得してフラッシュに保存する処理を考えます。処理の流れは、まずI2Cバスに接続したセンサーからデータを取得して、次にSPI接続されたフラッシュに記録する処理です。実際のコードは、これに時間的な処理、例えばI2Cバスからデータを読み出す、プロセッサを待ち状態にするにはもったいない程度に長い時間を別のコンカレントに実行されている処理に渡すとか、が入ります。

データの処理の流れと時間の流れの処理は別の処理ですが、1つのソースコードに入れるために、例えば支時間のかかる処理の終了通知を受け取るために、コールバック関数を使い、そのために処理関数を分割して、さらに関数ポインタを扱う、なんていうややこしいことになります。

プロトスレッドは、そういった時間の流れの処理の記述を、データ処理の流れと統一してシンプルにコードで表現できる、間違いをしにくいコードを書ける、よい仕組みだと思います。

CoAPクライアントのサンプルをnRF52ターゲットにビルドしてみると、text 37kバイト、data 552バイト、bss 15,700バイトでした。nRF52は64kバイトのメモリがあり、ソフトウェアデバイスに13kバイト取られても61kバイトが使えます。通常のスレッドの実装でも十分に足りるメモリ量がありますから、bss16kバイトというのは、特徴的ですが意味はありません。

しかし、NordicのIoTキットはver.0.9でnRF51のサポートを切りました。nRF51はメモリが16kBのものと32kBのものがあります。ソフトウェアデバイスに13kB取られたら、使えるメモリは、それぞれ3kBと19kBです。マウスなどに使うなら16kB版でも足ります。しかしIP層に使うには、32kB版でも19kBでは通常は実装できません。そういう場面で、ContikiOSなら実装できるのは、特徴かもしれません。

ContikiOSのソースコードを見ていると、ファミコンに使われているZ80互換のプロセッサ向けに、通常の意味での、コンテキストスイッチとスタックを実装したスレッドが実装されています。きっと研究を始めた頃は、普通のスレッドで開発していたけれども、研究で超低消費電力にするにはメモリ量が小さいものしか使えないでしょうし、それを打破するためにプロトスレッドを編み出したのかなと、勝手に思いました。

一般的なRTOSとは言えないけれども、プロトスレッドの仕組みは、実行順序を管理するライブラリとして組み込むことも可能です。非常にシンプルで、変わり種だけれども、よくできているなと思います。

FreeRTOS

古くからオープンであるRTOSらしい。GPLライセンスと商用ライセンスのダブルライセンス。今の最新版のver9は全面的にソースコードを書き直したらしい。メソッド名や変数名に、その値型がわかる命名規則を使っているので、ちょっと微妙な名前に見えるけれども、スマートなコード補完機能がないエディタを使っている環境でも安心なのだろうと思う。

FreeRTOSはtickレスモードに対応した。nRF51は、クロックカウンタであるsystick(Cortex-M0+ではsystickはオプション)がないので、このモードがでて初めてnRF51で使えるようになった。nRF52にはsystickがもちろんある。

FreeRTOSをnRF5で使う必要性は、IP層が欲しい時くらい。NordicのSDKにはメールボックスやセマフォそしてスケジューラがライブラリである。だからFreeRTOSをわざわざ使わなくても、協調的な処理でアプリケーションは作れる。ただIP層が欲しい時は、それらがRTOSをベースに作られているから、FreeRTOSを使うことになるだろう。

移植性が得られる(かもしれない)のは、あるかもしれない。他社のBLE半導体で、FreeRTOSベースのSDKを提供しているところへ移植するときに、BLEのAPIは異なるから書き換えが必要だけれども、自分のアプリケーション部分はそのまま使えるだろう。

mbed os

mbedプラットホームはすごいと思うけれども、mbed osは、何をやりたいのかわからない中途半端なものに思える。KeilについてくるARMのRTXというRTOSにC++ラッパを追加して提供している。ソースコードまであるのは、とてもよい。mbed cliが提供されていて、ローカル開発環境が手軽に構築できる。ただし、ローカルではツールチェーンはgccになる。

mbedプラットホームは、オンラインのIDEに純正ARMコンパイラをセットにして提供していて、アカウントを作ってサンプルコードをフォークすれば、できたHEXファイルをUSBストレージに見えるマイコンボードに転送するだけで、コード実行ができる。ユーザのコードはレポジトリで管理されるので、一度mbedプラットホームを使えば、開発環境構築の手間どころが必要性がなくなり、かつコードの開発履歴が自動的に保存される現代的なソフトウェア開発環境に、自動的にとりこまれるのが素晴らしいと思う。

一方で、mbed osは、mbedプラットホームにOSとネット側の監理サーバを加えて、OS側は、超低消費電力に必須なイベント駆動、ネットワークでのデバイス管理の仕組みを備えているものらしい。その意味でmynewtに似ているけれども、その監理のネットワーク・プロトコルはLWM2M、またIoT向け通信プロトコルとして、CoAP, HTTP, MQTT などのサポートが並ぶ。

LWM2Mの仕様は公開されているので見に行くと、めちゃくちゃたくさんの文章があって、私には把握できない。サーバ側のコードは公開していないみたい。

SPIドライバのソースコードをみると、メンバ関数がif defで定義されていた。イベント駆動で動かすマイコンでは、ペリフェラルは指定したメモリ領域にデータを溜め込んでから割り込みをかけてくる程度にはスマートに動くので、非同期処理をよく使う。でも初期化処理の時は、非同期を入れるとコールバック関数+状態変数で管理するバグりやすい手間なコードを書くはめになるから、ブロック呼び出しの関数を使ったりする。

SPIに限らず、ペリフェラルはノンブロッキング(非同期)とブロッキング(同期)を混じって使ったり、あるいはノンブロッキングはハードウェアとしてサポートがなかったりと、いろいろあるから、if defを使いたい気持ちはわかるけれども、C言語ではなくてC++なのだから、アブストラクトクラスで基本関数を定義して、ブロキングなSPIとノンブロッキングなSPIそれぞれ継承してクラス化する方法があったのではとも思う。

とりあえず、C++で if def が使われていて、その理由がよくわからないので、これはかかわらないでおこう、そう思った。あとは、ネットワーク対応というわりに、ファームウェア更新に大事なブートローダの機能が見当たらない。カーネルはカーネルでしかないから、ブートローダは別のコードになるはずに思えるけど、何処か別のところにあるのかもしれないけど。ブートローダを使うためには、カーネルとユーザアプリケーションのバイナリは個別アドレスで配置しないと更新できないと思うけど(カーネルとファームウェアを1つのバイナリにして更新するのもありだけど、その場合はブートローダがカーネルを使えないからネットワーク実装全てもたせることになって、バイナリサイズが大きくなるだろうし)、そういうアドレス分割でのファームウェア生成もあるのかよくわからず。探せばあるのかしら。

あとは、mbedのBLEのライブラリの評判が、まず悪い。APIがよく変わる、動作がおかしいみたいな。NordicのBLEのAPIのラッパーだと思うから、そんな変なものにはならないと思うだけど、まず口々に使えないというから、あまりよくないのだろう(伝聞)。

mbed osには、いろいろ、関わらないでおこうと思った。

まとめ

そんな感じ。

Read More

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) }
Read More

これは フリーランス残酷物語 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アプリ開発の黎明期だったのも、ラッキーです。まだ始まったばかりで、事業としての開発専業会社が確立していない頃はまだ、個人でも開発の仕事はありましたし。

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

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

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

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

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

では。

Read More

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

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

Read More

これは 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の事業を買収していたと思いますが。

Read More

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にエラッタがそのまま引き継がれた状態なのでしょうか。なかのことしらないですけど。

まとめ

そんな感じです。

Read More