Read I2C data from Arduino slave

I am new to Raspberry PI, more used to the Arduino and have done a little coding in Processing, and really like the concept of Processing. It’s a great combination, Processing and Arduino. Needed the Power and Speed of PI. The PI seems to be a big mess of diffrent Python 2 and 3, scripts and different kernels and config files etc. etc., but nice when its working.I have used Arduinos as Master and Slave in different combinations before in various projects. Because I feel for continue to work with processing instead of learning Python 2 or Python 3. I have started a robot project with Raspberry Pi running processing as the Master and two Arduinos as slaves. One Arduino drives two motors and it´s working great. The other Arduino should have been the link between a Garmin Lidar Lite V3 in PWM mode and the PI. Lidar Lite V3 works with I2C but not with the PI because of some Kernel update, and some patch not working on the latest PI3B+ but on older PI:s (some PI frustration here).

For the last two weeks I have tried to read data from the Arduino from processing running on the PI. I have tried a lot of different code both on the Arduinos and the PI.

I have started to think there are som issue with the read function in the I2C library or a combination with the “forced repeated start” kernel on the PI and the Arduino emulation of an I2C device. Or maybe processing on the latest PI3B+? The Arduino standard code are based on a Request Event. Does the processing Read() really Request data on I2C data line? or does it wait for incoming data in the buffer? (more like a slave device?)

Any advice or experience would be great!!

1 Like

Hello @Embring - thank you for your comments, those are greatly appreciated.

As for the I2C communication: I have never tried taking from the Raspberry Pi to an Arduino (“slave”) over I2C. That said, this should work, so I’d be happy to help you debug this issue and make the necessary changes to the Hardware I/O library to support this.

The the read() method tells the kernel driver (via the I2C_RDWR ioctl) to read in the given number of bytes from the previously opened I2C device handle.

Do you have this type of communication working in any other programming environment?
Could you upload sketches for both Pi, as well as Arduino for me to test?

2 Likes

Hello @gohai - thanks for your respons and your generous offer to help me debug this issue. :slight_smile:

First of all: I get a positive respons with the hex 0X21 adress running “i2cdetect -y 1” in the PI terminal. My set up works OK. And I can also read and write to and from other I2C devices like MCP23017 from the PI.

My referensers like “ARD2.read(2)” are picked from my code below so please read it not to be confused…

I have made the code very basic do get this going… also tried different “wait” times.
I think it’s something with this “requestEvent()” on the Arduino side and the “ARD2.read(2)” that doesn’t work well together.

Do look at this page with two Arduinos working fine as Master and Slave. https://www.arduino.cc/en/Tutorial/MasterReader
The Arduino code “Wire.requestFrom(8, 6);” in that code are interresting compared tho the processing “ARD2.read(2)”

The ARD2 receives and do “Serial.println(opcode);” with no problem and I can read it on my Serial Monitor from the Arduino.

In the Arduino Code:
I have also tried to remove the “if (opcode == Status) { }” statement and run the code anyway.
And I have also tried to put the “Wire.write(H_DIST);” directly in the receiveEvent() but that was a kind of desperate… :slight_smile:

Processing Code:

import processing.io.*;
I2C ARD2;
//**********************************************************
void setup() {
  //printArray(I2C.list());
  ARD2 = new I2C(I2C.list()[0]);
}
//**********************************************************
void draw() {
  // read DIST over I2C from Arduino2
  // with address 0x21
  ARD2.beginTransmission(0x21);
  // first send a command byte
  ARD2.write(0x41);
  // read in two bytes
  //wait(10);
  byte[] DIST = ARD2.read(2);
  println(DIST);
  //wait(500);
}

//**********************************************************
void wait(int num) {
  int j=0;
  int i=millis();
  while (j<i+num) {j=millis();}  
}

Arduino Code:

#include <Wire.h>

#define I2C_ADDRESS 0x21 // ARD2 address

uint8_t opcode; // command register
uint8_t H_DIST; // High byte
uint8_t L_DIST; // Low byte
uint8_t Status; //send data

void setup() {
Wire.begin(I2C_ADDRESS);
Serial.begin(9600); 
H_DIST=32;
L_DIST=64;
Status=65;

Wire.onRequest(requestEvent);

Wire.onReceive(receiveEvent); 
}

void loop() {
  Serial.println('*');
  delay(100);
}

void receiveEvent(int bytes) {
  // Read the first byte to determine which register is concerned
  opcode = Wire.read();
  Serial.print("received_opcode:");
  Serial.println(opcode);
  // If there are more than 1 byte, then the master is writing to the slave
  //if (bytes > 1) {
  // }
 
}

void requestEvent() {
  // Read from the register variable to know what to send back
  if (opcode == Status) {
  Wire.write(H_DIST);
  Wire.write(L_DIST);
  Serial.println("writes");
  }
}
1 Like

Thank you @Embring - I am traveling till the middle of the week, but I’ll try to give this a try as soon as I am back in the studio.

Hello @gohai -THX!, I have a lot of work this week. This is only my hobby, Happy to discuss this issue with you from time to time.

I found this article, maybe some clue here?


In this example Python use some I2C support called SMBus and it’s seems to be some kind of “derivative” of the I2C bus. I don´t understand the diffrence between I2C and SMBus. But it seems to do the work in a lot of python code.
But I also found this:
https://www.raspberrypi.org/forums/viewtopic.php?t=200546
“The Raspberries (all versions) have a hardware-bug in the i2c controllers. The bug being that the rPi cannot correctly handle clock-stretching.”

Can it be “bad timing”, “clock-stretching” in the I2C communication between PI and Arduino?
Arduinos are kind of slow, and PI much faster.

There are a lot of information out there, really hard to find the right one… :slight_smile:

1 Like

@Embring Sorry for the delayed response - but I have finally found time to look at it with a logic analyzer!

I was able to get your sketch to run by removing the Serial.println() in loop. My guess is that Arduino might either disable interrupts during parts of the interaction with the UART, or - perhaps - that the work in parallel makes it so that the Arduino can’t keep up with the rapid demands of the I2C bus.

Working on this I found two improvement - which you can find here, and which should become part of the upcoming Processing release: https://github.com/gohai/processing/commits/io-i2c

First: just doing a read() without a write() crashed - this is fixed now.
And secondly: while I can’t change the I2C bus speed, I can set a “timeout” too 100ms. This gives the bus partner more time to respond, and made it very smooth & stable.

Please give this a try when the next release is out - could also be a nice idea to write an example sketch for this type of communication (haven’t even thought of using the Arduino as a “slave” device).

Thanks! :+1: Looking forward to try this. Interesting with your removing of Serial.println().
Using Arduino as “slave” opens a lot of doors. I have made my own I2C motor controller with an small Arduino clone to save space. Excellent that you found two improvements for the next release! :+1::+1:

Agree, really like the idea of using I2C to trigger action on an attached microcontroller - compared to having to do message-parsing in Serial, which comes with its own sets of problems. Additionally, I2C communication with a 5V Arduino worked fine without any level-shifting even, which makes this even more versatile.

Processing 3.3.7.1 is out now, which has the aformementioned fixes.