User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

RPi to Arduino I2C Connections

Sat Jun 27, 2015 11:22 am

Hi;

Looking around the web it seems that you can connect the RPi to an Arduino without the need for a Logic Level Converter / Shifter (Sorry not sure of the correct term).

I have found several pages and using a mix of info now have a Sketch program that successfully compiles (many didn't) that turns the Arduino into a slave device.
Here:
https://raspberrypi4dummies.wordpress.c ... omment-452

The diagram in this uses 5v DC from the Pi to power the Arduino however I was using a separate power supply and opted for the connections on this page:

http://www.legomindstormsrobots.com/ard ... ry-pi-i2c/ (The Sketch code on this page would not compile hence using the code from the previous link.

Anyway before I connected the Pi to the Arduino (Nano ATMega328) I downloaded the Sketch program and with only the Motor Driver and Motors connected I powered on the Nano and checked the voltage between A4 and Gnd and A5 and Gnd, both returned 4.84v.

Does this mean that contrary to what I have read I will need a Logic Level Shifter to drop the Nano 5v to RPi 3.3v??

Many thanks
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

User avatar
B.Goode
Posts: 10356
Joined: Mon Sep 01, 2014 4:03 pm
Location: UK

Re: RPi to Arduino I2C Connections

Sat Jun 27, 2015 12:08 pm

JonnyAlpha wrote: Does this mean that contrary to what I have read I will need a Logic Level Shifter to drop the Nano 5v to RPi 3.3v??
The image with the feedback on the 'Dummies' item appears to have a Red 'blob' between the RPi and Arduino labelled with 'HV' and 'LV'.

I don't know what is is, but I'd wager that it is some sort of Logic Level Shifter! The writer says "You could use an I2C connection without level shifter between the 3.3v Raspberry Pi and 5v Arduino as signals are send by pulling the signal low. However, I prefer using a level shifter in order to avoid problems."

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino I2C Connections

Sat Jun 27, 2015 1:21 pm

Oh it's definitely a Logic Level Shifter but I have read in several places that when connecting over I2C a Logic Level Shifter is not required?
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino I2C Connections

Sat Jun 27, 2015 1:56 pm

OK well being the fool hardy reckless go getter that I am I connected everything up and powered on the Arduino, well no Blue Smoke so far :-)

Just need a sanity check here so if anyone could do me a huge favour and read the following to confirm I am on the right path.

Once everything was connected and powered up I ran i2cdetect -y 0 in terminal on the Pi and received back a table showing the address of the Arduino, but instead of showing where I thought it would be (at 33) its showed up at address 21? Is this correct?

When I looked at the two sets of code the Sketch code uses the following command:
int arduinoI2CAddress = 33;
And the Python code uses the following command:
device = 0x21

Are there supposed to be two separate addresses or should they be the same, or does this mean that the address of the Arduino I2C is 21 and the address of the Pi I2C is 33 - everything seems to be working though?

Next I fired up the I2C python program listed below to read and write data to / from the Arduino and it ran OK and seemed to be running OK.
When sending a command to make pin 13 High it would return the message 'Status Pin 13 =1' which I guess is correct (1 being high) however when sending a command to make Pin 13 Low instead of returning a message saying 'Status Pin 13 =0' it returns the following message:
30 byte status: 1PPPPPPPPPPPP01515152215281530

Looking at the python program this is working as expected but as these commands are repeated the Status of Pin 13, when set high remains the same, but the 30 byte message returned when Pin 13 is set low changes, is this OK?
30 byte status: 1PPPPPPPPPPPP01481148214901508

Here are the Sketch and Python programs that are currently running:

Code: Select all

// Arduino I2C Wire Slave version 0.21
// by Racer993 <http://raspberrypi4dummies.wordpress.com/>

// 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 17 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
}

  char sendStatus[31] = "000000000000000000000000000000";  // initialize the container variable
  int index = 0;                                           // initialize the index variable
  char pwm[15] = "00000000000000";                         // initialize the PWM flag container

void loop()
{
  
  String pinStatus="";                                      // initialize pinStatus variable
    
  for(int digitalPin = 0; digitalPin <= 13; digitalPin++)   // loop through 14 digital pins 0 - 13
     {
      if (pwm[digitalPin] == 0)                            // in case PWM is off for the pin, read the pin status
         {
           pinStatus += String (digitalRead(digitalPin));   // read the pin status & add it to the container variable
         }
      else
         { 
           pinStatus += "P";                                // in case PWM is on for the pin, add P to the pin status container string
         } 
     }
  
  for(int analogPin = 0; analogPin <= 3; analogPin++)      // loop through the 4 (unused) analog pins 0 - 3
     {
      pinStatus += String (1000+analogRead(analogPin));    // read the analog value from the pin, add 1000 to make it 4 digit & add it to the container variable
     }  
  
   pinStatus.toCharArray(sendStatus, 31);                  // convert the container variable pinStatus to a char array which can be send over i2c
    
  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() { 
   Wire.write(sendStatus[index]);
    ++index;
    if (index >= 30) {
         index = 0;
    }
 }

//--------------------------------------------------------------------------------
// 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();                                           // convert the awValue string to a value
  
  if (String(command[1]) != "A" ) { pinVal = pin.toInt();}                // in case of not an analog pin assignment convert into digital pin number
  if (String(command[1]) != "A" ) { pwm[pinVal] = 0;}                     // in case of not an analog pin assignment set PWM flag to 0

// 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);
       pwm[pinVal] = 1;
      }
  
}

Code: Select all

#// Arduino I2C Wire master version 0.2 
#// by Racer993 <http://raspberrypi4dummies.wordpress.com/>

#// Turns the Raspberry Pi into a I2C master device using I2C-tools. 
#// send commands to the I2C Arduino slave for configuring pins, 
#// read and write to pins via a simple instruction set.

#// the i2c-tools are required use "sudo apt-get install i2c-tools"
#// the I2C slave software must be installed on the Arduino

#// Supported instructions
#// pinMode             = setPin(device, pinnumber, INPUT/OUTPUT/INPUT_PULLUP)
#// digitalWrite        = writePin(device,pinnumber,HIGH/LOW)
#// analogWrite (=PWM)  = analogWritePin(device,pinnumber,0-255)
#// digital/analog read = getStatus(device) reads all the digital/analog pins
#// digital/analog read = pinValue gets the value for a single pin

#// 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
#// e.g. S13O000 Sets pin 13 to an OUTPUT

#// 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
#// e.g. W13H000 turns pin 13 on

#// 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)
#// e.g. A05X120 performs an analogWrite on digital pin 5 with a PWM cycle of 120

#// 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 P = PWM
#// 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

#// 5) to get the value for a single pin use pinValue(device,pin) to get the value back 1=HIGH, 0= LOW, P=PWM for digital pins and 0-1023 for analog pins

#// remark: the communication between the RPi and Arduino is very sensitive, especially for timings
#// as the RPi is much faster than the Arduino you need to included pauses between commands of atleast 
#// 1 sec to be save, I found that 0.5 seconds works as well (most of the time) but in that case you 
#// occasionally  need to perform a hard reset on the Arduino as it locks up.

#// Created 28 July 2013

#// This example code is in the public domain.

import smbus
import time

# RPi rev 1 = SMBus(0)
# RPi rev 2 = SMBus(1)
bus = smbus.SMBus(1)

# address of the Arduino use "i2cdetect -y 1" from the RPi prompt to detect the Arduinos (up to 127!)
device = 0x21

# initialize variables
pin      = ""  #holds the pin number 0 - 13 or A0 - A3
type     = ""  #holds the pin type: INPUT, OUTPUT, INPUT_PULLUP
mode     = ""  #holds the pinmode: HIGH, LOW, PWM
pwmValue = ""  #holds the pwmValue
pwm      = ""  #holds the pwmValue in 3 digits
val      = ""  #holds a String to be converted into ASCII
cmd      = ""  #holds the first byte of the message for the Arduino
message  = ""  #holds the second - seventh byte of the message for the Arduino
valCmd   = 88                #holds the command as ASCII value 88 = "X" 
valMessage  = [88,88,88,88,88,88] #holds the Message as ASCII values


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine sends a setPin command to the Arduino to make a pin INPUT, OUTPUT or INPUT_PULLUP
def setPin(device, pin, type):
        cmd = "S"
        message = pinString(pin)+type[0]+"000"
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine sends a writePin command to the Arduino to turn a pin HIGH or LOW
def writePin(device, pin, mode):
        cmd = "W"
        message = pinString(pin)+mode[0]+"000"
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send an analogWritePin command to the Arduino to set a PWM pin to a duty cycle between 0 and 255
def analogWritePin(device, pin, pwmValue):
        cmd = "A"
        message = pinString(pin)+"X"+pwmString(pwm)
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts Strings to ASCII code
def StringToBytes(val):
        retVal = []
        for c in val:
                retVal.append(ord(c))
        return retVal


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine actually transmits the command, 
# sleep is required in order to prevent a request overload on the Arduino
def sendMessage(device, cmd, message):
        cmd=cmd.upper()
        message = message.upper()
        valCmd = ord(cmd)
        valMessage  = StringToBytes(message)
        print("Message: " + cmd + message + " send to device " + str(device))        
        bus.write_i2c_block_data(device, valCmd, valMessage) 
        time.sleep(1)


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send a request to the Arduino to provide a 30 byte status update, return all 30 bytes
def getStatus(device):
        status = ""
        for i in range (0, 30):
            status += chr(bus.read_byte(device))
            time.sleep(0.05);
        time.sleep(0.1)        
        return status

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send a request to the Arduino to provide a 30 byte status update, return the value of a single pin
def pinValue(device,pin):
        status = ""
        for i in range (0, 30):
            status += chr(bus.read_byte(device))
            time.sleep(0.05);
        pinvalues = {'0':status[0],
                     '1':status[1],
                     '2':status[2],
                     '3':status[3],
                     '4':status[4],
                     '5':status[5],
                     '6':status[6],
                     '7':status[7],
                     '8':status[8],
                     '9':status[9],
                     '10':status[10],
                     '11':status[11],
                     '12':status[12],
                     '13':status[13],
                     'A0':int(status[14]+status[15]+status[16]+status[17])-1000,
                     'A1':int(status[18]+status[19]+status[20]+status[21])-1000,
                     'A2':int(status[22]+status[23]+status[24]+status[25])-1000,
                     'A3':int(status[26]+status[27]+status[28]+status[29])-1000}
        time.sleep(0.1)
        return  pinvalues[pin]


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts a 1 or 2 digit pin into a 2 digit equivalent
def pinString(pin):
        while len(pin) < 2:
              pin = "0"+pin;
        return pin


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts a 1, 2 or 3 digit pin into a 3 digit equivalent 
def pwmString(pwm):
        while len(pwm) < 3:
              pwm = "0"+pwm;
        return pwm


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this is where the main program starts

while True:

   setPin(33, "13", "Output")  #on Arduino with I2C ID #33 set pin 13 to OUTPUT
   writePin(33,"13", "High")   #on Arduino with I2C ID #33 set pin 13 HIGH
   print ("Status pin 13 =" + pinValue(33,'13'))
   
   writePin(33,"13","Low")     #on Arduino with I2C ID #33 set pin 13 LOW
   print("30 byte status:" + getStatus(33))
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

User avatar
B.Goode
Posts: 10356
Joined: Mon Sep 01, 2014 4:03 pm
Location: UK

Re: RPi to Arduino I2C Connections

Sat Jun 27, 2015 3:02 pm

JonnyAlpha wrote: Once everything was connected and powered up I ran i2cdetect -y 0 in terminal on the Pi and received back a table showing the address of the Arduino, but instead of showing where I thought it would be (at 33) its showed up at address 21? Is this correct?

When I looked at the two sets of code the Sketch code uses the following command:
int arduinoI2CAddress = 33;
And the Python code uses the following command:
device = 0x21

Are there supposed to be two separate addresses or should they be the same, or does this mean that the address of the Arduino I2C is 21 and the address of the Pi I2C is 33 - everything seems to be working though?
That little 'x' character in the Python code means it is represented in Hex (short for Hexadecimal, or Base16) notation.

So 0x21 is [ (2*16) + 1 ] = 32 + 1 = 33 in normal 'decimal' notation. Two ways of representing the same quantity.

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino I2C Connections

Sat Jun 27, 2015 3:20 pm

Ah Ha, many thanks.
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino I2C Connections

Sat Jun 27, 2015 4:08 pm

Trying to get one motor spinning but no joy so far, the Pi seems to be sending commands to the Nano and it would appear that the Nano is receiving, i.e. each command messages back saying:

Message: S080000 send to device 33 (different message for each pin)

But I keep getting an error when it finishes the read pin value part:

return pinvalues(pin) KeyError: '08'

Code: Select all

#!/usr/bin/env python

import cgi
import smbus
import time
import cgitb   # Comment out when debugged.
cgitb.enable() # Comment out when debugged.

print("Status: 204 No Content")
print("Content-type: text/plain")
print("")

# RPi rev 1 = SMBus(0)
# RPi rev 2 = SMBus(1)
bus = smbus.SMBus(1)

# address of the Arduino use "i2cdetect -y 1" from the RPi prompt to detect the Arduinos (up to 127!)
device = 0x21

# initialize variables
pin      = ""  #holds the pin number 0 - 13 or A0 - A3
type     = ""  #holds the pin type: INPUT, OUTPUT, INPUT_PULLUP
mode     = ""  #holds the pinmode: HIGH, LOW, PWM
pwmValue = ""  #holds the pwmValue
pwm      = ""  #holds the pwmValue in 3 digits
val      = ""  #holds a String to be converted into ASCII
cmd      = ""  #holds the first byte of the message for the Arduino
message  = ""  #holds the second - seventh byte of the message for the Arduino
valCmd   = 88                #holds the command as ASCII value 88 = "X" 
valMessage  = [88,88,88,88,88,88] #holds the Message as ASCII values

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine sends a setPin command to the Arduino to make a pin INPUT, OUTPUT or INPUT_PULLUP
def setPin(device, pin, type):
        cmd = "S"
        message = pinString(pin)+type[0]+"000"
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine sends a writePin command to the Arduino to turn a pin HIGH or LOW
def writePin(device, pin, mode):
        cmd = "W"
        message = pinString(pin)+mode[0]+"000"
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send an analogWritePin command to the Arduino to set a PWM pin to a duty cycle between 0 and 255
def analogWritePin(device, pin, pwmValue):
        cmd = "A"
        message = pinString(pin)+"X"+pwmString(pwm)
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts Strings to ASCII code
def StringToBytes(val):
        retVal = []
        for c in val:
                retVal.append(ord(c))
        return retVal


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine actually transmits the command, 
# sleep is required in order to prevent a request overload on the Arduino
def sendMessage(device, cmd, message):
        cmd=cmd.upper()
        message = message.upper()
        valCmd = ord(cmd)
        valMessage  = StringToBytes(message)
        print("Message: " + cmd + message + " send to device " + str(device))        
        bus.write_i2c_block_data(device, valCmd, valMessage) 
        time.sleep(1)


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send a request to the Arduino to provide a 30 byte status update, return all 30 bytes
def getStatus(device):
        status = ""
        for i in range (0, 30):
            status += chr(bus.read_byte(device))
            time.sleep(0.05);
        time.sleep(0.1)        
        return status

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send a request to the Arduino to provide a 30 byte status update, return the value of a single pin
def pinValue(device,pin):
        status = ""
        for i in range (0, 30):
            status += chr(bus.read_byte(device))
            time.sleep(0.05);
        pinvalues = {'0':status[0],
                     '1':status[1],
                     '2':status[2],
                     '3':status[3],
                     '4':status[4],
                     '5':status[5],
                     '6':status[6],
                     '7':status[7],
                     '8':status[8],
                     '9':status[9],
                     '10':status[10],
                     '11':status[11],
                     '12':status[12],
                     '13':status[13],
                     'A0':int(status[14]+status[15]+status[16]+status[17])-1000,
                     'A1':int(status[18]+status[19]+status[20]+status[21])-1000,
                     'A2':int(status[22]+status[23]+status[24]+status[25])-1000,
                     'A3':int(status[26]+status[27]+status[28]+status[29])-1000}
        time.sleep(0.1)
        return  pinvalues[pin]


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts a 1 or 2 digit pin into a 2 digit equivalent
def pinString(pin):
        while len(pin) < 2:
              pin = "0"+pin;
        return pin


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts a 1, 2 or 3 digit pin into a 3 digit equivalent 
def pwmString(pwm):
        while len(pwm) < 3:
              pwm = "0"+pwm;
        return pwm

        
# ------------------------------------------------------------------------------------------------------------------------------------------------
# this is where the main program starts

setPin(33, "08", "Output")  #on Arduino with I2C ID #33 set pin 08 to OUTPUT
setPin(33, "07", "Output")  #on Arduino with I2C ID #33 set pin 07 to OUTPUT
setPin(33, "11", "Output")
writePin(33,"08", "Low")   #on Arduino with I2C ID #33 set pin 08 HIGH
writePin(33,"07", "High")   #on Arduino with I2C ID #33 set pin 07 HIGH
writePin(33,"11", "High")   #on Arduino with I2C ID #33 set pin 07 HIGH
# analogWritePin(33, "11", 245): #on Arduino with I2C ID #33 set PWM on pin 11 to 245
print ("Status pin 08 =" + pinValue(33,'08'))
print ("Status pin 07 =" + pinValue(33,'07'))
print ("Status pin 11 =" + pinValue(33,'11'))


I tried it without the shebang statement at the top and also tried it by adding 3 after python?
I did open the program in IDLE Python 3 and save it on the Pi, would this cause a problem, should it be saved as a Python 2?

I am using an L298N Motor Driver with a motor connected as follows"
in1 - pin 8
in2 - pin 7
enA - pin 11

I wired the Motor Driver up following this guide (was working fine when driven direct by the nano):
http://tronixlabs.com/news/tutorial-l29 ... d-arduino/

When it was controlled direct from the Nano in Sketch the motor was powered using PWN by setting enA (pin 11) to 245.
The guide suggests that you can either use the Pin as PWM as above or set it High either should work.
When testing my code attached I have tried PWM (it's currently commented out), with the respective pin set as an OUTPUT, is this correct?
I have also tried simply setting pin 11 as an OUTPUT and setting it High, nothing seemed to work.

I'll double check my wiring?
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: RPi to Arduino I2C Connections

Sun Jun 28, 2015 10:36 am

After sleeping on this I decided to go back to basics and instead of jumping in and trying to turn a motor using the L298N Motor Driver Board attached to my Robot I decided to do a simple test and just light up an LED.

To test that the RPi and the Nano where communicating correctly and pins where being set High Low etc I tried to light up the Nano's onboard LED which is connected to pin13, I amended my code in IDLE 2 and just left in three commands in the main program at the bottom:

setPin(33, "13", "Output")
writePin(33, "13", "High")
print ("Status pin 13 =" + pinValue(33, '13'))

This worked fine so I know the problem with how to get send the correct commands to the L298N to operate the motors.
The changes above also did not return a KeyError but returned the status of pin 13 as =1 i.e. high.

Woop woop now to try a real LED using the pins that I am using for the L298N and then try to ID how to turn on a motor using the L298N via i2C?
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

Return to “Interfacing (DSI, CSI, I2C, etc.)”