I2C communication with the Arduino Uno

It has been a while since I last wrote. Too busy at work and no time to sit down and tinker… I’ve done some reading in the past couple of weeks, because I want to extend my Raspberry Pi with some additional IO ports for my garden project, especially some analog ports would be welcome.

In my garden project I want to be able to use the Raspberry Pi for the following tasks:

– operate the pool pump
– detect wether the filter is overflowing
– measure the water temperature
– open and close the garage door
– monitor the status of the garage door (open / closed)
– make time lapse photo’s of the garden
– turn the garden lights on / off
– detect day / night
– and anything else I can possibly think off

My journey starts with adding ports to the Raspberry Pi. I figured, instead of adding a ATTiny 85 I decided to buy for €9,50 a Arduino Uno copy from China, which is much more versatile than the ATTiny 85. I figured that I can make the Raspberry Pi and Arduino Uno talk via I2C.

All I need would be some code for the Arduino Uno to accept commands from the Raspberry PI via I2C. I want to be able to set all pins individually to INPUT or OUTPUT or INPUT_PULLUP. Secondly, I must be able to set the pins HIGH or LOW. Also I want to be able to use PWM on the PWM capable digital pins (pin 3, 5, 6, 9, 10, 11) and finally, I must be able to read the values from the digital and analog pins.

So, with barely any programming skills, I wrote some code that does exactly this. The Arduino Uno Slave code looks like this:

// Arduino I2C Wire Slave version 0.1
// by Racer993 <https://raspberrypi4dummies.wordpress.com/&gt;

// Turns the Arduino to a I2C slave device (for the Raspberry Pi)
// using the Wire library. Configure pins, read and write to pins
// via a simple instruction set.

// Supported instructions
// pinMode = setPin(device, pinnumber, INPUT/OUTPUT/INPUT_PULLUP)
// digitalWrite = writePin(device,pinnumber,HIGH/LOW)
// analogWrite = analogWritePin(device,pinnumber,0-255)
// getStatus(device)

// A0 – analog read / digital write
// A1 – analog read / digital write
// A2 – analog read / digital write
// A3 – analog read / digital write
// A4 – IN USE as SDA
// A5 – IN USE as SCL

// 1 – digital read / write + RX
// 2 – digital read / write + TX + Interrupt
// 3 – digital read / write + PWM + Interrupt
// 4 – digital read / write
// 5 – digital read / write + PWM
// 6 – digital read / write + PWM
// 7 – digital read / write
// 8 – digital read / write
// 9 – digital read / write + PWM
// 10 – digital read / write + PWM + SPI – SS
// 11 – digital read / write + PWM + SPI – MOSI
// 12 – digital read / write + SPI – MISO
// 13 – digital read / write + LED + SPI – SCK

// HOW TO USE

// sending commands

// general: all commands must be 7 bytes long + 1 ending byte

// 1) to set the pinMode write a message with 7 characters on I2C bus to the arduino
// first character = S for set pinMode
// second & third character are pin ID 00 – 13 for digital pins & A0 – A3 for analog pins
// fourth character is to set the mode I for INPUT, O for OUTPUT, P for INPUT_PULLUP
// character 5,6,7 are not used, set to 000

// 2) to turn the pin on or off write a message with 7 characters on I2C bus to the arduino
// first character = W for digitalWrite
// second & third character are pin ID 00 – 13 for digital pins & A0 – A3 for analog pins
// fourth character is to turn off or on H for HIGH and L for LOW
// character 5,6,7 are not used, set to 000

// 3) to turn use PWM write a message with 7 characters on I2C bus to the arduino
// first character = A for analogWrite
// second & third character are pin ID 00 – 13 for digital pins & A0 – A3 for analog pins
// forth character is not used, set to X
// fifth – seventh character are used to write the PWM cycle (000-255)

// 4) to get a status with pin readings send Wire.requestFrom(device, #chars = 30)
// the arduino will send back 30 chars
// char 1-14 for each digital pin 1 = on 0 = off
// char 15-18 for reading of A0, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 19-22 for reading of A1, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 23-26 for reading of A2, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 27-30 for reading of A3, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading

// Created 11 July 2013

// This example code is in the public domain.

#include <Wire.h>

void setup()
{
int arduinoI2CAddress = 33; // set the slave address for the Arduino on the I2C buss

Wire.begin(arduinoI2CAddress); // join i2c bus with specified address
Wire.onRequest(requestEvent); // register wire.request interrupt event
Wire.onReceive(receiveEvent); // register wire.write interrupt event
Serial.begin(9600);
}

void loop()
{
delay(1000); // wait for an interrupt event
}

//——————————————————————————–
// function that executes whenever a status update is requested by master
// this function is registered as an event, see setup()

void requestEvent()
{
String pinStatus=””;
char sendStatus[31];

for(int digitalPin = 0; digitalPin <= 13; digitalPin++)
{
pinStatus += String (digitalRead(digitalPin));
}

for(int analogPin = 0; analogPin <= 3; analogPin++)
{
pinStatus += String (1000+analogRead(analogPin));
}
pinStatus.toCharArray(sendStatus, 31);
Wire.write(sendStatus);
}

//——————————————————————————–
// function that executes whenever a message is received from master
// this function is registered as an event, see setup()

void receiveEvent(int howMany)
{
int receiveByte = 0; // set index to 0
char command[7]; // expect 7 char + 1 end byte
String mode = “”; // initialize mode variable for holding the mode
String pin = “”; // initialize pin variable for holding the pin number as a String
String awValue = “”; // intitalize the variable for holding the analogWrite value
int pinVal; // inititalize the variable for holding the pin number as integer
int awValueVal; // initialize the variable for holding the analog write value as integer (only PWM pins!)

while(Wire.available()) // loop through all incoming bytes
{
command[receiveByte] = Wire.read(); // receive byte as a character
receiveByte++; // increase index by 1
}

pin = String(command[1]) + String(command[2]); // combine byte 2 and 3 in order to get the pin number
awValue = String(command[4]) + String(command[5]) + String(command[6]); // combine byte 5, 6 and 7 in order to get the analogWrite value
awValueVal = awValue.toInt();

if (String(command[1]) != “A” ) { pinVal = pin.toInt();}

// incase of analog pin assignment determine analog pin to be set
if (String(command[1]) == “A” && String(command[2]) == “0”) { pinVal = A0;}
if (String(command[1]) == “A” && String(command[2]) == “1”) { pinVal = A1;}
if (String(command[1]) == “A” && String(command[2]) == “2”) { pinVal = A2;}
if (String(command[1]) == “A” && String(command[2]) == “3”) { pinVal = A3;}

// if requested set pinmode
if (String(command[0]) == “S” && String(command[3]) == “I”) { pinMode(pinVal, INPUT);}
if (String(command[0]) == “S” && String(command[3]) == “O”) { pinMode(pinVal, OUTPUT);}
if (String(command[0]) == “S” && String(command[3]) == “P”) { pinMode(pinVal, INPUT_PULLUP);}

// if requested perform digital write
if (String(command[0]) == “W” && String(command[3]) == “H”) { digitalWrite(pinVal, HIGH);}
if (String(command[0]) == “W” && String(command[3]) == “L”) { digitalWrite(pinVal, LOW);}

// if requested perform analog write
if (String(command[0]) == “A” && pinVal == 3 || pinVal == 5 || pinVal == 6 || pinVal == 9 || pinVal == 10 || pinVal == 11 ) { analogWrite(pinVal, awValueVal);}

Serial.println(“command recieved: ” + String(command));
Serial.println(“pwm value: ” + String(awValueVal));

}

At this moment I don’t have the Raspberry Pi code yet, but I wrote some code for an Arduino to act as master and send commands to the Arduino I2C slave.

// Arduino I2C Wire master version 0.1
// by Racer993 <https://raspberrypi4dummies.wordpress.com/&gt;

// Turns the Arduino to a I2C master device using the Wire library.
// send commands to the I2C Arduino slave for configuring pins,
// read and write to pins via a simple instruction set.

// Supported instructions
// pinMode = setPin(device, pinnumber, INPUT/OUTPUT/INPUT_PULLUP)
// digitalWrite = writePin(device,pinnumber,HIGH/LOW)
// analogWrite = analogWritePin(device,pinnumber,0-255)
// getStatus(device) = digital/analog read of the all the digital/analog pins

// A0 – analog read / digital write
// A1 – analog read / digital write
// A2 – analog read / digital write
// A3 – analog read / digital write
// A4 – IN USE as SDA
// A5 – IN USE as SCL

// 1 – digital read / write + RX
// 2 – digital read / write + TX + Interrupt
// 3 – digital read / write + PWM + Interrupt
// 4 – digital read / write
// 5 – digital read / write + PWM
// 6 – digital read / write + PWM
// 7 – digital read / write
// 8 – digital read / write
// 9 – digital read / write + PWM
// 10 – digital read / write + PWM + SPI – SS
// 11 – digital read / write + PWM + SPI – MOSI
// 12 – digital read / write + SPI – MISO
// 13 – digital read / write + LED + SPI – SCK

// HOW TO USE

// sending commands

// general: all commands must be 7 bytes long + 1 ending byte

// 1) to set the pinMode write a message with 7 characters on I2C bus to the arduino
// first character = S for set pinMode
// second & third character are pin ID 00 – 13 for digital pins & A0 – A3 for analog pins
// fourth character is to set the mode I for INPUT, O for OUTPUT, P for INPUT_PULLUP
// character 5,6,7 are not used, set to 000

// 2) to turn the pin on or off write a message with 7 characters on I2C bus to the arduino
// first character = W for digitalWrite
// second & third character are pin ID 00 – 13 for digital pins & A0 – A3 for analog pins
// fourth character is to turn off or on H for HIGH and L for LOW
// character 5,6,7 are not used, set to 000

// 3) to turn use PWM write a message with 7 characters on I2C bus to the arduino
// first character = A for analogWrite
// second & third character are pin ID 00 – 13 for digital pins & A0 – A3 for analog pins
// forth character is not used, set to X
// fifth – seventh character are used to write the PWM cycle (000-255)

// 4) to get a status with pin readings send Wire.requestFrom(device, #chars = 30)
// the arduino will send back 30 chars
// char 1-14 for each digital pin 1 = on 0 = off
// char 15-18 for reading of A0, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 19-22 for reading of A1, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 23-26 for reading of A2, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 27-30 for reading of A3, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading

// Created 11 July 2013

// This example code is in the public domain.

#include <Wire.h>

void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
Serial.println(“Starting communication…”);
}

String message = “”; // initialize the varaible to hold the message
byte x = 0; // initialize the variable to hold the byte count

void loop() // example code to show how the I2C communication works with the I2C slave
{

Serial.println(“Starting loop…”); // send info on serial port
delay(2500); // wait 2,5 seconds

// example of a digital write to an analog pin
Serial.println(“Turn pin A3 on…”); // communicate actions on serial port
setPin(33,”A3″,”Output”); // set analog Pin 1 on devicde 33 to OUTPUT
writePin(33,”A3″,”High”); // set analog Pin 1 on device 33 to HIGH / turn on
Serial.println(“Request Status…”); // communicate status on serial port
Serial.println(getStatus(33)); // get the status of the pins to verify action result

delay(250); // wait 0,25 seconds
Serial.println(“Turn pin A3 off…”); // communicate actions on serial port
writePin(33,”A3″,”Low”); // set analog Pin 1 on device 33 to LOW / turn off
Serial.println(“Request Status…”); // communicate status on serial port
Serial.println(getStatus(33)); // get the status of the pins to verify action result

// example of a digital write to a digital pin
delay(250); // wait 2,5 seconds
Serial.println(“Turn pin 13 on…”); // communicate actions on serial port
setPin(33,”13″,”Output”); // set digital Pin 13 on devicde 33 to OUTPUT
writePin(33,”13″,”High”); // set digital Pin 13 on device 33 to HIGH / turn on
Serial.println(“Request Status…”); // communicate status on serial port
Serial.println(getStatus(33)); // get the status of the pins to verify action result

delay(250); // wait 0,25 seconds
Serial.println(“Turn pin 13 off…”); // communicate actions on serial port
writePin(33,”13″,”Low”); // set digital Pin 13 on device 33 to LOW / turn off
Serial.println(“Request Status…”); // communicate status on serial port
Serial.println(getStatus(33)); // get the status of the pins to verify action result

// example of a analog write to a PWM pin (digital pin 3, 5, 6, 9, 10 or 11)
delay(250); // wait 2,5 seconds
Serial.println(“Set PWM on pin 3…”); // communicate actions on serial port
setPin(33,”3″,”Output”); // set analog Pin 1 on devicde 33 to OUTPUT
analogWritePin(33, “3”, “255”); // set analog Pin 1 on device 33 to HIGH / turn on
Serial.println(“Request Status…”); // communicate status on serial port
Serial.println(getStatus(33)); // get the status of the pins to verify action result

delay(250); // wait 0,25 seconds
Serial.println(“Turn pin 3 off…”); // communicate actions on serial port
writePin(33,”3″,”Low”); // set analog Pin 1 on device 33 to LOW / turn off
Serial.println(“Request Status…”); // communicate status on serial port
Serial.println(getStatus(33)); // get the status of the pins to verify action result

}

//——————————————————————————–
String getStatus(int device)
// get status of the arduino pins
{
String getStatus = “”;
Wire.requestFrom(device, 30); // request 30 bytes from slave device #33

while(Wire.available()) // slave may send less than requested

{
char c = Wire.read(); // receive a byte as character
getStatus += c;
}

return getStatus;
}

//——————————————————————————–
void setPin(int device, String pin, String type)
// prepare message to set the pin type (input/output) on the Arduino
{
Serial.println(“Setting pin ” + pin + ” to type ” + type + ” on device ” + device);
message = “S”+pinString(pin)+type[0]+”000”;
sendMessage(device, message);
}

//——————————————————————————–
void writePin(int device, String pin, String mode)
// prepare message to set the pin mode (high/low) on the Arduino
{
Serial.println(“Setting pin ” + pin + ” to mode ” + mode + ” on device ” + device);
message = “W”+pinString(pin)+mode[0]+”000”;
sendMessage(device, message);
}

//——————————————————————————–
void analogWritePin(int device, String pin, String pwmValue)
// prepare message to set the pin mode (high/low) on the Arduino
{
Serial.println(“Setting pin ” + pin + ” to PWM value ” + pwmValue + ” on device ” + device);
message = “A”+pinString(pin)+”X”+pwmString(pwmValue);
sendMessage(device, message);
}

//——————————————————————————–
void sendMessage(int device, String message)
// send the message to the Arduino, a 7 Byte message
// byte 1 is operation (set or write)
// bytes 2 and 3 are pin identifier
// byte 4 is parameter value (input / output or high / low)
// bytes 5,6 and 7 are the PWM value
{
message.toUpperCase(); // convert String to uppercase
char sendMessage[8]; // create char array to hold 4 characters + terminating 0
message.toCharArray(sendMessage, 8); // cast String to char array
Wire.beginTransmission(device);
Wire.write(sendMessage);
Wire.endTransmission();
// delay(500);
}

//——————————————————————————–
String pinString(String pin)
// resize pin descriptor to two bytes if necessary
{
while(pin.length() < 2)
{
pin = “0” + pin;
}
return pin;
}

//——————————————————————————–
String pwmString(String pwm)
// resize pin descriptor to two bytes if necessary
{
while(pwm.length() < 3)
{
pwm = “0” + pwm;
}
return pwm;
}

Connecting the Arduino Uno’s for I2C communication is quite simple:

Arduino Uno I2C communication

Arduino Uno I2C communication

The nice thing about the code is that it supports up to 127 Arduino’s to be controlled by a single master 🙂

In my next post I will try to have some code ready to show how the I2C communication between the 3.3v Raspberry Pi and the 5.0v Arduino Uno can be realized.

The Arduino Uno code can also be downloaded directly from my public dropbox.

Arduino Uno I2C connection

Arduino Uno I2C connection

Leave a comment