Raspberry Pi 3とArduino間の通信方法まとめ
Raspberry Pi 3とArduino間の通信方法としては、SPI、I2C、UARTの3種類がある。
(1-Wireもあるけどここでは割愛)
3種類の接続方法をまとめておく。
SPI
spidevというライブラリを使う方法を紹介。
接続方法
Arduino | Raspberry Pi |
MISO | SPI_MISO(GPIO9) |
MOSI | SPI_MOSI(GPIO10) |
CS | SPI_CLK(GPIO11) |
SS | SPI_CE0_N(GPIO8) |
GND | GND |
事前準備
spidevをインストール
pipでインストール。
1 | $ pip install spidev |
コード例
Arduino側のスケッチ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <SPI.h> void setup (void) { pinMode(MISO, OUTPUT); Serial.begin(9600); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE1 ); SPCR |= _BV(SPE); pinMode(SS, INPUT); SPI.attachInterrupt(); } // SPI割り込み ISR (SPI_STC_vect) { byte cc = SPDR; //SPDRレジスタにデータが送られてくる cc++; SPDR = cc;//SPDRレジスタにあるデータが送信される Serial.println(cc); } void loop (void) { } |
Raspberry Pi側のPythonスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # -*- coding: utf-8 -*- import time import sys import spidev CE = 0 count = 0 spi = spidev.SpiDev() spi.open(0, CE) spi.mode = 0b01 spi.max_speed_hz = 9600 def getdata(channel): global count result = spi.xfer([count]) data = result[0] count += 1 return data if __name__ == '__main__': try: while True: print(getdata(0)) time.sleep(1) except KeyboardInterrupt: spi.close() sys.exit(0) |
I2C
smbusというライブラリを使う。
ただし、smbusのインストール方法は、システムのPythonを使うかpyenv上のpythonを使うかで変わってくる。
システムのPythonにただインストールするだけならapt-getで十分だが、その場合pyenv上のPythonからはsmbusが使えない。
かといってこのライブラリはpipでは見つからない。
そこでpyenvを使う場合はsmbus-cffiというライブラリを使う。
自分はpyenv上でPythonを動かしているので、pyenvを使っている前提で説明する。 (とはいえシステムのpythonだとしても、こっちの方法で動くとは思う)
接続方法
Arduino | Raspberry Pi |
SDA | SDA1(GPIO2) |
SCL | SCL1(GPIO3) |
GND | GND |
事前準備
sudoersにパスを追記
1 | $ sudo nano /etc/sudoers |
開いたら、次のように修正する。
Before
Default secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
After
Default secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/pi/.pyenv/shims:/home/pi/.pyenv/bin"
色々とインストール
1 2 3 | $ sudo apt-get install libi2c-dev libffi-dev $ pip install cffi $ pip install smbus-cffi |
modulesの修正
1 | $ sudo nano /etc/modules |
末尾に下記を追記。
i2c-bcm2708
i2c-dev
raspi-blacklist.confの修正
1 | $ sudo nano /etc/modprobe.d/raspi-blacklist.conf |
もしこのファイルに「blacklist i2c-bcm2708」をいう記述があったら、行頭に#をつけてコメントアウトする。
コード例
Arduino側のスケッチ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <Wire.h> int SLAVE_ADDRESS = 0x04; //I2Cのアドレスを0x04に設定 int ledPin = 13; int analogPin = A0; boolean ledOn = false; //LEDの点灯制御用 void setup(){ pinMode(ledPin, OUTPUT); Serial.begin(9600); //I2C接続を開始する Wire.begin(SLAVE_ADDRESS); //Raspberry Piから何かを受け取るたび、processMessage関数を呼び出す Wire.onReceive(processMessage); //Raspberry Piから要求された場合に、sendAnalogReading関数を呼び出す Wire.onRequest(sendAnalogReading); } void loop(){ digitalWrite(ledPin, ledOn); Serial.println(ledOn); delay(500); } void processMessage(int n){ char ch = Wire.read(); if (ch == '1'){ ledOn = !ledOn; } Wire.read(); } //LEDの点灯・消灯用の関数 void toggleLED(){ ledOn = !ledOn; //trueとfalseの入れ替え digitalWrite(ledPin, ledOn); } //Analogピンの数値を読み、Raspberry Piに送る void sendAnalogReading(){ int reading = analogRead(analogPin); Wire.write(reading >> 2); //ビットを2つ右にずらした数値を送る(4で割った数値) } |
Raspberry Pi側のPythonスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import smbus bus = smbus.SMBus(1) SLAVE_ADDRESS = 0x04 def request_reading(): reading = int(bus.read_byte(SLAVE_ADDRESS)) print(reading) while True: command = input("Enter command: 1-Toggle LED, r-read A0:") if command == '1': bus.write_byte(SLAVE_ADDRESS, ord('1')) elif command == 'r': request_reading() |
UART
PySerialというライブラリを使う方法を紹介。
接続方法
Arduino | Raspberry Pi |
TX | RXD0(GPIO15) |
RX | TXD0(GPIO14) |
GND | GND |
事前準備
cmdline.txtの編集
1 | $ sudo nano /boot/cmdline.txt |
開いたら、「console=ttyAMA0,115200」とか「kgdboc=ttyAMA0,115200」という文字列を消す。115200とか書いてある奴は消す。
ttyサービスの無効化
次のコマンドを実行。
RPi3の場合はttyS0に割り当てられてるとか海外のサイトにしか書いてなかったぞ・・・
もちっとみんなバージョンの差異に目を向けようよ・・・
困ってる人がいるんだよ・・・
1 2 | $ sudo systemctl stop serial-getty@ttyS0.service $ sudo systemctl disable serial-getty@ttyS0.service |
config.txtの編集
1 | $ sudo nano /boot/config.txt |
ファイルの一番最後に以下を追記する。
1 | enable_uart=1 |
再起動
再起動すると、今までの変更が有効になる。
1 | $ sudo reboot |
PySerialをインストール
pipでインストール。
1 | $ pip install pyserial |
コード例
Arduino側のスケッチ(簡易版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #define LED_PIN 13 byte val; void setup(){ // Serial Setting Serial.begin(9600); // Port Setting pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); } void loop(){ if(Serial.available() > 1){ val = Serial.read(); // Serial.println(val, BIN); if(val == 'B'){ byte val2 = Serial.read(); // Serial.println(val2, BIN); if(val2 == 'A'){ digitalWrite(LED_PIN, HIGH); } if(val2 == 'B'){ digitalWrite(LED_PIN, LOW); } byte b[2]={val+1, val2+1}; Serial.write(b,2); } } } |
Arduino側のスケッチ(serialEvent対応版)
Arduino側でUARTの割り込み受信用のメソッドを探しているのなら、serialEvent()という関数が使える。
しかし見つけた時はびっくりした。まさか存在しているとは…
日本語リファレンスは全部を網羅しているわけじゃなかったんだね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #define LED_PIN 13 byte val; void setup(){ // Serial Setting Serial.begin(9600); // Port Setting pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); } void serialEvent(){ val = Serial.read(); // Serial.println(val, BIN); if(val == 'B'){ byte val2 = Serial.read(); // Serial.println(val2, BIN); if(val2 == 'A'){ digitalWrite(LED_PIN, HIGH); } if(val2 == 'B'){ digitalWrite(LED_PIN, LOW); } byte b[2]={val+1, val2+1}; Serial.write(b,2); } } |
Raspberry Pi側のPythonスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # -*- coding: utf-8 -*- import serial import time import sys try: with serial.Serial('/dev/ttyS0',9600,timeout=1) as ser: while True: print('A') ser.write(bytearray(b'BA')) time.sleep(2) bytes_read = ser.read(2) print(bytes_read) print('B') ser.write(bytearray(b'BB')) time.sleep(2) bytes_read = ser.read(2) print(bytes_read) except KeyboardInterrupt: sys.exit(0) ser.close() |
この記事へのコメントはこちら