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