I2C Slave Callback Problem (using Wire library)

Hello guys,

I’m actually trying to seperate a sketch of an I2C Slave (.ino) into .cpp and .h files.

I’m working on an Arduino Mega 2560 and using the Wire library.

The original and working sketch of the I2C slave looks like this:

I2CSlave.ino

#include <Arduino.h>
#include <Wire.h>

int receiveByte = 0;
byte myByteStruct[2];

void setup()
{
  Wire.begin(1);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  Serial.begin(9600);
}

void loop()
{
 
}

void receiveEvent(int howMany)
{
  while(Wire.available())
  {
    myByteStruct[receiveByte] = Wire.read();
    receiveByte++;
  }
  Serial.println(myByte[0], HEX);
  Serial.println(myByte[1], HEX);
  
  receiveByte=0;
}

void requestEvent()
{
  Wire.write("done");
}

I’m trying to make it look like this:

I2CSlave.h:

#ifndef I2CSlave_H
#define I2CSlave_H

/* I2CSlave.h */

#include <Arduino.h>
#include <Wire.h>

class I2CSlave
{
  public:
  I2CSlave();
  void receiveEvent(int howMany);
  void requestEvent();
  void wireOnReceive();
  void wireOnRequest();
  
  private:
  int receiveByte = 0;
  byte myByte[2];
};

#endif

I2CSlave.cpp:

/* I2CSlave.cpp */

#include "I2CSlave.h"

I2CSlave::I2CSlave()
{
}

void I2CSlave::receiveEvent(int howMany)
{
  while(Wire.available())
  {
    doseur[receiveByte] = Wire.read();
    receiveByte++;
  }
  Serial.println(doseur[0], HEX);
  Serial.println(doseur[1], HEX);

  receiveByte=0;
}

void I2CSlave::requestEvent()
{
  Wire.write("done");
}

void I2CSlave::wireOnReceive()
{
  Wire.onReceive(receiveEvent);
}

void I2CSlave::wireOnRequest()
{
  Wire.onRequest(requestEvent);
}

This is the compilation error I get… I tried a lot to understand it and to correct the errors but I don’t really understand… It’s apparently a callback problem but I don’t really get it… If someone could help me it would be nice !

I2CSlave.cpp: In member function ‘void I2CSlave::wireOnReceive()’:
I2CSlave.cpp:29:30: error: no matching function for call to ‘TwoWire::onReceive(<unresolved overloaded function type>)’
   Wire.onReceive(receiveEvent);
                              ^
I2CSlave.cpp:29:30: note: candidate is:
In file included from I2CSlave.h:7:0,
                 from I2CSlave.cpp:3:
/usr/share/arduino/libraries/Wire/Wire.h:66:10: note: void TwoWire::onReceive(void (*)(int))
     void onReceive( void (*)(int) );
          ^
/usr/share/arduino/libraries/Wire/Wire.h:66:10: note:   no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘void (*)(int)’
I2CSlave.cpp: In member function ‘void I2CSlave::wireOnRequest()’:
I2CSlave.cpp:34:30: error: no matching function for call to ‘TwoWire::onRequest(<unresolved overloaded function type>)’
   Wire.onRequest(requestEvent);
                              ^
I2CSlave.cpp:34:30: note: candidate is:
In file included from I2CSlave.h:7:0,
                 from I2CSlave.cpp:3:
/usr/share/arduino/libraries/Wire/Wire.h:67:10: note: void TwoWire::onRequest(void (*)())
     void onRequest( void (*)(void) );
          ^
/usr/share/arduino/libraries/Wire/Wire.h:67:10: note:   no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘void (*)()’

Thanks a lot for reading !

You can have multiple instances of your class. Which instance is supposed to be notified when the I2C bus gets data?

That fact that you only plan to have once instance changes nothing.

The callback functions must be static, so that all instances of the class share the same method. That makes sharing other class data problematic, but that is what you have to deal with.

Thanks for your answer PaulS! I don't know which instance is supposed to be notified.. Actually I'm totally wondering things about I2C!

In fact I just don't understand why it's working as a sketch (.ino) and not as .cpp and .h .. I think I used nicely the same syntax as on the sketch but it doesn't fit..

I took a more accurate look at the Wire library and I found that thing

void (*TwoWire::user_onReceive)(int);

I'm thinking that it's what I need to write in my class.. I tried kinda everything on that, but it still doesn't fit at all!

In fact I just don't understand why it's working as a sketch (.ino) and not as .cpp and .h ..

Because when the code is in the sketch, there is no ambiguity as to which instance should care.

I'm thinking that it's what I need to write in my class.. I tried kinda everything on that, but it still doesn't fit at all!

No, you need to define the method to call as static:

static void receiveEvent(int howMany); static void requestEvent();

Of course, as I mentioned, that is going to cause other problems.

Why do you need a class? You can create header and source files containing functions, without creating a class.

I need a class because it's what is asked in my project specifications..