SlideShare a Scribd company logo
Connecting
Raspberry Pi to
Arduino using I2C
Raspberry Pi
This work is licensed under the
Creative Commons Attribution-
ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/b
y-sa/4.0/.
Revision 2
Companion Github
What is I2C?
The Inter-IC Bus (I-IC or I2C) standard defines the hardware and
electrical characteristics of the interface between nearby devices using
only two wires (and a third for ground reference).
SMBus was introduced by Intel as a tight subset of I2C, strictly defining
the interface between devices.
It is commonly used in televisions, computer components, and many
ADC and DAC and GPS devices.
The Raspberry Pi most commonly uses an SMBus implementation
in Python 2.x, and this is what we will concentrate on in this
document.
Connecting Devices
Identify SDA, SCL, and GND pins on both Raspberry Pi and the Arduino.
Connect SDA to SDA, SCL to SCL, and GND to GND between the two devices.
It is possible to connect the two devices directly to one another without a level
shifter, but it is not best practice. I2C levels for a low signal is 0.3Vcc and for a
high, 0.7Vcc. With the Raspberry Pi being a 3.3V device and the Arduino requiring
5V for Vcc, a logic level 'high' coming from the 3.3V Raspberry Pi is slightly below
the 0.7*5= 3.5V required for a definite logic 'high' in the Arduino.
Voltage Levels
The Raspberry Pi is a 3.3V device while the Arduino is a 5V device, but because
both devices are connected in open-drain configuration with relatively high pull-up
resistor values, it is unlikely damage will occur. The problem is that the
communication between the two devices is not guaranteed to work well due to the
3.5V minimum logic level 'high' required by the Arduino, and the Raspberry Pi input
is not 5V resistant.
A level shifter isolates the two different
voltages, while transmitting on/off information
in both directions.
Having said that, I have had reliable
communication between the Raspberry Pi and
the Arduino without any loss of data, but I am
worried about the Arduino accidently driving
one of the SDA or SCL pins to 5V as an output
if the program is not written carefully and
damaging the Raspberry Pi. User beware.
I2C Level Shifter
This rather complicated-looking circuit creates a bi-directional bus that can
automatically translate 3.3V bus signals into 5V signals and vice versa at full bus
speed. This is one of the simplest and cheapest level shifters and can easily be built
on a breadboard or soldered in a few minutes on any of the prototyping stripboards
available.
On the next slide, we look at
this circuit in detail
The Level Shifter
Isolating one of the two identical bus lines, we find an
enhancement-mode n-channel MOSFET the star of the
show. It's Gate is tied directly to the low-voltage Vcc. A
pull-up resistor connects the High voltage side to the
Drain, and another pulls the Source up to the Low voltage
side.
S1 and S2 simulate the Open-Drain connection that drives
the bus at the microcontroller.
With both switches open, Gate and Source are at the
same potential so the transistor is turned off. Both sides
are pulled up to their respective Vcc.
With S1 closed and S1 open, the source is pulled down to
0V, taking Vgs (Gate-Source Voltage) above the threshold
and turning the transistor on hard. This pulls the Drain side
down to 0V as well.
With S2 closed and S1 open, Drain is pulled to 0V and the
internal diode is forward-biased by the 3.3V, dropping the
source to around 0.6V. This is enough to pull Vgs again
beyond the threshold, opening the transistor and dragging
the bus down to 0V.
With both switches closed, both sides will be at 0V.
Setting Up Raspberry Pi
Getting the tools for Python2 and/or Python3...
1. Open a terminal window.
Enter “sudo apt-get install python-smbus” (2.x)
Or “sudo apt-get install python3-smbus” (3.x)
And “sudo apt-get install i2c-tools”
Reboot the Pi.
2. Open “Raspberry Pi Configuration” in the GUI or use
a terminal to enter 'sudo raspi-config'.
Enable I2C, save, and reboot.
3. Edit file 'etc/modules' using GUI text editor or
enter “sudo nano etc/modules”
If these two lines are not in there, type them;
i2c-bcm2708
I2c-dev
And save.
4. Edit file 'etc/modprobe.d/raspi-blacklist.comf' using
GUI editor or
Enter “sudo nano etc/modprobe.d/raspi-blacklist.conf”.
If these two lines exist, make sure a '#' is placed before
them, or delete them altogether..
#blacklist spi-bcm2708
#blacklist i2c-bcm2708
And save.
5. Finally, enter “sudo nano /boot/config.txt'. These two
lines should appear:
dtparam=i2c1=on
dtparam=i2c_arm=on
If not, make them so, and save.
Reboot.
Testing Raspberry Pi
Enter “i2cdetect -y 1” in a terminal window to
check that the I2C bus is available and running
on the Pi.
If everything is good, it will show a table of
addresses. If any I2C devices are available, their
addresses will be shown in hexadecimal.
It is worth entering “i2cdetect -F 1” to see which
SMBus calls are supported. It will save hours of
debugging to know that “SMBus Block Read” is
not supported, but “I2C Block Read” is
supported, and that “Block Process Call” is
meaningless as it is also not supported.
Preparing the Raspberry Pi
Start Python, and create a new file.
Import the SMBus library.
Create an SMBus object linked to I2C port 1.
Define the slaves address(es) to connect to.
Collect the data to be transmitted, or prepare the
data variables for reading back from the slave.
These instructions are for Python 2. Python 3 may have small differences.
import smbus
i2c_object = smbus.SMBus(1)
# Link to SMBus(0) on older Pi
addrSlave1 = 8
command = 0
dataList = [1,2,3,4,5]
i2c_object.write_i2c_block_data(addrSlave1,
command,
DataList)
getDataList = i2c_object.read_i2c_block_data(
addrSlave1,
command)
Preparing the Arduino
Start Arduino IDE
Include the Wire.h standard library for Arduino.
Set up the slave address.
Register a Wire function that will run when the
interrupt for receiving information from the Master
triggers.
The event is onReceive(function)
This event will react to information coming from
the master, and prepare for sending data back to
the master, if necessary.
Register a Wire function that will run when the
interrupt for returning data to the Master triggers.
This event is on onRequest(function)
When the interrupt fires, send data to the Master
that was requested.
#include <Wire.h>
const byte slaveAddr = 8;
void setup() {
Wire.begin(slaveAddr);
Wire.onReceive(i2cReceive);
Wire.onRequest(i2cTransmit);
}
void loop() {
}
void i2cReceive(int numBytes) {
byte rxByte;
while Wire.available() {
rxByte = Wire.read();
// Do something with the byte
}
}
Void i2cTransmit() {
Wire.write(...) // send requested data
}
Example Project
Using a Raspberry Pi 2 and an Arduino
Leonardo, we will connect up the I2C bus,
and connect an RGB LED to the Arduino. The
LED color and brightness will be controlled by
a Python program running on the Pi.
We will also simulate analog data to send
back to the Pi from the Arduino, a
temperature sensor and a light-level sensor.
The source code for both Arduino and
Raspberry Pi can be found on github at
mikeochtman.github.io/Pi_Arduino_I2C.
This drawing was created in Fritzing.
Interface Specification
Before we can communicate with the Arduino, we
need an interface specification which describes, in
detail, how and what data should be moved over
the bus from Master to Slave, and from Slave to
Master.
Type Description Access
Byte Command private
Byte Control private
Real Temperature Read Only
Real Light Level Read Only
Byte Brightness Red Read/Write
Byte Brightness Green Read/Write
Byte Brightness Blue Read/Write
Arduino Internal Data Table
Command Action
0x81 Read Temperature. Return int(round(temperature*100))
0x82 Read Light. Return int(round(light*100))
0x0A Write three bytes for brightness RGB
0x0B Write single byte, brightness Red channel
0x0C Write single byte, brightness Green channel
0x0D Write single byte, brightness Blue channel
0x90 Read three bytes brightness RGB
0x91 Read single byte, brightness Red channel
0x92 Read single byte, brightness Green channel
0x93 Read single byte, brightness Blue channel
Read,
no command
Return Slave Address, one Byte
Write,
No command
Master interrogating slave presence. Ignore
Other commands
Ignore. Master software has to deal with communication
exceptions, if any.
Arduino Software//The full software can be found on github at
// https://github.com/MikeOchtman/Pi_Arduino_I2C
//This is a simplified collection of
//the most important parts.
#include <Wire.h>
#define rxFault 0x80
#define txFault 0x40
#define slaveAddress 8
struct {
byte volatile command;
byte volatile control;
float volatile temperature;
float volatile light;
byte volatile brightR;
byte volatile brightG;
byte volatile brightB;
} commsTable;
byte volatile txTable[32];
// prepare data for sending over I2C
Void setup() {
Wire.begin(slaveAddress);
Wire.onReceive(i2cReceive);
Wire.onRequest(i2cTransmit);
}
Void loop() {
}
void i2cReceive(int byteCount) {
if (byteCount == 0) return;
byte command = Wire.read();
commsTable.command = command;
if (command < 0x80) {
i2cHandleRx(command, byteCount - 1);
} else {
i2cHandleTx(command);
}
}
byte i2cHandleRx(byte command, int numBytes) {
switch (command) {
case 0x0A:
if (Wire.available() == 3)
commsTable.brightR = Wire.read();
commsTable.brightG = Wire.read();
commsTable.brightB = Wire.read();
result = 3;
} else {
result = 0xFF;
}
break;
// and so on, one case for each command according to the
Interface Specification
default:
result = 0xFF;
}
if (result == 0xFF) commsTable.control |= rxFault;
return result;
}
Arduino Software, cont'd
byte i2cHandleTx(byte command) {
// If you are here, the I2C Master has requested
information
// If there is anything we need to do before the interrupt
// for the read takes place, this is where to do it.
return 0;
}
void i2cTransmit() {
// byte *txIndex = (byte*)&txTable[0];
byte numBytes = 0;
int t = 0;
switch (commsTable.command) {
case 0x00: // send slaveAddress.
txTable[0] = slaveAddress;
numBytes = 1;
break;
case 0x81: // send temperature
t = int(round(commsTable.temperature*100));
txTable[1] = (byte)(t >> 8);
txTable[0] = (byte)(t & 0xFF);
numBytes = 2;
break;
case 0x91: // send RGB Red channel
txTable[0] = commsTable.brightR;
numBytes = 1;
break;
// Again, create a case statement for each legal command
default:
commsTable.control |= txFault;
}
if (numBytes > 0) {
Wire.write((byte *)&txTable, numBytes);
}
}
Python Softwareimport smbus
import time
i2c = smbus.SMBus(1)
addr = 8 # address of the arduino I2C
##/* Interface Specification
## Data in a table thus:
## byte purpose
## 0: command
## 1: control
## 2-5: Current Temperature (read-only)
## 6-9: Current light level (read only)
## 10: Brightness for RED r/w
## 11: Brightness for GREEN r/w
## 12: Brightness for BLUE r/w
## Commands:
## Write with no command: Ignore
## Read with no command: Return slave address
## Command 0x81: read temperature;
## Integer returned, int(round(temp*100))
## Command 0x82: read light level;
## Integer returned, int(round(lux*100))
## Command 0x0A: Write three bytes to RGB
## Command 0x0B: Write single byte brightness red;
## Command 0x0C: Write single byte brightness green;
## Command 0x0D: Write single byte brightness blue;
## Command 0x90: read three bytes brightness RGB
## Command 0x91: read single byte brightness red;
## Command 0x92: read single byte brightness green;
## Command 0x93: read single byte brightness blue;
##
## All other values are ignored, no data returned.
RGB = [20,200,128]
temperature = 0
light_level = 0
i2c.write_quick(addr) # no data expected back
time.sleep(0.5)
print i2c.read_byte(addr) # expect the slave address
time.sleep(0.5)
print i2c.read_word_data(addr,0x81)/100.0 # temperature
time.sleep(0.5)
print i2c.read_word_data(addr,0x82)/100.0 # light level
time.sleep(0.5)
i2c.write_byte_data(addr, 0x0B, 12) # write Red value
time.sleep(0.5)
print i2c.read_byte_data(addr, 0x91) # write Green value
time.sleep(0.5)
i2c.write_byte_data(addr, 0x0C, 123) # write Blue value
time.sleep(0.5)
print i2c.read_byte_data(addr, 0x92) # get Green value
time.sleep(0.5)
i2c.write_byte_data(addr, 0x0D, 234) # write Blue value
time.sleep(0.5)
print i2c.read_byte_data(addr, 0x93) # get Blue value
time.sleep(0.5)
print i2c.read_i2c_block_data(addr, 0x90, 3) # get RGB
time.sleep(0.5)
i2c.write_i2c_block_data(addr, 0x0A, RGB) # set all RGB
time.sleep(0.5)
print i2c.read_i2c_block_data(addr, 0x90, 3) # get all RGB
time.sleep(0.5)
print i2c.read_i2c_block_data(addr, 0x10, 3) # force error
time.sleep(0.5)
The Python ResultsPython 2.7.9 results.
>>>
8
23.95
6.97
12
123
234
[12, 123, 234]
[20, 200, 128]
[0, 255, 255]
>>>
No event for write_quick()
Got back the slave Address.
Got the simulated temperature
Got the simulated light level
Set and read back Red channel
Set and read back Green channel
Set and read back Blue channel
Got back RGB values as a list.
Set new RGB values from a list
Read back the RGB values as a list
Forced a read error by sending wrong command.
RGB = [20,200,128]
i2c.write_quick(addr) # no data expected back
print i2c.read_byte(addr) # expect the slave address
print i2c.read_word_data(addr,0x81)/100.0 # temperature
print i2c.read_word_data(addr,0x82)/100.0 # light level
i2c.write_byte_data(addr, 0x0B, 12) # write Red value
print i2c.read_byte_data(addr, 0x91) # get Red value
i2c.write_byte_data(addr, 0x0C, 123) # write Green value
print i2c.read_byte_data(addr, 0x92) # get Green value
i2c.write_byte_data(addr, 0x0D, 234) # write Blue value
print i2c.read_byte_data(addr, 0x93) # get Blue value
print i2c.read_i2c_block_data(addr, 0x90, 3) # get RGB
i2c.write_i2c_block_data(addr, 0x0A, RGB) # set all RGB
print i2c.read_i2c_block_data(addr, 0x90, 3) # get all RGB
print i2c.read_i2c_block_data(addr, 0x10, 3) # force error
Instructions in Detail
Master (Rpi, Python) Slave (Arduino
Bus.write_quick(slave)
Used to check presence, or trigger event in slave without data
transfer.
Wire.onReceive() event triggered, but no data is sent.
Bytes received: 0. Bytes returned: 0.
Bus.write_byte(slave, data)
Can be used to instruct the slave to do any of up to 255 different
actions, or to place this byte in one predefined byte variable.
Wire.onReceive() event triggered. Single byte received.
Bytes received: 1. Bytes returned: 0.
ByteValue = Bus.read_byte(addr)
Read slave status or a single predefined value
Wire.onRequest() event triggered only. No data received.
Slave must return one defined byte.
Bytes received: 0. Bytes returned: 1.
Bus.write_byte_data(addr, command, byteValue)
Send slave some information to accompany a command.
Wire.onReceive() event triggered. Two bytes sent. First byte is a
command, second byte is the byte data to accompany it.
Bytes received: 2. Bytes returned: 0.
ByteValue = Bus.read_byte_data(addr, command)
Interrogate a slave about a specific piece of information
Wire.onReceive event triggered. One byte sent, a command for
which byte is requested from slave. Wire.onRequest() then
triggered, and one byte to be returned.
Bytes received: 1. Bytes returned: 1
Instructions in Detail
Master (Rpi, Python) Slave (Arduino
Bus.write_word_data(addr, command, integerVal)
Send slave extra information accompanying a specific command
Wire.onReceive() event triggered, three bytes send. First byte is
command, second is LSB, third is MSB of integer.
Bytes received: 3. Bytes returned: 0.
WordValue = Bus.read_word_data(addr, command)
Retrieve two bytes, first LSB, then MSB of an integer. Python knows
to rebuild the integer. Retrieve integer data from slave.
Wire.onReceive() event triggered. Single byte received, a
command. Wire.onRequest() event triggered, slave must send back
two bytes, first LSB, then MSB, of an integer.
Bytes received: 1. Bytes returned: 2.
Bus.write_block_data(addr, command, [list of bytes])
Sending unstructured data.
Wire.onReceive() event triggered. Several bytes sent. First byte is
command, second is number of following bytes, remainder of bytes
must be processes according to interface list.
Bytes received: > 2. Bytes returned: 0.
Bus.write_i2c_block_data(addr, command, [list of bytes])
Use this to fill a table of related bytes quickly.
Wire.onReceive() event triggered. Several bytes sent. First byte is a
command, remaining bytes to be processed according to interface.
Bytes received: > 1. Bytes returned: 0.
ByteList = Bus.read_i2c_block_data(addr, command)
Use this to read a table of related bytes quickly
Wire.onReceive event triggered. One byte sent, a command for
which byte is requested from slave. Wire.onRequest() then
triggered, requested bytes returned as per interface list.
Bytes received: 1. Bytes returned: > 1
Instructions in Detail
Master (Rpi, Python) Slave (Arduino
List = bus.read_block_data(addr, command, bytes)
will fail. Always returns the empty list. Do not use this function.
Wire.onReceive() event triggered, two bytes send. First byte is
command, second is number of bytes to return. OnRequest() event
is triggered, sending back the correct number of bytes
Bytes received: 2. Bytes returned: > 1.
Bus.block_process_call(addr, command, [vals])
Following the protocol, it reads the correct number of bytes sent
back, but random data appears in the list returned. Do not use.
Wire.onReceive() event triggered. Several bytes received, first
command, then count of bytes in [vals], then vals.
Wire.onRequest() triggered, return first count, then number of bytes
Bytes received: > 2. Bytes returned: > 2.
process_call(addr, command, val) does sent the integer 'val', but
does not trigger the interrupt to return data, so it returns the empty
list
Wire.onReceive() event triggered. Three bytes sent. First byte is
command, second is LSB and last is MSB. SMBus Specification is
to return two byte integer, but onRequest() event never triggered.
Bytes received: 3. Bytes returned: n/a.
Conclusions
Communication over I2C is quite simple to
implement using the SMBus library on the
Raspberry Pi, and the Wire.h library on the
Arduino..
Knowing what each SMBus Write and Read
instruction sends to the Arduino and what data
the Arduino is expected to send back is crucial
to successful implementation of the I2C/SMBus
protocol.
Before coding, have a clear Interface
Specification with a list of commands to be sent
to the Arduino, and clear instructions of what
should happen with the data sent and how to
return requested data.
It is also important to specify the reaction to
bad data.
The SMBus protocol document can be found at
http://smbus.org/specs/smbus20.pdf
The linux Python implementation document can be
found at
https://www.kernel.org/doc/Documentation/i2c/smb
us-protocol
Some of the SMBus protocol is not fully
programmed in the existing SMBus python
libraries, even though the functions are
available to include in the program.
These functions do not appear to work with the
arduino:
● read_block_data(addr, command, bytes)
● block_process_call(addr, command, [vals])
● process_call(addr, command, val)
All the other functions appear to operate
according to the specification.
This work is licensed under the
Creative Commons Attribution-
ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/b
y-sa/4.0/.
Compiled by Mike Ochtman.
Find me at
https://za.linkedin.com/in/mikeochtman

More Related Content

PPS
What is Arduino ?
PPTX
Introduction to Arduino
ODP
Sensor Technology ppt
PPT
Intro to Arduino
PDF
Report on arduino
PPTX
Introduction to the Arduino
PPTX
Watch dog
PDF
Serial Communication Interfaces
What is Arduino ?
Introduction to Arduino
Sensor Technology ppt
Intro to Arduino
Report on arduino
Introduction to the Arduino
Watch dog
Serial Communication Interfaces

What's hot (20)

PPTX
USB Universal Serial Bus
PPTX
Raspberry Pi (Introduction)
PPTX
Electronics ESP processors
PDF
Arduino- Serial communication
PPT
Haptic technology
PPT
arduino
PPTX
Ardui no
PPTX
Introduction to Node MCU
PDF
Arduino Lecture 1 - Introducing the Arduino
PPTX
Drowsy Driver detection system
PPTX
Wi-Fi Esp8266 nodemcu
PPT
Raspberry Pi Presentation
PPT
Raspberry pi
PDF
Embedded system introduction - Arduino Course
PPTX
Introduction to arduino
PPTX
IOT Unit-1 (Introduction to IOT) by Durgacharan
PPTX
Raspberry pi
PDF
IoT sensor devices
DOCX
A seminar report on Raspberry Pi
PPTX
servo motor control using arduino
USB Universal Serial Bus
Raspberry Pi (Introduction)
Electronics ESP processors
Arduino- Serial communication
Haptic technology
arduino
Ardui no
Introduction to Node MCU
Arduino Lecture 1 - Introducing the Arduino
Drowsy Driver detection system
Wi-Fi Esp8266 nodemcu
Raspberry Pi Presentation
Raspberry pi
Embedded system introduction - Arduino Course
Introduction to arduino
IOT Unit-1 (Introduction to IOT) by Durgacharan
Raspberry pi
IoT sensor devices
A seminar report on Raspberry Pi
servo motor control using arduino
Ad

Viewers also liked (20)

PDF
I2C Bus (Inter-Integrated Circuit)
DOC
Arduino & raspberry pi - Un connubio stimolante
PPTX
What is Firewall?
PPTX
Arduino (terminado)
PDF
NGFW: MARKET GROWTH, DEPLOYMENTS, AND NSS TEST RESULTS
PPTX
PDF
PR_ALT_28072015_SiConTech_acquisition
PDF
Arduino arduino boardnano
PDF
I2C Subsystem In Linux-2.6.24
PDF
Arduino nanomanual23
PDF
PT Docs on bpm'online English version
PPTX
Network Security: Protecting SOHO Networks
PDF
S3 Group on Code Management - RDK Users Conference 2014
PDF
HKG15-506: Comcast - Lessons learned from migrating the RDK code base to the ...
PPTX
Reverse car-parking
PDF
Решения Cisco для обеспечения кибербезопасности промышленных систем автоматиз...
PPT
Размещение презентаций в Internet
PDF
Годовой отчет Cisco по информационной безопасности за 2016 год
PDF
How to Solve Real-Time Data Problems
PPT
Generalized Functors - Realizing Command Design Pattern in C++
I2C Bus (Inter-Integrated Circuit)
Arduino & raspberry pi - Un connubio stimolante
What is Firewall?
Arduino (terminado)
NGFW: MARKET GROWTH, DEPLOYMENTS, AND NSS TEST RESULTS
PR_ALT_28072015_SiConTech_acquisition
Arduino arduino boardnano
I2C Subsystem In Linux-2.6.24
Arduino nanomanual23
PT Docs on bpm'online English version
Network Security: Protecting SOHO Networks
S3 Group on Code Management - RDK Users Conference 2014
HKG15-506: Comcast - Lessons learned from migrating the RDK code base to the ...
Reverse car-parking
Решения Cisco для обеспечения кибербезопасности промышленных систем автоматиз...
Размещение презентаций в Internet
Годовой отчет Cisco по информационной безопасности за 2016 год
How to Solve Real-Time Data Problems
Generalized Functors - Realizing Command Design Pattern in C++
Ad

Similar to I2c interfacing raspberry pi to arduino (20)

PDF
Internet of things laboratory
PDF
I2 c communication between nodemcu and aeduino with dht11 (1)
PPT
ARDUINO AND ITS PIN CONFIGURATION
PPTX
INTODUCTION OF IOT AND INTERFACING WITH ARDUINO UNO
PDF
Linux Format - Get Into Linux Today
PDF
The I2C Interface
PDF
An Implementation of I2C Slave Interface using Verilog HDL
PPTX
Part-2: Mastering microcontroller with embedded driver development
DOCX
project 3 full report
PPTX
Serial Communication & Embedded System Interface
PDF
Arduino Comic-Jody Culkin-2011
PDF
Arduino comic v0004
PDF
Starting with Arduino
PDF
Webshield internet of things
PPTX
Fun with arduino
PDF
Analog data transmission on rf module using arduino
PPTX
Embedded system application
PPTX
Intro_to_Arduino_-_v30.pptx
PDF
INT4073 L07(Sensors and AcutTORS).pdf
PPT
Physical prototyping lab1-input_output (2)
Internet of things laboratory
I2 c communication between nodemcu and aeduino with dht11 (1)
ARDUINO AND ITS PIN CONFIGURATION
INTODUCTION OF IOT AND INTERFACING WITH ARDUINO UNO
Linux Format - Get Into Linux Today
The I2C Interface
An Implementation of I2C Slave Interface using Verilog HDL
Part-2: Mastering microcontroller with embedded driver development
project 3 full report
Serial Communication & Embedded System Interface
Arduino Comic-Jody Culkin-2011
Arduino comic v0004
Starting with Arduino
Webshield internet of things
Fun with arduino
Analog data transmission on rf module using arduino
Embedded system application
Intro_to_Arduino_-_v30.pptx
INT4073 L07(Sensors and AcutTORS).pdf
Physical prototyping lab1-input_output (2)

Recently uploaded (20)

PDF
How NGOs Save Costs with Affordable IT Rentals
PPTX
title _yeOPC_Poisoning_Presentation.pptx
PDF
Lifting Equipment Inspection Checklist with eAuditor Audits & Inspections
PPTX
kvjhvhjvhjhjhjghjghjgjhgjhgjhgjhgjhgjhgjhgjh
PPT
Welcome-to-Information-Technology.pptx.ppt
PDF
Colorful Illustrative Digital Education For Children Presentation.pdf
PPTX
INFERTILITY (FEMALE FACTORS).pptxgvcghhfcg
PPTX
sdn_based_controller_for_mobile_network_traffic_management1.pptx
PPTX
making presentation that do no stick.pptx
PPTX
了解新西兰毕业证(Wintec毕业证书)怀卡托理工学院毕业证存档可查的
PPTX
ERP good ERP good ERP good ERP good good ERP good ERP good
PDF
DOC-20250802-WA0013._20250802_161719_0000.pdf
PPTX
Embedded for Artificial Intelligence 1.pptx
PPTX
Disorders of the anterior horn cells.pptx
PPTX
udi-benefits-ggggggggfor-healthcare.pptx
PPTX
Presentacion compuuuuuuuuuuuuuuuuuuuuuuu
PPT
L1-Intro.ppt nhfjkhghjjnnnmkkjhigtyhhjjj
PDF
Layer23-Switch.com The Cisco Catalyst 9300 Series is Cisco’s flagship stackab...
PPTX
Eco-DROPLETS (1).pptx {watering smarter,not harder
PDF
Prescription1 which to be used for periodo
How NGOs Save Costs with Affordable IT Rentals
title _yeOPC_Poisoning_Presentation.pptx
Lifting Equipment Inspection Checklist with eAuditor Audits & Inspections
kvjhvhjvhjhjhjghjghjgjhgjhgjhgjhgjhgjhgjhgjh
Welcome-to-Information-Technology.pptx.ppt
Colorful Illustrative Digital Education For Children Presentation.pdf
INFERTILITY (FEMALE FACTORS).pptxgvcghhfcg
sdn_based_controller_for_mobile_network_traffic_management1.pptx
making presentation that do no stick.pptx
了解新西兰毕业证(Wintec毕业证书)怀卡托理工学院毕业证存档可查的
ERP good ERP good ERP good ERP good good ERP good ERP good
DOC-20250802-WA0013._20250802_161719_0000.pdf
Embedded for Artificial Intelligence 1.pptx
Disorders of the anterior horn cells.pptx
udi-benefits-ggggggggfor-healthcare.pptx
Presentacion compuuuuuuuuuuuuuuuuuuuuuuu
L1-Intro.ppt nhfjkhghjjnnnmkkjhigtyhhjjj
Layer23-Switch.com The Cisco Catalyst 9300 Series is Cisco’s flagship stackab...
Eco-DROPLETS (1).pptx {watering smarter,not harder
Prescription1 which to be used for periodo

I2c interfacing raspberry pi to arduino

  • 1. Connecting Raspberry Pi to Arduino using I2C Raspberry Pi This work is licensed under the Creative Commons Attribution- ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/b y-sa/4.0/. Revision 2 Companion Github
  • 2. What is I2C? The Inter-IC Bus (I-IC or I2C) standard defines the hardware and electrical characteristics of the interface between nearby devices using only two wires (and a third for ground reference). SMBus was introduced by Intel as a tight subset of I2C, strictly defining the interface between devices. It is commonly used in televisions, computer components, and many ADC and DAC and GPS devices. The Raspberry Pi most commonly uses an SMBus implementation in Python 2.x, and this is what we will concentrate on in this document.
  • 3. Connecting Devices Identify SDA, SCL, and GND pins on both Raspberry Pi and the Arduino. Connect SDA to SDA, SCL to SCL, and GND to GND between the two devices. It is possible to connect the two devices directly to one another without a level shifter, but it is not best practice. I2C levels for a low signal is 0.3Vcc and for a high, 0.7Vcc. With the Raspberry Pi being a 3.3V device and the Arduino requiring 5V for Vcc, a logic level 'high' coming from the 3.3V Raspberry Pi is slightly below the 0.7*5= 3.5V required for a definite logic 'high' in the Arduino.
  • 4. Voltage Levels The Raspberry Pi is a 3.3V device while the Arduino is a 5V device, but because both devices are connected in open-drain configuration with relatively high pull-up resistor values, it is unlikely damage will occur. The problem is that the communication between the two devices is not guaranteed to work well due to the 3.5V minimum logic level 'high' required by the Arduino, and the Raspberry Pi input is not 5V resistant. A level shifter isolates the two different voltages, while transmitting on/off information in both directions. Having said that, I have had reliable communication between the Raspberry Pi and the Arduino without any loss of data, but I am worried about the Arduino accidently driving one of the SDA or SCL pins to 5V as an output if the program is not written carefully and damaging the Raspberry Pi. User beware.
  • 5. I2C Level Shifter This rather complicated-looking circuit creates a bi-directional bus that can automatically translate 3.3V bus signals into 5V signals and vice versa at full bus speed. This is one of the simplest and cheapest level shifters and can easily be built on a breadboard or soldered in a few minutes on any of the prototyping stripboards available. On the next slide, we look at this circuit in detail
  • 6. The Level Shifter Isolating one of the two identical bus lines, we find an enhancement-mode n-channel MOSFET the star of the show. It's Gate is tied directly to the low-voltage Vcc. A pull-up resistor connects the High voltage side to the Drain, and another pulls the Source up to the Low voltage side. S1 and S2 simulate the Open-Drain connection that drives the bus at the microcontroller. With both switches open, Gate and Source are at the same potential so the transistor is turned off. Both sides are pulled up to their respective Vcc. With S1 closed and S1 open, the source is pulled down to 0V, taking Vgs (Gate-Source Voltage) above the threshold and turning the transistor on hard. This pulls the Drain side down to 0V as well. With S2 closed and S1 open, Drain is pulled to 0V and the internal diode is forward-biased by the 3.3V, dropping the source to around 0.6V. This is enough to pull Vgs again beyond the threshold, opening the transistor and dragging the bus down to 0V. With both switches closed, both sides will be at 0V.
  • 7. Setting Up Raspberry Pi Getting the tools for Python2 and/or Python3... 1. Open a terminal window. Enter “sudo apt-get install python-smbus” (2.x) Or “sudo apt-get install python3-smbus” (3.x) And “sudo apt-get install i2c-tools” Reboot the Pi. 2. Open “Raspberry Pi Configuration” in the GUI or use a terminal to enter 'sudo raspi-config'. Enable I2C, save, and reboot. 3. Edit file 'etc/modules' using GUI text editor or enter “sudo nano etc/modules” If these two lines are not in there, type them; i2c-bcm2708 I2c-dev And save. 4. Edit file 'etc/modprobe.d/raspi-blacklist.comf' using GUI editor or Enter “sudo nano etc/modprobe.d/raspi-blacklist.conf”. If these two lines exist, make sure a '#' is placed before them, or delete them altogether.. #blacklist spi-bcm2708 #blacklist i2c-bcm2708 And save. 5. Finally, enter “sudo nano /boot/config.txt'. These two lines should appear: dtparam=i2c1=on dtparam=i2c_arm=on If not, make them so, and save. Reboot.
  • 8. Testing Raspberry Pi Enter “i2cdetect -y 1” in a terminal window to check that the I2C bus is available and running on the Pi. If everything is good, it will show a table of addresses. If any I2C devices are available, their addresses will be shown in hexadecimal. It is worth entering “i2cdetect -F 1” to see which SMBus calls are supported. It will save hours of debugging to know that “SMBus Block Read” is not supported, but “I2C Block Read” is supported, and that “Block Process Call” is meaningless as it is also not supported.
  • 9. Preparing the Raspberry Pi Start Python, and create a new file. Import the SMBus library. Create an SMBus object linked to I2C port 1. Define the slaves address(es) to connect to. Collect the data to be transmitted, or prepare the data variables for reading back from the slave. These instructions are for Python 2. Python 3 may have small differences. import smbus i2c_object = smbus.SMBus(1) # Link to SMBus(0) on older Pi addrSlave1 = 8 command = 0 dataList = [1,2,3,4,5] i2c_object.write_i2c_block_data(addrSlave1, command, DataList) getDataList = i2c_object.read_i2c_block_data( addrSlave1, command)
  • 10. Preparing the Arduino Start Arduino IDE Include the Wire.h standard library for Arduino. Set up the slave address. Register a Wire function that will run when the interrupt for receiving information from the Master triggers. The event is onReceive(function) This event will react to information coming from the master, and prepare for sending data back to the master, if necessary. Register a Wire function that will run when the interrupt for returning data to the Master triggers. This event is on onRequest(function) When the interrupt fires, send data to the Master that was requested. #include <Wire.h> const byte slaveAddr = 8; void setup() { Wire.begin(slaveAddr); Wire.onReceive(i2cReceive); Wire.onRequest(i2cTransmit); } void loop() { } void i2cReceive(int numBytes) { byte rxByte; while Wire.available() { rxByte = Wire.read(); // Do something with the byte } } Void i2cTransmit() { Wire.write(...) // send requested data }
  • 11. Example Project Using a Raspberry Pi 2 and an Arduino Leonardo, we will connect up the I2C bus, and connect an RGB LED to the Arduino. The LED color and brightness will be controlled by a Python program running on the Pi. We will also simulate analog data to send back to the Pi from the Arduino, a temperature sensor and a light-level sensor. The source code for both Arduino and Raspberry Pi can be found on github at mikeochtman.github.io/Pi_Arduino_I2C. This drawing was created in Fritzing.
  • 12. Interface Specification Before we can communicate with the Arduino, we need an interface specification which describes, in detail, how and what data should be moved over the bus from Master to Slave, and from Slave to Master. Type Description Access Byte Command private Byte Control private Real Temperature Read Only Real Light Level Read Only Byte Brightness Red Read/Write Byte Brightness Green Read/Write Byte Brightness Blue Read/Write Arduino Internal Data Table Command Action 0x81 Read Temperature. Return int(round(temperature*100)) 0x82 Read Light. Return int(round(light*100)) 0x0A Write three bytes for brightness RGB 0x0B Write single byte, brightness Red channel 0x0C Write single byte, brightness Green channel 0x0D Write single byte, brightness Blue channel 0x90 Read three bytes brightness RGB 0x91 Read single byte, brightness Red channel 0x92 Read single byte, brightness Green channel 0x93 Read single byte, brightness Blue channel Read, no command Return Slave Address, one Byte Write, No command Master interrogating slave presence. Ignore Other commands Ignore. Master software has to deal with communication exceptions, if any.
  • 13. Arduino Software//The full software can be found on github at // https://github.com/MikeOchtman/Pi_Arduino_I2C //This is a simplified collection of //the most important parts. #include <Wire.h> #define rxFault 0x80 #define txFault 0x40 #define slaveAddress 8 struct { byte volatile command; byte volatile control; float volatile temperature; float volatile light; byte volatile brightR; byte volatile brightG; byte volatile brightB; } commsTable; byte volatile txTable[32]; // prepare data for sending over I2C Void setup() { Wire.begin(slaveAddress); Wire.onReceive(i2cReceive); Wire.onRequest(i2cTransmit); } Void loop() { } void i2cReceive(int byteCount) { if (byteCount == 0) return; byte command = Wire.read(); commsTable.command = command; if (command < 0x80) { i2cHandleRx(command, byteCount - 1); } else { i2cHandleTx(command); } } byte i2cHandleRx(byte command, int numBytes) { switch (command) { case 0x0A: if (Wire.available() == 3) commsTable.brightR = Wire.read(); commsTable.brightG = Wire.read(); commsTable.brightB = Wire.read(); result = 3; } else { result = 0xFF; } break; // and so on, one case for each command according to the Interface Specification default: result = 0xFF; } if (result == 0xFF) commsTable.control |= rxFault; return result; }
  • 14. Arduino Software, cont'd byte i2cHandleTx(byte command) { // If you are here, the I2C Master has requested information // If there is anything we need to do before the interrupt // for the read takes place, this is where to do it. return 0; } void i2cTransmit() { // byte *txIndex = (byte*)&txTable[0]; byte numBytes = 0; int t = 0; switch (commsTable.command) { case 0x00: // send slaveAddress. txTable[0] = slaveAddress; numBytes = 1; break; case 0x81: // send temperature t = int(round(commsTable.temperature*100)); txTable[1] = (byte)(t >> 8); txTable[0] = (byte)(t & 0xFF); numBytes = 2; break; case 0x91: // send RGB Red channel txTable[0] = commsTable.brightR; numBytes = 1; break; // Again, create a case statement for each legal command default: commsTable.control |= txFault; } if (numBytes > 0) { Wire.write((byte *)&txTable, numBytes); } }
  • 15. Python Softwareimport smbus import time i2c = smbus.SMBus(1) addr = 8 # address of the arduino I2C ##/* Interface Specification ## Data in a table thus: ## byte purpose ## 0: command ## 1: control ## 2-5: Current Temperature (read-only) ## 6-9: Current light level (read only) ## 10: Brightness for RED r/w ## 11: Brightness for GREEN r/w ## 12: Brightness for BLUE r/w ## Commands: ## Write with no command: Ignore ## Read with no command: Return slave address ## Command 0x81: read temperature; ## Integer returned, int(round(temp*100)) ## Command 0x82: read light level; ## Integer returned, int(round(lux*100)) ## Command 0x0A: Write three bytes to RGB ## Command 0x0B: Write single byte brightness red; ## Command 0x0C: Write single byte brightness green; ## Command 0x0D: Write single byte brightness blue; ## Command 0x90: read three bytes brightness RGB ## Command 0x91: read single byte brightness red; ## Command 0x92: read single byte brightness green; ## Command 0x93: read single byte brightness blue; ## ## All other values are ignored, no data returned. RGB = [20,200,128] temperature = 0 light_level = 0 i2c.write_quick(addr) # no data expected back time.sleep(0.5) print i2c.read_byte(addr) # expect the slave address time.sleep(0.5) print i2c.read_word_data(addr,0x81)/100.0 # temperature time.sleep(0.5) print i2c.read_word_data(addr,0x82)/100.0 # light level time.sleep(0.5) i2c.write_byte_data(addr, 0x0B, 12) # write Red value time.sleep(0.5) print i2c.read_byte_data(addr, 0x91) # write Green value time.sleep(0.5) i2c.write_byte_data(addr, 0x0C, 123) # write Blue value time.sleep(0.5) print i2c.read_byte_data(addr, 0x92) # get Green value time.sleep(0.5) i2c.write_byte_data(addr, 0x0D, 234) # write Blue value time.sleep(0.5) print i2c.read_byte_data(addr, 0x93) # get Blue value time.sleep(0.5) print i2c.read_i2c_block_data(addr, 0x90, 3) # get RGB time.sleep(0.5) i2c.write_i2c_block_data(addr, 0x0A, RGB) # set all RGB time.sleep(0.5) print i2c.read_i2c_block_data(addr, 0x90, 3) # get all RGB time.sleep(0.5) print i2c.read_i2c_block_data(addr, 0x10, 3) # force error time.sleep(0.5)
  • 16. The Python ResultsPython 2.7.9 results. >>> 8 23.95 6.97 12 123 234 [12, 123, 234] [20, 200, 128] [0, 255, 255] >>> No event for write_quick() Got back the slave Address. Got the simulated temperature Got the simulated light level Set and read back Red channel Set and read back Green channel Set and read back Blue channel Got back RGB values as a list. Set new RGB values from a list Read back the RGB values as a list Forced a read error by sending wrong command. RGB = [20,200,128] i2c.write_quick(addr) # no data expected back print i2c.read_byte(addr) # expect the slave address print i2c.read_word_data(addr,0x81)/100.0 # temperature print i2c.read_word_data(addr,0x82)/100.0 # light level i2c.write_byte_data(addr, 0x0B, 12) # write Red value print i2c.read_byte_data(addr, 0x91) # get Red value i2c.write_byte_data(addr, 0x0C, 123) # write Green value print i2c.read_byte_data(addr, 0x92) # get Green value i2c.write_byte_data(addr, 0x0D, 234) # write Blue value print i2c.read_byte_data(addr, 0x93) # get Blue value print i2c.read_i2c_block_data(addr, 0x90, 3) # get RGB i2c.write_i2c_block_data(addr, 0x0A, RGB) # set all RGB print i2c.read_i2c_block_data(addr, 0x90, 3) # get all RGB print i2c.read_i2c_block_data(addr, 0x10, 3) # force error
  • 17. Instructions in Detail Master (Rpi, Python) Slave (Arduino Bus.write_quick(slave) Used to check presence, or trigger event in slave without data transfer. Wire.onReceive() event triggered, but no data is sent. Bytes received: 0. Bytes returned: 0. Bus.write_byte(slave, data) Can be used to instruct the slave to do any of up to 255 different actions, or to place this byte in one predefined byte variable. Wire.onReceive() event triggered. Single byte received. Bytes received: 1. Bytes returned: 0. ByteValue = Bus.read_byte(addr) Read slave status or a single predefined value Wire.onRequest() event triggered only. No data received. Slave must return one defined byte. Bytes received: 0. Bytes returned: 1. Bus.write_byte_data(addr, command, byteValue) Send slave some information to accompany a command. Wire.onReceive() event triggered. Two bytes sent. First byte is a command, second byte is the byte data to accompany it. Bytes received: 2. Bytes returned: 0. ByteValue = Bus.read_byte_data(addr, command) Interrogate a slave about a specific piece of information Wire.onReceive event triggered. One byte sent, a command for which byte is requested from slave. Wire.onRequest() then triggered, and one byte to be returned. Bytes received: 1. Bytes returned: 1
  • 18. Instructions in Detail Master (Rpi, Python) Slave (Arduino Bus.write_word_data(addr, command, integerVal) Send slave extra information accompanying a specific command Wire.onReceive() event triggered, three bytes send. First byte is command, second is LSB, third is MSB of integer. Bytes received: 3. Bytes returned: 0. WordValue = Bus.read_word_data(addr, command) Retrieve two bytes, first LSB, then MSB of an integer. Python knows to rebuild the integer. Retrieve integer data from slave. Wire.onReceive() event triggered. Single byte received, a command. Wire.onRequest() event triggered, slave must send back two bytes, first LSB, then MSB, of an integer. Bytes received: 1. Bytes returned: 2. Bus.write_block_data(addr, command, [list of bytes]) Sending unstructured data. Wire.onReceive() event triggered. Several bytes sent. First byte is command, second is number of following bytes, remainder of bytes must be processes according to interface list. Bytes received: > 2. Bytes returned: 0. Bus.write_i2c_block_data(addr, command, [list of bytes]) Use this to fill a table of related bytes quickly. Wire.onReceive() event triggered. Several bytes sent. First byte is a command, remaining bytes to be processed according to interface. Bytes received: > 1. Bytes returned: 0. ByteList = Bus.read_i2c_block_data(addr, command) Use this to read a table of related bytes quickly Wire.onReceive event triggered. One byte sent, a command for which byte is requested from slave. Wire.onRequest() then triggered, requested bytes returned as per interface list. Bytes received: 1. Bytes returned: > 1
  • 19. Instructions in Detail Master (Rpi, Python) Slave (Arduino List = bus.read_block_data(addr, command, bytes) will fail. Always returns the empty list. Do not use this function. Wire.onReceive() event triggered, two bytes send. First byte is command, second is number of bytes to return. OnRequest() event is triggered, sending back the correct number of bytes Bytes received: 2. Bytes returned: > 1. Bus.block_process_call(addr, command, [vals]) Following the protocol, it reads the correct number of bytes sent back, but random data appears in the list returned. Do not use. Wire.onReceive() event triggered. Several bytes received, first command, then count of bytes in [vals], then vals. Wire.onRequest() triggered, return first count, then number of bytes Bytes received: > 2. Bytes returned: > 2. process_call(addr, command, val) does sent the integer 'val', but does not trigger the interrupt to return data, so it returns the empty list Wire.onReceive() event triggered. Three bytes sent. First byte is command, second is LSB and last is MSB. SMBus Specification is to return two byte integer, but onRequest() event never triggered. Bytes received: 3. Bytes returned: n/a.
  • 20. Conclusions Communication over I2C is quite simple to implement using the SMBus library on the Raspberry Pi, and the Wire.h library on the Arduino.. Knowing what each SMBus Write and Read instruction sends to the Arduino and what data the Arduino is expected to send back is crucial to successful implementation of the I2C/SMBus protocol. Before coding, have a clear Interface Specification with a list of commands to be sent to the Arduino, and clear instructions of what should happen with the data sent and how to return requested data. It is also important to specify the reaction to bad data. The SMBus protocol document can be found at http://smbus.org/specs/smbus20.pdf The linux Python implementation document can be found at https://www.kernel.org/doc/Documentation/i2c/smb us-protocol Some of the SMBus protocol is not fully programmed in the existing SMBus python libraries, even though the functions are available to include in the program. These functions do not appear to work with the arduino: ● read_block_data(addr, command, bytes) ● block_process_call(addr, command, [vals]) ● process_call(addr, command, val) All the other functions appear to operate according to the specification. This work is licensed under the Creative Commons Attribution- ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/b y-sa/4.0/. Compiled by Mike Ochtman. Find me at https://za.linkedin.com/in/mikeochtman