🚚

PC←→Arduino間のデータ送受信

2022/12/29に公開

使用するライブラリ

https://github.com/hparra/ruby-serialport/

ポートの名前はどこでわかる?

  • ポートの名前は Arduino IDE から確認できる
  • 1414401 がなんのことなのかはわからない

Arduino → PC

いきなり送受信のコードを書き始めたら混乱してきたので最初は一方通行の確認だけする。

Arduino (送信)
void setup() {
  Serial.begin(9600);
}

int count = 0;

void loop() {
  Serial.println(count);
  count++;
  delay(1000);
}
PC (受信)
require "serialport"

SerialPort.open("/dev/cu.usbmodem1414401", 9600) do |sp|
  loop do
    puts sp.gets
  end
end

これで次のようにRuby側で1秒毎に連番が表示されれば次へ。

$ ruby main.rb
30
1
2
3

試してわかったこと。

  • ポートファイルは排他的?
    • Arduino IDE のシリアルモニタを起動していると Ruby 側が起動できない
      • Resource busy - /dev/cu.usbmodem1414401 (Errno::EBUSY)
  • Ruby 側を実行するたびに Arduino 側がリセットされている?
    • 毎回カウンタ 0 から始まるのは想定外だった

PC → Arduino → PC

今度は Arduino 側の受信を確認したい。ただ簡単に確認する方法がないため受信したデータをそのまま送り返すとする。

Arduino (送信)
void setup() {
  Serial.begin(9600);
}

int count = 0;

void loop() {
  while (true) {
    char ch = Serial.read();
    if (ch < 0) {
      break;
    }
    Serial.print(ch);
  }

  Serial.println(count);
  count++;
  delay(1000);
}

裏で受信するためスレッドを使って工夫する。

PC (受信)
require "serialport"

sp = SerialPort.new("/dev/cu.usbmodem1414401", 9600)
t = Thread.start do
  loop do
    p ["受信", sp.gets]
  end
end

loop do
  s = gets
  p ["送信", s]
  sp.write(s)
end

t.join
sp.close

これで次のようにカウンタを受信しつつ、入力した内容が返ってくれば成功。

$ ruby main.rb
["受信", "3\r\r\n"]
["受信", "0\r\n"]
["受信", "1\r\n"]
["受信", "2\r\n"]
["受信", "3\r\n"]
abc["受信", "4\r\n"]
["受信", "5\r\n"]

["送信", "abc\n"]
["受信", "abc\n"]
["受信", "6\r\n"]
["受信", "7\r\n"]
["受信", "8\r\n"]
["受信", "9\r\n"]

ポートファイルを直接読み書きできた

cat してみたら Ruby で書いたのと同じ挙動になった。

$ cat /dev/cu.usbmodem1414401
106
1
2
3

端末が壊れそうに思えたが普通に表示された。その上、書き込みもできた。

$ echo ok > /dev/cu.usbmodem1414401

echo の内容は cat 側に返ってくる。どこにも指定してないけど 9600 bps だったってどこでわかったんだろう?

さっきからカウンタの前に受信しているマジックナンバーは何?

何回も試したところ前のが残ってるっぽい。

  1. Ruby 側でカウンタ 3 を受信したときにプログラムを終了する
  2. Arduino 側も連動して止まる
  3. でも Arduino 側のカウンタは 5 ぐらいまで進んでいる
  4. Ruby 側のプログラムを実行する
  5. Ruby 側はまず 5 を受信してしまう

そんな感じだけど実際のところよくわかっていない (TODO)

参照

https://rubydoc.info/gems/serialport/SerialPort

Discussion