I am currently trying to establish two-way communications between an Arduino Leonardo and a Pi model B using the I2C bus, as slave and master respectively. Spookily, the Pi can successfully send commands to the Arduino, but fails when requesting data from the Arduino. Spcifically, smbus2
throws OSError: [Errno 121] Remote I/O error
whenever I call the moisture_read()
function. The included water()
function has the intended outcome -- the Arduino reacts and alters its pins (more or less) correctly.
Arduino Code:
#include <Wire.h>
#define SOILPWR 11
#define SOILSIG A0
#define MOTORPWR 5
#define MOTORDTEC A1
#define BUFSIZE 16
#define CHANNEL 0x03
int readMoisture(byte power, word dlay){
analogWrite(SOILPWR,power);
delay(dlay);
int moisture = analogRead(A0);
analogWrite(SOILPWR,0);
return moisture;
}
void motorPulse(byte power, word dlay){
// Motor is active low
analogWrite(MOTORPWR,255-power);
delay(dlay);
analogWrite(MOTORPWR,255);
delay(1000);
}
void setup() {
// put your setup code here, to run once:
pinMode(SOILSIG, INPUT);
pinMode(SOILPWR, OUTPUT);
pinMode(MOTORPWR, OUTPUT);
pinMode(MOTORDTEC, INPUT);
digitalWrite(MOTORPWR, HIGH);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
Wire.begin(CHANNEL);
#ifdef DEBUG
Serial.begin(9600);
#endif
}
// BYTE STRUCTURE:
// ['S'][speed][time_LSB][time_HSB]
void receiveEvent(int nbytes){
byte msg[nbytes];
for (int i=0;i<nbytes;i++){
msg[i]=Wire.read();
}
#ifdef DEBUG
Serial.print("Message:");
for (int i=0;i<nbytes;i++){
Serial.print(msg[i]);
Serial.print('\n');
}
Serial.println();
#endif
byte b=msg[0];
if (nbytes >= 4 && b == 'S'){
// S for [S]oak
// look I don't know why it's like this
byte fast = msg[1];
word t = msg[2] + (word) msg[3] << 8;
#ifdef DEBUG
Serial.print("Time: ");
Serial.println(t);
#endif
// Limit watering to 1 minute
t = t < 60000 ? t : 60000;
motorPulse(fast,t);
} else {
#ifdef DEBUG
Serial.println("FAILED");
#endif
}
}
byte sendbuf[BUFSIZE];
// BYTE STRUCTURE
// [soil_LSB][soil_MSB][0][0]
void requestEvent(){
int moisture = readMoisture(255, 200);
sendbuf[0] = moisture&0x0f;
sendbuf[1] = moisture&0xf0;
for (int i=2; i<BUFSIZE;i++){
sendbuf[i]=0x00;
}
Wire.write(sendbuf, 4);
}
void loop() {
delay(100);
}
Pi Code:
import smbus2
import struct
import time
CHANNEL = 0x03
# Bus number is 1
bus = smbus2.SMBus(1)
def water(speed, time):
req_str = struct.pack('>B',speed)
req_str += struct.pack('<H',1024*time)
bus.write_i2c_block_data(CHANNEL, ord('S'), req_str)
def moisture_read():
# read a block of 4 bytes (offset 0)
res = bus.read_i2c_block_data(CHANNEL, 0, 4)
return struct.unpack('<HBB',res)
read_byte_data
,read_byte
andread_i2c_block_data
fail with the same (rather unhelpful) message. – catalogue_number Jul 10 '19 at 22:12sudo pigpiod
pigs i2co 1 x 0
where x is the Arduino address and then usepigs i2crd 0 4
– joan Jul 11 '19 at 7:36