2015年11月24日火曜日

nrf51822 でデバイスをスキャンして Macアドレスと RSSI を取得する方法

概要

nrf51822 に Central の機能を実装して BLE デバイスをスキャンしてみました
スキャンした際に対象デバイスの Mac アドレスと RSSI (受信信号強度) を取得してデバッグ出力してみます

環境

  • Windows7 64bit
  • nRF51822
  • nRF51 DK
  • nRF51 SDK 9.0.0
  • SoftDevice S130

実装方法

今回は Peripheral ではなく Central の機能を実装するので SoftDevice のバージョンは s120 or s130 が必要になります
なので、あらかじめ上記の SoftDevice を書き込んでおいてください
実装方法は SDK に含まれる Central のサンプルコードベースで説明します

スキャンできるように初期化する

まずスキャンの方法を設定します
サンプルコードでは基本的に main メソッド内で初期化用のメソッドをコールすることで初期化を行っています

スキャンの初期化の流れは ble_stack_init -> softdevice_ble_evt_handler_set -> ble_evt_dispatch -> on_ble_central_evt になります
簡単に説明するとスキャンを実行した際に発生するイベントを on_ble_central_evt メソッドでハンドリングする設定を行っています

このコールの流れは基本サンプルのままで OK です
これが設定されていればスキャンの基本の設定は完了です
次にハンドリングしたイベント内で実際に Macアドレスと RSSI を取得してみます

Macアドレスと RSSI を取得するロジックを追加

修正は on_ble_central_evt メソッドだけになります
このメソッドは発生したイベント ID を取得して switch 文でイベントを分岐しています

static void on_ble_central_evt(ble_evt_t * p_ble_evt) {
    uint32_t                err_code;
    const ble_gap_evt_t   * p_gap_evt = &p_ble_evt->evt.gap_evt;

    switch (p_ble_evt->header.evt_id) {
    case BLE_GAP_EVT_ADV_REPORT: {
            // ...
            break;
        }
    case BLE_GAP_EVT_TIMEOUT:
        // ...
        break;

    case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
        // ...
        break;

    case BLE_GAP_EVT_DISCONNECTED:
        // ...
        break;

    default:
        break;
    }
}

今回はこの中の BLE_GAP_EVT_ADV_REPORT の switch 文を修正します
Mac アドレスと RSSI は引数として渡ってきた ble_evt_t のポインタから取得します

取得方法は以下の通りです

ble_gap_addr_t pa = p_gap_evt->params.adv_report.peer_addr;
int8_t rssi = p_gap_evt->params.adv_report.rssi;
printf("Mac Address: %02x:%02x:%02x:%02x:%02x:%02x \r\n", pa.addr[5], pa.addr[4], pa.addr[3], pa.addr[2], pa.addr[1], pa.addr[0]);
printf("RSSI %d \r\n", rssi);

これをそのまま BLE_GAP_EVT_ADV_REPORT の switch 文中に記載すれば OK です
Mac アドレスは LSB (下位ビットから格納する方式) になっているので、添字の大きい方から参照します

ドットや矢印でいろいろと参照していますが流れとしては

  1. ble_evt_t から evt.gap_evt で GAP (General Access Profile) のイベントを取得
  2. イベントの params フィールドからパラメータを参照
  3. パラメータの adv_report フィールドからアドバタイジング情報を参照
  4. アドバタイジング情報の peer_addr フィールドから Mac アドレス情報を参照
  5. アドレス情報の addr[6] フィールドが Macアドレスが格納されている配列になっている

という感じです
RSSI は adv_report から直接数値が参照できます
この辺りはリファレンスを参照すると、この構造体のこのフィールドを参照しているという流れがわかると思います

スキャンを開始する

これもサンプルをそのまま使っているのであれば特に変更する箇所はありません
main メソッド内 scan_start というメソッドをコールしていれば Central のスキャンを開始することができます

int main(void) {
    uint32_t err_code;
    bool erase_bonds;

    // Some Initialize ...
    ble_stack_init();
    // Some Initialize ...

    // Some Initialize ...
    scan_start();
    // Some Initialize ...

    for (;; ) {
        power_manage();
    }
}

scan_start を辿ってみるとわかると思いますが結構いろいろな設定をしています
スキャンの間隔やホワイトリストを使って、信頼するデバイスだけスキャンするようにするとか、タイムアウトをどうするかなど設定できます
興味があれば、scan_start メソッドのサンプルとリファレンスを見ながらいろいろとパラメータチューニングしてみてください

コードが記載できたら Keil でビルドして nRFgo Studio でアプリケーションを焼きこんでみましょう
焼きこみが完了すれば自動的に nRF51 DK が BLE デバイスとなりスキャンを開始します

試す

アドバタイズしている BLE デバイスは結構街中に飛んでいるのでスキャンを開始すればデバイスが見つかるかもしれません
ない場合はスマホアプリで Peripheral になれるアプリがあるので、それを使ってみてください
自分は SensorTag CC2650 を使いました

デバッグは nRF51 DK を PC に USB で接続して TeraTerm でシリアルモニタをすればいいと思います
ボーレートは 38400 を設定してください

今回のサンプルは printf しているだけなので、デバイスが見つかればそのまま出力されると思います

最後に

コンシューマ向けの BLE デバイスは基本は Peripheral のみが実装されており、iPhone とか Android などのスマホが Central になっています
nrf51822 は Central にもなることでできるのでちょっとおもしろい実装ができます

nrf51822 には pstorage という仕組みもあってデバイス上にデータも保存できるので、スキャンした情報を保存しておくこともできます

pstorage の話もどっかでできればなぁと思っています
あと、サラッと書いたデバッグの方法とかも書いたほうがいいかなと思っています

2015年11月20日金曜日

CentOS7 + AWS で docker-swarm を試してみた

概要

docker-swarm は複数の docker-engine を 1 つの docker-engine として扱うことができるクラスタリングツールです
コンテナがいっぱいになってスケールしたいとき docker-engine をクラスタに追加することで簡単にコンテナをスケールすることができるようになります
今回は例のごとく Getting Started を試してみたので紹介します

環境

  • CentOS7 64bit
  • docker-machine 0.5.0
  • docker-engine 1.8.3

事前準備

docker-swarm は docker-machine の機能を使っているので事前に docker-machine をインストールしてください
http://kakakikikeke.blogspot.jp/2015/11/try-docker-machine-with-aws.html

クラスタをディスカバリするためのトークンを生成する

docker-swarm で生成するクラスタはトークンを使って、どのクラスタかを管理します
まずはトークンを作成するために 1 台マシンを構築します

  • docker-machine create --driver amazonec2 --amazonec2-access-key [Your Access Key] --amazonec2-secret-key [Your Secret Key] --amazonec2-vpc-id vpc-xxxxxxxx --amazonec2-region ap-northeast-1 --amazonec2-zone c token-machine

この辺のコマンドは前回の docker-machine の紹介で記載したコマンドと同じです
10 分くらいでインスタンスが立ち上げるので待ちましょう
作成できたら、マシンを確認後、環境変数を読み込みます

  • docker-machine ls
  • eval "$(docker-machine env token-machine)"

そしてこのマシン上で 1 つコンテナを立ち上げます
コンテナの元イメージになるのは DockerHub で公開されている swarm というイメージを使います

  • docker run swarm create

これを実行するとイメージのダウンロードが始まり、その後コンテナが起動され、以下のようにコンソールに出力されます

Unable to find image ‘swarm:latest’ locally
latest: Pulling from library/swarm
2bc79aec8ea0: Pull complete
dc2fb86a875a: Pull complete
435e648d0f23: Pull complete
e16042a92d05: Pull complete
045bd7b00b5b: Pull complete
3caea1253d76: Pull complete
2b4c55187a27: Pull complete
6b40fe7724bd: Pull complete
Digest: sha256:51a30269d3f3aaa04f744280e3c118aea032f6df85b49819aee29d379ac313b5
Status: Downloaded newer image for swarm:latest
6c56e07a671372421a6724ce79047d2f

この出力の最後の文字列「6c56e07a671372421a6724ce79047d2f」これをトークンとして使います

クラスタ内でのマスターノードを作成する

トークンが取得できたらクラスタを構築していきます
docker-swarm のクラスタの世界ではマスターと、それ以外のエージェントノードで構成されます
まずはマスターノードを作成する必要があります

  • docker-machine create --driver amazonec2 --amazonec2-access-key [Your Access Key] --amazonec2-secret-key [Your Secret Key] --amazonec2-vpc-id vpc-xxxxxxxx --amazonec2-region ap-northeast-1 --amazonec2-zone c --swarm --swarm-master --swarm-discovery token://6c56e07a671372421a6724ce79047d2f swarm-master

コマンドが長くなってきました
前半のドライバの指定やアクセスキーの指定は同じです
違うのは --swarm 以降のコマンドでこれを指定することでクラスタとして作成することができます
--swarm-master オプションを付けることでマスターとしてノードを立ち上げることができます
そして、先ほど作成したトークンを --swarm-discovery オプションを使って指定します

無事ノードが作成されれば完了です

エージェントノードを追加する

マスターノードができたらエージェントノードを追加していきます
クラスタをスケールさせたいときにはこのエージェントノードを追加していきます

  • docker-machine create --driver amazonec2 --amazonec2-access-key [Your Access Key] --amazonec2-secret-key [Your Secret Key] --amazonec2-vpc-id vpc-xxxxxxxx --amazonec2-region ap-northeast-1 --amazonec2-zone c --swarm --swarm-discovery token://6c56e07a671372421a6724ce79047d2f agent01

コマンドは先程のコマンドとほぼ同じです
--swarm-master の指定がないのと作成するノード名がことなるだけです
こんな感じで agent02 まで作成しましょう

作成できたノードを確認する

  • docker-machine ls
NAME          ACTIVE   DRIVER      STATE     URL                        SWARM
token-machine *        amazonec2   Running   tcp://xx.xx.xx.xx:2376    
swarm-master  -        amazonec2   Running   tcp://xx.xx.xx.xx:2376     swarm-master (master)
agent01       -        amazonec2   Running   tcp://xx.xx.xx.xx:2376     swarm-master
agent01       -        amazonec2   Running   tcp://xx.xx.xx.xx:2376     swarm-master

でノードの一覧を確認することができます
SWARMの欄にマスターノードの名前が記載されていることが確認できると思います
また、クラスタ内のマスターノードには (master) があると思います

docker-machine コマンドの他に docker-info コマンドでもクラスタの状況を確認することができます

クラスタ内にコンテナを作成する

まずは docker コマンドでこのコンテナを操作できるようにします

  • eval "$(docker-machine env --swarm swarm-master)"

env コマンドで環境を表示させ、eval で評価します
これで docker コマンドをコンテナ内で使うことができます
あとはいつも通り docker run すればマスターノードが適当なエージェントノード内にコンテナを作成してくれます

  • docker run hello-world
  • docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS                                   NAMES
103e4ed46f78        hello-world         "/hello"                 2 minutes ago       Exited (0) 2 minutes ago                                           agent01/drunk_kalam

最後に

紹介は以上です

ノードを増やしてスケールさせたい場合は docker-machine create して、ノードを削除したい場合は docker-machine rm すれば OK です
docker-machine rm でマスターノードを指定するとクラスタがおかしくなるので注意してください

コンテナの数が数百や数千を超えるような場合には非常に便利な機能だと思いました
コンテナの数やコンテナのリソース消費を監視してキャパシティプランニングまでやってくれると非常に嬉しいなと思いました
いまマシンが足りないから自動で EC2 上にマシンを構築してクラスタに追加しましたとか
さすがに現状だとそこまではやってくれないっぽいです

2015年11月19日木曜日

Redis でバイナリファイルを保存する方法

概要

Redis で PNG 画像のようなバイナリファイルを保存する方法を紹介します
今回は特にプログラムから保存するのではなく、redis-cli を使ってやってみます

環境

  • Mac OS X 10.10.5
  • Redis 3.0.2

バイナリデータの保存

  • redis-cli -x HSET some_key image_binary <image.png

画像データを保存しています
HSET を使って保存しており some_key という key の image_binary というフィールドの値として画像を保存しています
画像情報はパスやファイル名を指定するのではなく、ファイルをリダイレクトで入力してあげます
-x を使うことで最後のパラメータを標準入力から受け取ることができるようになります

バイナリデータの取得

  • redis-cli --raw HGET some_key image_binary > image_new.png

取得する場合は HGET で key とフィールド名を指定します
オプションに --raw を指定する必要があります
これを指定することでマルチバイト文字 (バイナリ) を扱えるようになります
あとは結果を画像ファイルにリダイレクトしてあげれば OK です

取得した画像がちゃんと表示されるかプレビュー等で確認するといいと思います

参考サイト

2015年11月17日火曜日

nrf51822 で Real Time Clock を使って起動時間を取得する方法

概要

nrf51822 上で起動時間を取得するために RTC (RealTimeClock) の仕組みを使ってみました
RTC の値はデバイスの電源の ON/OFF でリセットされる値になります

環境

  • Windows7 64bit
  • nRF51822
  • nRF51 DK
  • nRF51 SDK 9.0.0

実装方法

nRF51 DK に含まれる App Timer (以下タイマー) という機能を使って実現します

nRF51 SDK に含まれているサンプルを見るといろいろなサンプルでこのタイマーを使っているので基本はそれをベースにすれば OK です
今回はバッテリーレベル用のタイマーを定義して、それを例に
流れを説明していきます

初期化

まず、タイマーを使うために APP_TIMER_INIT というマクロをコールする必要があります
サンプルではだいたいこんな感じで実装されていると思います

APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);

基本はこれで問題ないです

タイマーの生成

次にタイマーを生成します
生成するには app_timer_create をコールします

uint32_t err_code;
err_code = app_timer_create(&m_battery_timer_id, APP_TIMER_MODE_REPEATED, battery_level_meas_timeout_handler);
APP_ERROR_CHECK(err_code);
static app_timer_id_t m_battery_timer_id;

app_timer_id_t は typedef されている型で実体は uint32_t になります
この ID に対するタイマーを生成していることになります
また、 battery_level_meas_timeout_handler ですがこれは関数になっており、タイマーがタイムアウトしたときにコールされる関数を指定できます
この辺もサンプルを見ると流れが把握できると思いますが、この関数内でバッテリーレベルの再取得を実施し Characteristics の value の更新を行っています

タイマーが生成できたらタイマーをスタートします

タイマーを開始する

タイマーを開始するには app_timer_start をコールします

uint32_t err_code;
err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);

これをコールしたタイミングから RTC によるカウントが開始されます

現在のカウントを取得する

タイマーを開始したら、現在のカウントを取得してみます
現在のカウントを取得するには app_timer_cnt_get をコールします

uint32_t p_ticks;
app_timer_cnt_get(&p_ticks);
printf("%d \r\n", p_ticks);

こんな感じでカウントしている値を取得できます

ここでちょっとはまったのは app_timer_cnt_get の返り値を参照してもずっと 0 でした
リファレンスを見たのですが、この関数の返り値は正常に取得できたかのフラグを return しているだけなので常に 0 が返っていました
関数側では、受け取った引数のポインタに対して書き込みを行っているので、引数として渡した変数を参照する必要がありました

で実際にこの値を定期的に出力してみたのですが、値を見ると 1, 2, 3, … のような秒数を刻む値ではなく 3256, 6785, 125678, … のようにガンガン値が増えていました
ちょっとこの値に説明します

カウント値について

以下もしかすると、間違いがあるかもしれません

この値について目視でどの程度増えているのか計測してみたのですが、だいたい 30 秒で 1,000,000 カウント増えている感じでした
なので 1 秒間で約 33,000 ほど増えていることになります

何かそれっぽい値を定義している箇所がないか調べてみたのですが、APP_TIMER_CLOCK_FREQ というマクロが定義されておりこの値が 32768 になっていました
おそらくですが 1 秒間にこの APP_TIMER_CLOCK_FREQ の値だけ増加しており、app_timer_cnt_get ではそれが取得できているのだと思います
なので、時間を取得したいのであれば、取得した値を APP_TIMER_CLOCK_FREQ で割り算すればそれっぽい起動時間が取得できると思います

また、値の最大値なのですが、約 16, 800,000 くらいになるとまた 0 にリセットされてカウントが始まりました
( 16, 800, 000 / 33,000 = 510 (sec) = 8 min 30 sec )
なので何回リセットされたかどうかも管理しないとダメそうな感じです
最大値を伸ばす方法とリセットされた回数を取得できる方法があるかどうかは、すいません、調査しきれていません、、、

ちょっとこの辺が曖昧な記載なのは、そこまで深く調べていないので自信がないためです
間違いがや追加の情報あればご指摘いただけると助かります

最後に

とりあえず nrf51822 で起動時間っぽい値を取得できるようになりました
完全に使うためにはもう少し実装が必要そうですが、ベースとなる呼び出し方は理解できたと思います

10分くらいでリセットされるタイマーだとちょっと管理が大変なので、半日分くらいは管理できるようにしたいなと思っています

2015年11月16日月曜日

Jekyll に theme を適用する方法

概要

Jekyll のデザインには様々なテーマが公開されており、これを使って簡単にデザインをカスタマイズすることができます
今回はテーマを使って jekyll build する方法を紹介します

環境

  • Mac OS X 10.10.5
  • Jekyll 2.5.3

テーマを適用してみる

http://jekyllthemes.org/ から好きなテーマを選択してください
今回は Jekyll Now というテーマを使ってみます

  • mkdir -p jekyll/work
  • cd jekyll/work
  • git clone https://github.com/barryclark/jekyll-now.git
  • gem install jekyll-sitemap
  • cd jekyll-now
  • jekyll server

で起動まで実施します
途中サイトマップを表示するためのプラグインをインストールしています
jekyll server を実行すると localhost:4000 で LISTEN するのでブラウザでアクセスしてみましょう

今回のテーマだと以下のようなサイトが表示されると思います
jekyll-apply-theme.png

ちょっとカスタマイズする

名前を変更する

  • emacs jekyll/work/jekyll-now/_config.yml

name: Your Name

を編集して再度 jekyll server すれば変更できます

記事をポストする

  • cd jekyll/work/jekyll-now
  • cp _posts/2014-3-3-Hello-World.md _posts/2015-11-16-sample-post.md
  • emacs _posts/2015-11-16-sample-post.md

jekyll server を実行すれば (内容は特に変更しなくても) 記事が増えていることが確認できると思います
あとはこんな感じで markdown ファイルを増やしていけば OK です

ビルドする

サイトなどで公開するための HTML ファイルを生成します

  • cd jekyll/work/jekyll-now
  • jekyll server

_site というディレクトリ配下に HTML や CSS が作成されるのでそれをそのまま Web サーバの DocumentRoot に配置すれば OK です

最後に

既存の Jekyll に対して適用するやり方は今回紹介しませんでしたが、おそらく _posts 配下にある .md ファイルを一旦退避し、テーマをダウンロード後に _posts ファイルに移動すれば適用できるはずです
プラグインを使って記載されている markdown やレイアウトがある場合はプラグインの再インストールも必要になると思います

特にデザインを自分でゴリゴリ作成したいということでなければ、テーマをベースに作成するといいと思います
レイアウトや 404 ファイルなどサイトの公開に必要なファイルは一通りそろっている状態なので、これをベースにカスタマイズしたほうがいいと思います

自分で作成したテーマはもちろん jekyllthemes.org で公開することもできます

あと Jekyll 3.0 が 2015/10/26 に正式にリリースされましたが、 3.0 だともしかしたらエラーになるテーマがあるかもしれません

2015年11月12日木曜日

CentOS7 + AWS で docker-machine を試してみた

概要

docker-machine は仮想マシン上に docker-engine が動作する環境を構築することができるツールです
今回は CentOS7 上の docker-machine から AWS の EC2 インスタンス上に docker 環境を構築してみます

環境

  • CentOS7 64bit
  • docker-machine 0.5.0
  • docker-engine 1.8.3

docker-machine インストール

事前に docker-engine のインストールを行っておいてください

  • mkdir ~/mydocker.machine
  • cd ~/mydocker.machine
  • curl -L https://github.com/docker/machine/releases/download/v0.5.0/docker-machine_linux-amd64.zip >machine.zip
  • unzip machine.zip
  • rm machine.zip
  • mv docker-machine* /usr/local/bin

docker-machine -v でバージョンが表示されればインストール完了です

AWS 上での準備

API でもマネージメントコンソールでもどちらでも OK です

VPC およびサブネットの作成

マネージメントコンソールにログインしたら

サービス > VPC -> VPCウィザードの起動

と実行すれば VPC とそれに紐づくサブネットを一気に作成できます
create_vpc_and_subnet.png

VPC を作成したリージョンと VPCID をメモしておいてください
サブネットを作成したゾーンもメモしておいてください

API をコールすることになるので、アカウントが持つアクセスキーとシークレットキーを用意してください

docker-machine の実行

では、 AWS 上にインスタンスを作成し docker-engine が動く環境を構築します

  • docker-machine create --driver amazonec2 --amazonec2-access-key [Your Access Key] --amazonec2-secret-key [Your Secret Key] --amazonec2-vpc-id vpc-xxxxxxxx --amazonec2-region ap-northeast-1 --amazonec2-zone c aws01

入力しなければいけないのは [Your Access Key], [Your Secret Key], vpc-xxxxxxxx の部分です
VPC およびサブネットを作成したリージョンとゾーンが異なる場合は ap-northeast-1 と c の部分も書き換えてください

上記の場合は ap-northeast-1 リージョンにある vpc-xxxxxxxx 内のサブネットに aws01 というインスタンスを作成します
作成されるインスタンスのタイプは t2.micro になります

しばらく待っていると docker-engine がインストールされたインスタンスが起動します

Running pre-create checks…
Creating machine…
Waiting for machine to be running, this may take a few minutes…
Machine is running, waiting for SSH to be available…
Detecting operating system of created instance…
Provisioning created instance…
Copying certs to the local machine directory…
Copying certs to the remote machine…
Setting Docker configuration on the remote daemon…
To see how to connect Docker to this machine, run: docker-machine env aws01

AWS 上に作成した docker-engine を操作してみる

  • docker-machine ls

で作成したインスタンスの情報を取得できます
IP アドレスを取得するときに API をコールしているので API のコールに失敗するとエラーが表示されます

docker コマンドで AWS 上のインスタンスを操作できる準備をします

  • eval “$(docker-machine env aws01)”

を実行すると以降の docker コマンドの操作が AWS 上のインスタンスに対する操作になります
docker-machine env aws01 だけ実行するとわかりますが、いくつかの環境変数を export するコマンドが出力されます
それを eval しているだけです

例えば すでに CentOS 上で docker-engine が動作しておりイメージが存在しているのであれば eval したあとで docker images とかを実行してみると何もイメージが表示されないことが確認できると思います

  • docker run busybox echo hello world

超簡単なコンテナの実行です
DockerHub から busybox イメージをダウンロードして echo しているだけです
問題なく実行できると思います
終了後に docker ps -a で確認すると終了していることがわかると思います

これだと本当に AWS 上のインスタンスで実行されているかわからないので nginx を動かしてアクセスできるか確認してみます

  • docker run -d -p 8000:80 nginx

nginx のイメージを取得して起動します
インスタンスの 8000 番をコンテナの 80 番にフォーワードして -d でデタッチモードで起動します

  • curl $(docker-machine ip aws01):8000

で Nginx の「Welcome to Nginx」の HTML が返ってくることを確認してください
返ってこない場合は 8000 番がセキュリティグループで閉じられていると思います
docker-machine で作成したインスタンスには docker-machine というセキュリティグループが自動で作成され割り当てられるようで、これが 22 と 2376 しかデフォルトでは空いていませんでした
ポートを開放して再度実行して HTML が返ってくることを確認してください

  • docker-machine aws01 stop

使わなくなったら上記コマンドでインスタンスを停止することができます
再度使いたい場合は start で OK です

最後に

今回は AWS 上で試しましたが docker-machine には driver という概念があり、様々なクラウドサービスや仮想環境にアクセスできる driver が備わっています
https://docs.docker.com/machine/drivers/

今後もいろいろな driver が追加されると思います
どうしても追加してほしい driver があれば pull - req でもいいと思います

テストとかで試すために自分の環境に docker-engine をインストールすることはあると思いますが、プロダクションなどで複数のインスタンスに docker-engine をインストールする場合は docker-machine を使って構築すれば管理が楽になると思います

あとは、SWARM という欄が ls 実行時にあったので docker-swarm と連携して、複数クラウドサービス間で docker クラスタを組めたりする感じでしょうか
この辺りはまた次回にでも試せればと思います

トラブルシューティング

  • Error creating machine: Error with pre-create check: unable to find a subnet in the zone: us-east-1a
    デフォルトのリージョンは us-east-1a にインスタンスを作るみたいで、そこに VPC とサブネットがないと上記のエラーになります

  • Error creating machine: Error in driver during machine creation: Error launching instance: Problem with AWS API call: Non-200 API response: code=400 message=Your requested instance type (t2.micro) is not supported in your requested Availability Zone (ap-northeast-1a). Please retry your request by not specifying an Availability Zone or choosing ap-northeast-1b, ap-northeast-1c.
    デフォルトのインスタンスタイプは t2.micro のようでゾーンによっては t2.micro が推奨されないタイプになります
    その場合に上記のエラーが発生します
    インスタンスを作成するゾーンを変更するかインスタンスタイプを変更 (--amazonec2-instance-type) してあげれば OK です

  • Host already exists: “aws01”
    すでにインスタンスが作成されているため create コマンドが失敗しています
    実際に AWS 上にインスタンスは作成されていないみたいで、不整合が発生しているのでローカルの情報を削除してあげましょう

    docker-machine rm -f aws01

  • Error creating machine: Error with pre-create check: There is already a keypair with the name aws01. Please either remove that keypair or use a different machine name.
    キーペアがすでに作成されているためにエラーが発生しています
    マネージメントコンソールから EC2 -> キーペア -> aws01 を削除して再度実行してください

  • Error creating machine: Error with pre-create check: unable to find a subnet in the zone: ap-northeast-1c
    指定したゾーンにサブネットが存在しない場合に発生するエラーです
    マネージメントコンソールの VPC から該当のゾーンにサブネットを作成しましょう

参考サイト

2015年11月11日水曜日

nRF51822 の Advertise で Scan レスポンスパケットを設定する方法

概要

Scan レスポンスパケットは Advertising でスキャンした後にレスポンスとして Peripheral が Central に送信できるおまけパケットです
Advertising パケットは上限が 31 バイトになっておりその上限を超えて何かデータを送りたいときなどに使います
データの構造は Advertising パケットと同様になっています

環境

  • Windows7 64bit
  • nRF51822
  • nRF51 DK
  • nRF51 SDK 9.0.0

背景 (なぜ Scanパケットを設定したかったのか)

ちょっと長くなりますが背景を詳しく説明します

サンプルのせいにするわけではないですが nRF51 SDK に含まれている各種サンプルでは基本的に Scanパケットを設定していません
で、Central 側の実装を Python ベースの gattlib というライブラリを使って実装していたのですが、Advertising パケットをスキャンした際に DeviceName が含まれずに困っていました
具体的には DiscoveryService.discover というメソッドを使うと DeviceName が取得できない状況でした

切り分けのため TI 社が提供する SensorTag や iPhone で実装した Peripheral デバイスを使ってスキャンしてみたのですが、その場合はうまく DeviceName が取得できました
なので、nRF51822 上に実装しているアプリが悪そうだ、ということでいろいろ調べたところ Scan レスポンスパケットを設定してあげることで、nRF51822 でも DeviceName を取得することができるようになりました

更に余談になってしまうのですが、hcitool の lescan というコマンドを使っていると DeviceName が (unknown) と表示されることにも気付きました
unknown がちょくちょく出ていましたが、ちゃんと DeviceName が表示されることもあったので初めは無視していました
また、nRF51822 の場合でも SensorTag の場合でも unknown が表示されていたので「こんなもんなのか」と思っていたのですが、discover すると nRF51822 だけ DeviceName が取得できないのはおかしいということに気が付き詳しく調べてみたところ Scan レスポンスパケットという仕様にたどり着き設定してみたらうまくいった感じです

ということは今思うと SensorTag は Scan レスポンスパケットを設定しておりかつ、gattlib の DiscoveryService.discover は Advertising パケットの DeviceName を見ているわけではなく、Scan レスポンスパケットの DeviceNam を見ている ( のではと今になって予想しています、コードレベルまで見ていないので正解がどうかは不明です )

長くなりましたが、まとめると

  • gattlib の DiscoveryService.discover で DeviceName が取得したかった

ために設定した感じです

設定方法

前置きかなり長くなりましたが実装方法です
nRF51 SDK のサンプルでは多くの場合、 main メソッド内で各処理の初期化を行っています
その中の advertising_init というメソッドで Advertising パケットの設定をしておりそこを修正します

具体的なコードの全貌は以下のとおり

static void advertising_init(void) {
    uint32_t      err_code;
    ble_advdata_t advdata;
    ble_advdata_t rspdata;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));
    memset(&rspdata, 0, sizeof(rspdata));

    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance      = true;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    advdata.uuids_complete.p_uuids  = m_adv_uuids;

    rspdata.name_type               = BLE_ADVDATA_FULL_NAME;

    ble_adv_modes_config_t options = {0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata, &rspdata, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);
}

ポイントは rspdata 変数を宣言して rspdata.name_type = BLE_ADVDATA_FULL_NAME; として DeviceName を設定するところです

冒頭でも説明していますが、Advertising パケットと Scan レスポンスパケットはデータ構造が同じです
なので、すでに宣言している advdata を使いたいところですがこれを使うとうまく動作しませんでした

また、 rspdata に DeviceName だけ表示するように設定していますが、これもポイントで他のパラメータも advdata と同じように設定したところ、これまたうまく動作しませんでした
( うまく動作しない明確な理由がわからない状態です、すいません )

なのでとりあえず上記のように、記載することで Scan レスポンスパケットを設定することができました

最後に

これで再度 hcitool lescan をすると nRF51822 で実装した BLE デバイスに unknown が表示されなくなっていることが確認できると思います
Advertising + Scan レスポンスパケット両方で DeviceName を設定するようにしたためです

ただ、他のデバイスを見るとほとんどのデバイスで unknown になることがあるので、おすすめ実装的にはやっぱりどっちかだけで DeviceName を送信するほうがいいのかもしれません
まぁ確かに同じデータを 2 回送るのであれば無駄といえば無駄ですからね
とは言え Central 側の実装も考慮しなければいけないと考えるととりあえず両方設定しておいたほうが無難といえば無難なのでしょうか

相変わらず BLE の実装は大変です