ブログ

κυσο βλογ

Switch Bot が届いたのでSingle Board Computerから操作してみた

Switch Botとは

実世界の物理スイッチを押すためのIoTデバイスである。プロジェクトページのURLには「世界最小の無線ロボット」(switch-bot-the-worlds-smallest-remote-robot)という文字が埋め込まれている。

www.kickstarter.com

KICKSTARTERで出資していたものが製品となって届いた。素晴らしい。2個で59US$(7077円)だった。
amazonで探すと、一個7000円くらいで売ってる転売屋が見つかるけど、今なら上の公式サイトから飛べる
https://www.indiegogo.com/projects/switch-bot-the-world-s-smallest-remote-robot#/
で1個25US$で注文できる。量産効果でだんだん価格が下がっているようだ。
公式サイトの The journey of Switch Bot, please find more in our gallery.という写真↓
https://ksr-ugc.imgix.net/assets/014/378/118/10d8a35cf74342236b38988a76e63285_original.jpg?w=680&fit=max&v=1478196260&auto=format&q=92&s=ec09861a971e3700ec29d2330e0a5b3b
から試行錯誤と進歩の歴史が垣間見られて興味深い。

開封

Switch Bot本体2個と、お礼の紙、クイックスタートガイド、固定用両面テープ(交換用)二枚が頑丈な紙のケースに入ってきた。この紙ケースは小物の収納に使えそうなので捨てない。
f:id:XX-Prime:20170701202437j:plain

どうでもいいがお礼の紙にCEOの手書きサイン(の印刷)があるが、きったなすぎて何と書いてあるのか全く読めないw
f:id:XX-Prime:20170701202541j:plain
b~???

スマートフォンアプリで動作テスト

バッテリー絶縁用のテープを引き抜くとSwitch Botが起動する。この状態でSwitch Bot用のスマートフォンプリを開くと、自動的にBLE Scanが行われてスマートフォンのBLE通信範囲内にあるSwith Botがアプリ上に表示される。

f:id:XX-Prime:20170701202047p:plain

バイスアイコンをタップすると数秒のラグの後にモーターが動作して腕(?)が一回動作する。モーターのトルクはかなり強く、硬めのスイッチでも問題なく操作できそうである。スイッチを押すことだけに特化しているので、このあたりの調整はかなりの経験の蓄積を感じさせる。
バイスアイコンの下にある歯車マークで詳細設定を行える。デバイスの名前を変えられるので、操作対象をデバイス名にすると分かりやすい。上のイメージでわかるように、デバイス名には日本語を入力しても正しく表示される。
設定を変更するとDFU(たぶんDevice Firmware Updateの略)と表示されて通信が開始される。
詳細設定からBattery Levelも確認できるが、これは詳細設定ではなくデバイスリストに直接表示したほうが良いと思う。つまりBLEアドバタイズにBattery Levelを含ませてしまった方が良いと思う。

セキュリティ

工場出荷状態ではスマートフォンアプリをインストールして、BLE通信範囲内に入れば誰でもSwitch Botを操作できるようになっている。このままでは、悪意のある第三者においしいモーニングコーヒーを淹れられる事案が発生したりする恐れがある。
これでは問題がある場合は、スマートフォンアプリからデバイスにパスワードを設定できる。パスワードを知らないデバイスは操作できないので、一応のセキュリティは確保できている。
パスワード設定でどの程度の暗号化が行われるかは未調査。

Single Board Computerから動作テスト

むしろこっちが私にとっては本命の操作方法である。公式にpythonオープンソースライブラリが用意されているのでBLE信号を傍受して解読する手間は要らない。
github.com
日本の企業にもこのオープン性は見習って欲しいと思う。

GithubページにはRaspberry Pi3がデバイス例として挙げられているが、情報追加の目的でBeaglebone Green Wireless(BBGW)でやってみる。

$ sudo apt-get install python-pexpect
$ git clone https://github.com/OpenWonderLabs/python-host.git
$ cd python-host/

では実行!

$ sudo python switchbot.py
Scanner inited
Start scanning...
scan timeout
No SwitchBot nearby, exit

(しーん)
はい動かないー。ばんざーい。
試しにRaspberry Pi3でもやってみたが同様にscanに失敗して動作しない。
仕方ないのでswitchbot.pyの中身を少し見てみる。

    def scan_loop(self):
        self.con = pexpect.spawn('hciconfig')
        pnum = self.con.expect(["hci0",pexpect.EOF,pexpect.TIMEOUT])
        if pnum==0:
            self.con = pexpect.spawn('hcitool lescan')
            self.con.expect('LE Scan ...', timeout=10)
            print "Start scanning..."
        else:
            raise Error("no bluetooth error")
        exit_counter=0
        #some bluetooth dongles may upload duplicates
        repeat_counter=0
        dev_list  = []
        while True:
            pnum = self.con.expect(["WoHand",pexpect.EOF,pexpect.TIMEOUT], timeout=5)

lescanをしてWoHandという文字列を含む応答が返ってくることを期待しているようである。
試しにlescanしてみる。

$ sudo hcitool lescan
LE Scan ...
AA:BB:CC:DD:EE:FF (unknown)
AA:BB:CC:DD:EE:FF (unknown)
FF:EE:DD:CC:BB:AA (unknown)
FF:EE:DD:CC:BB:AA (unknown)

これら2つはSwitch Botなのだが、BBGWでやってもPi3でやっても同様にunknownになっている。それは先に進まんわけだ。
scan自体には成功しているので、Switch Botの腕(?)を動かすメソッドで直接BLEアドレスを指定してやれば動きそうである。
switchbot.pyの当該部分は以下の通りである。

def trigger_device(add):
    print 'Start to control'
    con = pexpect.spawn('gatttool -b ' + add + ' -t random -I')
    con.expect('\[LE\]>')
    print "Preparing to connect."
    con.sendline('connect')
    #To compatible with different Bluez versions
    con.expect(['\[CON\]','Connection successful.*\[LE\]>'])
    print 'Write command'
    con.sendline('char-write-cmd 0x0016 570100')
    con.expect('\[LE\]>')
    con.sendline('quit')
    print 'Trigger complete'

なるほど。ではまずgatttoolで直接やってみよう。

$ gatttool -b AA:BB:CC:DD:EE:FF -t random -I
[AA:BB:CC:DD:EE:FF][LE]> connect
Attempting to connect to AA:BB:CC:DD:EE:FF
Connection successful
[AA:BB:CC:DD:EE:FF][LE]> char-write-cmd 0x0016 570100
Command Failed: Disconnected

うーん失敗。connectしてからコマンドを手で入力しているうちに切断されているような感じである。よってconnectの後にConnection successfulが返ってきた直後にchar-write-cmdを実行してみると

[AA:BB:CC:DD:EE:FF][LE]> connect
Attempting to connect to AA:BB:CC:DD:EE:FF
Connection successful
[AA:BB:CC:DD:EE:FF][LE]> char-write-cmd 0x0016 570100

(ちゅぃーん)
成功した!よって、trigger_device(add)に直接アドレスを指定するように、switchbot.pyを以下のように変えてみた。

def main():
    trigger_device('AA:BB:CC:DD:EE:FF')
    sys.exit()

これで実行すると

$ sudo python switchbot.py
Start to control
Preparing to connect.
Write command
Trigger complete

(ちゅいーん)
成功した!ただし、正常終了しない場合は永久に待ち状態になってしまうので適切にタイムアウトを設定する必要がある。

このように、switchbot.pyはまだまだα版のような状態なので、適切なメソッド群だけをライブラリとして別ファイルに追い出したり、スキャンの方法を変えたりと色々と改善の余地がある。Switch Bot用のvendor idが分かれば、Switch Botのみをスキャンすることができるはずで、恐らくスマートフォンアプリはその方法を採用しているはずである。
とは言え骨格部分はすでに完成しているので修正や改善は困難な仕事ではない。
が、バッテリーレベルの取得コマンドやセキュリティまわりが不明なので、それを自力で解明しようとするのはやや大変そうである。

スキャン失敗の追記(2017/07/03)

中の人にコメントを送ってみた所、

Oops, the github project is out of date now and unable to find bots since the SwitchBot broadcast format has been changed... We will fix this later...

Regards
Wonderlabs

と返信があり、問題は把握済みで直す予定とのことである。

Single Board Computer を持ってない普通の人専用デバイス

こんな所まで読み進めてきたド変態はSingle Board Computerの数枚や数十枚なら持っているだろうけど、そうでない正常な人用にはSwitch-LinkというBLE/Wi-Fiゲートウェイが公式に用意されている。
https://ksr-ugc.imgix.net/assets/014/448/371/cc3b44fc97d42800453826c8a2b97db1_original.png?w=680&fit=max&v=1478698927&auto=format&lossless=true&s=32cd3aca41e2b33d5ab25b37ae13f729

固定方法の提案

モーターのトルクに負けないようにかなり強力な両面テープ(剥離紙には3MのVHBと書いてある)で固定するようになっているが、これは剥がすのがなかなか大変で、最悪の場合壁面を破壊する恐れがある。
そこで私はこれ↓を固定に使うことを提案する。
コマンド™ タブ
3Mのコマンドタブは、強力に接着できる両面テープであるが、タブを引っ張っていくときれいに無理なく剥がせるという製品である。これの開発者は相当賢い。脳の一部を分けてもらいたい。
家の中、勤務先、工場内などで色々なものをこれで固定してきたが、今のところ問題が起きたことはない。天井にEnOceanのモーションセンサをコマンドタブで固定して2年以上経過したが、落ちてくる気配は今のところない。
Switch Botには一番小さいタイプSSを二枚か、タイプSを一枚で適切に固定できるはずである。
タブが見えると気になって発狂するという人には透明タイプのコマンドタブもあるよ。

応用

IFTTTと連携させるといくらでも複雑なことをできそうだし、Amazon Dashをわざわざこれで押すというネタで一笑い取るというのもアリ。
個人的な例では、温湿度センサと連動して換気扇を自動操作したいので早速実用を開始している。

感想

既存機器をIoT化できる製品としての完成度は高い。スマートフォンアプリも良くできていて、説明書を読まずに誰でも使える水準に達している。
価格は正直に言ってまだまだ高いとは思うが、あらゆるモノを遠隔操作できる可能性について少しでも考える人であれば購入するに足るものだと思う。しかし転売屋から一個7000円というボッタクリ価格で買うのは馬鹿げている。
最後に、スイッチが動作したことを確認する手段が無いので、生命維持用途とかには使わないほうがいいだろう。

esp32のBLE scanでRSSIを表示する

esp32のBLE scanでRSSIが取得できるか?

現時点で "esp32 ble scan RSSI" でweb検索すると、上手くいかないとか、変な値が取得できるといった情報ばかりが見つかる。そこで、本当にできないのか試してみたら上手くいったのでメモ。

開発環境の準備

まずesp-idfの開発環境を揃える。esp-idfはWindowsよりLinuxで開発したほうが色々と捗るので、WindowsVirtualboxdebian linuxを動作させ、その中でesp-idfの開発環境をセットアップした。特にハマる点もないので詳細な手順は省略。検索すればすぐ見つかる。

次に、ソースを見る際に grep -r しまくっても良いが、IDEの方がなにかと便利なので、eclipseからesp-idfを扱えるようにセットアップした。ついでにeclipseからbuild→flashまでできるようにした。これも簡単なので詳細な手順は省略。

一点だけハマる可能性があるのは、Virtualboxにesp32開発ボードのUSB-Serialのインターフェイスをアタッチする所で、何かの拍子に乱暴に切断してしまった後だと再アタッチが失敗することがある。そんな場合は、windowsのデバイスマネージャの「ポート(COMとLPT)」にあるCOMポートを削除してから開発ボードを接続して再アタッチするとうまくいく。これに気づくまではいちいちwindows自体を再起動させていた。

BLE scanできるサンプルソース

esp-idf/examples/bluetooth/gatt_client/

がそれ。昔はexample 15_gatt_clientと呼ばれていたもののようだ。

このプロジェクトをeclispeにインポートして色々と探ってみると、rssiはesp_gap_ble_api.hで定義されているesp_ble_gap_cb_param_tという共用体にintとして入っており、gattc_demo.cのesp_gap_cbというコールバック関数の中でアクセスできることがわかる。よって、この関数の中で

    case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            for (int i = 0; i < 6; i++) {
                ESP_LOGI(GATTC_TAG, "%x:", scan_result->scan_rst.bda[i]);
            }
            ESP_LOGI(GATTC_TAG, "\n");
            adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv,
                                                ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
            ESP_LOGI(GATTC_TAG, "Searched Device Name Len %d\n", adv_name_len);

            ESP_LOGI(GATTC_TAG, "RSSI: %d\n", scan_result->scan_rst.rssi);  //これを追加。以下省略。

とRSSIを出力するようにしてみた。

簡単なテスト

使ったesp32デバイスはdoitの開発ボードと、秋月でも買えるespressifの開発ボードで、両方とも同じ動作をした。
変更したプログラムをビルドしてesp32にflashして、適当なシリアルモニタ(LinuxのCuteComが使いやすかった)で出力を見てみると、BLEタグ(スマートフォンで代用。BLE Peripheral Simulatorを使用。)がesp32の数十cm程度の近くにあるときは

\0x1b[0;32mI (9350) GATTC_DEMO: RSSI: -64

程度で、数メートル離すと

\0x1b[0;32mI (7260) GATTC_DEMO: RSSI: -85

程度となったので、正常な値を取得できているようだ。ただ、当然だがesp32の向きが変わると値が大きく変動するので、色々と工夫してタグの接近・離脱を判断する必要はある。

まとめ

安価な据え置き型ble scanner(wifiへのゲートウェイ)としての利用可能性が薄ぼんやりと見えた。その用途では、これまではRaspberry PiやEdisonなどを使うしかなかったが、esp32で代用できれば色々と可能性が広がる。

Beaglebone Green WirelessでEnOceanゲートウェイ(の基本部分)を用意する

BBGWでnode.jsのEnOceanモジュールを使ってゲートウェイを動作させてみたら極めて高品質で使いやすかったのでメモ。Raspberry Piでも同様に動作した。

EnOceanとは

バッテリーなしで動作するセンサ機器と受信機、データフォーマットの総称。現時点で実用的と言える数少ないIoTデバイスの一つ。

EnOceanの利点

電池交換の必要がない。

太陽電池で動作するもの、スイッチの押す力で発電して動作するものが代表的である。プロトタイピングやPOCの段階ではデバイスの電池交換は問題にならないが、実用フェーズに入ると電池交換は人的・物的コストを生み続ける悪魔になる。電池交換作業でデバイスを破壊するような事故も頻発する。

送受信の信頼性が高い。

送受信の信頼性を高めるために、一度にパケットを3つ送信するようになっている。電波状況は色々と変化するが、経験上、受信機との距離が10メートル以内ならデータの取りこぼしはほぼ無い。928MHz帯の電磁波を利用するため、障害物を回りこんで届く。

データフォーマットがよく整理されている。

様々なセンサ機器のデータフォーマットが規定されており、更に機器の登録などのコマンド体系も整備されている。

EnOceanの欠点

バイスが高価。

電池交換の人的・物的コストがゼロなので、デバイス自体の高価さは長期運用を考えるとあまり問題にはならないが、単純に他のデバイスと較べてしまうと高価。

あまり流通していない。

10 高いからあまり売れない。

20 あまり売れないから高い。

30 GOTO 10

まともなゲートウェイソフトウェアがない(ように見える)。

EnOcean Linkという名のEnOcean公式のゲートウェイソフトウェアライブラリがあるが、C++で書かれているのでコンパイルして動作させるまでが大変面倒である。最近ではrubypythonで簡易的なテレグラムパーサを自作してセンサデータを抽出するような使い方が増えてきたようだが、EnOceanの良く整備された機能群を利用できていない。

今回、node.jsで書かれた大変優れたフル機能のゲートウェイソフトウェアライブラリがBBGWやR-Pi上で動作することが検証できたので、まともなゲートウェイソフトウェアが無い問題は私の勝手な幻想だったと言って良い。

日本国内では1ホップまでのリピータしか使えない。

これはあまり問題にならない。2ホップ以上必要ならゲートウェイを増やすべき。

今回使ったハードウェア

Beaglebone Green Wireless

シングルボードコンピュータ

SeeedStudio BeagleBone Green Wireless

他にも、node.jsが動作する機器なら多分なんでもOK。

USB400J

EnOeanのUSBドングル型受信機。これは割りと安い。

www.switch-science.com

ところで某Wi-S○Nの受信機ってなんであんなに高いの?買って試す気が起こらないので誰かくれ。

STM431J

温度センサ。これも割りと安いが、ケースがない。ケースだけを某所で買うと4000円くらいする。うーん・・・。タカチのケースを加工した方が良いだろう。個人利用なら紙や布でケースを作ってしまうのが手軽で良い。

www.switch-science.com

ESM210R

プッシュスイッチ。物理世界からの様々なイベント入力に使える。

EnOcean ロッカースイッチ・シングル ESM210R - スイッチサイエンス

売り切れですかそうですか。

node.jsのEnOceanモジュール

今回の主役。node.jsのEnOceanモジュールは幾つかあるが、BBGWやR-Piでも動作したフル機能のモジュールは

node-enocean-utils

だった。作者は日本の方のようだ。素晴らしいモジュールをありがとうございます。

最初に見つかるnode-enoceanというモジュールはコンパイルに失敗して動作しなかった。

導入準備

今回使うモジュールはnode.jsの4.4以上でのみ動作するので、それより古い環境ではnode.js自体の更新が必要である。今回用いたBBGWでは、OSイメージとして

https://debian.beagleboard.org/images/bone-debian-8.6-seeed-iot-armhf-2016-11-06-4gb.img.xz

を使ったが、これのnode.jsが白亜紀の0.12系だったので更新が必要であった。

sudo apt-get purge bb-node-red-installer nodejs

sudo apt-get update

sudo apt-get install -y curl locales ntpdate avahi-utils python build-essential

curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -

sudo apt-get install -y nodejs

hash -r

これでBBGW上でnode.jsの6系が使えるようになった。

$ node -v
v6.10.1
$ npm -v
3.8.2

bonescriptもここで再インストールすればちゃんと動く。

導入

動作検証用に適当にディレクトリを掘ってモジュールをローカルインストールする。

mkdir enocean_exp

cd enocean_exp

npm install node-enocean-utils

基本的な動作テスト

まずは、全てのテレグラムを出力する単純なコードを書いてみる。'data-unknown'イベントを捕まえて、テレグラムのmessage部分をコンソールに出力する。

simple1.js

var enocean = require('node-enocean-utils');

// Start to monitor telegrams incoming from the Enocean devices
enocean.startMonitor({'path': '/dev/ttyUSB0', 'rate': 57600});

// Set an event listener for 'data-unknown' events
enocean.on('data-unknown', (telegram) => {
var message = telegram['message'];
console.log(message);
});

 これを動作させて

node simple1.js

ロッカースイッチを押して離すと

{ packet_type: 10,
packet_type_desc: 'RADIO_ERP2 (ERP2 protocol radio telegram)',
device:
{ id: '0000002C8674',
eep: '',
name: '',
manufacturer: '',
learned: false },
oid: '00 2C 86 74',
crc: true,
eep: '',
known: false,
rorg: '',
rorg_desc: '',
func: '',
func_desc: '',
type: '',
type_desc: '',
data_dl_buffer: <Buffer 84>,
dbm: -50,
dbm_desc: '-50 dBm' }
{ packet_type: 10,
packet_type_desc: 'RADIO_ERP2 (ERP2 protocol radio telegram)',
device:
{ id: '0000002C8674',
eep: '',
name: '',
manufacturer: '',
learned: false },
oid: '00 2C 86 74',
crc: true,
eep: '',
known: false,
rorg: '',
rorg_desc: '',
func: '',
func_desc: '',
type: '',
type_desc: '',
data_dl_buffer: <Buffer 00>,
dbm: -52,
dbm_desc: '-52 dBm' }

パケット2つぶんが基本的なデコードを経て出力された。eepの指定を行っていないので、ON/OFFのデータはdata_dl_buffer部分に数値として現れている。

今回のロッカースイッチは2ボタンのタイプで、EEPとしてF6-02-04を指定するとテレグラムの完全な解釈ができる。このEEPタイプはnode.jsモジュール内で定義されているのでそのまま利用できる。

バイスIDが 00 2C 86 74 のロッカースイッチを、EEP: F6-02-04としてteach(登録)して、data-knownイベントで解釈済みのデータを出力する機能を追加すると以下のようになる。

simple2.js

var enocean = require('node-enocean-utils');

//// Teach the information of Enocean devices
enocean.teach({
'id' : '00 00 00 2C 86 74',
'eep' : 'F6-02-04',
'name': 'Rocker Switch, 2 Rocker'
});

// Start to monitor telegrams incoming from the Enocean devices
enocean.startMonitor({'path': '/dev/ttyUSB0', 'rate': 57600});

// Set an event listener for 'data-known' events
enocean.on('data-known', (telegram) => {
var message = telegram['message'];
console.log(message['device']['id'] + ': ' + message['device']['name'] + ': ' + message['desc']);
});

これを動作させて、ロッカースイッチを左右(上下?)押して離すと以下のように完全に意味までデコードされた情報が出力される。

$ node simple2.js
0000002C8674: Rocker Switch, 2 Rocker: B0 pressed
0000002C8674: Rocker Switch, 2 Rocker: released
0000002C8674: Rocker Switch, 2 Rocker: BI pressed
0000002C8674: Rocker Switch, 2 Rocker: released

ボタン0が押されて離され、ボタンIが押されて離されたことが記録された。releaseはどちらのボタンからのものかは区別できないようだ。DolphinViewで見てもそうなので、デバイスから出る信号そのものが同じと考えられる。

温度センサも全く同様に、teachでEEPを登録してデータを解釈できた。

ここまで理解できれば、モジュールのドキュメントを読み進める知識が整ったので

node-enocean-utils

を参照して機能の全貌を理解できる。

M2Xへのアップロードも普通にできたが省略。

次は

これをNode-Redから使えるようにして管理や運用を容易にして実用性を高める。teach-inテレグラムの扱いとteachメソッドの呼び出しといったデバイスの登録処理、クラウドへのデータ送信などはNode-Redのノードとして構成すれば、コツメカワウソちゃんでも扱えるようになるはずである。

まとめ

これでシングルボードコンピュータ上でEnOceanのフル機能のまともなゲートウェイをやっと作れる見通しが立った。

ESP32の開発ボード(DOIT ESP32 Development Board)をArduino IDEで使ってみる

2016年末に買って使い方がわからず放置してあったESP32の開発ボードを、ふと思い立って触ってみたらプログラムのアップロードから動作検証までいけたのでメモ。

ESP32開発ボード

github.com

このボードをAliExpressで買った。人類の天敵であるTELECの認証マークが付いているので日本国内で使用しても殺害される危険は少ない。

http://esp32.net/

を見ると、すでに膨大な種類の開発ボードが出回っているが、日本製の良いものはまだ無いようだ。

開発環境準備

Windows上で行う。基本的には解説ページ

github.com

に従えばOK。私はgit clientとしてSourceTreeを使ったが、解説ページに書いてあるようにgitの純正?クライアントを使っても同じだろう。

解説ページに書いてない部分としては、Arduino IDE上でボードを選ぶときは

f:id:XX-Prime:20170312194916p:plain

のようにESP32 Dev ModuleでOK。

もう一つ、プログラムをアップロードするときにBOOTボタンを押しっぱなしにしておく必要がある。ボードの初期リビジョンのバグみたいだが、これはこれでうっかりアップロードを防げるので良い仕組みのような気もする。

BOOTボタンを押さずにアップロードしようとすると

esptool.py v2.0-dev
Connecting...

A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header
A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header

というエラーがでる。ここまで来た人はあとはBOOTボタンを押しっぱなしにするだけなので成功したも同然である。ちなみに私がBOOTボタン押下の必要性に気づくまで試行錯誤や検索を繰り返して数時間を浪費したことは恥ずかしくて書けない。

追記:macarduino IDEではBOOTボタン押下の必要がなかった。

サンプルプログラムを試す

Arduino IDE

ファイル→スケッチ例→Simple BLE→SimpleBleDevice

をアップロードしてみる。BOOTボタンを押すたびに、アドバタイズする文字列(の数値部分)が変わるというもので、デバッグ出力をシリアルモニタで確認できる。シリアルモニタは115200bpsで開く。

このプログラムを動作させた状態でスマートフォンでBLEスキャンをすると

f:id:XX-Prime:20170312200801p:plain

のように、BLE32 at: XX という名でアドバタイズされているのを確認できる。BOOTボタンを押すとXXの部分が増えていく。これでESP32の開発環境からデバイスまで一通りが正常に機能していることを確認できる。ちなみにみんな大好きBlinkプログラムは(そのままでは)エラーが出て動作しないので泣かないように。Blinkは

int ledPin = 2;

と定義してこのピンをON/OFFするようにするとボード上の青いLEDが点滅する。

どっかに配線図ないのかな?

→どうやらこれっぽい:https://www.dropbox.com/s/jefwxxtufgwg0ex/esp32_Schematic%20Prints.pdf?dl=0 

まとめ

ESP32開発ボードを買って使い方がわからずに放置してる人は再挑戦してみてはいかがだろうか。

 

BMP085とNode-REDでM2Xに気圧データを送る

Node-REDで簡単にI2C接続の気圧センサの取得値をM2Xにアップロードできるだろうとやってみたら、初心者が踏むであろう地雷を余すこと無く踏んでいって手こずったのでメモ。

ハードウェア準備

今回はホストマシンにBeaglebone Blackを、OSに

BeagleBoard.org - latest-images

の Jessie IoT (non-GUI) for BeagleBone via microSD card を選んだ。このイメージは最初からBeaglebone用のNode-REDが動作しているので便利である。BBGでもBBGWでもBBBWでもBBBlueでも同様に動作するはずである。

センサはサインスマート(SainSmart)BMP085 気圧センサーモジュール を使った。選択に積極的な理由は無い。そのへんに転がってたから。

BMP085の配線はI2Cなので

BeagleBoard.org - demo_bmp085

の通りだが、残念ながら新しいOSイメージでは /sys/bus/i2c/drivers/bmp085/ がもはや存在しないのでこのbonescriptデモは動作しない。bme280あたりで動くデモにそろそろ作り直したほうが良いと思う。bonescriptのデモは他にも色々と古くなってて残念な感じである。

bmp085のnode.jsモジュールをインストー

一応、全部をnode.jsで行ってみようと思ったので、読み取りライブラリもnode.jsのを用いた。

www.npmjs.com

の通り、npmでモジュールをインストールする。今回はNode-REDで使いたいので

$ sudo npm -g install bmp085

とグローバルインストールでOK。/usr/local/lib/node_modules/bmp085/にモジュールがインストールされる。

気圧と気温を読み取れるかテストする際には、

var BMP085 = require('bmp085'),
barometer = new BMP085({
  'device': '/dev/i2c-2'
});
barometer.read(function (data) {
  console.log("Temperature:", data.temperature);
  console.log("Pressure:", data.pressure);
});

と、I2Cバスの場所として/dev/i2c-2を明示的に指定しないと失敗する(地雷1)。

M2XのNode-REDモジュールをインストー

www.npmjs.com

$ sudo npm -g install node-red-m2x

でOK。

$ sudo systemctl restart node-red.service

でnode-redを再起動してからNode-REDのwebページを開き、storageカテゴリにM2Xノードが現れていればインストール成功。

f:id:XX-Prime:20170214204619p:plain

M2X準備

M2Xのwebページにログインして、デバイス一つと、その配下にストリーム2つ(気圧用と温度用)を作っておく。

Node-REDフロー構成

一分に一回、気圧と温度データを取得してM2Xにアップロードするフローを構成した。

f:id:XX-Prime:20170214205921p:plain

やることは単純なので直線的でわかりやすいフローである。

測定開始タイマー

injectノードを1分のインターバルでリピート構成した。出力ペイロードは使わないのでデフォルトのタイムスタンプのままにしてある。

f:id:XX-Prime:20170214210659p:plain

気圧測定(BMP085)

問題はここ。functionノードでbmp085モジュールを呼び出して使う部分である。色々あったが動作する状態になったのが以下の図。

f:id:XX-Prime:20170222222823p:plain

topic_idにM2XのDEVICE IDを、sub_topic_idにストリーム名を指定する。

まず、素朴にfunctionノード内でrequireによるbmp085モジュールのロードを試みると

function : (error)
ReferenceError: require is not defined

などと怒られて動かない(地雷2)。

ちょっと調べると、Node-REDで外部モジュールを使う方法が分かった。requireはsettings.jsの中のfunctionGlobalContextに書け、というのが正解である。と言われてもBBB上のnode-redのsettings.jsがどこにあるかがわからんので、/var/logあたりにnode-redの起動ログがあるはずと予想して

root@bbb0001:/var/log# grep -r 'node-red' * | grep 'settings.js'

で探してみたら

daemon.log:Feb 11 11:52:30 bbb0001 Node-RED[2814]: 11 Feb 11:52:30 - [info] Settings file  : /root/.node-red/settings.js

と、あっさり見つかった。このファイルの中を見てみると、

functionGlobalContext: {
// os:require('os'),
// octalbonescript:require('octalbonescript'),
// jfive:require("johnny-five"),
// j5board:require("johnny-five").Board({repl:false})
},

と、外部モジュールのrequire方法がコメントとして例示されている。ありがたい。よって、ここに

bmp085:require("bmp085")

を追加した。functionノード内ではrequireではなく

var BMP085 = global.get('bmp085');

のようにしてグローバルコンテクストの中からロードするとrequireと同様にモジュールを使うことができる。

次に、取得したセンサ値をmsgのペイロードに詰めてreturnで次のノードに送ろうとしたが何も送られなかった(地雷3)。

この問題の原因は、bmp085モジュールのセンサ値取得が非同期で行われるためである。センサの取得値はモジュールのコールバック関数の中でしか触れなく、普通にreturnしてしまうと値の取得が行われない状態でfunctionノードが終了してしまう。node.jsで毎回この手の地雷を踏んでる気がするがいい加減覚えたらどうだ俺。

Node-RED日本ユーザ会 : Functions Nodeの書き方 日本語訳にいくつか誤りがあるので原文を参照したほうが良いかもしれない。いま修正案を作成中。→修正案が受理されてサイトに反映された(2017/02/27)。

の 非同期メッセージ送信 の所に、そのものずばりの答えが書いてあった。node.sendを使えば非同期で呼ばれる関数の中からメッセージを後続のノードに送ることができる。メッセージを複数返せるというのが嬉しい所で、今回は気圧と温度を別のメッセージとして送りたかったので正に2回node.sendを実行している。actionとしてsetStreamValue以外の何かを指定すれば一回で複数ストリームへのputが行えるかもしれないが、もう調べる気力が残ってない。

2017/02/20 追記

functionノード内で、毎回newによるbarometerオブジェクト生成を行っていたが、

Feb 17 09:24:42 bbb0001 Node-RED[11054]: (node) warning: possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit.

というエラーがsyslogに出るので、コンテキストを利用して最初の一回だけnewするようにした所、当該エラーが消えた。これも地雷だな(地雷3.5)。Node-REDのノードは実行中に消えずに生き続けるということか。

M2Xへアップロード

M2XノードでAPIキーを指定する。ここは間違いようがない。

f:id:XX-Prime:20170214215216p:plain

Feedの鉛筆ボタンをクリックして

f:id:XX-Prime:20170214215357p:plain

API keyにM2XのデバイスAPI keyを入力してUpdateをクリックする。

確認用出力

このノードは特に要らないが、putされているオブジェクトを見るためにdebugノードを置いている。

出力例

f:id:XX-Prime:20170214215633p:plain

こんな感じで記録されていたら成功。台風の目が近くを通り過ぎると、尖点を持つかなり面白いグラフが得られる。

M2Xは、webサイトに埋め込むためのグラフも自動的に作ってくれるので、自動生成されたimgタグを埋め込むことで以下のように簡単にリアルタイムグラフを表示できる。

二軸までの同時表示が可能。timezoneの指定が出来ないのが残念な所で、UTCのみである。

imgではなくwidgetにはローカル時間表示のオプションがあって機能することがわかった。以下のようにdata-localize-timestamp="true"を指定したwidgetは日本時間(システムの時刻設定を読み込んでるのかな?)で表示される。読み込み中のアニメーションが永久に出るバグはhatenaの仕組みとの不整合によるものだと思われる。普通のwebページに埋め込んだwidgetは問題なく表示される。

ローカル時間のテスト

widgetの仕様はhttps://m2x.att.com/developer/documentation/v2/widgetsに記述がある。

Node-RED自動起動

デフォルトではnode-redサービスは自動起動しない。webブラウザで1880番ポートにアクセスするとxinetd的な何らかの仕組みでnode-redサービスが立ち上がるようだが、自動起動していると勘違いすると痛い目に会う(地雷4)。

$ sudo systemctl enable node-red.service

自動起動するようにしておかないとリブート後にデータアップロードが途切れて号泣する。

まとめ

Node-REDは大変扱いやすいけど、非同期で動作する外部モジュールを使う時にはここで書いたような地雷を踏まないようにしてね。

Nexus 6P 早期シャットダウン問題(と対処法)

寒いと落ちるNexus 6P

バッテリーの残りが20~30%あるのに、突然Nexus6Pがシャットダウンするという問題が世界中で起こっている。私のも昨年の12月半ばあたりからその症状が出始めて、今では残り60%でも寒いとシャットダウンするという鬼畜仕様になっている。

世界中の叫び

https://code.google.com/p/android/issues/detail?id=227849

大まかにまとめると、寒い場所で落ちやすい、電力消費の大きいときに落ちやすい、優先度smallってフザケンナさっさと直せ、という感じでみんな怒ってる。特に、車で事故って写真撮ったり電話したりしてるときに落ちて困った、という報告はかなり気の毒である。死者がでたらどうすんだゴルァという書き込みもある。

今日は40%くらいまでは持った。本当に、ありがとうございます。

f:id:XX-Prime:20170124220307p:plain

一旦冷えてからスリープ解除すると強制シャットダウンというお馴染みの流れ。寒いと動かなくなるのは爬虫類だけで十分である。

Nexusヘルプセンターに連絡

support.google.com

Nexusヘルプセンターの右上に お問い合わせ というリンクがあり、(昼間だけ)電話での連絡を依頼することができる。これを依頼して症状を説明した所、交換対応という結果になった。保証はすでに切れているが、特別に対応してもらえた。一年後にまた爬虫類状態になる可能性は極めて高いが、このまま使い続けるよりはマシである。

まとめ

同じ問題に苦しんでるかたは連絡を依頼してみてはどうだろうか。向こうから自発的に連絡してくるほどgoogleのサポートは優しくないようだ。

ジャンパケーブルオスメス問題

なんか作るときにジャンパケーブルが必要になって

  • オスーメスのケーブルが欲しいのにオスーオスしかない
  • オスーオスのケーブルが欲しいのにメスーメスしかない
  • メスが欲しいのにまわりにオスしかいない

などの問題が生じて発狂することがよくあるが、少なくともケーブルの問題を解決するそこそこうまい方法はある。

ケーブルはメスーメスしか買わない

オスーオス、オスーメス、メスーメスの三種類のジャンパケーブルがあるが、メスーメスだけあれば十分である。

バラ売りされているジャンパケーブルは地味に高価なので、無用なケーブルを用意するのは馬鹿げている。

さらに、

digitalparts.net

のようなリボン状のメスーメスケーブルを買えば比較的安く大量の本数を入手できる。割いて使うこともできるので、I2C用に4本だけまとめて切り離して使う、などバラ売りのケーブルより便利である。

オス化

akizukidenshi.com

のような両端が長いピンヘッダを用意しておく。これも1つずつ割いて使えるので、オスが必要な方に挿せばオスになる。メスからオスが生まれるのは生物界と同じである。

まとめ

オスのジャンパケーブルって要らなくね?