I am testing i2c communication between Pi and Arduino.
The doc says:
write_i2c_block_data(addr,cmd,vals) Block Write transaction. int addr,char cmd,long[] None
I have this test:
On Pi:
import smbus
bus = smbus.SMBus(0)
bus.write_i2c_block_data(address, 48, [49, 50, 51] )
On Arduino:
void receiveData(int byteCount){
Serial.print("byte count=");
Serial.println(byteCount);
while(Wire.available()) {
number = Wire.read();
Serial.print((char)number);
}
}
On the Arduino I see this output:
byte count=4
0123
My question is: what is the use of the cmd
parameter?
I don't see a distinction on the Arduino of which byte represents what.
I guess I can deal with it as a see fit. Maybe I want to use the first 2 bytes
as a command.
This page has not much information on the method: http://wiki.erazor-zone.de/wiki:linux:python:smbus:doc
4 Answers 4
I2C
protocol is very simple. It does not really define data structures that are sent over the wire. The frame consist of a slave address (with direction bit indicating if master wants to read or write) and (in case of writing) some bytes of data. Since it doesn't make sense to initiate write with 0 bytes of data, first byte is mandatory.
This first byte is often used as a slave register address or command number but it doesn't have to. There may or may not be additional bytes after the first one. The higher level protocol defining what each byte means is device specific.
This may explain why there are two separate arguments - first one (cmd
) is mandatory and second one (vals
) is optional. While your example is in Python
language, the API used here is actualy very close mapping of original C
API where you can't easly create optional arguments.
-
This is a little longer explanation of what I wrote in the comment under the question.Krzysztof Adamski– Krzysztof Adamski2013年07月17日 17:01:43 +00:00Commented Jul 17, 2013 at 17:01
-
I'm glad you did! These kinds of simple, yet "aha!" explanations are really helpful some times, like today :-)uhoh– uhoh2018年01月19日 07:08:43 +00:00Commented Jan 19, 2018 at 7:08
When you issue a block write/read from the Pi with:
bus.write_i2c_block_data(i2c_address, register, list of bytes)
Example:
bus.write_i2c_block_data(80, 0, [49, 50, 51])
or
bus.read_i2c_block_data(i2c_address, register, length)
Example:
bus.read_i2c_block_data(80, 0, 16)
Two things happen on the Arduino depending on read or write.
The register
byte (e.g., 0 or any other byte like for instance 48) is the first byte written to the I2C bus from the Pi; it is always sent as a "write" request. This means that if the Pi is issuing a
bus.read_i2c_block_data(i2c_addr, register, length)
or
bus.write_i2c_block_data(i2c_addr, register, data)
it first writes register
to the I2C bus, then terminates in case of write_i2c_block_data
(with no return value), or reads in case of read_i2c_block_data
, returning a list of bytes.
This is a useful feature because some I2C hardware requires initialization before a read can be made (e.g., the process can be differentiated according to the register
value).
On the Arduino this means that:
First the,
Wire.onReceive(yourCallback)
function is called because register
was written to the bus by the Pi. register
will be the first byte available on the bus (and should be read through a Wire.read()
inside yourCallback
). If the Pi sent a write request, then the Arduino will stay in the Wire.onReceive
callback until the function is complete. If the Pi sent a read request, the Arduino will complete the Wire.onReceive
callback, then call the Wire.onRequest
callback.
In principle, the Wire.onReceive
callback should include one or more Wire.read()
operations to read data from I2C (one byte if read_i2c_block_data
is used, or possibly multiple bytes if write_i2c_block_data has a list of bytes in its third parameter); the Wire.onRequest
callback should instead include a Wire.write()
function, to write data to I2C.
You must ensure that the value placed in register
does not cause unintended behavior in your system by appropriately handling its value.
I am writing to an I2C LCD, the Newhaven NHD‐0216K3Z‐FL‐GBW‐V3. Its spec sheet can be googled. In its case, when the command byte is 0xfe it means that the following byte is a command - there are about 20 of them. Clear, backlight, blink cursor and so on. If cmd is not 0xfe it is just some character to display.
Updating deltatango's answer with some corrections and additional information.
Using i2c_register
instead of cmd
.
When you issue a block write from the Raspberry Pi with:
bus.write_i2c_block_data(i2c_address, i2c_register, list of bytes)
example:
bus.write_i2c_block_data(80, 0, [49, 50, 51])
or a block read
bus.read_i2c_block_data(i2c_address, i2c_register, length)
example:
bus.read_i2c_block_data(80, 0, 16)
the following things happen on the Arduino, depending on the usage of the read or write functions.
The i2c_register
byte (e.g., 0 or any other byte like for instance 48 in the above question) is the first byte written to the I2C bus from the Pi; it is always sent as a "write" request. This means that if the Pi is issuing a
bus.read_i2c_block_data(i2c_addr, i2c_register, length)
or
bus.write_i2c_block_data(i2c_addr, i2c_register, data)
it first writes i2c_register
to the I2C bus, then terminates in case of write_i2c_block_data
(with no return value), or reads in case of read_i2c_block_data
, returning a list of integers.
This is a useful feature because some I2C hardware requires initialization or flow differentiation before performing the read operation (e.g., the Arduino callback process can vary according to the i2c_register
value).
On the Arduino this means that:
First the
Wire.onReceive(yourCallback)
function is called because i2c_register
was written to the bus by the Pi. i2c_register
will be the first byte available on the bus (and should be read through a Wire.read()
inside yourCallback
). If the Pi sent a write request, then the Arduino will stay in the Wire.onReceive
callback until the function is complete. If the Pi sent a read request, the Arduino will complete the Wire.onReceive
callback, then it will call the Wire.onRequest
callback.
In principle, the Wire.onReceive
callback should include one or more Wire.read()
operations to read data from I2C (one byte if read_i2c_block_data
is used, or possibly multiple bytes if write_i2c_block_data has a list of bytes in its third parameter); the Wire.onRequest
callback should include a Wire.write()
function, to write data back to I2C.
Full working Python example on a Raspberry Pi (sudo apt-get install -y python3-smbus
):
import struct
import smbus
import time
bus = smbus.SMBus(3)
address = 0x2a
# Example of write_i2c_block_data
print("Starting")
print("LED on (write, register 254).")
bus.write_i2c_block_data(address, 254, []) # LED on
# Examples of read_i2c_block_data
print("Read the whole out_set structure (32 bytes, 8 values; read, register 0):")
i2c_register = 0
byte_list = bus.read_i2c_block_data(address, i2c_register, 2 * 4 * 4) # a list of 4 float values + 4 long values, each consisting of 4 bytes
temp = []
for i in [byte_list[x:x+4] for x in range(0, int(len(byte_list)/2), 4)]:
temp.append(struct.unpack('<f', bytes(i))[0])
value = []
for i in [byte_list[x:x+4] for x in range(int(len(byte_list)/2), len(byte_list), 4)]:
value.append(struct.unpack('<L', bytes(i))[0])
print(temp, value)
# output: [0.0, 3.0, 6.0, 9.0] [32, 4, 1, 4]
print("Read the initial value of the counter after incrementing it (0+1=1; read, register 1):")
i2c_register = 1 # increment the counter and then read it
counter = bus.read_i2c_block_data(address, i2c_register, struct.calcsize("<L")) # one unsigned long value (made of 4 bytes)
print(struct.unpack('<L', bytes(counter))[0])
# output: 1
print("Read the True value (read, register 4):")
i2c_register = 4 # default case in sendDataVector
bool = bus.read_i2c_block_data(address, i2c_register, struct.calcsize("?")) # one Boolean value (1 byte)
print(struct.unpack('?', bytes(bool))[0])
# output: True
print("Read the False value with unused register 3 (default):")
i2c_register = 3 # default case in sendDataVector
bool = bus.read_i2c_block_data(address, i2c_register, struct.calcsize("?")) # one Boolean value (1 byte)
print(struct.unpack('?', bytes(bool))[0])
# output: False
print("Read the 3.1415 float value (read, register 48):")
i2c_register = 48
float = bus.read_i2c_block_data(address, i2c_register, struct.calcsize("<f")) # one float value (made of 4 bytes)
print(struct.unpack('<f', bytes(float))[0])
# output: 3.1414999961853027
print("Set counter to 1025 (write, register 6).")
bus.write_i2c_block_data(address, 6, list(struct.pack('<L', 1025)))
print("Read the counter without incrementing it (read, register 2).")
counter = bus.read_i2c_block_data(address, 2, 4) # read counter
print(struct.unpack('<L', bytes(counter))[0])
# output: 1025
print("Increment the counter (read, register 1).")
bus.write_i2c_block_data(address, 1, []) # increment counter
print("Read the counter without incrementing it (read, register 2).")
counter = bus.read_i2c_block_data(address, 2, 4) # read counter
print(struct.unpack('<L', bytes(counter))[0])
# output: 1026
print("Read the counter after incrementing it (read, register 1).")
counter = bus.read_i2c_block_data(address, 1, 4) # increment counter and read it
print(struct.unpack('<L', bytes(counter))[0])
# output: 1027
print("Read the counter without incrementing it (read, register 2).")
counter = bus.read_i2c_block_data(address, 2, 4) # read counter
print(struct.unpack('<L', bytes(counter))[0])
# output: 1027
print("LED off (write, register 253).")
bus.write_i2c_block_data(address, 253, []) # LED off
Full working example on Arduino:
#include <Wire.h>
#define SLAVE_ADDRESS 0x2A
typedef struct { // Example of byte structure
float temp[4];
unsigned long value[4];
} type_temp;
type_temp temp_set, out_set;
unsigned char i2c_register = 0;
unsigned long counter = 0;
float pi = (float) 3.1415;
bool true_value = true;
void receiveEvent(int byteCount) {
i2c_register = Wire.read();
if ((i2c_register == 6) && (byteCount == 5)) {
unsigned long val = 0;
val = ((unsigned long) Wire.read());
val += ((unsigned long) Wire.read()) << 8;
val += ((unsigned long) Wire.read()) << 16;
val += ((unsigned long) Wire.read()) << 24;
counter = val;
}
while(Wire.available()) {
Wire.read();
}
switch (i2c_register) {
case 1:
counter ++;
case 253:
digitalWrite(LED_BUILTIN, LOW);
break;
case 254:
digitalWrite(LED_BUILTIN, HIGH);
break;
}
} // of receiveEvent
void sendDataVector() {
switch (i2c_register) {
case 0:
Wire.write((byte*) &out_set, sizeof(out_set));
break;
case 48:
Wire.write((byte*) &pi, sizeof(pi));
break;
case 1:
Wire.write((byte*) (&counter), sizeof(counter));
break;
case 2:
Wire.write((byte*) (&counter), sizeof(counter));
break;
case 4:
Wire.write((byte*) (&true_value), sizeof(true_value));
break;
default:
bool false_value = false;
Wire.write((byte*) (&false_value), sizeof(false_value));
break;
}
} // of sendDataVector
void compute_temp_set() {
for (int i=0; i<4; i++) {
temp_set.temp[i] = i*3;
}
temp_set.value[0] = sizeof(type_temp);
temp_set.value[1] = sizeof(counter);
temp_set.value[2] = sizeof(true_value);
temp_set.value[3] = sizeof(pi);
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Wire.begin(SLAVE_ADDRESS); // Join I2C as a slave
Wire.onRequest(sendDataVector);
Wire.onReceive(receiveEvent);
}
void loop() {
compute_temp_set();
noInterrupts();
memcpy( (void*)&out_set, (void*)&temp_set, sizeof(type_temp) );
interrupts();
}
Output of the Python program:
Starting
LED on (write, register 254).
Read the whole out_set structure (32 bytes, 8 values; read, register 0):
[0.0, 3.0, 6.0, 9.0] [32, 4, 1, 4]
Read the initial value of the counter after incrementing it (0+1=1; read, register 1):
1
Read the True value (read, register 4):
True
Read the False value with unused register 3 (default):
False
Read the 3.1415 float value (read, register 48):
3.1414999961853027
Set counter to 1025 (write, register 6).
Read the counter without incrementing it (read, register 2).
1025
Increment the counter (read, register 1).
Read the counter without incrementing it (read, register 2).
1026
Read the counter after incrementing it (read, register 1).
1027
Read the counter without incrementing it (read, register 2).
1027
LED off (write, register 253).
Note on the following error
OSError: [Errno 121] Remote I/O error
This error might occasionally occur while using the smbus.SMBus(1)
I2C hardare device on pins 3 (GPIO2/SDA1) and 5 (GPIO3/SCL1),
especially when multiple read/write operations are issued without delay.
It happens due to incompatibility between the Raspberry Pi hardware and many peripheral devices, including AVR microcontrollers (ATMEGA TWI / I2C slave mode requires clock stretching, which the I2C hardare device does not support).
One possible workaround (which might not be supported by all the Raspberry Pi models) is to slow down the I2C speed, e.g. with dtparam=i2c_arm_baudrate=10000
in /boot/config.txt
. Such configuration, while improving bus robustness, might result in unacceptable I2C performance.
Another solution consists in selecting the Raspberry Pi Software I2C implementation, bit-banging the I2C protocol from the kernel. By default, the Software I2C uses smbus.SMBus(3)
(e.g., dtoverlay=i2c-gpio,bus=3
in /boot/config.txt
) and SDA will be on GPIO23, while
SCL will be on GPIO24 which are pins 16 and 18 on the GPIO header respectively. They do not have the pullup resistors.
cmd
parameter is... I had to do a fair bit of looking to figure out what you meant. I didn't find an answer though... It may only be used by specific chips like a GPIO expander or something...vals
,cmd
is mandatory.