Page 1 of 1

use a member function as wire2 callback function

Posted: Sat May 23, 2020 10:44 am
by jenspogo
Hey everybody,
I have a problem in writing some Code. I'm definetly not a C++ expert - therefore it might be an easy problem for somebody who knows what he is doing.
I'm would like to use three Bluepill Boards communicating with a RaspberryPi over the I2C2 Bus as Slaves on the I2C2 Bus (the standard wire.begin() for the first BUS, I would like to use for some I2C Sensors) Every Bluepill Board collects Data and does some stuff which will be displayed on the Rpi.

I wrote some example code for the communication which works quite well. It is Basicly a combination of SCD30 Sensor and I2C examples. Not very Beautifull but it works.

Code: Select all

/*
  Reading CO2, humidity and temperature from the SCD30
  and send data to the Pi

  Version 002: some safety stuff: flags for checkup i2c activity and reload the i2c Bus
  VERSION 003: with watchdog
*/

#include <Wire.h>
#include <IWatchdog.h>
#include "SparkFun_SCD30_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_SCD30




#define SLAVE_ADDRESS 0x09
#define DATA_TO_SEND_LENGHT 30
#define STM_I2C2_FREQ 100000
#define DONE 1



void getPINandVariableInitSettings(void);
void getWireSettings(void);
void getWire2Settings(void);

char number[30];
char test[DATA_TO_SEND_LENGHT];
char test2[DATA_TO_SEND_LENGHT];
int   state = 0;
float  f_CO2 = 0;
char  c_CO2_buffer[8];
float f_humidity=0;
char  c_humidity_buffer[8];
float f_temp=0;
char  c_temp_buffer[8];

int I2CErrorCounter = 0;
int count2;
int F_firstTransmit = 0;
int F_transmit = 0;

//            SDA  SCL
TwoWire Wire2(PB11, PB10);
SCD30 airSensor;


void setup()
{
  
  getPINandVariableInitSettings();
  getWireSettings();
  getWire2Settings();

if (IWatchdog.isReset(true)) {
   digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
}
  
  if (airSensor.begin() == false)
  {
    //Serial.println("Air sensor not detected. Please check wiring. Freezing...");

        digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
        delay (200);
        digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
        delay (200);
        digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
        delay (200);
        digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
        delay (200);
    
  }

  // Init the watchdog timer with 30 milliseconds timeout

  IWatchdog.isReset();
  IWatchdog.begin(50000);

    if (!IWatchdog.isEnabled()) {
    // LED blinks indefinitely
    while (1) {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);
    }
  }

}

//********************************************************************************************************
//****************************************************LOOOP***********************************************

void loop()
{
  // read out the Sensor values and store them in the Variable
 
  if (airSensor.dataAvailable())
  {
    
    digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
    
    f_CO2 = airSensor.getCO2();
    dtostrf(f_CO2, 5, 0, c_CO2_buffer);
    
    for (int i = 0; i<5;i++){
      test[4+i] = c_CO2_buffer[i];
      }
    
    f_temp = airSensor.getTemperature();
    dtostrf(f_temp, 5, 2, c_temp_buffer);
    
    for (int i = 0; i<5;i++){
      test[12+i] = c_temp_buffer[i];
      }
  
    f_humidity = airSensor.getHumidity();
    dtostrf(f_humidity, 5, 2, c_humidity_buffer);
    for (int i = 0; i<5;i++){
      test[20+i] = c_humidity_buffer[i];
      }
    
  }

// if (test[8]==48){
//    test[28]=41;
//    }

if (test[5]==32){
  I2CErrorCounter++;  
  if(I2CErrorCounter == 5){
      getWireSettings();
      I2CErrorCounter =0;
      }
  }
  test[27]=I2CErrorCounter +48;

  // read out PIN to Reset the I2C Bus
  test[28]=digitalRead(PB3)+48;
  if(digitalRead(PB3)==LOW){
    getWireSettings();
    getWire2Settings();
    }
    
  F_transmit = 0;
  delay(15);
  if(F_firstTransmit == 1 && F_transmit == 0){
    getWire2Settings();
    }
  //delay(15);

  delay(485);
}


//void(* resetFunc)(void)=0;


//********************GET get PIN and Variable Init Settings Routine**********************
// @brief: this functions sets the in and output Pins and initializes variables if needed;
void getPINandVariableInitSettings(void){

  pinMode(LED_BUILTIN, OUTPUT);

  // so und dann direkt auf GND legen.
  pinMode(PB3, INPUT_PULLUP);
  
    for (int i=0; i<DATA_TO_SEND_LENGHT ; i++){
  test[i]=95;
  }
  for (int i=0; i<DATA_TO_SEND_LENGHT ; i++){
  test2[i]=95;
  }
  test[0]='C';
  test[1]='O';
  test[2]='2';
  test[3]=':';
  test[10]='T';
  test[11]=':';
  test[18]='H';
  test[19]=':';
  }



//********************GET WIRE SETTINGS Routine**********************

void getWireSettings(void){
  
    // initialize wire
  Wire.begin();  
  }
// *******************************************
void getWire2Settings(void){
  
    // initialize wire
  Wire2.begin(SLAVE_ADDRESS);
  //Wire2.setClock(STM_I2C2_FREQ);
  Wire2.onReceive(receiveData2);
  Wire2.onRequest(sendData2);
  F_firstTransmit = 0;
  }

// *****************************callback for received data***********************************

void receiveData2(int byteCount) {
  //digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
  int i = 0;
  while (Wire2.available()) {
    number[i] = Wire2.read();
    i++;
  }
  number[i] = '\0';
  //Serial.print(number);
}  // end while

// *****************************callback for sending data*************************************

void sendData2() {
  //digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));// Turn the LED from off to on, or on to off
  Wire2.write(test,sizeof(test));
  F_firstTransmit = 1;
  F_transmit = 1;
  IWatchdog.reload();  
}



//End of the program
Now I want to implement my own Library with a class that implements this basic Communication an some configuration Data for every Board type.

Code: Select all

class MKE_data
{
private:

	int BoardType;
	char DataBuffer[MKE_DATA_BUFF_SIZE];
	char DataConfig[MKE_DATA_BUFF_SIZE];
	bool ConfigIsSent;
	char I2C_BaseAdress;
	char I2C_AdressOffset;
	char I2C_Adress;	
	char AdressPIN[3];
	TwoWire *Wire2;


public:
	
	MKE_data(int type);
	~MKE_data();
	int getBoardType(void);
	void val2buf(int value, int position);
	void float2buf(float value, int position);
	void printBuffer();
	char getData (int position);
	void setI2CAdrrOffset(void);
	char getI2CAdrrOffset(void);
	void updateI2CAdress(void);
	char getI2CAdress(void);
	
	void I2CSysBusInit();
	void receiveSysBusData(int byteCount);
	static void Wrapper2CallRecieveSysBusData(void* ptr2Object, int byteCount);
	void sendSysBusData(void);
	static void Wrapper2CallSendSysBusData(void* ptr2Object);

};
My goal is to get the communication initialisation to work automaticly. In the constructor I make a new Wire2 object and I'm calling the Init function:

Code: Select all

    Wire2 = new TwoWire(PB11, PB10)
    I2CSysBusInit();
init:

Code: Select all

void MKE_data::I2CSysBusInit(){
  
    // initialize wire
  Wire2->begin(getI2CAdress());
  //Wire2.setClock(STM_I2C2_FREQ);
  int byteCount;
  Wire2->onReceive(Wrapper2CallRecieveSysBusData);
  Wire2->onRequest(Wrapper2CallSendSysBusData);
  //F_firstTransmit = 0;
  }
and the wrapper function:

Code: Select all

 void MKE_data::Wrapper2CallSendSysBusData(void* ptr2Object)
{   
    // explicitly cast to a pointer to MKE_data
    MKE_data* mySelf = (MKE_data*) ptr2Object;
    // call member
    mySelf->sendSysBusData();
}

 void MKE_data::Wrapper2CallRecieveSysBusData(void* ptr2Object, int byteCount)
{   
    // explicitly cast to a pointer to MKE_data
    MKE_data* mySelf = (MKE_data*) ptr2Object;
    // call member
    
    mySelf->receiveSysBusData(byteCount);
}
I tried a lot to get the Callback function to work (with wrapper and without wrapper, static and not static) and I am confused about what am I doing wrong. How would you implement the Callback fuction? Maybe I'm completly wrong with the stuff I am doing?

I would really appreciate some help!

Greetings, Jens

Re: use a member function as wire2 callback function

Posted: Wed Jun 03, 2020 12:35 pm
by fpiSTM
Hi @jenspogo
Sorry for the delay, missed that...

It seems the callback prototype is not good:

Code: Select all

    void onReceive(void (*)(int));
    void onRequest(void (*)(void));

Code: Select all

 void MKE_data::Wrapper2CallSendSysBusData(void* ptr2Object) --> void(*)(void*)
 void MKE_data::Wrapper2CallRecieveSysBusData(void* ptr2Object, int byteCount) --> void(*)(void*, int)
Did you enabled all the verbose and warnings and check them. I guess you should have one.

Re: use a member function as wire2 callback function

Posted: Thu Feb 25, 2021 5:00 pm
by jacksonliam341
Does callback function need to be declared under extern "C"?

NO. extern "C" is necessary only when you are calling a C++ function directly, without the use of function pointers, from C. If function pointers are used, extern "C" is not required.

Can I use non-static member functions as a callback?

NO. Non-static member functions of class A have an implicit first parameter corresponding to this pointer.

Can I use static member functions as a callback?

YES, as long as signature matches with that of the callback.

Does it matter if the function is a template function or not?

NO, template function can be used as callbacks as long as the signature of the instantiated template matches with the callback.

Hopefully it will work