BLE NanoをmbedとしてMacから使うとき、Finderでプログラムを書き込まないほうがよい

BLE Nano — RedBearLab
RedBearLabのBLE NanoはNordic nRF51822 SoCを簡単に使えるようにしたボード。話題のBLE(Bluetooth LE)が簡単かつちっちゃく使える。開発環境としては、Nordic nRF51822 BLE SDK、mbed、Arduinoが使える。今回mbedで使おうとしたのだが、肝心のプログラムの書き込み部分がうまくいかないことがあって、その解決方法を書いておく。

BLE Nanoキット

BLE Nanoキット

問題

BLE Nanoでは、というかmbedでは、"MBED"としてマウントされたボリュームに、サーバー側でコンパイルしたバイナリ(hexファイル)をコピーするとプログラムが書き込まれる。Macだと当然Finderからやると思うのだが、そうすると10回に1回程度しか成功しなかった。流れとしては以下の様な感じになった。

  1. サーバー側でコンパイル
  2. hexファイルをMBEDにコピー
  3. コピー終了後にMBEDが再マウントされる
  4. MBEDを開くと中に"SWD ERROR"と書かれた"fail.txt"が出現

解決策

Macから書き込む場合は「cp -X」コマンドでファイルをコピーするといいでしょう
プラットフォームごとのクセ | mbed

これがドンピシャだった。"cp -X" って実際なにしてるのか気になってman cpしたら、

-X Do not copy Extended Attributes (EAs) or resource forks.
cp(1) Mac OS X Manual Page

とのことで、Extended Attributes、日本語にすると拡張ファイル属性ってのが悪さしているらしい。知らんかった。ということで、ちょっとめんどくさいけど、Terminal立ち上げて "cp -X" するとあっさり成功する。

余談

Yosemiteではmbedが自動的にマウントされないという話があるが、最新版のYosemite(10.10.3)では解決済みなので、そのへんの情報は無視して大丈夫。
あとFinderでのコピーで、10回に1回成功していたのは一体なぜなのか、他のmbedでは2回に1回ぐらい成功していてその成功率の違いはなんなのか、などの謎は残ってたりする。
EAsがどういうふうにOS Xで使われているかはここが詳しい。
OS X ハッキング! (253) Leopard解体新書(4) ~拡張された拡張属性~ | マイナビニュース

ZigBeeによるセンサネットワークを作った時のメモ

ZigBeeで複数台のモジュールから高頻度に情報を送信するネットワークを作ったので、その時のノウハウをまとめる。ZigBeeのやりとりにはXBee ZBシリーズを利用。2.4GHzのZigBeeを利用するXBeeモジュール。主要各国の規制をクリアしているのでグローバルに安心して使える。

概要

通信するモジュールは全8台でPCに繋がる受信機を2台用意し、1:4を2セット配置した。当初は1:8だったのだが、XBeeがネットワークから離脱することが多かったためにそのような仕様に。受信機はXBee-PRO ZB。送信側のモジュールはXBee ZBを利用。

[asin:B011URI6F2:detail]

通信の仕様としては、100msに1度加速度センサと現在の電圧の情報をモジュールから受信機側に送る。1回あたり24Byteになってた。このうちデータは加速度センサのXYZそれぞれが1Byteずつと電圧の1Byteの4Byteである。

モジュール側

モジュール側はXBeeとセンサ、Arudinoから構成されている。Arduinoがセンサ情報を読み取り、XBeeAPIモードでXBeeに送信要求を送る、のが基本の動作。
XBee側の設定はファームウェアはRouter APIで、その他の特徴的な設定はNW=1、JV=1で、ネットワークがなかったら再接続し、その際は全チャンネルをスキャンするという設定。明示的に設定しないとチャンネル変更されても他チャンネルをスキャンしないので。

ArduinoXBeeのやりとりにはxbee-arduinoライブラリを利用した。自分でバイトを作ったりしなくてよくなり、かなり実装は楽に。オススメ。google codeからgithub移行してるんだけど、ドキュメントがまだgoogle codeに残っているので、どちらのリンクも置いておく。
https://code.google.com/p/xbee-arduino/
https://github.com/andrewrapp/xbee-arduino

ArduinoからXBeeへは、

  1. データの送信リクエスト TxRequest
  2. 電圧のリクエスト
  3. ネットワークの接続状況のリクエスト

の3種類をシリアル通信で送っている。XBeeからArduinoへはそれぞれのリクエストに対するレスポンスが3種類返ってくる。
また、ArduinoからXBeeへはリセット端子を接続していて、3のレスポンスから、ネットワークが接続されずに一定時間経過した時、強制的にリセットをかけている。シリアル通信からコマンドでXBeeに対しソフトウェアリセットをかけることは出来るのだが、意図した動作にならなかった(再接続されなかった)ので、ハードウェアリセットをかけている。

受信機側

XBeeファームウェアはCoordinator API。PCとの接続は秋月の「XBee USBインターフェースボードキット」を利用してUSBでつなげている。このキットは普通にXBeeの開発に便利で重宝した。安いし。
XBee USBインターフェースボードキット: 組立キット 秋月電子通商 電子部品 ネット通販
PC側でのXBeeとのやりとりは、openFrameworksで実装した。oFにはXBeeのライブラリとかなかったので、自分でAPIモードのXBeeとシリアルでやりとりするコードを書いた。実はこのときに書いた記事がこれ。しょっちゅうフリーズさせていたので……。
miso-engine.hatenablog.com
XBeeからPCに来るデータは1種類のみで、「ZigBee受信パケット」というものだ。これにはデータそのものと、送信元ZigBeeの全世界的にユニークな64bitのアドレスがあるので、どのセンサからのデータなのかがわかるという仕組み。この複数台のセンサの情報をどう管理するかというところで色々大変だったが、一般化出来るノウハウという感じでもないので割愛。

その他ノウハウ・注意点

1. 高頻度なデータのやり取りにはRouterを使う

ZigBee規格ではそれぞれのZigBeeはCoordinator, Router, End Deviceの3つの役割のどれかを担う。このうちCoordinatorはネットワークに1台は必ず必要なネットワークを構成する親機になる。残りのRouterとEnd Deviceがそこにぶら下がるのだが、この2つの違いはEnd Deviceがいずれかの1台のRouterかCoordinatorに対して繋がる形でネットワークに参加するのに対し、Routerは他の複数台のRouterと繋がりネットワーク自体を作っていく存在であることだ。
今回の用途ではメッシュネットワークはいらないので子機側はEnd Deviceにしてネットワークを作ってみたところ、ことXBeeに関してはEnd Deviceは完全にスリープして使うことが前提*1のようであり、スリープしない状態のEnd Deviceを複数台をネットワークに入れて使うとネットワークの安定性が著しく悪かった。そこで単純にEnd DeviceをRouterにしたところ、ものすごく改善した。つまり、スリープさせる必要がない高頻度なデータのやり取りではRouterを使うべきであるようだ。

2. ネットワークの安定性の改善

2-1. ネットワークの参加要求

「ネットワークの参加要求」はZigBeeネットワークに対して高負荷のようであり、これが出されたタイミングでネットワークが不安定化することが多かった。ネットワークから外れたモジュールは今回のやり方では再接続を試みる。その結果さらにネットワークが不安定化し全体としていつまでたっても安定しない、というようなことも起きた。ネットワークから落ちたらすぐ復帰してほしかったので、1:8から1:4にすることで対策したが、すぐ復帰しなくてもよいなら再接続までのタイミングを長く取るなどの対策もあるかと。

2-2. チャンネルの混雑

ネットワークから離脱するのはチャンネルが混んでいる時のようでWi-Spyという製品を使って電波状況を監視してチャンネルを変えたりした。チャンネルの変更は受信機側(1:4の1側)のモジュールのチャンネルに関するビットマスクの設定を変えることで可能。

3. XBeeのリセット

ピンによるハードウェアリセット、コマンドからのソフトウェアリセット、ネットワークリセット(そのモジュールのみと、ネットワーク全体の2種類がある)と様々なリセットがあるので、自分の意図した状態を作れるのがどのリセットか見極めて実装する必要があった。

4. 秋月のXBeeピッチ変換基板

最終的には使ってないのだが、モジュール側のプロトタイプで秋月のXBeeピッチ変換基板を使っていたが、電圧レギュレータが内蔵されているため、3.3Vをそのまま電源のところに入れると少し低い電圧になることに注意。
XBee用2.54mmピッチ変換基板: 半導体 秋月電子通商 電子部品 ネット通販

検証していないこと

1. 800MHz帯のモジュール

2.4GHz帯はやはり混んでおり、チャンネルの変更が必要になった。最初から混んでない帯域をできれば使いたい。

2. ZigBeeではないXBee

今回の用途においてメッシュネットワークは不必要だったので、ツリー型のネットワークを構成可能なZigBeeではない独自プロトコルに対応したXBeeにするべきだったかもしれない。ZigBeeにおいてツリー型のネットワークは出来ると書いてあったが、XBeeではいかんせん明示的に作る方法がわからなかった。

本について

オライリーCQ出版から出ている。どちらも買った。

XBeeで作るワイヤレスセンサーネットワーク (Make: PROJECTS)

XBeeで作るワイヤレスセンサーネットワーク (Make: PROJECTS)

初心者はオライリーの方をざーっと読むとよい。1冊の本を通して仕様を理解していくという形になっているためわかりやすい。シリアル通信におけるプロトコルなどを丁寧に解説していて助かった。
CQ出版の方はやはりトランジスタ技術誌の読者層向けなのでレベルが高い。チュートリアル的な記事もあるが、電波状況に関する話や到達距離の検証の記事等は役に立った。ただ、Digi社が出しているマニュアルの翻訳的な内容も多く、マニュアルのほうが公式なので信頼性がおけると考えると買う必要あるのだろうかという面も。
また、この2冊は方向性は違うものの被っている内容は多く、2冊とも買う必要性は薄い。

その他リソース

XBeeのDigi社によるマニュアル「Product Manual: XBee / XBee-PRO ZB modules」。日本語のページからだと辿りつけないんだよね。
http://www.digi.com/products/xbee-rf-solutions/modules/xbee-zigbee#resources
ZigBeeXBeeについてガガッと概要を知りたいならここ。ググると1番上に出てくる。
ボクにもわかるZigBee方式 XBee (ボクにもわかる地上デジタル 地デジ方式編)

*1:設定もスリープしない設定がない。ただ、これはポートによるスリープの制御を選ぶとスリープさせないことが可能で、今回はそれでスリープさせなかった

Arduinoのタイマーライブラリ

Arduinoでなんらかの時間的に正確な処理をしたい時、Arduinoのタイマーライブラリを利用すると簡単に出来る。ArduinoのタイマーライブラリはMsTimer2とTimerOneがあり、これらのライブラリを利用すると、一定時間ごとに関数を「割り込み(interrupt)」で呼び出す、ということがさくっと出来る。

【永久保証付き】Arduino Uno

【永久保証付き】Arduino Uno

Arduinoのタイマー

Arduinoはハードウェアの機能としてタイマーを持っている。これはArduinoで使用しているAVRというマイコンの機能で、各種ライブラリ上から使われている。Arduinoで使用している主要なAVRマイコン*1では、Timer0、Timer1、Timer2の3つのタイマーがある。これらのハードウェアのタイマーを利用し、一定時間ごとにある関数を「割り込み(interrupt)」で呼び出すことをArduinoのタイマー系ライブラリは実現している。

  • Timer0 8bitのタイマーでArduinoの時間を管理する用途で利用されている。delay(), millis(), micros()などである。UNOでは5,6番ピンのPWMで利用されている。
  • Timer1 16bitのタイマーでUNOではServoライブラリと9,10番ピンのPWMで利用されている。
  • Timer2 8bitのタイマーでUNOではtone()と3,11番ピンのPWMで利用されている。

上記のTimerの説明にある通り、AVRのタイマー機能はArduinoの各種ライブラリによって利用されていて、タイマーライブラリを使う際は、利用されていないタイマーを使うなど競合を避けないければならない。PWMもまた、タイマーを利用しているため、同時に使用することは出来ない。

タイマーライブラリ

今回紹介するライブラリは、Arduinoソフトウェア(v1.6.3)の下記階層に行くと、Library Managerが起動し、そこからインストールすることが出来る。

スケッチ > Include Library > Manage Libraries...

MsTimer2

Arduino Playground - MsTimer2
最も利用されるTimer2を利用したシンプルなライブラリ。まずはこれの利用を検討するとよい。
100ms毎に呼びだすサンプル。

#include <MsTimer2.h>

void timerFire() {
  //100ms毎にここが呼び出される
}

void setup() {
   //100ms毎にtimer発火
  MsTimer2::set(100, timerFire);
  MsTimer2::start();
}

void loop() {

}

TimerOne, TimerThree

TimerOne & TimerThree Arduino Libraries
Timer2以外のTimerを利用したライブラリ。Timer2が使えなかったり、複数個のタイマーを使いたいときに。また、MsTimer2に比べてより細かく設定が可能。TimerOneとTimerThreeの利用の仕方はまったく同じだが、TimerThreeはTimer3以上が実装されたArduino MEGA等でないと当然使えない。MsTimer2ではミリ秒で設定するが、TimerOneとTImerThreeはマイクロ秒単位で設定することに注意。*2
同じく100ms毎に呼び出すサンプル。

#include <TimerOne.h>

void timerFire() {
  //100ms(=100000us)毎にここが呼び出される
}

void setup() {
  Timer1.initialize(100000); //マイクロ秒単位で設定
  Timer1.attachInterrupt(timerFire);
}

void loop() {

}

割り込み(interrupt)

割り込みもまたマイコンの機能のひとつで、割り込みはある事柄が発生した時に、処理を割りこませるという機能である。今回はTimerがある一定時間経つと割り込みを発生させるということになる。割り込みの発生源としては、他に外部入力などがある。割り込みが発生するとソフトウェア上では、あらかじめ指定された関数のところに処理が飛ばされる。今回のサンプルプログラムではtimerFireという関数を呼び出している。

利用時のソフトウェア面での注意

割り込みしたときと、メインループ(Arduinoではloop()の中とそこから呼び出している関数)の時とで、共有して使いたい変数がある場合、volatile属性をつけないと値が共有されないので注意する必要がある。

volatile int value;

*1:Arduino毎に利用しているAVRマイコン自体が異なることがある

*2:とみっくすさんのコメントで気づきました。ありがとうございます。