I2C Requestfrom bytes not in use?

Hi

I want to create a MasterReader / Slave Sender scenario with 2 arduinos. The slave has different types of data which the Master has to select.

I wanted to use the Master Reader example here: https://www.arduino.cc/en/Tutorial/MasterReader

First I thought that I could use requestFrom with the byte count as pointer but this value is never forwarded to the slave, is it?

Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8

what is the use of the byte count?

right now Im using this on the Slave:

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

void requestEvent()
{
  switch (requestedbyte) {
    case 1:  //does nothing
    
    case 2: { //Sends the Device Hardware ID (Serial)
        Wire.write(ID);
      
      }
    case 3: { //Sends the Device Hardware Version
        Wire.write(HARDVERSION);
     
      }
    case 4: { //Sends the Device Software Version
        Wire.write(SOFTVERSION);
       
      }
}
}

Master:

void readCoProcessor() {
  //Reading the Pressure Values

    Wire.beginTransmission(COPROCESSOR_ADRESS);
    Wire.write(6);// set the Pointer
    Wire.endTransmission();


    Wire.requestFrom(COPROCESSOR_ADRESS, 1);   // request from


    pressure[1] = Wire.read();

You are right, the Slave does not know how many bytes are requested.

When the Slave is a normal hardware chip, then it makes sense. Most I2C devices have registers and a register address (you call it a "pointer"). First the register address is written, then one or more bytes from the registers are requested. The chip keeps sending the consecutive bytes as long as the Master is requesting them.

The Arduino Slave can not do that, because the I2C is part hardware and part software. The Arduino Slave can write up to 32 bytes with Wire.write() in the requestEvent(). They will be written in a buffer and the Master can request one or more bytes. Everything is okay if the Master reads less. The Slave can also return less than the Master requests. Then the Wire.requestFrom() function returns the actual received bytes and Wire.available() also returns the actual received bytes that are waiting in a buffer (a receive buffer inside the Wire library).

You can make your own registers.

byte myRegisters[] = { 0, 0, ID, HARDVERSION, SOFTVERSION };
byte myRegisterAddress;

void receiveEvent(int howMany) {
  // howMany is the number of bytes, we use only the first byte.
  myRegisterAddress = Wire.read();    // set the index (or pointer) to the registers.
}

void requestEvent() {
  // return just one byte. It is unknown how many bytes the Master did request.
  Wire.write(myRegisters[myRegisterAddress]);
  // myRegisterAddress++;     // often the register address increments.
}

The Master can be like this:

    Wire.beginTransmission(COPROCESSOR_ADRESS);
    Wire.write(6);// set the Pointer
    Wire.endTransmission();

    delay(1);     // Sometimes an Arduino Slave I2C needs some time to recognize the new transmission.

    int n = Wire.requestFrom(COPROCESSOR_ADRESS, 1);   // request one byte from Slave
    if(n == 1) {
      // The returned number of bytes is equal to the requested number of bytes.
      // That means the Slave is okay and responding.

      pressure = Wire.read();
    }

Does it make sense ? Do you understand how requesting more bytes is very useful when reading a sensor or I2C memory ? If not, just ask.

Thank you for the detailed explanation!

As already mentioned, I tried the MasterReader sketch and it worked, so my Hardware is properly wired.

I wrote these two sketches for testing purpose only but somehow, all I get is “0” on the master’s terminal :-/

Master

#include <Wire.h> //Include I2C Library


#define COPROCESSOR_ADRESS 0x22
void setup() {
  // put your setup code here, to run once:
Serial1.begin(38400);
}

void loop() {
  // put your main code here, to run repeatedly:
Wire.beginTransmission(COPROCESSOR_ADRESS);
    Wire.write(6);// set the Pointer
    Wire.endTransmission();

    delay(1);     // Sometimes an Arduino Slave I2C needs some time to recognize the new transmission.

    int n = Wire.requestFrom(COPROCESSOR_ADRESS, 1);   // request one byte from Slave
    if(n == 1) {
      // The returned number of bytes is equal to the requested number of bytes.
      // That means the Slave is okay and responding.

      int pressure = Wire.read();
      Serial1.println(pressure);
    }
}

Slave:

#include <Wire.h> //Include I2C Library

#define i2CAdress 0x22    // I2C Adress
#define DEBUG 0

void setup() {

  if (DEBUG) Serial.begin(9600);
  Wire.begin(i2CAdress); //joining I2C Bus as Master
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

}

//********** I2C functions **********//
void receiveEvent(int howMany) {
  myRegisterAddress = Wire.read();    // set the index (or pointer) to the registers.
}


void requestEvent()
{
  switch (myRegisterAddress) {
    case 1:  //does nothing
    
    case 6: { // Sends the Module Presssure
      Wire.write(1);
         //Wire.write(getModulePressure());

     
      }
    case 7: {
      Wire.write(2);
       // Wire.write(PressCh1.getPressure());

  
      }
    case 8: {
      Wire.write(3);
        //Wire.write(PressCh2.getPressure());
       
     
      }
  }
  byte result = Wire.endTransmission ();
}

Thanks!

You set the register-address ("pointer") to 2 in the Master sketch, and you only return something when it is 6,7 or 8 in the Slave sketch.

Maybe you misunderstood this ? "The Arduino Slave can not do that, because the I2C is part hardware and part software. I ment that the Arduino as Slave has hardware I2C in the ATmeg328P chip, but also software (the Wire library and the onRequest and onReceive handlers). Therefor the Arduino as Slave is not the same as a I2C sensor or I2C memory chip.

Did you know that you can develop more than one sketch at the same time ? Connect both Arduino boards to the computer. Start the Arduino IDE and select the proper Arduino board with the serial port. Move this Arduino IDE to one side of the screen. Start another instance of the Arduino IDE and select the other board. Now you have them both on your screen, and also each one with its own serial monitor.

Koepel:
You set the register-address (“pointer”) to 2 in the Master sketch, and you only return something when it is 6,7 or 8 in the Slave sketch.

Yes, that was my mistake which I corrected after I sent my answer :smiley: But I still get 0’s in the terminal :frowning:

Im developing under Mac with two custom boards. One Atmega328 as slave and a Sam3x8e as master. The slave is programmed over avrdude, the master over openocd, both over Atmel Sam-ICE

The atmega is reading some sensors over SPI and has about 20 values to read by the Sam3x8e over I2C.

That is why I have to read the values one by one by selecting and transferring it via I2C.

The size of the values is from one 8 to 16 bit.

I forgot the word 'volatile', sorry. Every global variable that is changed in an interrupt routine must be volatile.

volatile byte myRegisterAddress;

The Slave sketch in Reply #2 does not declare "myRegisterAddress" ? And it should not use Wire.endTransmission() at the end of requestEvent().

How much time do those PressCh2.getPressure() functions need ? I hope they don't have a delay and they don't use Serial function. I even think it should not be there inside the interrupt handler requestEvent().

I have my Slave like this: In the loop() all the information from the sensors is gathered. The DS18B20 and DHT11 require certain timing, that is far too slow for the interrupt handler. I store everything in a byte array : volatile byte myRegisters [20] ; When more than one byte is written, I disable the interrupts. The Master can request one or more bytes from that byte array.

Another Slave receives data wireless and stores the incoming packets with a timestamp in EEPROM. The Master can request one or more bytes from the EEPROM. The timestamp is the unix GMT time which the master sends to the Slave at startup and also a few times a day.

If the received packet is from a motion detector, that Slave signals the Master via an extra wire. The Master can sound an alarm.

Sorry, I was declaring the “myRegisterAddress” before the setup. I added the volatile but it makes no difference at all.

The ressCh2.getPressure() is uncommented. I always send 1, 2 or 3 just for testing purpose only but I only get back 0.

On the slave I added a Serialprint when a receive or request function was fired, but it looks like that only the receiveEvent was triggered, the request Event not at all!

This is the masters code

void readCoProcessor() {
   Wire.beginTransmission(COPROCESSOR_ADRESS);
    Wire.write(7);// set the Pointer
    Wire.endTransmission();

      int n = Wire.requestFrom(COPROCESSOR_ADRESS, 1);   // request one byte from Slave
    if(n == 1) {
      // The returned number of bytes is equal to the requested number of bytes.
      // That means the Slave is okay and responding.

      RS422.print("Data received: ");
     RS422.println(Wire.read());
    }
}

This is the full slave code

#include <Wire.h> //Include I2C Library
#define i2CAdress 22    // I2C Adress
volatile byte myRegisterAddress = 0; //I2C request Byte
volatile bool triggered=0;
void setup() {

  Serial.begin(9600);
  Wire.begin(i2CAdress); //joining I2C Bus as Master
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);

}

void loop() {
  // put your main code here, to run repeatedly:
if (triggered){
  Serial.println("requestevent Triggered");
  triggered =0;
}
}

//********** I2C functions **********//
void receiveEvent(int howMany) {
  Serial.print("Event received: ");
  
  myRegisterAddress = Wire.read();    // set the index (or pointer) to the registers.
  Serial.print(myRegisterAddress);
}


void requestEvent()
{
  triggered = 1;
  switch (myRegisterAddress) {
    case 1:  //does nothing
    
    case 6: { // Sends the Module Presssure
      Wire.write("1");
         //Wire.write(getModulePressure());

     
      }
    case 7: {
      Wire.write("2");
       // Wire.write(PressCh1.getPressure());

  
      }
    case 8: {
      Wire.write(3);
        //Wire.write(PressCh2.getPressure());
       
     
      }
  }
  //byte result = Wire.endTransmission ();
}

The Output from the master is always: Data received: 0

The Slaves Output: Event received: 7

Using Serial inside an interrupt handler is not okay. If the communication is a problem, try to fix that with a very simple sketch. For example return the myRegisterAddress plus 100. A value of at least '100' should be returned and not '0'.

void loop() {
  // put your main code here, to run repeatedly:
  if (triggered){
    Serial.println("requestEvent Triggered");
    Serial.print("myRegisterAddress=");
    Serial.println(myRegisterAddress);
    triggered = false;
  }
}

void receiveEvent(int howMany) {
  myRegisterAddress = Wire.read();    // set the index (or pointer) to the registers.
}

void requestEvent() {
  triggered = true;
  Wire.write(myRegisterAddress + 100);
}

In the Master, the return value of Wire.endTransmission contains an error. You could check that.

      int error = Wire.endTransmission();

      int n = Wire.requestFrom(COPROCESSOR_ADRESS, 1);   // request one byte from Slave
      if(n == 1) {
        // The returned number of bytes is equal to the requested number of bytes.
        // That means the Slave is okay and responding.

        RS422.print("Data received: ");
        RS422.println(Wire.read());
      }

      if( error != 0 || n != 1) {
        Serial.print("Error, ");
        Serial.print("error=");
        Serial.print(error);
        Serial.print(", n=");
        Serial.println(n);
      }