0

I tried to write an Library for the Sensirion SFM3000 Flow Sensor in the Arduino IDE for an ESP32. As long as it was one sketch, everything worked. But as I tried to seperate it into an .h and .cpp file and implemented it as a class, I get some weird error when I try to compile the sketch.

sketch\SFM3019_HSKA.cpp.o:(.literal._ZN12SFM3019_HSKA17i2c_read_flow_CRCEv+0x4): undefined reference to `SFM3019_HSKA::crc8(unsigned char, unsigned char)'
sketch\SFM3019_HSKA.cpp.o: In function `SFM3019_HSKA::i2c_read_flow_CRC()':
sketch/SFM3019_HSKA.cpp:124: undefined reference to `SFM3019_HSKA::crc8(unsigned char, unsigned char)'
sketch/SFM3019_HSKA.cpp:128: undefined reference to `SFM3019_HSKA::crc8(unsigned char, unsigned char)'
collect2.exe: error: ld returned 1 exit status
exit status 1
Fehler beim Kompilieren für das Board ESP32 Dev Module.

As I understand the error message, it occurs when the i2c_read_flow_CRC() meethod is calling the crc8() method in the last thrid of SFM3019_HSKA.cpp file. By now I don´t see an error in the syntax. But I have also no other idea on how to continue debugging...

This is the main code im trying to use the library in:

#include "SFM3019_HSKA.h"
#include <Wire.h>


SFM3019_HSKA::SensVal_s SensVal = {0, 0, 0};

SFM3019_HSKA SFM3019(0x2E); //Generate Object SFM3019 of class SFM3019_HSKA
 
void setup() {
  // put your setup code here, to run once:
  Serial.begin (115200);
  delay(100);  
  Wire.begin (21, 22);   // for ESP32: SDA= GPIO_21 /SCL= GPIO_22

  SFM3019.i2c_device_check();  //checks if I2C device is available
  SFM3019.i2c_write(0x3608); //write 2Byte to start continuous measurement
  delay(100);
}

void loop() {
  // put your main code here, to run repeatedly:

  
  SensVal.flow = SFM3019.i2c_read_flow_CRC();  //read only Flow Bytes
  Serial.println(SensVal.flow, 10);
  delay(100);
  SensVal = SFM3019.i2c_read_all();  //read all Sensor Bytes
  Serial.print("Flow: ");
  if (SensVal.flow >= 0) Serial.print(" "); //just for beauty reasons
  Serial.print(SensVal.flow,10);
  Serial.print("  Temp: ");
  Serial.print(SensVal.temp,3);
  Serial.print("  Statusword: ");
  Serial.print(SensVal.statusword, HEX);
  delay(400);
  Serial.println();
}

and those are the library files: SFM3019_HSKA.h

#ifndef SFM3019_HSKA_H  // include guard
#define SFM3019_HSKA_H  

#include "Arduino.h"



    class SFM3019_HSKA {
      
    public: //can be accessed public

        SFM3019_HSKA(uint8_t i2cAddress); //Constructor
    
        //may be nicer in private if it works anyway
        typedef struct {float flow; float temp; int16_t statusword;} SensVal_s; //Struct for complete Sensor Readout
        SensVal_s SensVal = {0, 0, 0};  //initialize with 0

        int8_t i2c_device_check(); //function for Initialisation
        int8_t i2c_write(const uint16_t SendData); //function to write 16Bit Data to sensor
        SensVal_s i2c_read_all();  //function to read Flow, Temperature and Statusword
        float i2c_read_flow(); //function to only read Flow
        float i2c_read_flow_CRC(); //function to only read Flow with CRC check



    private:  //can only be accessed by functions oh same object
        
        uint8_t SFM3019addr = 46; //SFM3019 I2C Adress: 46 / 0x2E
        uint8_t crc8(const uint8_t data, uint8_t crc);  //fuction for CRC confirmation
    };

#endif

and the C++ file leading to the error: SFM3019_HSKA.cpp:

#include "SFM3019_HSKA.h" //when placed in same folder
//#include <SFM3019_HSKA.h> //when placed in standard library folder
#include <Wire.h>

//  some values needed for calculation of physical flow and temperature values
#define SFM3019Offset 24576
#define SFM3019ScaleFactorFlow 170.0 //needs to be a float, otherwise it will not calculate in float
#define SFM3019ScaleFactorTemp 200.0 //needs to be a float, otherwise it will not calculate in float


SFM3019_HSKA::SFM3019_HSKA(uint8_t i2cAddress)  //constructor
{
  //: mI2cAddress(i2cAddress)
  SFM3019addr = i2cAddress;
}


/*  checks if a Device at the desired address is responding with an ACK */
int8_t SFM3019_HSKA::i2c_device_check(){
  Wire.beginTransmission(SFM3019addr);    // Begin I2C transmission Address (i)
  if (Wire.endTransmission() == 0)  // Receive 0 = success (ACK response) 
    {
      Serial.print ("Found Seosor at address");
      Serial.print (SFM3019addr, DEC);
      Serial.print (" (0x");
      Serial.print (SFM3019addr, HEX);     // 7 bit address
      Serial.println (")");
      return 0;                        //0=device sent ACK
      
    }else{
      
      Serial.print ("Did not receive Acknowledge from I2C address ");
      Serial.print (SFM3019addr, DEC);
      Serial.print (" (0x");
      Serial.print (SFM3019addr, HEX);     // 7 bit address
      Serial.println (")");
      return 1;                        //no ACK received
    }
}



/*  writes a 16bit "SendData" to I2C Bus Device "address" */
int8_t SFM3019_HSKA::i2c_write(const uint16_t SendData) {
    Wire.beginTransmission(SFM3019addr);
    //fill I2C outbuffer
    Wire.write((SendData>>8)& 0xFF); //isolate HighByte
    Wire.write(SendData & 0xFF); //isolate LowByte
    //send I2C outbuffer
    Wire.endTransmission();

    return 0;
}



/*  reads all 9 measurement bytes for flow, temp and status */
SFM3019_HSKA::SensVal_s SFM3019_HSKA::i2c_read_all(){
    SensVal_s SensVal = {0};  //create empty struct
    Wire.requestFrom(SFM3019addr, 9, true); // Request 9byte (3x16bit + CRC) from the sensor
   //while(Wire.available()<3){};  //wait for all the data to be received //ATTENTION may be critical loop forever, however not using this may lead to an error, as the Buffer may be processed faster, then the input is read on I2C
    
    //get Flow Bytes
    int16_t flow = Wire.read()<<8;  //get Highbyte and shift 8bit to 8MSB
    flow = flow | Wire.read();      //get Lowbyte 8LSB
    byte CRCflow = Wire.read();         //get CRC Check Byte (you could do a data validy check with that)    
    //Serial.println(flow, HEX);    //raw values for debugging
    SensVal.flow = (flow + SFM3019Offset) / SFM3019ScaleFactorFlow; //calculate the flow in slm as Datasheet mentions        

    //get Temperature Bytes
    int16_t temp = Wire.read()<<8;  //get Highbyte and shift 8bit to 8MSB
    temp = temp | Wire.read();      //get Lowbyte 8LSB
    byte CRCtemp = Wire.read();         //get CRC Check Byte (you could do a data validy check with that)    
    //Serial.println(temp, HEX);    //raw values for debugging
    SensVal.temp = temp / SFM3019ScaleFactorTemp; //calculate the flow in slm as Datasheet mentions        

    //get StatusWord Bytes
    int16_t stat = Wire.read()<<8;  //get Highbyte and shift 8bit to 8MSB
    stat = stat | Wire.read();      //get Lowbyte 8LSB
    byte CRCstat = Wire.read();         //get CRC Check Byte (you could do a data validy check with that)    
    //Serial.println(stat, HEX);    //raw values for debugging
    SensVal.statusword = temp / SFM3019ScaleFactorTemp; //calculate the flow in slm as Datasheet mentions        

    //return all data
    return SensVal;
    
}



/*  reads only first 3 bytes for flow and does NO CRC!*/
float SFM3019_HSKA::i2c_read_flow(){
    Wire.requestFrom(SFM3019addr, 3, true); // Request 9byte (2x16bit + CRC) from the sensor
    //while(Wire.available()<3){};  //wait for all the data to be received //ATTENTION may be critical loop forever, however not using this may lead to an error, as the Buffer may be processed faster, then the input is read on I2C
    int16_t flow = Wire.read()<<8;  //get Highbyte and shift 8bit to 8MSB
    flow = flow | Wire.read();      //get Lowbyte 8LSB
    byte CRC = Wire.read();         //get CRC Check Byte (you could do a data validy check with that)    
    //Serial.println(flow, HEX);    //raw values for debugging
    return (flow + SFM3019Offset) / SFM3019ScaleFactorFlow; //calculate the flow in slm as Datasheet mentions  
}

/*  reads only first 3 bytes for flow and DOES CRC*/
float SFM3019_HSKA::i2c_read_flow_CRC(){
    Wire.requestFrom(SFM3019addr, 3, true); // Request 9byte (2x16bit + CRC) from the sensor
    //while(Wire.available()<3){};  //wait for all the data to be received //ATTENTION may be critical loop forever, however not using this may lead to an error, as the Buffer may be processed faster, then the input is read on I2C
    uint8_t Highbyte = Wire.read();    //get Highbyte 8MSB
    uint8_t Lowbyte =  Wire.read();      //get Lowbyte 8LSB
    uint8_t CRC = Wire.read();         //get CRC Check Byte

    //Confirm CRC
    uint8_t mycrc = 0xFF; // initialize crc variable
    mycrc = crc8(Highbyte, mycrc); // let first byte through CRC calculation
    mycrc = crc8(Lowbyte, mycrc); // and the second byte too
    if (mycrc != CRC) { // check if the calculated and the received CRC byte matches
      //Serial.println("Error: wrong CRC");
      return -10000;  //extreme low value, so user knows somethig is wrong
    } else {
      //Serial.println("Success: identical CRC");
      int16_t flow = (Highbyte<<8) | Lowbyte; //stack the to bytes together as signed int16
      return (flow + SFM3019Offset) / SFM3019ScaleFactorFlow; //calculate the flow in slm as Datasheet mentions  
    }
}

/*  calculate a CRC Byte, (Cyclic Redundancy Check) */
uint8_t crc8(const uint8_t data, uint8_t crc)
{
     crc ^= data;                     //crc XOR data
     for ( uint8_t i = 8; i; --i ) {
       crc = ( crc & 0x80 )           
       ? (crc << 1) ^ 0x31
       : (crc << 1);
    }
  return crc;
}

I would be really thankfull for any idea on how to continue...

1 Answer 1

1
/*  calculate a CRC Byte, (Cyclic Redundancy Check) */
uint8_t crc8(const uint8_t data, uint8_t crc)
{
     crc ^= data;                     //crc XOR data
     for ( uint8_t i = 8; i; --i ) {
       crc = ( crc & 0x80 )           
       ? (crc << 1) ^ 0x31
       : (crc << 1);
    }
  return crc;
}

Needs to be:

/*  calculate a CRC Byte, (Cyclic Redundancy Check) */
uint8_t SFM3019_HSKA::crc8(const uint8_t data, uint8_t crc)
{
     crc ^= data;                     //crc XOR data
     for ( uint8_t i = 8; i; --i ) {
       crc = ( crc & 0x80 )           
       ? (crc << 1) ^ 0x31
       : (crc << 1);
    }
  return crc;
}

You just forgot to mark the defined method as part of the class. Simple typo error.

Sign up to request clarification or add additional context in comments.

1 Comment

Oh hell, thanks a lot. Not seeing the forest with all the trees around. You saved my weekend!:)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.