0

I have an Arduino sketch that will be working on an Arduino UNO and I am trying to get uno to communicate over the i2c connection with a raspberry pi. Problem is using wire.h library where method Wire.onRequest is working just fine when I use it like this.

#include <Wire.h>
#define COMM_DELAY 50
#define SLAVE_ADDRESS 0x04

int current_rule = 0;

void initI2c() {
  // initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  Wire.onReceive(receiveData);
}

// callback for received data
void receiveData(int byteCount) {
  while (Wire.available()) {
    current_rule = Wire.read();
  }
}

but when I try to make this exact result with a class method, I get an error : invalid use of non-static member function (with Wire.onRequest(this->receiveData) line gets to be marked red)

Just like this:


void (*funptr)();
typedef void (*Callback)(byte);
class Comm{
public:
  int callback_list_size = 0;
  bool option_debug;
  byte option_address;
  int option_comm_delay;
  void(*callback_list[256]);
  byte *rules;

  // function for receiving data. raspberry -> arduino
  // Whenever the master sends new data, this method will call the appropriate callback.
  void receiveData()
  {
    byte data;
    Serial.println("[INFO] Received new data from master");
    while (Wire.available())
    {
      data = Wire.read();
    }
    for (int i = 0; i < callback_list_size; i++)
    {
      if (rules[i] == data){
        funptr = callback_list[i];
        funptr();
        }
    }
  }

  // function for sending data. Called when raspberry request data. arduino -> raspberry
  // Whenever the master requests data, this method will be called. For now we don't need this but anyway.
  void sendData(int s)
  {
    if (option_debug)
      Serial.println("[INFO] Master requests data!");
  }

  /* Constructor that takes 3 parameters at max. Only the adress is mandatory others are optional and will be filled with default values
     :address - adress of slave(arduino) - Example 0x04
     :delay - a delay is needed because I2C clock is quite slow compared to the CPU clock - 50
     :debug - for debug purposes if true debug info will be sent to Serial interface - true/false 
  */
  Comm(byte address, int delay = 50, bool debug = false)
  {
    option_address = address;
    option_comm_delay = delay;
    option_debug = debug;
    if (debug)
      Serial.println("[INFO] Comm Object Created!");
  }

  // Function needs to be called to initialize the communication channel.
  void initI2c()
  {
    Wire.begin(option_address);
    Wire.onReceive(this->sendData);
    Wire.onRequest(this->receiveData);
    if (option_debug)
      Serial.println("[INFO] I2C channel initialized");
  }

  // Function to add new callback for a rule.
  // This function returns id of passed callback
  int addCallback(Callback func, byte rule)
  {
    callback_list_size++;
    // Enlarge rules array to keep 1 more byte
    byte *temp = new byte[callback_list_size];       // create new bigger array.
    for (int i = 0; i + 1 < callback_list_size; i++) // reason fo i+1 is if callback_list_size is 1 than this is the first initializition so we don't need any copying.
    {
      temp[i] = rules[i]; // copy rules to newer array.
    }
    delete[] rules; // free old array memory.
    rules = temp;   // now rules points to new array.
    callback_list[callback_list_size - 1] = &func;
    rules[callback_list_size - 1] = rule;
    return callback_list_size;
  }
};

Comm *i2c_comm;
void loop()
{
}
void setup()
{
  Serial.begin(9600);
  initI2C();
}


void initI2C()
{
  i2c_comm = new Comm(0x04, 50, true);
  i2c_comm->initI2c();

  //Callback Definitions
  i2c_comm->addCallback(&rule_1, 0x01);
      i2c_comm->addCallback(&rule_2, 0x02);
          i2c_comm->addCallback(&rule_3, 0x03);
              i2c_comm->addCallback(&rule_4, 0x04);
}

I also tried to make the receiveData method to be static. But in this case I have an error like this: invalid use of member Com::callback_list_size in static member function

which makes sense to me as static method won't know which callback_list_size I am talking about.

so I am quite confused about how I can handle such a problem?

Ali Yousefi Sabzevar
  • 958
  • 2
  • 19
  • 29

1 Answers1

0

You're almost there. Generally speaking in C++ you need to pass a static class method for callback functions.

The error you received after changing your method to static is expected as you're trying to access a member of an instance of the class Comm which cannot be done in a static method in which there is no 'this'.

Here's one of many techniques to consider, but please read over the SO post Using a C++ class member function as a C callback function.

Anyway the approach here is to leverage a static pointer to an instance.

class Comm {
private:
    static Comm* pSingletonInstance;

    static void OnReceiveHandler() {
        if (pSingletonInstance)
            pSingletonInstance->receiveData();
    }
    static void OnSendHandler(int s) {
        if (pSingletonInstance)
            pSingletonInstance->sendData(s);
    }
    void initI2c() {
        Comm::pSingletonInstance = this; // Assign the static singleton used in the static handlers.
        Wire.onReceive(Comm::OnSendHandler);
        Wire.onRequest(Comm::OnReceiveHandler);
        Wire.begin(option_address);
    }
}
// static initializer for the static member.
Comm* Comm::pSingletonInstance = 0;

Again there are many ways to get around this issue but above is an easy one and likely suitable for your project. If you need to manage multiple instances of Comm, you'll have to do something quite different.

Good luck!

RamblinRose
  • 3,551
  • 2
  • 13
  • 24