0

ble-firmata npmにBLEの自動再接続機能を付けた

node.jsからBluetooth Low EnergyでArduinoをリモートコントロールできるble-firmata npmに、v0.1.1から自動再接続機能がついた。

BLEは有線のシリアル通信よりも接続切断は頻繁におこる。
見通し20メートルぐらいまで問題なく通信できるけど、家のドアなど金属製の遮蔽物があると切断される。そういう場合でも自動再接続されて、回線を意識せずロジックのプログラミングに集中できるようにした。


https://www.npmjs.org/package/ble-firmata

インストール

% npm install ble-firmata


注意


on ‘connect’イベントはデバイスが再接続される毎に呼ばれるので、その中でsetIntervalとかすると良くない。

once ‘connect’を使うとかすると良い。


BLEの切断


切断を検知するとarduino.on ‘disconnect’をemitするようにしておいた。

切断は色々な要因で起こる。

  • BlendMicroの電源が落ちる
  • BlendMicroが遠く離れて、電波が届かなくなる
  • BlendMicro側からnRF8001を切断する
  • node側でarduino.close()する
  • Macのbluetoothメニューで、[デバイス名]→[接続解除]を選択する
  • Macのbluetoothメニューで、bluetoothを「切」にする

いずれの場合も自動再接続が働く。


自動再接続を無効にする


reconnect = falseすれば自動再接続しない。一瞬接続して、切断して、しばらくしたらまた接続するような用途の場合は無効にすればいい。

arduino = new BLEFirmata().connect()
arduino.reconnect = false

0

ble-firmata npm作った

前:blendmicro npm 作った


作った

https://github.com/shokai/node-ble-firmata
https://www.npmjs.org/package/ble-firmata

node.jsからBLE上のfirmataプロトコルでarduinoを操作できるのを作った。
firmataはarduino側にあらかじめファームウェアを書き込んでおいて、PCからリモコンするというもの。プログラムがPC側だけで完結するのでシンプルになって良い。

既存のfirmata(Arduino IDEのスケッチ例に入ってるやつ)はシリアル通信だけど、BLEが使えるBlendMicroというArduinoMicro派生ボードと通信できるようになった。

あと試してないけど、同じRedBearLabが出してるBLE Shieldとも通信できると思う。

スクリプト言語でハードウェアやるのは、最近はnodeが一番色々と揃ってたり、やってる人が多かったりする感じある。


BlendMicro


インストール


node側
% npm install ble-firmata

arduino側
sketch exampleに入っているBLEFirmataSketchは動かないので、 firmwareディレクトリの下に置いておいたファームウェアを書き込む必要がある。

詳しくはInstallの項目を読むといいと思う。

一応プルリクだしておいたので、そのうち取り込まれるかもしれない。


使う


こんな感じで接続して使う。
digitalRead、servoWriteやanalogWrite、sysexもある。
BLEFirmata = require 'ble-firmata'

arduino = new BLEFirmata().connect()

arduino.on 'connect', ->
console.log "connect!"
console.log arduino.boardVersion

digitalWrite 13, true
console.log analogRead 0


複数使う

arduinoに書き込むファームウェアの上の方に
#define BLE_NAME "BlendMicro"
というのがあって、これが名前になっている。最大10文字まで。

new BLEFirmata().connect(“shokai1”) みたいな感じで、指定したBlendMicroに接続できる。
デフォルト値は”BlendMicro”。

Macbook Airで試したところ、1つのnodeプロセス内で2つのBlendMicroに接続はできた。しかし2つnodeプロセスを立ててそれぞれでBLEを使おうとすると、BLEのラッパーがエラーだして落ちた。どうやらMacの中にハードウェア的に1つしかBluetoothモジュールが無いのが、複数プロセスで共有できないらしい。


socket.ioと一緒に使う

他にもサンプルにはsocket.ioでブラウザからarduino(サーバーとはBLEでつながっている)を操作できるのも入っているので、サンプル見るとだいたいわかると思う。

0

node-arduino-firmataを古いArduinoに対応させた

nodeにarduinoのコードを埋め込めるarduino-firmata npmを古いArduino(diecimila、duemillanove、Seeduino等)に対応させた。

arduino-firmataを使うとnodeとarduinoのそれぞれのコードを書いて通信させるのではなく、nodeの中にarduino.digitalWrite(13, true)とかarduino.analogRead(3)とか書けるのでコードが綺麗になって大変便利です。ご利用ください。
実装としては古来からあるMIDIをベースにしたFirmataというプロトコルを使っている。


現行最新のArduino LeonardやUNOとdiecimila/duemillanove等の違いは、USBシリアル通信変換機能が新しいArduinoのAVRマイコンには内蔵されているけど古いArduinoでは外部のFTDI等のチップを使っている事で、つまりPCから使う時にドライバが違う。

ドライバの差はserialport npmがどうにかしてくれるので良いが、node内でのシリアルポート関連のイベントのタイミングが違うので個別に処理をわける必要がある。
ちなみに同様の処理をRubyの方のarduino_firmata gemでも実装している。


古いArduinoと新しいArduinoを見分ける方法

USBのデバイス名で見分ける事もできるけど、それだと無数にあるArduinoクローンに対応できない。
古いArduinoを見つけたいだけなのでデバイスファイル名で判定した。

Debian/Ubuntu/Raspberry pi(raspbian)など
  • /dev/ttyACM0 -> Leonard, Micro, UNO
  • /dev/ttyUSB0 -> Decimilla, Duemillanove

Mac OSX
  • /dev/cu.usbmodem1234 -> Leonard, Micro, UNO
  • /dev/cu.usbserial-A1234 -> Decimilla, Duemillanove
という風にデバイスファイル名が違うので、正規表現 /usbserial|USB/ にマッチするボードは古いArduinoである事がわかる。


古いArduinoでやらなければならない処理

古いArduinoだとシリアルポートが開いてから実際に通信が可能になるまで2〜5秒程度待たなければならない。この時間は一定ではない。これはFTDIチップとそのドライバのせいだと思う。

また、nodeやRubyからREPORT_VERSIONをリクエストして、返答がArduinoボードから返ってきた後に、IOの初期化命令を送るのをだいたい3秒ぐらい待たなければならない。
新しいArduinoだとノータイムでIOの初期化命令を送って良い。
こっちはCPUの性能だと思う。REPORT_VERSIONが往復しているわけだからArduino側でFirmataのプログラムは起動して通信できているんだろうけど、何かが遅いっぽい。


FirmataにおけるArduinoボード初期化処理の流れ

IOの初期化とは、アナログピン0〜5番は逐次アナログ値を計測して送ってくれとか役割を指示する処理のこと。

シリアルポート開く→REPORT_VERSIONをボードに送る→versionが返ってくる→IOを初期化する
という流れだが、古いボードの場合
シリアルポート開く→(2〜5秒待つ)→REPORT_VERSIONをボードに送る→versionが返ってくる→(3秒待つ)→IOを初期化する
という風になる。IO初期化はarduinoから値が返ってこないのでちゃんと初期化できるタイミングまで待ってやる必要がある。

古いArduinoで使われているFTDIのチップは、RubyでもNodeでも、各言語のシリアルポートライブラリが発行するopenイベントより数秒経ってから実際の通信ができるようになるのと、ちょっとよくわからないけど古いAVRマイコンだとFirmata自体が動き始めるまで時間がかかるみたいでversionが返されてからも待たないといけないらしい。

この辺はFirmata内部のコードも全部把握してるけど、特に変な事はしてないみたいなので、マイコンの世代差だと思う。



という処理をしている。
主にこの辺でやってる
https://github.com/shokai/node-arduino-firmata/blob/v0.3.0/src/arduino-firmata.coffee#L58-L95

1

NodeにArduinoのコードを埋め込む arduino-firmata npm

作った。

インストール

% npm install arduino-firmata

https://npmjs.org/package/arduino-firmata


以前作ったruby版android版と同じ実装なのと、coffee-scriptがほとんどRubyなので一瞬でできた。
JSのビット演算子の優先順位がrubyなどより低いらしくて、より過剰にカッコつける必要があった。


既存のライブラリとの違い

johnny-fiveが有名だけど、より普通のfirmata(proce55ing版のオリジナルのやつ)っぽい書き方ができるようにした。関数名がarduinoと同じなので学習コストが低い。
firmataはdigitalRead/analogReadがコールバック返してくるけど、それは使いにくいので(例えばピン1がHIでピン2がLOWの時〜という条件式が書きにくい)値を返す普通の関数として実装した。

ようするにarduino使ったことがあるならすぐ使えるようにした。


使い方


まず
Arduino IDE -> [File] -> [Examples] -> [Firmata] -> [StandardFirmata]
をArduinoに書き込む。これでnodeからの命令を受けて動くarduinoになる。

くわしくは
https://github.com/shokai/node-arduino-firmata#readme


12, 13番ピンのLEDを点滅させる例
ArduinoFirmata = require 'arduino-firmata'
arduino = new ArduinoFirmata().connect()

arduino.on 'connect', ->
stat = true
setInterval ->
console.log stat
arduino.digitalWrite 13, stat
arduino.digitalWrite 12, !stat
stat = !stat ## blink
, 500

なおconnectはArduinoっぽいデバイスを適当に探して接続するが、引数にデバイスへのパスを入れて指定もできる。connect(“/dev/tty.usb-device”)

他にdigitalRead, analogRead, analogWrite, servoWrite, sysex、入力が変化した時のanalogChange, digitalChangeなどが実装してある。
https://github.com/shokai/node-arduino-firmata#io



socket.ioと一緒に使う例

samples/serverに入れておいた物の抜粋

  • センサー読んで値が変わったらsocket.ioでクライアントに送る
  • クライアント側でボタン押したらLED点灯/消灯

server.js
var io = require('socket.io').listen(app);

var ArduinoFirmata = require('arduino-firmata');
arduino = new ArduinoFirmata().connect();


// センサー(A0)の値が変化していたらHTML側に送る
arduino.on('analogChange', function(e){
if(e.pin != 0) return;
console.log(e);
io.sockets.emit('analogRead', e.value);
});

io.sockets.on('connection', function(socket) {

// 初回接続してきたクライアントに最新のセンサーの値を送る
socket.emit('analogRead', arduino.analogRead(0));

// HTML側のボタンをクリックしたら、LED点灯/消灯
socket.on('digitalWrite', function(stat) {
console.log("pin13:"+stat);
arduino.digitalWrite(13, stat);
});

});

0

Ubuntu/Debianでarduino_firmata動くようになった

これ → 64bit Ubuntu+Arduino UNOでarduino_firmataが動かないらしい

なおった。Raspberry Piでも安定して動くようになった。

serialport gemの read_nonblockとwrite_nonblockを使っていると、DebianやUbuntuで動かなくなっていたのでデフォルトでnonblock使わないようにした。


今まで通りノンブロッキングIOで使いたい人は接続時に

arduino = ArduinoFirmata.connect "/dev/tty.usb-device-name", :nonblock_io => true
で接続すればいい

Arduino Firmata on Ruby