serial LCD display circuit
LCD connection to Arduino

Raspberry Pi Arduino I2C Interface

by Lewis Loflin

YouTube video Raspberry Pi, Arduino, and Learning Linux

What follows is the code used in my YouTube video above and an explanation of its operation. This is a very basic demo that will be used as a basis for more complex programs.

The first program was written for the Raspberry Pi in python. Python is a high level, general purpose interpreted language. High level in the fact it's remote from the underlying hardware. Being an interpreted language means each line of code is read, interpreted, and implemented as it's running. Python is best used for numbers and strings.

It's for those reasons python is worthless for machine control. Yes it has some GPIO and an I2C interface, but those can be accessed only with additional libraries but the system is still slow being an interpreted language. While it can read some I2C or SPI sensors it would be far better to use a micro-controller such as Arduino to deal with hardware control, while the Raspberry Pi using Tkinter would create a general user interface.

These two programs, one for Raspberry Pi, the other for Arduino respectively, demonstrate how both devices exchange information back in forth in single bytes over I2C.

While the GPIO and I2C on Raspberry Pi are three volts, my Arduino Nano was operating at five volts. Raspberry Pi has it's own pull ups for I2C so don't use external resistors tied to five volts. The Arduino works just fine.

Note that the Arduino is operating in the SLAVE mode with Raspberry Pi as MASTER that limits Arduino's access to the I2C buss. I borrowed my serial LCD program and modified it for this demo. One could rewrite this to operate a non-serial LCD connection by re-writing the ssrWrite() routine and some changes in electrical connections.

For more on that see

# Code for Raspberry Pi
# This program writes a value of o-255 to Arduino via I2C.
# This is written to work on Python 2.7

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
var = 0
while True:
    print "RPI: Hi Arduino, I sent you ", var
    # sleep 0.5 second

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

    var = var + 1
    if var > 255:
        var = 0	
Arduino code.
This is for use with the Raspberry Pi through the
I2C. RPi sends a value from 0-255 which is displayed
on the LCD display. Arduino echoes back a response and
the number sent from RPi.

LCD section taken from


#define Bit_out 12 // Pin 1-2 SN74164
#define CLK 11 // Pin 9 SN74164
#define RS 7  // Pin 4 LCD
#define E 8  // Pin 6 LCD

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

// location LCD row 0 col 0 or line 1 LCD
#define Line1 0x80  
// location row 1 col 0 or line 2 LCD
#define Line2 0x80 + 0x40  

#include <Wire.h>

void setup() {
  pinMode(Bit_out, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(RS, OUTPUT);
  pinMode(E, OUTPUT);

  digitalWrite(CLK, LOW); 
  // LCD in command mode default
  digitalWrite(RS, LOW); 
  digitalWrite(E, HIGH);
  initLCD(); // se below
  // initialize i2c as slave

  // define callbacks for i2c communication
  typeln("Arduino Ready!", Line1);
  typeln("Number = ", Line2);

void loop() {
} // end loop

// location is place on LCD display. 
void typeInt(int k, int location)   {
  char array1[10];
  itoa(k, array1, 10); // int to string
  typeln(array1, location);

void typeChar(byte val)   {
  digitalWrite(RS, HIGH);
  digitalWrite(RS, LOW);

void writeCommand(byte val)   {

  ssrWrite(val); // send byte to 74164
  digitalWrite(RS, LOW); // make sure RS in Com mode

// Below we pass a pointer to array1[0].
void typeln(char *s, int location)   {
  writeCommand(location); // where to begin 
  while (*s)  typeChar(*(s++));
}  // end typeln

// inverts state of pin, delays, then reverts state back
void    pulseOut(byte x)   {
  byte z = digitalRead(x);
  z = !z; // reverse state
  digitalWrite(x, z);
  z = !z; // return to original state
  digitalWrite(x, z);
} // end pulsout()

void ssrWrite(byte val)  {  // shift data to 74164
  for (int j=1; j<=8; j++)  {   // shift out MSB first
    byte  temp = val & B10000000; // MSB out first
    if (temp == 0x80) digitalWrite(Bit_out, HIGH); 
    else digitalWrite(Bit_out, LOW); 
    val = val << 1; // shift one place left
  }  // next j
}  // end byteWrite

void initLCD(void)   {

  writeCommand(0x38); // setup for 2 lines
  writeCommand(0x0F); // blinking cursor
  writeCommand(0x01); // clear
  writeCommand(0x02); // home

void ClearDisplay(void)   {
  writeCommand(0x01); // clear
  writeCommand(0x02); // home

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

  while(Wire.available()) { 
    number =;
    typeInt(number, Line2 + 9);

}  // end while

// callback for sending data
void sendData(){