Raspberry pi とArduinoとのI2C通信

ここではRaspberry pi をマスター、ArduinoをスレーブとしたI2C通信について紹介します.

接続を簡単にするためにArduino側は3.3V動作にします。Arduino Unoは5V動作なのでI2Cレベルコンバーターを間に挟むなどの対応が必要です.Google検索で得た情報では、コンバーターなしで接続して良いとかどうとか、海外のブログで見つけましたが確認はしていません.

そこで3.3Vで動作しながらArduino IDEからもプログラミング可能なI2Cインターフェイス基板を使います.これはATmega328が使われていて、標準のシールドは使えないものの中身的にはArduinoと同様だともいえます。

接続は下記の通り
AVR board <-> Raspberry pi
27 SDA <-> 3
28 SCL <-> 5
GND <-> GND
Vcc <-> 3.3V

レベルコンバーター不要、電源もRaspberry pi 側からもらうので結線も楽チンですね。

さてハードウェアの接続の次はソフトの準備です。

Arduino側のコード。Arduino IDEは0.22、PCはMac OS X 10.9で確認。
WireライブラリーはArduino 1.01でメソッドが変更になっていますのでご自分の環境に合わせて適宜修正して使ってください。
wire.read wire.write がそれぞれ wire.receive wire.send に変更


#include 

#define SLAVE_ADDRESS 0x04
int number = 0;
int state = 0;

void setup() {
    pinMode(0, OUTPUT);
    pinMode(1, OUTPUT);
    digitalWrite(0, LOW); // set the LED off
    digitalWrite(1, LOW); // set the LED off
    // initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);

    // define callbacks for i2c communication
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);
}

void loop() {
    delay(100);
}

// callback for received data
void receiveData(int byteCount){

    while(Wire.available()) {
        number = Wire.receive();
        if (number == 1){

            if (state == 0){
                digitalWrite(1, HIGH); // set the LED on
                state = 1;
            }
            else{
                digitalWrite(1, LOW); // set the LED off
                state = 0;
            }
         }
     }
}

// callback for sending data
void sendData(){
    Wire.send(number);
}

I2Cから文字”1″を受け取ったらI2Cインターフェイス基板上のLEDをオンオフ切り替えを行うものです.受け取った文字列をそのままマスターに送り返す機能も持っています.
Raspberry piからsudo i2cdetect 1でArduinoが認識されているかどうか確認しましょう。
上のコードを使った場合、04にスレーブが検出できれば、上手く動作している事になります。

次にRaspberry Pi側のサンプルコードを紹介します.こちらはPythonで記述しています.



import smbus
import time
# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)

# This is the address we setup in the Arduino Program
address = 0x04

def writeNumber(value):
    bus.write_byte(address, value)
    # bus.write_byte_data(address, 0, value)
    return -1

def readNumber():
    number = bus.read_byte(address)
    # number = bus.read_byte_data(address, 1)
    return number

while True:
    var = input("Enter 1 - 9: ")
    if not var:
        continue

    writeNumber(var)
    print "RPI: Hi Arduino, I sent you ", var
    # sleep one second
    time.sleep(1)

    number = readNumber()
    print "Arduino: Hey RPI, I received a digit ", number
    print

スレーブ側に一文字送信して、スレーブ側から送り返してくる文字を端末に表示する簡単なスクリプトです.連続して文字を送りつけると、通信がこけることがあります.エラーでスクリプトが停止します.ここでは動作確認のためのスクリプトの紹介に止めますが、実際に運用する事を考えた場合にはI2C通信のところでtry,except文によるエラー処理を入れておくのは言うまでもありませんね。

I2Cインターフェイス基板には制御に便利なPython ライブラリーが付いています.LCDの表示制御、DIOのインアウト、アナログ入力、電子ブザー出力。Raspberry piを使って何かを制御して、それを簡単に表示する、小型機器の応用に便利です.
専用ライブラリーについて