RaspberryPiとArduinoを連携する
RaspberryPiを使っているとセンサ系での物足りなさを感じてくることが良くあります。 特にアナログ値を使うようなセンサでは回路の知識がないと悲しくなります。 そこでそういった部分をカバーすることを考えるとArduinoとの連携を行いたくなってきます。 ArduinoであればGroveインターフェースで回路側の負担が少なくできることもメリットです。 あとオリジナルのUnoではなく激安品まで考えると1枚当たりのコストはセンサより安価にもできます。 (300円程度の商品もあるようです。絶対という保証はないですが。)
Arduino以外にmbedも使うことができると思うのですが、まずはArduinoってことで。
Arduinoとの接続は
- I2C接続
- シリアル通信
- Firmata
とありますが、今回はシリアル通信を行うことにします。 個人的にはシリアルが一番汎用的に使用できるロジックだと思いますが、I2Cもいい選択肢かなと思います。 コードのベースができてしまえば使いまわしができます。Firmataはどうなんでしょうか?(RaspberryPiでFirmata接続するArduinoの エントリがあんまりないなと思いますが一般的じゃない?node.jsでJohnny-Fiveのほうが一般的?)
USBポートにArduinoを接続してみる
【接続前】
$ lsusb Bus 001 Device 004: ID 0411:01ee BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070] Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
【接続後】
$ lsusb Bus 001 Device 005: ID 2341:0001 Arduino SA Uno (CDC ACM) Bus 001 Device 004: ID 0411:01ee BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM2 Wireless LAN Adapter [Ralink RT3070] Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 005: ID 2341:0001 Arduino SA Uno (CDC ACM)
が追加されています。Arduinoって書いてあるので発見は楽です。認識はされているので今度はシリアルデバイスとしてどのように認識されているか確認してみます。
【接続前】
$ ls /dev/ttyA*
/dev/ttyAMA0
【接続後】
$ ls /dev/ttyA*
/dev/ttyACM0 /dev/ttyAMA0
接続後にはシリアルデバイスとして/dev/ttyACM0
が追加されています。これを使うことになります。
Arduinoのシリアル接続制御のひな形
Arduinoとの接続がうまく行ったのでArduinoでシリアル送受信をする処理を書いてみました。stringのライブラリがうまく使えなかったので微妙なところはあるのですが…
#include <String.h> #define maxLength 64 String inString = String(maxLength); void setup() { Serial.begin(9600); pinMode(13, OUTPUT); digitalWrite(13, HIGH); inString = ""; } void loop() { if (Serial.available() > 0) { char inChar = Serial.read(); if (inString.length() < maxLength) { inString.concat(String(inChar)); if (inChar == '\n') { if (inString.startsWith("ON")) { Serial.println("<--HI-->"); digitalWrite(13, HIGH); } else if (inString.startsWith("OFF")) { Serial.println("<--LOW-->"); digitalWrite(13, LOW); } inString = ""; } Serial.println(inString); } else { Serial.println("Maximum number of characters."); } } }
このコードではON【CR】【LF】(またはON【LF】='\n')を受信すると13pin(オンボード上)のLEDをONとし、 OFF【CR】【LF】(またはOFF【LF】)を受信するとLEDをOFFするという動きになります。 (厳密にいうとONから始まり【LF】で終わるような場合にONし、OFFから始まり【LF】で終わるような場合はOFFにします。)
この受信文字("ON"など)の処理部分を拡張することで多様な処理ができるかなと思います。 ただ、センサの値によりトリガとして動作させる時には別途処理は考えないといけないかと。
pythonからシリアル制御を行う
pySerialを使用します。
http://pythonhosted.org//pyserial/
このブログでも過去に使用していますので以下を参考にします。
インタラクティブシェルで以下のようにするとLEDのON/OFFができました。
$ python Python 2.7.9 (default, Sep 17 2016, 20:26:04) [GCC 4.9.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import serial >>> import time >>> s = serial.Serial('/dev/ttyACM0',9600) >>> time.sleep(2) >>> s.write("ON\n") 3 >>> s.write("OFF\n") 4 >>> s.write("ON\n") 3 >>>
これでシリアル通信を介したRaspberryPiとArduinoの通信処理はうまく行ったようです。
#!/usr/bin/env python # -*- coding: utf-8 -*- import serial import time s = serial.Serial('/dev/ttyACM0', 9600) time.sleep(2) s.write("OFF\n") time.sleep(10) s.write("ON\n") s.close()
まとめ
とりあえず、RaspberryPiとArduinoをUSB接続してシリアルで制御できました。 これでそれなりにセンサデバイスとしてArduinoが使えそうです。 なんとなくですが、能動的にセンサーのデータを取得するのはArduinoで行い、RaspberryPiは受信に徹する方がいいのかなと思います。その場合は、RaspberryPi側での受信にはtimeoutを設定することでブロッキング時間を短縮したほうがいいかなと思います。例えば以下のような感じ。
s = serial.Serial('/dev/ttyACM0', baudrate=9600, timeout=3)
折角なので他の方法も試してみたいと思います。