0

arduino_firmata gemの起動が速くなった

Arduino Firmata on Ruby

必要ないsleep削って速くしたぜ!ってpull requestが来たんだけど、そのsleepは古いArduino(Duemillanove/Decimila等)をサポートするのに必要なんですって言ってrejectしようと思ったけどよく考えたら接続する前にUSBデバイスのファイル名でUNO以降/Duemillanove以前を判別できる事に気づいた。


このようにデバイス名でわかる。
Debian Linux(raspberry pi)の場合
/dev/ttyACM0 -> Leonard, Micro, UNO
/dev/ttyUSB0 -> Decimilla, Duemillanove


Mac OSXの場合
/dev/tty.usbmodem1234 -> Leonard, Micro, UNO
/dev/tty.usbserial-A1234 -> Decimilla, Duemillanove


新しいArduinoの場合、sleepを省略するようにした。
UNO以降のArduino + Ruby2.0の環境ではarduino_firmataの初期化プロセスが0.1秒ぐらいになった。Duemillanove以前のArduinoだったり、Ruby2.0以前の場合は3秒ぐらいかかる。


あと、at_exitを知らなかった。
at_exit使わずにtrapでSITHUPやSIGTERMを受信してたので、なんかシグナルを握りつぶしていたらしい。
Ruby exit, exit!, SystemExit and at_exit blunder | Big Fast Blog

このようにclass内でブロック渡ししておくと、プロセスが終了する時に呼び出される。

class MyClass
at_exit do
# threadを止めたり、シリアルポートを閉じる等の処理
end
end

0

1台のArduinoを複数のRubyプロセスで共有できるようになった

Arduinoを複数のRubyプロセスで共有したいの続き


Arduino Firmata on Ruby使うと複数のプログラムで同時に1台のArduino動かせるようになった。

v0.2.9をリリースした

gem install arduino_firmata


結局dRuby使う必要は無かった。
昔はserialport gemは1つのプロセスがportを専有してしまっていた気がするのだが、そうでもなくなってた。
Firmataの初期化プロセスを見なおしたら複数プロセスから使えるようになった。


サンプルの
arduino_firmata/samples at master · shokai/arduino_firmata · GitHub
– led_blink.rb
– on_analog_read.rb
– servo.rb
– digital_read.rb
を同時に動かせた。


1つのArduinoにあるセンサーを同時の複数のアプリから読み取ったりできるようになってコードがすっきりします。
1台で複数アプリ動かせて、アプリがそれぞれ別々にバージョン管理できるというのはものすごい利点です。よほど速度が重要な処理でない限りArduinoにコード直書きやめて、Ruby Firmata使いましょう。

0

Arduinoを複数のRubyプロセスで共有したい

Arduino Firmata gemにdRubyを用いたプロセス間共有機能を標準で入れようと考えている。
マイコンを複数プロセスで共有して同時に使えると、実にコードが綺麗になってRubyらしくていいと思う。

ここで試している
share an arduino with dRuby · Issue #25 · shokai/arduino_firmata


Arduinoを複数プロセスで共有したい

Lindaで色々やっていると、センサーで色々値を取ったり、モーターを動かしたり、色々やりたくなる。

Arduinoを複数のプロセスで共有し、プログラムの密結合を解きアプリケーションごとに別のコードとして分けたい。

ハードウェアをネットワークに接続して複数のプロセスを起動し、色々なサービスを同時に走らせる時に、1つの仮想シリアルポートは1プロセスからしか同時アクセスできない問題がある。(Arduinoはいまだに仮想シリアルポートなのだ)

解決方法は2つで、Arduinoを複数使う。しかしこれは格好悪いし金もかかるし電気も食うから良くない。
もしくは1つのプログラム内でThreadを立てて、1プロセスで複数のアプリケーションを動かす方法があるけどこれも格好悪い。プログラムをアプリケーション毎にバージョン管理できない。


dRuby

dRubyはRubyに標準で付いている分散オブジェクトライブラリであり、複数プロセス間でオブジェクトが共有できてメソッド呼び出しもできる。
marshalされたオブジェクトがTCP上で飛んでいくらしい。$stdoutなんかも別プロセスに渡せる。
先日のRubyHirobaでのtoRubyの人たちによるワークショップでちょっと触った瞬間にArduino共有したいと思った。


実験

0.5秒間隔で13番ピンのLEDを点滅させるプログラムと、
0.1秒ごとにADコンバータでセンサーを読んで11番ピンにPWM出力するプログラムを1台のArduinoで同時に走らせた。

server.rb
require 'rubygems'
require 'arduino_firmata'
require 'drb/drb'

arduino = ArduinoFirmata.connect
DRb.start_service "druby://localhost:5010", arduino

loop do
arduino.digital_write 13, true
sleep 0.5
arduino.digital_write 13, false
sleep 0.5
end


client.rb
require 'drb/drb'

DRb.start_service

arduino = DRbObject.new_with_uri "druby://localhost:5010"

loop do
ad = arduino.analog_read 0
puts ad
arduino.analog_write 11, ad/4
sleep 0.1
end

すんなりと両方同時に1台のArduinoの上で動いた。というのも当たり前で、firmataはArduinoから逐次送られてくるinput系の値はRuby VM上のArduinoFirmataインスタンスに貯めておくし、write系は即時にシリアル通信で送られてArduinoの出力ピンに反映される。通信は全二重で行われるので複数プロセスが1台のArduinoにfirmataで同時にアクセスしても何も問題はない。

無理なのはlambda渡しでイベント登録してコールバック受ける arduino.on(:analog_read, &block) や arduino.on(:sysex, &block) で、これはしょうがないかもしれない。
常に使う機能では無いし。


問題

開いているポートを探す

普通、RubyでTCPSocketなどを使おうとするとportが開いていなかったら例外が発生するのだが、dRubyでstart_serviceするとそのまま開けてしまう。
他のプロセスにもdRubyのメッセージが行ってしまう。

先にTCPSocket開いてそのインスタンスをdRubyで使うとかしたい。


だれがサーバーになるか

普通に考えて最初にArduinoを開いた人がサーバーになるだろうけど、その人が強制終了したら全員死んでしまいます。


オプションでもいい気がしてきた

arduino = ArduinoFirmata.connect "デバイス名", :share => true
とかでいいかも

0

加速度センサMMA7361をArduino Firmataで試した

一昨日発売の加速度センサを使ってみた。計測範囲を±6Gか±1.5Gか選択できる。450円。
3軸加速度センサモジュール MMA7361: センサ一般 秋月電子通商 電子部品 ネット通販

ピンだけ自分ではんだづけする必要がある。
秋月の通販ページにデータシートもある。


今まで日本でよく使っていたKXM52という加速度センサが1000円に値上げされて後継のKXR94が同じ850円で入ってきて、秋月から私達に「もうKXM52オワコンだから新しいのに移行してね」というシグナルが送られてきている。

MMA7361はKXR94のさらに半額で安いし、出力も安定してて良いと思う。


回路

マイコンは千石で買ったArduino Micro。
x,y,zをアナログ出力する3軸加速度センサなので、analog input 0,1,2に接続した。
MMA7361+ArduinoMicro


加速度センサ MMA7361の各ピンの役割



VDDピン
3Vを入れる

GNDピン
0Vに接続

gSピン
g-selectピン。計測範囲を±6Gか±1.5Gか選択できる。LOWかオープン状態で高感度な±1.5G、HIGH(3Vでいい)を入れるとレンジの広い±6Gになる。

0gピン
重力ゼロ、つまり自由落下している時にHIGHを出力するらしい。デフォルトでLOW。今回は試していない。

STピン
平らな場所に置いてHIGHにするとx=0, y=0, z=1Gとして自動キャリブレーションしてくれるらしい。今回は試していない。

SLピン
sleepピン。HIGHの時にスリープ解除なので、使うときは3Vを入れるか、Arduinoからdigital writeを入れてやる必要がある。LOWかオープン状態でsleepモードになる。
名前からしてHIGHにしたらsleepになると思って、30分ぐらい悩んだけどデータシート読み直したらHIGHでスリープ解除って書いてあった。そういう機能ならenableとかwakeupとかONとかdisable sleepって名前にしろよと思った。


加速度センサとArduinoの動作電圧

動作電圧は2.2~3.6Vまで。!!!5Vを入れてはならない!!! Arduinoには3V出力ピンがあるのでそれを加速度センサの電源電圧として使う。

Arduinoで3Vのセンサーを使う時は、3Vピンと隣のRFピンを接続しておかないと正しいアナログ入力値が計測ができない。
RFはAVRマイコンのAREFピン(アナログ変換器基準電圧ピン)で、これに入っている電圧を最大値1023として、6つのanalog inputピンの電圧が計測される。

この部分



プログラム

例によってArduino Firmata on Rubyを使った。

% gem install arduino_firmata

arduino_accel.rb
#usr/bin/env ruby
require 'rubygems'
require 'arduino_firmata'

arduino = ArduinoFirmata.connect

loop do
acc = {
:x => arduino.analog_read(0), # x,y,z軸
:y => arduino.analog_read(1),
:z => arduino.analog_read(2)
}
p acc
arduino.digital_write(12, acc[:z] > 512 ) # 表の時にLED点灯、裏返した時は消灯
sleep 0.1
end


実行。センサはgSピンをオープンにして±1.5Gの高感度モードにした。
% ruby arduino_accel.rb
{:x=>525, :y=>531, :z=>714}
{:x=>527, :y=>534, :z=>712}
{:x=>528, :y=>533, :z=>712}
{:x=>527, :y=>532, :z=>710}
{:x=>525, :y=>532, :z=>713}
{:x=>528, :y=>532, :z=>711}
{:x=>525, :y=>533, :z=>711}
{:x=>526, :y=>531, :z=>713}
{:x=>525, :y=>534, :z=>714}
z軸だけ多いのは重力加速度。約180(約0.58V)ぐらいGで増える。基盤を裏返したらz = 350ぐらいになった。

0

ピエゾ素子で振動センサを作る

回路

参考:http://arduino.cc/en/Tutorial/Knock

上と同じように、ピエゾ素子と抵抗4MΩを並列にして、GNDとAnalog input 0の間に接続した。
ピエゾはたしか千石で80円で買った奴。



振動を取る

道具箱の中に接着剤がついたゴム板があったので、サンドイッチしてみたらとても調子よくなった。
ゴムのどの位置を叩いてもピエゾ素子の真ん中に振動が伝わるようにする必要があるので、思い切り接着した。

これを床のカーペットに仕込んだら足音がバッチリ取れた。


最初に作った物。
ドラムセットにつけてみたけど、セロテープで付けたのでちゃんと伝わっていない。



プログラム

RubyのFirmataで書いた。
analog readして、最近1000回分を配列に保存しておいて最大値のみをterminalに表示する。(細かい数字がバラバラ出ると見づらいので)
#!/usr/bin/env ruby
require 'rubygems'
require 'arduino_firmata'

arduino = ArduinoFirmata.connect
puts arduino.version

buf = []
loop do
buf << arduino.analog_read(0)
puts buf.max
buf = [] if buf.size > 1000
end