I am trying to send data from an rp2040 to a Raspberry Pi via i2c. I am using Arduino on the rp2040 and python on the Raspberry Pi.
The use case is similar to that of a midi controller; the data bandwidth doesn't need to be particularly high but the latency and frequency need to be fast.
The following, in which I request one byte at a time works well, with very fast throughput, meaning it is printing hundreds of bytes per second. I suspect the bottleneck is actually the print, even though it's inducing the overhead of a new request for every single byte:
Edit: Equivalent code in Rust using the rppal library, and the same arduino code, does not have this problem. However if at all possible I'd rather use python for other reasons.
Aruino:
#include <Wire.h>
const int ADDRESS = 0x08; // I2C address
volatile int tenBitValue = 0; // 10-bit value
volatile byte val = 0; // 8-bit value to be transmitted
void setup() {
Serial.begin(115200);
Wire.begin(ADDRESS);
Wire.setClock(400000); // Set I2C clock speed to 400kHz
Wire.onRequest(sendData);
}
void loop() {
tenBitValue = analogRead(A0); // Example: read value from analog pin A0
val = map(tenBitValue, 0, 1023, 0, 255);
Serial.println(val);
}
void sendData() {
Wire.write(val); // Send the 8-bit value
}
Python:
import smbus
device_address = 0x08
while True:
print(bus.read_byte(device_address))
However I need more than one byte. The following, in which I use the block_data function and specify 2 bytes, and send 2 bytes from the arduino, is sending maybe 5 - 10 bytes a second which orders of magnitude slower than 1 byte at a time and won't work for my use case:
Arduino:
#include <Wire.h>
const int ADDRESS = 0x08; // I2C address
volatile int tenBitValue = 0; // 10-bit value
volatile byte val = 0; // 8-bit value to be transmitted
void setup() {
Serial.begin(115200);
Wire.begin(ADDRESS);
Wire.setClock(400000); // Set I2C clock speed to 400kHz
Wire.onRequest(sendData);
}
void loop() {
tenBitValue = analogRead(A0); // Example: read value from analog pin A0
val = map(tenBitValue, 0, 1023, 0, 255);
Serial.println(val);
}
void sendData() {
Wire.write(val); // Send the 8-bit value
Wire.write(234); // Send dummy data
}
Python:
import smbus
device_address = 0x08
while True:
print(bus.read_i2c_block_data(device_address, 0, 2))
- Printing from the main loop on rp2040 to the serial port of my dev machine happens at expected speed even when the i2c connection is active.
- Printing from the actual callback in the arduino code rather than the arduino loop is exactly as slow as the data received by the python script, which makes sense since it's responding to requests.
- Note that with 2 bytes it is much, much more than 2 times slower.
- I am testing with one and two bytes, but I will need this to work with any number of bytes (within reason). Each byte would represent a value from a different sensor or other input.
- I am not experienced with this sort of thing. I am sure there's a way to roll my own protocol so that the arduino sketch can send a new byte per cycle and the python script can decode it and handle the data accordingly. I am not fully opposed to that if that's what it takes, but I would much rather focus on other things if I can just get the library to work.