Go Down

Topic: [I2C] onRequest and onReceive as slave with Raspberry Pi Master (Read 9391 times) previous topic - next topic

brainvoid

Sep 10, 2013, 06:39 pm Last Edit: Sep 10, 2013, 06:57 pm by brainvoid Reason: 1
Hey guys,
I discovered a big Problem for my Project today. I try to connect a few Arduinos to a Raspberry Pi with I2C. I have a onRequest and a onReceive function (code below). On my Raspberry Pi I use smbus with python.

My onReceive function works fine and well, every "bus.write[...]" function calls my onReceive function as it should.
But not onRequest. Only when i call "read_byte(addr)" in my python script, the Arduino will call the onRequest function. But it is necessary that I can use at least "byte read_byte_data(addr,cmd)" or "long[] read_block_data(addr,cmd)". These functions call the onReceive Method, whyever.

So I thought "no Problem", just use onReceive like onRequest and write data there. But then the BUS is corrupted and the Python on Raspberry Pi gets an I/O Error.

I just recognized that with the command "print bus.read_i2c_block_data(0x04, 0x01)" it prints nearly the right output (take a look in my log below), but on my Arduino Serial connection the "dataRequest() called with command" is not printed. I just don't understand o_O

I don't know what I'm doing wrong o_O. Can someone explain this?

Best regards
brainvoid

Arduino Code: (for test purposes I call the functions on the Raspberry directly from the python shell, so no script here)
Quote

#include <Wire.h>

#define I2C_ADDRESS 0x04

void setup() {
 Serial.begin(9600);
 Serial.print("initialize..");
 Wire.begin(I2C_ADDRESS);
 Wire.onReceive(dataReceive);
 Wire.onRequest(dataRequest);
 Serial.println(".");
}

void loop() {
 delay(10);
}

void dataRequest(){

 byte b[32];
 int availableBytes = Wire.available();
 Serial.print("dataRequest() called with command: ");
 Serial.println(availableBytes);
 for(int i = 0; i < 32; i++)
   b = i;
 byte *ptr = b;
 Wire.write(ptr, 32);
}

void dataReceive(int byteCount) {
 byte receiveArray[32];
 byte *arrayPtr = receiveArray;
 while(Wire.available())
 {
   byte b = Wire.read();
   *arrayPtr = b;
   arrayPtr++;
 }
 Serial.println("Data received");
}



Here is a log of my SSH commandline with python:

Quote
pi@raspberrypi:~$ i2cdetect -y 1
    0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- 04 -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
pi@raspberrypi:~$ python
Python 2.7.3 (default, Jan 13 2013, 11:20:46)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from smbus import SMBus
>>> bus = SMBus(1)
>>> print bus.read_byte(0x04) || prints dataRequest() called with command: 0
0
>>> print bus.read_byte_data(0x04, 0x01)  || prints Data received
9
>>> print bus.read_block_data(0x04, 0x01)  || prints Data received
[208, 119, 179, 190, 208, 119, 179, 190, 36]
>>> print bus.read_i2c_block_data(0x04, 0x01)  || prints Data received
[9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
>>> bus.write_byte(0x04, 0x05) || prints Data received



pylon

Quote
But not onRequest. Only when i call "read_byte(addr)" in my python script, the Arduino will call the onRequest function. But it is necessary that I can use at least "byte read_byte_data(addr,cmd)" or "long[] read_block_data(addr,cmd)". These functions call the onReceive Method, whyever.


You misunderstood something. The onReceive method will only be called if the master does a read. The read_byte_data() call probably first sends a command (which calls onReceive) and then reads the response (which in turn will call onRequest).

brainvoid


You misunderstood something. The onReceive method will only be called if the master does a read. The read_byte_data() call probably first sends a command (which calls onReceive) and then reads the response (which in turn will call onRequest).


Okay, thanks for the fast answer.
But in this case i ask myself, why my Arduino just Serial.prints "Data received" when i call ">>> print bus.read_byte_data(0x04, 0x01)" when read_byte_data first calls onReceive and then onRequest. IMHO it should print

Data received (from onReceived)
dataRequest() called with command... (from onRequest)

pylon

Quote
SMBus Read Byte:  i2c_smbus_read_byte_data()
============================================

This reads a single byte from a device, from a designated register.
The register is specified through the Comm byte.

S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P


It seems that your call transfers the byte and then does not send a stop condition but a repeated start condition. The TWI library doesn't handle this correctly. You either have to program the hardware directly or use other commands on the Raspberry Pi (a combination of write() and read_byte()).

lialosiu

I just found the answer with this problem.

First, sorry for my poor english. And...

When we use read_i2c_block_data(), It was too fast to write a command byte and read the data.

In that time, if we use Serial.println() to print log to serial. Arduino will can not follow the speed. and it missing the i2c read flag.

So, the dataReceive() funtion will not be run at all.

The solution is simple: just do not use Serial library until the i2c is free.

Go Up