use a member function as wire2 callback function

Post here first, or if you can't find a relevant section!
Post Reply
jenspogo
Posts: 30
Joined: Mon Feb 24, 2020 10:42 am

use a member function as wire2 callback function

Post 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
User avatar
fpiSTM
Posts: 1745
Joined: Wed Dec 11, 2019 7:11 pm
Answers: 91
Location: Le Mans
Contact:

Re: use a member function as wire2 callback function

Post 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.
jacksonliam341
Posts: 1
Joined: Thu Feb 25, 2021 4:41 pm

Re: use a member function as wire2 callback function

Post 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
Post Reply

Return to “General discussion”