This post present the protocol specification between Raspberry Pi and Arduino of the ArduiPi board.
The protocol will be the same and independant of the communication channel used. If you are on this page, this means that you probably already know the goal of the ArduiPi project. If not please refer on the post that present this project on this blog.
The communication could be done by three ways
- Using Serial Port, this is not my favorite one because I like to use the serial for doing something else.
- Using SPI, better but once again not my favorite because you need 4 wires and need a “chip select”, so another Rasbperry I/O. Also it is synchronous, so you can’t send back results immediatly, but it is cool and fast.
- Using I2C, really simple to implement, lot of devices can take place on i2c and just need 2 wires.
This is beta API documentation the accorded driver on Raspberry Pi and Arduino Firmware are working but may not be stable yet
Arduino Port Mapping
At this step, you need to know that Arduino pins that you can see on the arduino board are mapped to physical Arduino ports that may not have the same name into the Arduino chip. Here below the port mapping definition :
Remember, Arduino has 3 main ports named PORTB, PORTC, PORTD where
- PORTD is digital pin 0 (PD0) to 7 (PD7), PD0 and PD1 are used by serial communication
- PORTB is digital pin 8 (PB0) to 13 (PB5), PB6 and PB7 are reserved for external crystal
- PORTC is analog pin A0 (PC0) to A6 (PC5), PC6 is reserved for reset pin
Protocol Communication
The communication is done by sending one or more byte to Arduino, then Arduino can immediatly do the action such of controlling a port and if needed return data such as analog value for example.
The communication is done as follow
- A command byte : what we wand to control (port, port pin, pwm, ….)
- Data Value, what value we want to set on action. This value can be :
- Data byte Or
- Data word
Somme overhead could be added depending on protocol used. For example to communicate with i2c we need to add before the device address (which is part of the i2c protocol)
On ArduiPi the arduino i2c slave address is defined to 0x2a and it is connected to i2c bus 0 of the Raspberry Pi or i2c bus 1 for the Raspberry Pi Revision 2. So we can issue the linux i2c command i2cset and i2cget to set or get information.
Each i2cset or i2cget commmand should begin with the following parameters :
-y 0 0x2a
-y to avoid prompt from the command line tool i2cset ot i2cget
0 refers to i2c bus 0 (so 1 for Raspberry Revision 2)
0x2a refers to i2c slave address (here the arduino)
If i2cget or i2cset command use a word instead of a byte for the last parameter, the command should end by w to indicate the value is a word value.
To simplify the project, I’m writing a program to replace i2cget and i2cset command that could also be used to communicate with spi. The program is called ardupi and this is the preliminary command line syntax
pi@pi02:raspberry# ./arduipi --help arduipi Usage is: arduipi [options] [protocol] [mode] [-d device] [-a address] [-t data] --<D>evice : device name, i2c or spi (default /dev/i2c-1) --<a>ddress : i2c device address (default 0x2A) --<d>data : data to send protocol is: --<I>2c : set protocol to i2c (default) --<S>pi : set protocol to spi mode is: --<s>et value: set value (byte or word type determined by data size) --<g>etbyte : get byte value --<G>getword : get word value --<q>uick : i2c quick check device --ac<k> : i2c check if device sent ack Options are: --ma<x>speed : max spi speed (in KHz) --dela<y> : spi delay (usec) --<b>its : spi bits per word --<l>oop : spi loopback --cp<H>a : spi clock phase --cp<O>l : spi clock polarity --<L>sb : spi least significant bit first --<C>s-high : spi chip select active high --<3>wire : spi SI/SO signals shared --<N>o-cs : spi no chip select --<R>eady : spi Ready --<v>erbose : speak more to user --<h>elp <?> indicates the equivalent short option. Short options are prefixed by "-" instead of by "--".
You can communicate with i2c communication with classic i2c program and/or arduipi program. The sample that are in the following table can be run on Raspberry Pi Revision 2. If you are using Revision 1 change all i2cget and i2cset command -y 1 to -y 0 (Revision 1 use i2c bus 0, not 1). arduipi program autodetect revision and adjust correct i2c bus if not specified.
Protocol Definition
Command Byte
The command will always refers to the port where we want to make action. You have the possibily to talk to the Arduino using either the AVR name or Arduino name depending on what is your preference.
The following present the command list used by ArduiPi
Command Byte Value | Refers to | Notation |
---|---|---|
0x00 to 0x12 or in decimal 0 to 18 | Digital Pin 0 to 18 Note that Digital Pin 14 to 18 refers to analog pin A0 to A5 when used as digital | Arduino |
0xa0 to 0xa5 | Analog Pin 0 to 5 | Arduino |
0x1b | Port B | AVR |
0x1c | Port C | AVR |
0x1d | Port D | AVR |
0x2b | Port B DDR | AVR |
0x2c | Port C DDR | AVR |
0x2d | Port D DDR | AVR |
0xe0 | Ping Command | - |
Basic Command
Command | Value | Description | Notation |
---|---|---|---|
0xe0 | Ping Return the ping value. Examples : arduipi -g -d 0xe0 i2cget -y 1 0x2a 0xe0 | - | |
0xe0 | Any byte value ex : 33 or 0xAA | Set Ping Value Set the value to be returned by the above ping command. The value can be decimal or hexadecimal (prefixed by 0x) Examples : arduipi -s -d 0xe0aa i2cset -y 1 0x2a 0xe0 0xaa | - |
Command for Port Manipulation
Command | Data | Description | Notation |
---|---|---|---|
0x01b or 0x01c or 0x01d | Get Port - Get the given Port value. Examples (Get Port C Value) : arduipi -g -d 0x1c i2cget -y 1 0x2a 0x1c | AVR | |
0x01b or 0x01c or 0x01d | [00-FF] | Set Port - Set the given Port to the following given hex value. Examples (Set Port B Value to 0xa5) : arduipi -s -d 0x1ba5 i2cset -y 1 0x2a 0x1b 0xa5 | AVR |
0x02b or 0x02c or 0x02d | Get Port Direction - Get all pins direction of given Port in one shot. Each bit in the data value determine the pin mode as : 0 = Input 1 = Output Examples (Get Port B DDR Value) : arduipi -g -d 0x2b i2cget -y 1 0x2a 0x2b | AVR | |
0x02b or 0x02c or 0x02d | [00-FF] | Set Port Direction - Configure all pins of given Port in one shot as input and/or output. Each bit in the data value determine the pin mode as : 0 = Input 1 = Output Examples (Set Port D DDR Value to 0xf0) : arduipi -s -d 0x2df0 i2cset -y 1 0x2a 0x2d 0xf0 | AVR |
0x01b or 0x01c or 0x01d | 0[0-7] | Get Port Pin - Get the given port pin. Each bit in the data value determine the pin mode as : 0 = Low 1 = High Examples (Get Port B pin 2 Value) : arduipi -g -d 0x1c02 i2cget -y 1 0x1c 0x1c 0x02 | AVR |
0x01b or 0x01c or 0x01d | [0-7][0-1] | Set Port Pin - Set or clear the given Port pin. Each bit in the data value determine the pin mode as : 0 = Low 1 = High Examples (Set Port D pin 4 Value to 1) : arduipi -s -d 0x1d41 i2cset -y 1 0x1c 0x1d 0x41 | AVR |
0x00 to 0x12 | Get Digital Pin - Get the given digital pin value. 0 = Low 1 = High Examples (Get digital pin 5 value) : arduipi -g -d 0x05 i2cget -y 1 0x1c 0x05 | Arduino | |
0x00 to 0x12 | [0-1] | Set Digital Pin - Set or clear the given digital pin given by item byte. 0 = Low 1 = High Examples (Set digital pin 8 to High) : arduipi -s -d 0x0801 i2cset -y 1 0x1c 0x08 0x01 | Arduino |
0x00 to 0x12 | 0xDD | Get Digital Pin Mode - Get the given digital pin mode. Return is as : 0 = Input 1 = Output 2 = Input with pullup Examples (Get digital pin 3 mode) : arduipi -g -d 0x03dd not possible with i2cget | Arduino |
0x00 to 0x12 | 0xD[0-2] | Set Digital Pin Mode - Set the given digital pin mode. 0 = Input 1 = Output 2 = Input with pullup Examples (Set digital pin 4 to Input Pullup) : arduipi -s -d 0x04d2 i2cset -y 1 0x2a 0x04 0xd2 | Arduino |
Command for Analog Pins/Ports
In this group the command are used to control analog Pins/Ports
Command | Data | Description | Notation |
---|---|---|---|
0xa[0-5] | Get Analog Value Get analog value for the analog arduino pin given by command byte (A0 to A5). Examples : arduipi -G -d 0xa5 i2cget -y 1 0x2a 0xa5 w | Arduino |
Command for PWM Duty Cycle Pins/Ports
In this group the command are used to control PWM Pins/Ports
Command | Data | Description | Notation |
---|---|---|---|
3,5,6,9,10 or 11 | 0xEE[00-FF] | Set PWM Duty Cycle Set PWM value for the arduino pin given by command byte. Examples (Set PWM duty cycle of pin 9 to 50%) : arduipi -s -d 0xa5 i2cget -y 1 0x2a 0xa5 w | Arduino |
- PWM frequencies are tied together in pairs of pins. If one in a pair is changed, the other is also changed to match:
- Pins 5 and 6 are paired on timer0
- Pins 9 and 10 are paired on timer1
- Pins 3 and 11 are paired on timer2
Note that theese functions will have side effects on anything else that uses timers:
- Changes on pins 3, 5, 6, or 11 may cause the delay() and millis() functions to stop working. Other timing-related functions may also be affected.
- Changes on pins 9 or 10 will cause the Servo library to function incorrectly.
Thanks to macegr of the Arduino forums for his documentation of the PWM frequency divisors. His post can be viewed here