I2C read a specific byte

Hi,

Im want to get my Pi to communicate with the DUE over I2C.

I have several data bytes on my Due which I want to read out.

For example, the brightness sensor is on the same bus and is read like this:

i2cbus.read_byte_data(dev_address, DATAMSB)

where dev_adress is the adress of the sensor and DATAMSB is the Register the data is stored in.

How do you read a specific register from my Due?

I looked into the MasterReader example, but there is just a "Wire.onRequest(requestEvent)" which is called on a request.

Is the "selection of the register" a write to the Slave and I have to write my own function to send the selected register on the due?

Thanks!

EDIT:

would that work? Can I read the bus when the "requestEvent" is fired? Or will the "onReceive" Event start first and then the "requestEvent" ?

#include <Wire.h>

void setup()
{
  Wire.begin(2);                // join i2c bus with address #2
  Wire.onRequest(requestEvent); // register event
}

void loop()
{
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  char requestedbyte;
  while(1 < Wire.available()) // loop through all but the last
  {
     requestedbyte = Wire.read(); // receive byte as a character
  }
  switch (requestedbyte) {
    case 1:
      //do something when requestedbyte equals 1
      Wire.write("timer1");
      break;
    case 2:
      //do something when requestedbyte equals 2
      Wire.write("timer2");
      break;
  }   
 }

I'm not sure about the Due, but on the AVR models, the "onReceive" is when you get the data from the master. Then in "requestEvent" you respond. Basically the I2C bus has switched from "send to slave" mode to "receive from slave" mode. You would not read data in requestEvent.

Thank you for your fast responce.

I got it to work and here is my sketch. Pretty simple in retrospect.

//********** I2C functions **********//
void requestEvent()
{
 switch (requestedbyte) {
    case 2:
      //do something when requestedbyte equals 1
      Wire.write(2);
      break;
    case 3:
      //do something when requestedbyte equals 2
      Wire.write(3);
      break;
  }
}

void receiveEvent(int howMany)
{
 while(Wire.available())
  {
    // read I2C value
    requestedbyte = Wire.read();
    }
  }
}

The next problem I have is to send Integer. I tried to divide them into msb and lsb but this only works for values up to 255, it wraps back to 0 when I send 256.

Here is my Arduino Code:

void requestEvent()
{

 uint8_t val = 260;
  byte lsb = val & 0xff;  // lsb = Least significant byte
  byte msb = val >> 8; // msb = Most significant byte

byte buf [2] = { lsb, msb};
      Wire.write(buf, sizeof buf);

}

And this is my Python (Raspberry Code)

    def test(self,byte):
          data = i2cbus.read_word_data(dev_address, byte)
        return data

long read_word_data(int addr,char cmd)
is defined as 16bit reading

Are you saying that is working, or not working? If not, what numbers are you getting?

It works only with one Byte. When I change val to different values:

When I change it to 255, I get 255
When I change it to 256, I get 0
When I change it to 260, I get 4
When I change it to 1024, I get 0
When I change it to 1124, I get 100

and so on...

uint8_t val = 260;

uint8_t is the same thing as a byte, its max value is 255, after that it will wrap around.

Woops, I didnt see that, Thanks for the hint! It works now :slight_smile:

Next thing is to also send negative numbers

I changed the code to the following

int16_t val = -1124;
  int8_t lsb = val & 0xff;  // lsb = Least significant byte
  int8_t msb = val >> 8; // msb = Most significant byte

int8_t buf [2] = { lsb, msb};
      Wire.write(buf, sizeof buf);

But I get the following error when compiling

Analog_Board.ino: In function 'void requestEvent()':
Analog_Board.ino:138:33: error: call of overloaded 'write(int8_t [2], unsigned int)' is ambiguous
Analog_Board.ino:138:33: note: candidates are:
In file included from Analog_Board.ino:2:0:
C:\Users\email_000\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.4\libraries\Wire/Wire.h:48:17: note: virtual size_t TwoWire::write(const uint8_t*, size_t) <near match>
  virtual size_t write(const uint8_t *, size_t);
                 ^
C:\Users\email_000\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.4\libraries\Wire/Wire.h:48:17: note:   no known conversion for argument 1 from 'int8_t [2] {aka signed char [2]}' to 'const uint8_t* {aka const unsigned char*}'
In file included from C:\Users\email_000\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.4\cores\arduino/Stream.h:26:0,
                 from C:\Users\email_000\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.4\libraries\Wire/Wire.h:27,
                 from Analog_Board.ino:2:
C:\Users\email_000\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.4\cores\arduino/Print.h:54:12: note: size_t Print::write(const char*, size_t) <near match>
     size_t write(const char *buffer, size_t size) {
            ^
C:\Users\email_000\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.4\cores\arduino/Print.h:54:12: note:   no known conversion for argument 1 from 'int8_t [2] {aka signed char [2]}' to 'const char*'
call of overloaded 'write(int8_t [2], unsigned int)' is ambiguous

As I see write() only takes unsigned values....

You must send bytes, so use type casting :wink:

Example

Sender:

int8_t var = -123;

Wire.write( (uint8_t) var ); // Send as a byte

Receiver:

int8_t var = Wire.read(); // Read a byte as a signed int

Like this?

{
int bigVal = 46852;
//I2CWrite(bigVal);
int16_t val = -1124;
  uint8_t lsb = val & 0xff;  // lsb = Least significant byte
  uint8_t msb = val >> 8; // msb = Most significant byte

uint8_t buf [2] = { lsb, msb};
      Wire.write((uint8_t*)buf, sizeof buf);
}

But I still get 64412 as the output :(. All I want is 16bit signed :smiley:

Can't you just send those 2 bytes separately, instead of an array ?

Also here is another method that should work

Sender:

int16_t var = -12345;
Wire.write( (uint8_t *) &var, sizeof( var ) );

Receiver:

int16_t var = ( (uint16_t) Wire.read() ) | ( (uint16_t) ( Wire.read() << 8 ) );

I suggest that you write out the hexadecimal value of the bytes you actually transmit and receive, to check they are correct for negative numbers.

Make sure the 16 bit int, that you put the values into when you receive them, is a signed int.

See I2C - send and receive any type.