2

I'm building a complex I2C-based networking protocol and have noticed that the Wire.onReceive() handler is called both for I2C read and write operations. When the master writes to the slave, it is called as you'd expect. But it also seems to be invoked when the master reads from the slave which is unexpected.

Ideally, I'd like a transaction with both a read and a write - e.g. the master writes a value (for instance a variable argument) and then reads back from the slave the result (for instance, the lookup of that variable.)

Compiled on the SAMD21 ArduinoCore, the following programs generate this result:

1400,700
>>> x is 
<<< hello 
>>> 
>>> x is 
<<< hello 
>>> 
1404,702
>>> x is 

Showing that the onReceive is called twice as often as onRequest.

I don't see this behavior documented anywhere.

Master: #include

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop()
{
  Wire.beginTransmission(4); // transmit to device #4
  Wire.write("foobar");        // sends five bytes
 // Wire.write(3);              // sends one byte  
  Wire.endTransmission(true);    // stop transmitting


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

  while(Wire.available())    // slave may send less than requested
  { 
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(500);
}

Slave

#include <Wire.h>

void setup()
{
  Wire.begin(4);                // join i2c bus with address #4
  Wire.onReceive(receiveEvent); // register event
  Wire.onRequest(requestEvent);
  Serial.begin(9600);           // start serial for output
}

long inCount = 0;
long outCount = 0; 

void loop()
{
  char c[200];
  snprintf( c, 200, "%d,%d\n", inCount, outCount );
  Serial.print( c );
  delay(1000);

}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
inCount++;

  Serial.print( ">>> ");
  while(1 < Wire.available()) // loop through all but the last
  {
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }

  Serial.println();         // print the integer
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  Serial.println( "<<< hello ");

  outCount++;
  Wire.write("hello "); // respond with message of 6 bytes
                       // as expected by master
}
  • 1
    The Wire.onReceive is not called when the Master is reading data. If you encounter that, it is a bug, write a minimal sketch and start an issue at Github. Please don't use any Serial function inside receiveEvent or requestEvent. In the receiveEvent you can use 'howMany' for the number of received bytes, there is no need for the strange while(1 < Wire.available()). I know that is in an example, but that is a terrible example. The 'inCount' and 'outCount' should be volatile. The sprintf format should perhaps use %ld a long, but I suppose that is also 32-bit for a SAMD21. – Jot Jul 12 '17 at 18:23
  • @jot: +1 for the hint, not to use any Serial function within the event handler. – NilsB Aug 11 '18 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.