Raspberry Pi and Arduino Connected Using I2C

Raspberry-PI-I2c-Arduino

With Raspberry Pi and I2C communication, we can connect the Pi with single or multiple Arduino boards. The Raspberry Pi has only 8 GPIO’s, so it would be really useful to have additional Inputs and outputs by combining the Raspberry Pi and Arduino.

There are many ways of Linking them such as using USB cable and Serial Connection. Why do we choose to use I2C? One reason could be it does not use your serial, USB on the Pi. Given the fact that there are only 2 USB ports, this is definitely a big advantage. Secondly, flexibility. You can easily connect up to 128 slaves with the Pi. Also we can just link them directly without a Logic Level Converter.

In this article I will describe how to configure the devices and setup Raspberry Pi as master and Arduino as slave for I2C communication. Article1 and Article2 if you don’t know what is I2C.

In the next article I will be doing some Voice Recognition, if you are interested see here Raspberry Pi Voice Recognition Works Like Siri

How Does It Work? Is It Safe?

The Raspberry Pi is running at 3.3 Volts while the Arduino is running at 5 Volts. There are tutorials suggest using a level converter for the I2C communication. This is NOT needed if the Raspberry Pi is running as “master” and the Arduino is running as “slave”.

The reason it works is because the Arduino does not have any pull-ups resistors installed, but the P1 header on the Raspberry Pi has 1k8 ohms resistors to the 3.3 volts power rail. Data is transmitted by pulling the lines to 0v, for a “high” logic signal. For “low” logic signal, it’s pulled up to the supply rail voltage level. Because there is no pull-up resistors in the Arduino and because 3.3 volts is within the “low” logic level range for the Arduino everything works as it should.

Raspberry-PI-I2c-Arduino-connected

Remember though that if other I2C devices are added to the bus they must have their pull-up resistors removed. For more information, see here.

These are the images showing where the I2C pins are on the Raspberry Pi and Arduino.

Note that the built-in pull-up resistors are only available on the Pi’s I2C pins (Pins 3 (SDA) and 5 (SCL), i.e. the GPIO0 and GPIO1 on a Rev. 1 board, GPIO2 and GPIOP3 on a Rev. 2 board:

I2C Raspberry Pi and Arduino Connect Link

On the Arduino Uno, the I2C pins are pins A4 (SDA) and A5 (SCL), On the Arduino Mega, they are 20 (SDA), 21 (SCL)

I2C Raspberry Pi and Arduino Connect Link

For information about the Arduino I2C Configuration and for other models of Arduino, check out this documentation Wire library.

Setup Environment on Raspberry Pi for I2C Communication

I will describe the process briefly here, if you are in doubt please refer to a more detailed process here and here.

Remove I2C from Blacklist:

$ cat /etc/modprobe.d/raspi-blacklist.conf
# blacklist spi and i2c by default (many users don't need them)
blacklist spi-bcm2708
#blacklist i2c-bcm2708

Load i2c.dev in Module File

Add this to the end of /etc/modules

i2c-dev

Install I2C Tools

$ sudo apt-get install i2c-tools

Allow Pi User to Access I2C Devices

$ sudo adduser pi i2c

Now reboot the RPI. After that you should see the i2c devices:

pi@raspberrypi ~ $ ll /dev/i2c*
crw-rw---T 1 root i2c 89, 0 May 25 11:56 /dev/i2c-0
crw-rw---T 1 root i2c 89, 1 May 25 11:56 /dev/i2c-1

Now we run a simple test, scan the i2c bus:

pi@raspberrypi ~ $ i2cdetect -y 1 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

Hint: if you’re using the first revision of the RPI board, use “-y 0″ as parameter. The I2C bus address changed between those two revisions.

Install Python-SMBus

This provides I2C support for Python, documentation can be found here. Alternatively, Quck2Wire is also available.

sudo apt-get install python-smbus

Configure Arduino As Slave Device For I2C

Load this sketch on the Arduino. We basically define an address for the slave (in this case, 4) and callback functions for sending data, and receiving data. When we receive a digit, we acknowledge by sending it back. If the digit happens to be ’1′, we switch on the LED.

This program has only been tested with Arduino IDE 1.0.


#include <Wire.h>

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

void setup() {
    pinMode(13, OUTPUT);
    Serial.begin(9600);         // start serial for output
    // initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);

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

    Serial.println("Ready!");
}

void loop() {
    delay(100);
}

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

    while(Wire.available()) {
        number = Wire.read();
        Serial.print("data received: ");
        Serial.println(number);

        if (number == 1){

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

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

Configure Raspberry Pi As Master Device

Since we have a listening Arduino slave, we now need a I2C master.

I have written this testing program in Python. This is what it does: the Raspberry Pi asks you to enter a digit and sends it to the Arduino, the Arduino acknowledges the received data by send the exact same number back.

In the video, I used a built-in programming tool called “IDLE” in Raspberry Pi for compiling.


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

For more read/write functions, check out this useful look up table for the functions.

Connect Your Arduino With Raspberry Pi

Finally, we need to connect the Raspberry Pi and Arduino on the I2C bus. Connection is easy:

Raspberry-PI-I2c-Arduino

RPI               Arduino (Uno/Duemillanove)
--------------------------------------------
GPIO 0 (SDA) <--> Pin 4 (SDA)
GPIO 1 (SCL) <--> Pin 5 (SCL)
Ground       <--> Ground

To make sure this is working, run i2cdetect -y 1 again in the terminal, you should get something like this. 04 is the address we defined in the Arduino sketch.

pi@raspberrypi ~ $ i2cdetect -y 1 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- 04 -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

That’s the end of this article, for results, please see video on top. From here, you can add sensors to the Arduino, to send data back to the Raspberry. Or have servos and motors on the Arduino that can be controlled from the Raspberry Pi. It’s just Fun.

Updates: 07/07/2013

Someone messaged me asking how to use logic level converter for i2c connection between Raspberry Pi an d Arduino. I happen to have a spare Logic Level converter, so I gave it a go.

This is how I connect them.

GPIO0 (SDA) -- | TX1  -- TX0 | -- A4 (SDA)
GPIO1 (SCL) -- | RX0  -- RX1 | -- A5 (SCL)
3.3V        -- | LV   -- HV  | -- 5V
GND         -- | GND  -- GND | -- GND

08745-04-L

But the result was a little weird. The data successfully sent to the Arduino, and the data was also received successfully from the Arduino on the Pi, but the data was wrong at the raspberry pi side.

  • When I sent number 1 to the Arduino, I got 0 back.
  • When I sent 2, I got 1 back.
  • sent 3 and got 1 back
  • sent 4 and got 2 back… etc…

I don’t know why this is happening, something to do with the converter? or maybe the connection is wrong? I don’t have time to figure this out. Since I can get it working without a logic level converter, i will leave it for now, if you do know why, please let me know.

If you find this article useful.
Donate any amount, to help us maintain this website.

67 thoughts on “Raspberry Pi and Arduino Connected Using I2C

  1. I followed this tutorial but I have a problem where no matter what integer I put in, I get the integer 9 back.
    Example:
    RPI: Hi Arduino, I sent you 1
    Arduino: Hey RPI, I received a digit 9
    or
    RPI: Hi Arduino, I sent you 7
    Arduino: Hey RPI, I received a digit 9

    • I have the same exact problem. No matter what integer I put in, I get integer 9 back. I’m on Rev. 1 of Rasperry Pi, and I’m using I2C Bus 0.

      • On the Arduino Serial terminal, this is what I see if I send a ’6′:
        Ready!
        data received: 0
        data received: 6
        data received: 1

        For some reason, 2 additional bytes are always sent… a ’0′ in front, and a ’1′ behind the byte that I select.

      • Figured it out… for some reason, the python code was not working properly.

        Had to replace the write_byte_data() with write_byte() and the read_byte_data() with a read_byte() for things to finally work. Not sure why… I’m new to i2c

      • you are absolutely right, the write_byte_data() has a ‘cmd’ parameter, it’s part of the data transferred, that’s why you are getting the extra byte zero.

        This is entirely my fault, I made a change in the codes, updated this post a couple of days after it was posted. I think I must have forgotten to update the Python code. I will do so now. The reason for the change is that I am still not confident with the write_byte_data() function (couldn’t find any good documentation about it), that’s why I used write_byte() as well at the end.

        Thank you very much for pointing that out!

  2. Thanks for the tutorial. I’m having a bit of trouble, though. My pi is recognizing my arduino when I type i2cdetect -y 1, but when I run the python script, nothing seems to be sent or returned. In order to get the py script to work properly, I had to tab over lines 25 – 33. I don’t know python, but I don’t see how that could have affected the I2C… the script ran and asked for a digit, but the Arduino doesn’t show anything. I’d appreciate any help. Thanks!

    • by “tab over” you mean create indentation? I don’t know Python very well either, but I do know that Python is very strict about line indentation. Because it doesn’t use the semi colon ; like C or C++, wrong indentation could make your code get executed completely differently.

  3. Hey, thanks for the great walkthrough! I got it working no problems with continuous servos and leds for a robot. I’m looking for another guide to help me make a web gui which can interface with this setup (and can also do video) any suggestions?

    • Hi, I think that’s a very interesting idea to create a web GUI to enhance this project! I might actually do some work and write a post on this (just a thought)

      If I was going to do it
      1. I will setup a web server on my laptop/PC, maybe using Apache.
      2. Build the web GUI in HTML/PHP/Javascript, you will be using this GUI on your client (laptop/PC)
      3. We can send data from the Pi to the web server using GET or POST in Python, which can be interpreted in PHP on the server.
      4. That data will be sent to the web GUI client from the web server.

      does that make sense? Any better idea?

      • That makes perfect sense. I’m just a bit of a novice when it comes to programming especially when it comes down to multiplatform interfacing. If you could create a guide I would greatly appreciate it. Using your modified code I was able to remotely drive a arduino-pi robot around my room over wifi from the inputs into python while my laptop was vnc’d into the raspberry pi. It works fine but a web based gui would be much slicker. Thanks in advance!

  4. Hello,
    thanks for this nice tutorial.

    I’m trying to make it work, with Rpi REV1 and Arduino 2009/Arduino UNO. Connections are ok (I think), because when I run i2detect -y 0 I see this (Ihave two MCP23017 also connected to i2c):

    $ i2cdetect -y 0
    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: — 04 — – — – — – — – — – –
    10: — – — – — – — – — – — – — – — –
    20: 20 21 — – — – — – — – — – — – — –
    30: — – — – — – — – — – — – — – — –
    40: — – — – — – — – — – — – — – — –
    50: — – — – — – — – — – — – — – — –
    60: — – — – — – — – — – — – — – — –

    but when I run the python script, I see:

    python i2c_arduino.py
    Enter 1 – 9: 1
    Traceback (most recent call last):
    File “i2c_arduino.py”, line 24, in
    writeNumber(var)
    File “i2c_arduino.py”, line 10, in writeNumber
    bus.write_byte(address, value)
    IOError: [Errno 5] Input/output error

    any suggestion? I missed something?
    Thank in advance for you help

    • sorry I don’t have a Rev1 Rpi, so I can’t replicate your problem. have you tried the commented out function:
      bus.write_byte_data(address, 0, value)

      Also, make sure the cabling is correct, because the pin arrangement has changed from Rev1 to Rev2.

      • Hello,
        thanks for your answer and sorry for my late reply

        It was my NEWBIE and distraction error… I have REV 1 Pi, so
        bus = smbus.SMBus(0)
        and not
        bus = smbus.SMBus(1)

        :-)

        now all seems to be working fine, thanks again

  5. Hello!
    I really appreciate your entry and the help it provides!

    One note, though – in Python, the Print command requires a value set by print(value).

    You leave out these parentheses, which will trigger syntax errors if left.

    Thanks again!

    • Actually, it looks like I was wrong. In this case, it can work without the parentheses, although I do think it is more “hardy” if you leave them in. This will allow the code to function even if there is a mistype somewhere along the way.

      That said, the code printed above DOES WORK (woo!) and the only reason you would need the parentheses is if you did something differently.

      • thank you for the comment, I am not familiar with Python, so I don’t really know if leaving the parentheses would cause any problem potentially, but it did work like you said. Anyway, what I was trying to achieve with “print” without any argument is to create a newline, so if any problem occur because of this, we can just add “” to it as argument i guess…

  6. I think the reason your Level Shifter doesn’t work is because it’s uni-directional. If that’s the Sparkfun Level Shifter, you need to use Transmit and Receive properly based on the circuitry involved. I2C has one uni-directional signal (Clock) and one *Bi*-directional signal (Data). There is some circuit theory behind this, but here is a product that will work for you: https://www.adafruit.com/products/757

    • that could be the reason, I didn’t really look at the specification when I was using it, so you might be right!
      Thanks for the pointing that out! :-)

    • For me, the line with resistor is uni-directional but the line with the transistor is bi-directional.
      When i’m using it, i let the two resistor-line alone and i use the two other (extreme Right, extreme left).
      Less hassle.

  7. Hello Oscar,

    Very good tuto. Tested and … working. But is it really safe AND reliable ? Reading on internet, some says yes some say no ! I’m quite lost.

    And what do you mean by “Remember though that if other I2C devices are added to the bus they must have their pull-up resistors removed”. I have a ds18b20+ sensor. You mean that I dont have to use the 4.7k resistor ?
    Do you have a sample schema for connecting those other i2c devices.

    @Dantheman2865 : you mean the spakfun module is not working for linking raspberry and arduino ?

    • hey~ thanks for the comment.
      It is very controversial whether we should connect both devices directly.

      Like I explained there are pullup resistors on the Pi, so in theory it should be okay (I have been using this configuration since I wrote this tutorial, I have not had any problem with it yet.)
      But if you are not confident and doesn’t want to take the risk, USE a level converter. They are so cheap (~$1) so there is no reason why you shouldn’t do more to protect your pi.

  8. Hi, sorry to disturb you, I have a very important question, I have a project in my house where I use an arduino board in each room to control everything that is on that room. I want to command this arduinos (8) from a tablet running android, how can i do it? Using wifi on each one? Using Bluetooth on each one? Using one as a master and connecting this one to the rest and to the tablet? Doing the same but using a raspberry pi? Can you help me? Just point me the direction you think it’s better. Thanks so much

    • You are limited to 2 choices by the Androoid Tablet, Bluetooth (BT) or Wifi.

      BT is quite easy to use on Arduino, but it’s very range limited. Not to mention you have 8! I am not sure if Bluetooth can support that many connections at the same time on the Arduino (i have not looked into this)

      Another option would be wifi. It’s not common to use wifi on Arduino (have not seen people implemented this) what are you controlling in each room?

      I personally would use Raspberry Pi to replace the Arduinos for what you described, because it supports wifi (which has longer range than BT), and all clients can connect to the network at the same time. If you are short of GPIO or need Analogue I/O, you can always extend it by adding new components.

  9. Hey Oscar, thank you for this great post.

    I finally got to trying it out myself and I had trouble getting it to work on my Raspberry Pi running Arch Linux. The package python-smbus does not seem to be available for Arch Linux.

    After some research, I decided to give Quick2Wire a try. After modifying the code, it worked like a charm. I have created a repository on github (https://github.com/kmaragos/raspi2cino) hoping it might help others. FYI, I have tested this with an Arduino Pro Mini, Nano and Uno.

    I have included the URL to your post. I hope it is ok with you. Let me know if there is any other specific attribution method I should use. Thanks again for an amazing tutorial!

  10. Try to use only the TX of the Logic Level converter from Sparkfun.
    TX are bi-directional.
    RX are uni-directional.
    GPIO0 (SDA) — | TX1 — TX0 | — A4 (SDA)
    — | RX0 — RX1 | –
    3.3V — | LV — HV | — 5V
    GND — | GND — GND | — GND
    GPIO1 (SCL) — | TX1 — TX0 | — A5 (SCL)

  11. Hi,

    I am doing a new project with the Raspberry PI, and we are doing a sensor board consisting of PIR, IR, temperature, humidity, air quality sensor, siren, andcamera.

    What do you think would be the best way to integrate these sensors to the Raspberry PI. Do we need to have an external processor board, or might we just as well connect all of these directly to the Raspberry PI?

    • is it like a weather station kind of thing? :-)
      I think you can get those sensors with I2C communications, and connect them directly to the raspberry pi!

      this sounds very interesting I might do a separate post for it!

      thanks for sharing the idea.

  12. Absolutely AWESOME article. I was able to get my Rpi rev2 talking to my arduino micro in no time and your sample code and instructions were spot on!
    One problem I encountered was a sequence issue resulting in Rpi not booting up.
    When physically wired between Rpi GPIO and Arduino you MUST have Ardiuno code running and it be available for communication. If not, my Rpi would lock up in boot. Took me a little while to figure out and was a bit alarming at first but quickly figured it out.

    Thank you for this AWESOME tutorial on i2c with Rpi and Arduino!

  13. I have a bit of a trouble here.

    I did all the instruction until i2cdetect -y 1 in the terminal
    and it shows the address 04.

    The problem is when I tried the program above no matter what I wrote the arduino would not response . I have no idea what is wrong. I tried update and upgrade the Rpi the result is still the same. Is it possible that the pi is broken?

    Thanks in advance for the answer

    • I managed to solve the problem, seems like the psition of variable print in the code needs to be parallel with number. Thanks a bunch for the code

  14. Hi!
    I would like to transfer multiply bytes at once. With the python method “bus.write_block_data()” I was able to do this in direction to Arduino.
    But I cannot transfer more bytes from the Arduino. I guess I should use the Wire.write(resultBuffer, resultLen); on the Arduino side, but “bus.read_block_data()” does not make the Arduino to enter the sendData() callback.
    Anyone has a hint on this?
    Thx,
    Balazs

  15. hi
    can we connect multiple Arduino Uno boards to Rapberry pi ? can you provide any link or explain procedure for connecting multiple Arduino uno boards to Raspberry pi ?
    thanks for your help in advance………………………………..

    • yes, like I said in the post you can link up to 128 devices. You just need to assign each on with a different address. sorry i can’t give you more explanation right now, hope that helps.

  16. Hello.
    Thanks for this great tutorial. Everething works fine.
    I have one question, maybe You can help me. For example, I’m going to send a byte array from arduino to RPi, my array looks like this:
    sysState[0]=00000001
    sysState[1]=11111111
    I send it this way:
    Wire.write(sysState,sizeof(sysState))
    And the question is, it is possible to read this two values in Python on RPi? At moment I receive only first value. Thank you again.

    • I actually tried to do that as well, but because I am not familiar with Python, and there isn’t much information about i2c communication with Python on the internet, I couldn’t find a way to do it. I would suggest reading it byte by byte if you are not getting the answer from anyway.

  17. Magnificent web site. Plenty of helpful info here.
    I am sending it to a few pals ans additionally sharing in delicious.
    And naturally, thanks on your sweat!

  18. […] The Raspberry Pi’s gpio pins are 3.3V and my ATmega runs at 5V so I used a logic level converter to connect both. I connected my Bajduino Mega 3A board to the Raspberry Pi. My big Bajduino has an I2C logic level converter on board so I didn’t need to connect any extra electronics. To test the Raspberry Pi I2C to Arduino connection I found some useful information on Oscar Liangs blog. […]

  19. Not sure if anyone said this already but that particular logic level converter is not suitable for I2C, that is why you are getting errors. Try this one adafruit.com/product/757 from adafruit or look for I2C converters that are I2C compatible

  20. Hi.

    I would like to share some important info regarding Wire library on Arduino. I spent many hours debugging communication on the Raspberry Pi when I randomly got “IOError: [Errno 5] Input/output error”.

    Don’t put heavy code in receiveData() function on you Arduino. This code is called from an interrupt and to execute code here will disrupt communication. I suggest you to put received data in your own buffer and close the function as soon as you can.

    Like this:

    unsigned char tmpBuffer[120];
    unsigned char currentPos = 0;

    void receiveData(int byteCount){
    unsigned int number = 0;
    currentPos = 0;
    while(Wire.available()) {
    number = Wire.read();
    tmpBuffer[currentPos] = number;
    currentPos++;
    }
    }

    Good luck!

    /Daniel

  21. Thanks!

    Perfect tutorial!

    Two reamarks :
    “ll /dev/i2c*” required the uncommented alias ll in file “.bashrc” (or using ls -la)

    If you use a Arduino Leonardo pins are :
    Pin 2 (SDA)
    Pin 3 (SCL)

  22. Hey Oscar. Great post!
    But 2 problems.

    1- Sending values larger than 255!

    2- Sending a continuous stream of data . My goal is to send MouseXY data in realtime.

    Since I am a novice arduino hacker, haven’t made much progress.

    • Hi eco_bach,

      depends on what the possible maximum number you would send, then you should work out how many bytes of data needs to be sent, in order to represent that max number. For example if you send 2 bytes of data, it represents a number between 0-65536.
      And now, modify the receiving side code, as you will expect to receive 2 bytes of data every time.

Leave a Reply

Your email address will not be published. Required fields are marked *


four − 3 =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>