RubyでArduinoを動かす方法について感想メモ

[Arduino Playground - Ruby](http://playground.arduino.cc/Interfacing/Ruby)に載ってた方法をそれぞれ試したのでメモ。

いずれもCRubyが動くコンピュータをArduinoに接続して動かす想定です。

https://github.com/takeyuweb/ruby-arduino-sample

- serialport gem
- arduino_firmata gem
- dino gem

今回、Windows 10に接続したArduinoで試してみたので、その備忘録です。

## serialport gem

1. シリアルポート入出力するスケッチを自分で作ってArduinoに書き込む
2. 一般的なシリアルポート入出力でRubyを書く

なお、Windowsの場合は事前にDevKitのインストールが必要です。

### 感想

#### よい

- 低レベルな実装なので、gemの機能的制限やバージョンなどを気にしなくて良い
- 制御がArduino側にあるので速そう

#### いまいち

- スケッチを書くの軟弱Rubyistには辛い
- シリアルポートとの通信を意識したコードを書くのが面倒

## arduino_firmata gem

Firmata（ふぁるまーた）というArduinoを制御するためのプロトコルをRubyに移植したもの、らしい。内部でserialport gemを使用している。

[橋本商会 - RubyからArduinoを操作できるようにFirmataプロトコルを移植した](http://shokai.org/blog/archives/6631)

1. FirmataをArduinoに書き込む（Arduino IDEのスケッチの例にあります）
2. 制御コードをRubyで書く

### 感想

#### よい

- Rubyで制御できるのでRubyist的には嬉しい
- シリアル通信などライブラリが面倒見てくれて楽
- 公式のIDEに入ってる安心感はある

#### いまいち

- 制御がPC側にあるので通信する分遅そう

## dino gem

基板上のLED、ボタン、サーボなどをオブジェクトとして捉え、メッセージパッシングで動作を書く、よりオブジェクト指向的なアプローチ。
内部でarduino_firmataを使用している。

1. `dino generate-sketch serial`コマンドで作成されるスケッチをArduinoに書き込む
2. 制御コードをRubyで書く

### 感想

#### よい

- Rubyで制御できるのでRubyist的には嬉しい
- シリアル通信などライブラリが面倒見てくれて楽そう
- オブジェクト指向たのしいそうです。

#### いまいち

- 制御がPC側にあるので通信する分遅そう
- 他とあまりに考え方が異なるのでちょっと変わったことをしようとすると辛そう
- Windowsで上手く動かなかった
- 機能的な不備はまだありそうなのに長らくmasterが更新されていないのが気になる

## サンプルコード

以下の2つをそれぞれのgemで書いてみた。

- Arduino上のスイッチの押し下げ回数をコンソールに表示する
- Arduino上のLEDをコンソール入力に従って付けたり消したりする

### serialport gem

https://github.com/takeyuweb/ruby-arduino-sample/tree/master/serialport_sample

#### Read

```rb
# Arudino受信サンプル
# Arduinoからのシリアルポート出力を受け取って表示するRubyスクリプトサンプル
#
# C:\Ruby22-x64\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) C:/Users/Yuichi/IdeaProjects/Arduino/serialport_sample/sample.rb
# Count : 1
# Count : 2
# Count : 3
# Count : 4
# Count : 5
# Count : 6
# Count : 7
# Count : 8
# Count : 9
# Count : 10

require 'serialport'

port = 'COM3'
baud_rate = 9600

STDOUT.sync = true

SerialPort.open(port, baud_rate) do |sp|
  sp.read_timeout = 10
  loop do
    begin
      puts  sp.readline
    rescue EOFError
      retry
    end
  end
end

```

```c
int SWITCH_SOCKET = 2;
int count = 0;

void setup() {
  pinMode(SWITCH_SOCKET, INPUT);
  Serial.begin(9600);
}

void loop() {
  if (digitalRead(SWITCH_SOCKET) == 1) {
    count = count + 1;
    Serial.print("Count : ");
    Serial.println(count);
    while(digitalRead(SWITCH_SOCKET) == 1) {
      delay(100);
    }
  }
}
```

#### Write

```rb 
# Arudino送信サンプル
# Arduinoへのシリアルポート入力でLEDを付けたり消したりするサンプル
#
# C:\Ruby22-x64\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) C:/Users/Yuichi/IdeaProjects/Arduino/serialport_sample/write_sample/write_sample.rb
# "ON" or "OFF"
# INPUT> ON
# Turn on
# INPUT> OFF
# Turn off
# INPUT> ON
# Turn on
# INPUT> OFF
# Turn off
# INPUT>

require 'serialport'

port = 'COM3'
baud_rate = 9600

$stdout.sync = true

SerialPort.open(port, baud_rate) do |sp|
  puts '"ON" or "OFF"'
  print 'INPUT> '
  while input = readline
    input.chomp!
    case input
      when 'ON'
        puts 'Turn on'
        sp.write '1'
      when 'OFF'
        puts 'Turn off'
        sp.write '0'
      else
        puts 'Invalid'
    end
    print 'INPUT> '
  end
end
```

```c 
int LED_SOCKET = 13;
char ch = '0';

void setup() {
  Serial.begin(9600);
  pinMode(LED_SOCKET, OUTPUT);
}

void loop() {
  if(Serial.available() > 0){
    ch = Serial.read();
    if (ch == '1') {
        digitalWrite(LED_SOCKET, HIGH);
    } else if (ch == '0') {
        digitalWrite(LED_SOCKET, LOW);
    }
  }
}
```

### arduino_firmata gem

https://github.com/takeyuweb/ruby-arduino-sample/tree/master/arduino_firmata_sample

#### Read

```rb
# Arudino受信サンプル
# スイッチの押し下げ回数を arduino_firmata gem でカウントする
#
# C:\Ruby22-x64\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) C:/Users/Yuichi/IdeaProjects/Arduino/arduino_firmata_sample/read_sample/read_sample.rb
# Count : 1
# Count : 2
# Count : 3
# Count : 4
# Count : 5

require 'arduino_firmata'

PORT = 'COM3'
SWITCH_SOCKET = 2

arduino = ArduinoFirmata.connect(PORT)
arduino.pin_mode(SWITCH_SOCKET, ArduinoFirmata::INPUT)

$stdout.sync = true
count = 0
loop do
  if arduino.digital_read(SWITCH_SOCKET)
    count = count.succ
    puts "Count : #{count}"
    while arduino.digital_read(SWITCH_SOCKET)
      sleep 0.1
    end
  end
end
```

#### Write

```rb
# Arudino送信サンプル
# PCでの入力に基づいてLEDを付けたり消したりする

require 'arduino_firmata'

PORT = 'COM3'
LED_SOCKET = 13

arduino = ArduinoFirmata.connect(PORT)
arduino.pin_mode(LED_SOCKET, ArduinoFirmata::OUTPUT)

loop do
  puts '"ON" or "OFF"'
  print 'INPUT> '
  while input = readline
    input.chomp!
    case input
      when 'ON'
        puts 'Turn on'
        arduino.digital_write(LED_SOCKET, true)
      when 'OFF'
        puts 'Turn off'
        arduino.digital_write(LED_SOCKET, false)
      else
        puts 'Invalid'
    end
    print 'INPUT> '
  end
end
```

### dino gem

手元の環境で動かなかった（シリアルポート接続のあたりで止まる）ので、サンプルを用意できませんでした。

## 僕の結論

`arduino_firmata gem`あたりが丁度よいバランスな気がします。

でもちゃんと書くなら、dinoのような設計がよさそうなので、そういう実装をしたいです。