Question 14.11: You want to exchange data with an Arduino using the I2C inte......

You want to exchange data with an Arduino using the I2C interfaces of both devices.

Step-by-Step
The 'Blue Check Mark' means that this solution was answered by an expert.
Learn more on how do we answer questions.

The I2C bus has the concept of master and slave devices. The master device controls the bus and a number of slave devices can be connected to one master, with each slave having its own address.

You will install a sketch on the Arduino that makes it an I2C slave device and use the SMBus Python library to write an I2C program for the Raspberry Pi. The SDA, SCL, and ground connections of both devices will need to be connected to each other, and the Arduino powered separately or from the 5V supply of the Raspberry Pi.

To make this recipe, you need:

• Arduino Uno (see “Modules” on page 381)
• Male-to-female jumper wires (see “Prototyping Equipment” on page 380)

Connect the breadboard, holding the components to the Arduino as shown in Figure 14-16.

The Arduino board used here is the latest Arduino Uno R3. If you have an older board that doesn’t have the two dedicated SCL and SDA pins, then you can connect the Raspberry Pi SDA and SCL pins to Arduino pins A4 and A5.

Install the following sketch onto the Arduino. You can find it named ArduinoI2C in the program downloads for this book at http://www.raspberrypicookbook.com.

#include <Wire.h>
int SLAVE_ADDRESS = 0x04;
int ledPin = 13;
int analogPin = A0;
boolean ledOn = false;
void setup()
{
pinMode(ledPin, OUTPUT);
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(processMessage);
Wire.onRequest(sendAnalogReading);
}
void loop()
{
}
void processMessage(int n)
{
char ch = Wire.read();
if (ch == ‘l’)
{
toggleLED();
}
}
void toggleLED()
{
ledOn = ! ledOn;
digitalWrite(ledPin, ledOn);
}
void sendAnalogReading()
{
int reading = analogRead(analogPin);
Wire.write(reading >> 2);
}

You need to follow Recipe 8.4 to set up Raspberry Pi for I2C communication.

Open an editor (nano or IDLE) and paste in the following code. As with all the program examples in this book, you can also download the program from the Code section of http://www.raspberrypicookbook.com, where it is called ardu_pi_i2c.py.

import smbus
import time
# for RPI revision 1, use “bus = smbus.SMBus(0)”
bus = smbus.SMBus(1)

# This must match in the Arduino Sketch
SLAVE_ADDRESS = 0x04
def request_reading():
reading = int(bus.read_byte(SLAVE_ADDRESS))
print(reading)
while True:
command = raw_input(“Enter command: l – toggle LED, r – read A0 “)
if command == ‘l’ :
bus.write_byte(SLAVE_ADDRESS, ord(‘l’))
elif command == ‘r’ :
request_reading()

The test program is very similar to the one used to demonstrate serial communication in Recipe 14.10. It’s designed to show communication in both directions. Entering the l command toggles the Arduino’s built-in LED on and off, and using the r command reports a reading from the Arduino’s A0 analog input. You can use a male-to-male jumper wire to connect A0 to 3.3V, 5V, or GND on the Arduino to get different readings.

$ sudo python ardu_pi_i2c.py
Enter command: l – toggle LED, r – read A0 l
Enter command: l – toggle LED, r – read A0 l
Enter command: l – toggle LED, r – read A0 r
184
Enter command: l – toggle LED, r – read A0

Discussion

The Arduino acts as the I2C slave in this arrangement and must therefore be given an address so that the master running on the Raspberry Pi can identify it if there is more than one slave device connected. In this case, the address is set to 0x04 and held in the SLAVE_ADDRESS variable.

The setup function in the Arduino then sets up two callback functions that will be used. The function processMessage will be invoked whenever the onReceive event occurs. This will be whenever a command is sent from the Raspberry Pi. The other callback function, sendAnalogReading, is associated with the onRequest event. This occurs when the Raspberry Pi requests data, and will read the analog value, divide it by four to make it fit into a single byte, and then send it back to the Raspberry Pi.

The Python counterpart to this sketch first creates a new instance of SMBus called bus. This takes a single argument of 1. This is the I2C port on the Raspberry Pi to be used. This will be 1 unless you are using the very first version of the Raspberry Pi (revision 1). These boards have a black audio connector rather than the light blue connector of the revision 2 boards. If you have one of these older boards, use 0 as the argument here. The I2C port available at the GPIO connector was swapped between these two revisions.

The program prompts the user for a command (l or r). If the command is l, it writes the character l to the Arduino, which in turn causes the onReceive handler (process Message) to be run. This in turn will call toggleLED.

If, on the other hand, the user enters the r command, then the request_reading function will be called. This will call read_byte in the SMBus library that will cause the onRequest event in the Arduino sketch to be invoked.

You may be wondering why it is OK to connect the 5V Arduino directly to the I2C connections of the Raspberry Pi, without the level converter module that you had to use for serial communications (see Recipe 14.5). The reason is that the I2C bus standard operates at whatever voltage the pull-up resistors connected to SDA and SCL use. In this case, the Arduino Uno has no pull-up resistors connected to the I2C lines. These are provided by the Raspberry Pi, which pulls them up to 3.3V.

Although the Arduino has no pull-up resistors on the I2C lines, this is not true of all I2C devices. So, if the I2C device operates at 5V, make sure that it has no pull-up resistors (but if it does, you can usually remove them relatively easily).

See Also

For the serial equivalent to this setup, see Recipe 14.10.

Table A-8. Modules
Raspberry Pi camera module Adafruit: 1367, MCM: 28-17733, CPC: SC13023
Arduino Uno SparkFun: DEV-11021, Adafruit: 50, CPC: A000066
Level converter, four-way SparkFun: BOB-11978, Adafruit: 757
Level converter eight-way Adafruit: 395
LiPo boost converter/charger SparkFun: PRT-11231
PowerSwitch tail Adafruit: 268
16-channel servo controller Adafruit: 815
Motor driver 1A dual SparkFun: ROB-09457
RaspiRobot board Sparkfun: KIT-11561, raspirobot.com
PiFace digital interface board MCM: 83-14472, CPC: SC12827
Humble Pi MCM: 83-14637, CPC: SC12871
Pi Plate Adafruit: 801
Gertboard MCM: 83-14460, CPC: SC12828
Breakout board with paddle terminals MCM: 83-14876, CPC: SC12885
PIR motion detector Adafruit: 189
Venus GPS module SparkFun: GPS-11058
Methane sensor SparkFun: SEN-09404
Gas sensor breakout board SparkFun: BOB-08891
ADXL335 triple-axis accelerometer Adafruit: 163
4×7-segment LED with I2C backpack Adafruit: 878
Bicolor LED square-pixel matrix with I2C backpack Adafruit: 902
PiLite interface board Ciseco, CPC: SC13018
aLaMode interface board Makershed: MKWY1, Seeedstudio: ARD10251P
Freetronics Arduino LCD shield www.freetronics.com
RTC module Adafruit: 264
16 x 2 HD44780 compatible LCD module SparkFun: LCD-00255, Adafruit: 181
Table A-3. Prototyping equipment
Description Suppliers
M-M jumper wires SparkFun: PRT-08431, Adafruit: 759
M-F jumper wires SparkFun: PRT-09140, Adafruit: 825
F-F jumper wires SparkFun: PRT-08430, Adafruit: 794
Half-sized breadboard SparkFun: PRT-09567 Adafruit: 64
Pi Cobbler Adafruit: 1105
14.16

Related Answered Questions

Question: 14.10

Verified Answer:

Use a level converter or pair of resistors to conn...
Question: 14.9

Verified Answer:

Use PyFirmata to send commands to an Arduino to ge...
Question: 14.8

Verified Answer:

Use PyFirmata to send commands to an Arduino to ge...
Question: 14.7

Verified Answer:

Use PyFirmata to read an analog input on the Ardui...
Question: 14.6

Verified Answer:

Use PyFirmata to read a digital input on the Ardui...
Question: 14.5

Verified Answer:

Use a level converter to connect the RXD pin of th...
Question: 14.14

Verified Answer:

Use an aLaMode interface board. You may find that ...
Question: 14.15

Verified Answer:

Follow this detailed tutorial to set up the Arduin...