0

EdisonにArduinoをUSB接続する

EdisonのGPIOを使ってセンサーなどを接続するのもいいけど、USB接続でArduinoを追加するのもいいと思う。間に給電機能付きUSBハブを挟めば電気的に分離できるので難しい回路を作ってEdisonにいきなり接続してショートして全損という事態も避けられる。
もちろん超コンパクトに作るならEdisonのGPIOからI2Cとかで直接やった方がいい。


USBホスト

Edison Breakout Board KitのJ16 USBコネクタは、USBホストケーブルを接続してその先にUSBデバイスを接続できる。

Arduinoの他にもUSBハブ経由でkorgのUSB MIDIコントローラやMIDIキーボード、USBスピーカーも接続できた(lsusbにデバイス名は表示された)けど、alsaがデフォルトでは入っていないのでコントロールできなかった。

Edison+ArduinoMicro

J21から給電する

普通は給電用に使うJ16をUSBホストケーブル用に使うには、別の口から給電する必要がある。
J21に7〜15Vを入れればいい。J21の外側がGND、内側がVCCになっている。

Breakout Board Hardware Guideより

7〜15Vを用意するのが面倒くさいので、試しに5Vを入れてみたら問題なく動いた。
余っているUSBケーブルをむいて、赤白黒緑の4本の線を取り出すと赤が5V、黒が0Vになってるのでそれを使う。こういうケーブルを作っておくと持ち運びも便利だし、しょっちゅう役立つ。

念の為にテスターで調べるか、持っていなければいらないLEDに一瞬チョンとつけて光るか確かめた方がいい。

あと、裏面のJ22に面実装のDCジャックを追加して、ACアダプタから動かす事もできるらしい。


Arduino

手元にあったArduino Micro(Leonardoと同じATMega32U4内蔵のUSB機能を使用)とArduino UNO(FTDIのチップのUSB機能を使用)の両方とも、ドライバなしでEdisonの「/dev/ttyACM0」として認識された。


EdisonからArduinoを動かす

Arduinoにプログラム書いてEdisonと通信させるのは面倒なのでFirmataを使う。

去年作ったarduino-firmata npmとcoffeeで書く

ArduinoFirmata = require 'arduino-firmata'
arduino = new ArduinoFirmata().connect process.env.ARDUINO

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

led_stat = false
setInterval ->
arduino.digitalWrite 13, led_stat
led_stat = !led_stat
, 1000 # 1秒ごとに点滅

setInterval ->
console.log arduino.analogRead 0
, 500 # 0.5秒ごとにADC0読み取り

% ARDUINO=/dev/ttyACM0 coffee led_blink.coffee

0

node.jsとarduinoでジョグシャトルのシャトルを取得


ジョグシャトル


ジョグシャトルは昔のビデオデッキのリモコンや、パチンコのハンドル部分についている回転部品。
「ジョグシャトル」でGoogle検索するとパチンコの攻略動画がたくさんでてくる。


内側の無限回転するジョグダイヤルと、外側の左右に30〜45度くらいまで回転するシャトルで構成される。

shuttleというのはシャトルランという英語のように「左右に素早く動く」という意味がある。
シャトル部分は左右にどれだけ回転させたかを細かく取得する事ができるアナログ入力装置で、今でもUSB接続のジョグシャトルがプロ用のビデオ編集機器として売られている。



ハードオフなどでビデオデッキのリモコンを買ってきて分解すると、こういうジョグシャトル部品が得られる。
ピンが8本出ているが、そのうち3本はnode.jsとarduinoでロータリーエンコーダーで書いたロータリーエンコーダで、のこり5本のがジョグシャトルのシャトル部分になっている。


この5本のピンが現在のシャトルの回転状態を2進数で表していると思いきや、グレイコードで表している。

グレイコード


グレイコードとは (グレイコードとは) [単語記事] – ニコニコ大百科
グレイコード – Wikipedia


かならず1桁ずつピン状態が変わるコードで、まあたしかにCPU等を持たない入力センサー側に搭載しやすそうだし変なチャタリングでなそう。

wikipediaより4bitのグレイコード

0000
0001
0011
0010
0110
0111
0101
0100
1100
1101
1111
1110
1010
1011
1001
1000



回路

ロータリーエンコーダと同じく、1つのピンがコモンなのでそこに5Vを入れる(基板のパターンを読めばどれかわかる)
残り4ピンを10KΩでGNDにプルダウンしつつ、Ardunoのデジタルピン(今回は5,6,7,8ピン)に入れた。


arduinoでジョグシャトルの制御

arduino-firmata npmを使った。


内側のジョグダイヤルは、ロータリーエンコーダのclassを使えばいい。2つ同時に使えるように実装してある。

外側のシャトルは、グレイコードから10進数の数値に変換するのは面倒そうだし、そもそもハードオフで買ってきたリモコンのシャトルだからどのピンが何番ピンかわからない。
よく考えたら16通りしか無いので、ちょっとずつ回転させながら全部確かめてJogShuttle.codes配列にメモした。

JogShuttle.coffee
events = require 'eventemitter2'
ArduinoFirmata = require 'arduino-firmata'

module.exports = class JogShuffle extends events.EventEmitter2
constructor: (@arduino, @pinA, @pinB, @pinC, @pinD) ->
console.log "set jogshuffle pin : #{@pinA}, #{@pinB}, #{@pinC} and #{@pinD}"

if @arduino.isOpen()
@initPins()

@arduino.on 'connect', =>
@initPins()

@arduino.on 'digitalChange', (e) =>
@pastState = @state
@state = @getState()
if @state isnt @pastState
@emit 'shuttle', @state ## shuttleイベントを発行

initPins: ->
@arduino.pinMode @pinA, ArduinoFirmata.INPUT
@arduino.pinMode @pinB, ArduinoFirmata.INPUT
@arduino.pinMode @pinC, ArduinoFirmata.INPUT
@arduino.pinMode @pinD, ArduinoFirmata.INPUT

getState: ->
@code = [@pinA, @pinB, @pinC, @pinD].map (i) =>
if @arduino.digitalRead i then 1 else 0
.join ''
return @codes.indexOf(@code) - @codes.length/2

codes: [
"1001"
"1101"
"1111"
"1011"
"0011"
"0111"
"0101"
"0001"
"0000" # neutral
"0100"
"0110"
"0010"
"1010"
"1110"
"1100"
"1000"
]


JogShuttle classを使う。
回転させるごとに”shuttle”イベントが発行され、初期状態が0、左に回すほどに-1,-2,-3…-7と減り、右に回すほど1,2,3…7と増えていく。
path = require 'path'
ArduinoFirmata = require 'arduino-firmata'
JogShuttle = require path.resolve 'JogShuttle'

jogshuttle = new JogShuttle arduino, 5, 6, 7, 8 ## digital pin 5,6,7,8 を使う

## 回転した時のイベントを受信
jogshuttle.on 'shuttle', (state) ->
console.log state

arduino-firmata npmのon “digitalChange”イベントを使うと、ジョグシャトルみたいな謎の入力インタフェースもかんたんにnodeらしくイベント駆動で使えて便利。

0

node.jsとarduinoでロータリーエンコーダー

ロータリーエンコーダー

2相出力のふつうの秋月で200円で売ってるやつを使った


マウスのホイールなどの中にも入っているセンサーで、上のツマミの部分を回すと両端の2つのピンからon-on・on-off・off-on・off-offの4状態を送ってくる。
中央のピンに5V入れて、両端の出力ピンは10kΩの抵抗でGNDにプルダウンしてArduinoのデジタルIOピンに入れた。

以前にも使ったことある。


プログラム

nodeとarduino-firmata npmでarduinoを制御した


こういうRotaryEncoderクラスを作った。回転させると”rotate”イベントを発行してくれる。

RotaryEncoder.coffee
events = require 'eventemitter2'
ArduinoFirmata = require 'arduino-firmata'

module.exports = class RotaryEncoder extends events.EventEmitter2
constructor: (@arduino, @pinA, @pinB) ->
console.log "set rotary encoder pin : #{@pinA} and #{@pinB}"
@state = 0

if @arduino.isOpen()
@initPins()

@arduino.on 'connect', =>
@initPins()

@arduino.on 'digitalChange', (e) =>
@pastState = @state
@state = @getState()
if (@state+3+1)%3 is @pastState
@emit 'rotate', 'right'
else if (@state+3-1)%3 is @pastState
@emit 'rotate', 'left'

initPins: ->
@arduino.pinMode @pinA, ArduinoFirmata.INPUT
@arduino.pinMode @pinB, ArduinoFirmata.INPUT

getState: ->
if @arduino.digitalRead @pinA
if @arduino.digitalRead @pinB
return 2
else
return 0
else
if @arduino.digitalRead @pinB
return 1
else
return 3


使う
path = require 'path'
ArduinoFirmata = require 'arduino-firmata'
RotaryEncoder = require path.resolve 'RotaryEncoder'

arduino = new ArduinoFirmata().connect()
rotenc = new RotaryEncoder arduino, 4, 3 ## digital pin 4,3を使う

rotenc.on 'rotate', (direction) ->
console.log "#{direction}に回った"

arduino-firmata npmはデジタルIOピンの状態が変化した時に発行してくれる、on “digitalChange”イベントがあるのでこういうハードウェアも簡単にイベント駆動で扱える。

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);
});

});