If you’ve been doing Raspberry Pi or Arduino projects you’ve likely encountered the term I2C being tossed around, or at least seen the I2C pins on the devices and wondered what they do.  Put simply, I2C is a point to multi-point serial communications bus for both on-board and off-board devices.  I2C can operate at up to 400 kilo-bits per second over only two wires.  It works on a master/slave design with generally a single master and up to 1023 slave devices. Of course, Raspberry Pi to Arduino I2C communication is the logical thing to do with this technology!  Allowing your Raspberry Pi to control numerous Arduinos. Exactly what I am doing in my R2-D2 build.

Connecting a Raspberry Pi to an Arduino over I2C is Really Cool

There are many reasons to setup Raspberry Pi to Arduino I2C communication channels. For one, the Raspberry Pi has a limited amount of PWM pins (and to be frank, they’re not very clean in comparison to the Arduino PWM pins). Connecting an Arduino, or multiple Arduinos to your Raspberry Pi gives you a seemingly unlimited amount of GPIO connectivity for controlling a world of devices and systems.  In my R2-D2 build I have incorporated this to run all of the servos, LEDs, relays, etc. all over the robot’s body.

It also allows me to write autonomous code on the Arduino that the Raspberry Pi can simple send a command to enable or disable. For example, I can send a command over the I2C bus to tell the dome Arduino to go into auto mode.  This means the Arduino will randomly move the dome, open a panel, blink a light, etc.  It might respond to motion sensors if a kid walks up to R2 he will automatically look at the kid and whistle! This activity would continue to run even in the Raspberry Pi was turned off.  Another simple command over the I2C bus would of course return the droid to full manual mode.  It’s a perfect use case for I2C.

Wiring a Raspberry Pi to an Arduino for I2C Communication

Let’s start with the process of wiring the Raspberry Pi to the Arduino.  I’ll show two examples.  A direct I2C connection, and a shared bus with three Arduinos. One note, when connecting an RPi to an Arduino with the RPi being the master, you do not need pull up resistors. This is a common misconception and a lot of the online tutorials are wrong.  The Raspberry Pi 3 has built in pull up resistors on the I2C pins.  If you’ve tried this project in the past and it did not work, this could be the source of your frustration.  It is important that all boards share a common ground. If you’re not sure this is the case connect a breadboard wire from the RPi ground (GND) to the Arduino ground (GND).

Raspberry Pi to Arduino I2C Communication: Single Arduino

  • Raspberry Pi SDA –> Arduino SDA
  • Raspberry Pi SCL –> Arduino SCL
  • Raspberry Pi GND –> Arduino GND

Raspberry Pi to Arduino I2C Communication: Multiple Arduinos

To connect multiple Arduinos to a Raspberry Pi for I2C you’ll need to a a breadboard (or custom board) to connect all of the SDA, SCL, and GND pins together.

  • Raspberry Pi SDA –> Shared SDA on breadboard
  • Raspberry Pi SCL –> Shared SCL on breadboard
  • Raspberry Pi GND –> Shared GND on breadboard
  • All Arduino SDA –> Shared SDA on breadboard
  • All Arduino SCL –> Shared SCL on breadboard
  • All Arduino GND –> Shared GND on breadboard

I am not going to go into how I2C works electrically in this how-to. There are plenty of resources on the web that already cover that topic. However, I do want to define some terms for you to help you better understand what these pins are doing and why they are wired the way they are.

  • GND are the ground pins. It is important that all devices share a common ground for I2C to work properly.
  • SCL is the Serial Clock pin. The master creates a timing pulse on this line that keeps all devices in sync.
  • SDA is the Serial Data pin. This is the pin that reads and writes data on the bus.
  • I2C Master is the device that owns the SCL line, starts, and stops all communication on the bus.
  • I2C Slave is a device on the bus that listens and responds to communications from the master. These devices have individual addresses that are statically assigned via hardware or software.

Raspberry Pi to Arduino I2C Communication: Connecting Devices

From here you can connect devices to the Arduinos. This could be relays, servos, LEDs, motion sensors, or just about any other device you can think of. On the Arduino side they are controlled exactly the same as they would be without I2C being involved.  For our little demo we will just control the internal on-board LED on pin 13.  But feel free to change the pin number and connect your own LED or other device as you follow along. (If you’re new to Arduinos there is a surface mount LED connected to pin 13 on the board.)

The Raspberry Pi Python Code for I2C

On your Raspberry Pi, use the following set of code to initiate I2C communications as a slave.  We’re going to ask for input of device number, and then on or off.  Based on your entry the Raspberry Pi will tell a specific Arduino to turn on or off its LED on pin 13.

# Raspberry Pi to Arduino I2C Communication 
# Python Code 

import smbus 

# Slave Addresses for Arduinos 
ARDUINO_1_ADDRESS = 0x04 # I2C Address of Arduino 1 
ARDUINO_2_ADDRESS = 0x05 # I2C Address of Arduino 2 
ARDUINO_3_ADDRESS = 0x06 # I2C Address of Arduino 3 

# Create the I2C bus 
I2Cbus = smbus.SMBus(1) 

aSelect = input("Which Arduino (1-3): ") 
bSelect = input("On or Off (on/off): ") 

if aSelect == 1: 
  SlaveAddress = ARDUINO_1_ADDRESS 
elif aSelect == 2: 
  SlaveAddress = ARDUINO_2_ADDRESS 
elif aSelect == 3: 
  SlaveAddress = ARDUINO_3_ADDRESS 
else: 
  # quit if you messed up 
  quit() 

# also quit if you messed up 
if bSelect != "on" or bSelect != "off": quit() 

BytesToSend = ConvertStringsToBytes(bSelect) 
I2Cbus.write_i2c_block_data(SlaveAddress, 0x00, BytesToSend) 
print("Sent " + SlaveAddress + " the " + bSelect + " command.") 

# This function converts a string to an array of bytes. 
def ConvertStringToBytes(src): 
  converted = [] 
  for b in src: 
    converted.append(ord(b)) 

  return converted</pre>

The Arduino Code for I2C

Use the following code on each Arduino. Be sure to change the slave address for each device so they are unique.  If two devices share the same address things will not work.


//Arduino code to receive I2C communication from Raspberry Pi

#include &amp;lt;Wire.h&amp;gt;

// Define the slave address of this device.
#define SLAVE_ADDRESS 0x04
// #define SLAVE_ADDRESS 0x05
// #define SLAVE_ADDRESS 0x06

// string to store what the RPi sends
String str_recieved_from_RPi = "";

void setup() {

  // setup the LED
  pinMode(LED_BUILTIN, OUTPUT);

  // begin running as an I2C slave on the specified address
  Wire.begin(SLAVE_ADDRESS);

  // create event for receiving data
  Wire.onReceive(receiveData);
}

void loop() {
  // nothing needed here since we're doing event based code
}

void receiveData(int byteCount) {

  while ( Wire.available()) {
    str_recieved_from_RPi += (char)Wire.read();
  }

  // turn on or off the LED
  if (str_recieved_from_RPi == "on") {
    digitalWrite(LED_BUILTIN, HIGH); 
  }
  if (str_recieved_from_RPi == "off") {
    digitalWrite(LED_BUILTIN, LOW);
  }

  str_recieved_from_RPi = "";

}