ESP32 build in Arduino IDE: undefined reference to `CLASS::function' error - c++

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...

/* 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.

Related

How do I read data via i2c from a MAX11613 chip using C++ on a RPI 3B+

I'm trying to write a driver for a MAX11613 ADC chip (MAX11613 Datasheet) in c++. I think I've got the write code correct for the setup and config, but I'm having some trouble with the read code. I am setting the chip up to read using the internal clock in Unipolar mode and the internal voltage reference, then writing the config to scan using AIN0 as the + signal and AIN1 as the - signal channels and report the differential reading. It does seem to read data, though the data appears to be very erratic and not what is anticipated based on oscilloscope results.
Setup bits: //1111 0000=0xF0 SEL2=1, SEL1=1, SEL0=1, INTERNAL CLOCK, UNIPOLAR
Config bits: //0110 0000=0x60 SCAN1=1, SCAN0=1, AIN0-AIN1, DIFFERENTIAL
Here's my read code, which may be part of the problem:
static uint16_t readMAXRegister(uint8_t i2cAddress, uint8_t reg) {
unsigned char buff[16];
beginMAXTransmission(i2cAddress);
i2c_smbus_read_i2c_block_data(i2cMAXHandle, reg, 16, buff);
endMAXTransmission();
uint16_t res = (buff[1] << 8) + buff[0];
return res;
}
int16_t MAX11613::readMAXADC_Differential_0_1() {
// Write config register to the ADC
writeMAXRegister(m_i2cAddress, MAX_WRITE, MAX_CONFIG);
// Wait for the conversion to complete
usleep(m_conversionDelay);
// Read the conversion results
uint16_t res = readMAXRegister(m_i2cAddress, 1) >> m_bitShift;
// Shift 12-bit results right 4 bits
res = (res >> 11) == 0 ? res : -1 ^ 0xFFF | res;
std::bitset<12> y(res);
std::cout << "READ RESULT " << y << " " << res << std::endl;
return (int16_t)res;
}
It appears that I am now able to read data from the device after some more review. Here's the final code that works, for anyone else that might be interested.
static void writeMAXRegister(uint8_t i2cAddress, uint8_t reg, uint8_t value) {
beginMAXTransmission(i2cAddress);
i2c_smbus_write_word_data(i2cMAXHandle, reg, payload);
endMAXTransmission();
uint8_t payload = value;
}
static uint16_t readMAXRegister(uint8_t i2cAddress, uint8_t reg) {
const uint8_t datalength = 2;
unsigned char data[datalength];
beginMAXTransmission(i2cAddress);
i2c_smbus_read_i2c_block_data(i2cMAXHandle, reg, datalength, data);
endMAXTransmission();
uint16_t res =((data[0]&0xF)<<8)+data[1];//<---THIS READS 16 BITS AND REMOVES FIRST 4 '1111' BITS OF DATA
return res;
}

Arduino CANBUS MCP2515 Extended ID Receive Filter Problem

I would like to read CANBUS output of a device, but instead of reading every message, I need to filter out all messages except "0x18DAF938" message.
I am using Cory J. Fowler's library.
I changed Extended_MaskFilter example code as below but still no success:
#include <mcp_can.h>
#include <SPI.h>
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
MCP_CAN CAN0(53); // Set CS to pin 10
void setup()
{
Serial.begin(115200);
if(CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_8MHZ) == CAN_OK) Serial.print("MCP2515 Init Okay!!\r\n");
else Serial.print("MCP2515 Init Failed!!\r\n");
pinMode(2, INPUT); // Setting pin 2 for /INT input
CAN0.init_Mask(0,1,0xFFFFFFFF); // Init first mask...
CAN0.init_Filt(0,1,0x18DAF938); // Init first filter...
Serial.println("MCP2515 Library Mask & Filter Example...");
CAN0.setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted
}
void loop()
{
if(!digitalRead(2)) // If pin 2 is low, read receive buffer
{
CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
Serial.print("ID: ");
Serial.print(rxId, HEX);
Serial.print(" Data: ");
for(int i = 0; i<len; i++) // Print each byte of the data
{
if(rxBuf[i] < 0x10) // If data byte is less than 0x10, add a leading zero
{
Serial.print("0");
}
Serial.print(rxBuf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
}
I am using Arduino Mega2560 and NiRen MCP2515 module.
Help would be appreciated. Thanks.

Separating a MQ4 gas Sensor particle photon code into a .h and .cpp files

I am new here. I was working with particle Photon and MQ4 I2C gas sensor.
I have a normal I2C code for this sensor
#include <application.h>
#include <spark_wiring_i2c.h>
// ADC121C_MQ4 I2C address is 0x50(80)
#define Addr 0x50
int raw_adc = 0;
double ppm = 0.0;
void setup()
{
// Set variable
Particle.variable("i2cdevice", "ADC121C_MQ4");
Particle.variable("PPM", ppm);
// Initialise I2C communication as MASTER
Wire.begin();
// Initialise serial communication, set baud rate = 9600
Serial.begin(9600);
delay(300);
}
void loop()
{
unsigned int data[2];
// Start I2C transmission
Wire.beginTransmission(Addr);
// Select data register
Wire.write(0x00);
// Stop I2C transmission
Wire.endTransmission();
// Request 2 bytes of data
Wire.requestFrom(Addr, 2);
// Read 2 bytes of data
// raw_adc msb, raw_adc lsb
if (Wire.available() == 2)
{
data[0] = Wire.read();
data[1] = Wire.read();
}
delay(300);
// Convert the data to 12-bits
raw_adc = ((data[0] & 0x0F) * 256) + data[1];
ppm = (10000 / 4096.0) * raw_adc + 200.0;
// Output data to dashboard
Particle.publish("Methane concentration : ", String(ppm));
delay(1000);
}
So I am a noob in coding and don't know how to separate this code into .cpp and .h files to get the clean code.
Please help me out, how this code can be converted into .cpp and .h files.
Thanx in advance.

TM4C123 I2C Library to Arduino Wire Library for MPU6050

I study to read MPU6050 gyro data but the main problem is that I could not achieve. Let me to explain why this problem happen.
I found MPU6050 example for Arduino MPU6050BasicExample.
I have started to change basic Arduino example. Then I applied all codes to my project except of these "writeByte", "readByte", "readBytes" functions.
I have tried to change these code according to Arduino as follow;
These are Arduino codes;
void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
{
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.write(data); // Put data in Tx buffer
Wire.endTransmission(); // Send the Tx buffer
}
uint8_t readByte(uint8_t address, uint8_t subAddress)
{
uint8_t data; // `data` will store the register data
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
Wire.requestFrom(address, (uint8_t) 1); // Read one byte from slave register address
data = Wire.read(); // Fill Rx buffer with result
return data; // Return data read from slave register
}
void readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest)
{
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
uint8_t i = 0;
Wire.requestFrom(address, count); // Read bytes from slave register address
while (Wire.available()) {
dest[i++] = Wire.read(); } // Put read results in the Rx buffer
}
These are my codes I have tried to change;
void writeByte(uint16_t address, uint16_t subAddress, uint8_t data)
{
writeI2C0(address, subAddress, data);
}
uint8_t readByte(uint16_t address, uint16_t subAddress)
{
uint8_t data;
return readI2C0(address, subAddress);
}
void readBytes(uint16_t address, uint16_t subAddress, uint8_t count, uint8_t * dest)
{
writeByte(address, subAddress, count);
uint8_t i = 0;
while (I2CMasterBusy(I2C0_BASE)) {
dest[i++] = readByte(address, subAddress); }// Put read results in the Rx buffer
}
In my opinion, if I port Arduino codes to Tiva-c codes, I will achieve to read gyro data.

Serial communication not working when class is instantiated in Arduino Environment

I'm trying to write code for my Arduino Mega to communicate with ADXL345 accelerometer using c++ style library.
This is my Accelerometer.h file:
#include <Wire.h>
#define ADXL345 (0x53) // I2C Address of ADXL345 accelerometer
struct Acceleration
{
float x;
float y;
float z;
};
class Accelerometer
{
public:
Accelerometer(void); // Constructor
Acceleration readData(void); // Read sensor data
private:
char buffer[6]; // Buffer to store data (x, y, z: LSB and MSB of each)
char DATA_FORMAT; // Address of DATA_FORMAT Register
char POWER_CTL; // Address of POWER_CTL Register
char DATAX0; // Address of X-Axis LSB Data
char DATAX1; // Address of X-Axis MSB Data
char DATAY0; // Address of Y-Axis LSB Data
char DATAY1; // Address of Y-Axis MSB Data
char DATAZ0; // Address of Z-Axis LSB Data
char DATAZ1; // Address of Z-Axis MSB Data
void writeToAccelerometer(char address, char value);
void readFromAccelerometer(char address, int numOfBytes);
};
And this is my Accelerometer.cpp file:
#include "Accelerometer.h"
#include <Wire.h>
Accelerometer::Accelerometer()
{
Wire.begin(); // Initialize I2C bus
writeToAccelerometer(DATA_FORMAT, 0x01); // +/- 4g range
writeToAccelerometer(POWER_CTL, 0x08); // Measurement Mode
DATA_FORMAT = 0x31; // Address of DATA_FORMAT Register
POWER_CTL = 0x2D; // Address of POWER_CTL Register
DATAX0 = 0x32; // Address of X-Axis LSB Data
DATAX1 = 0x33; // Address of X-Axis MSB Data
DATAY0 = 0x34; // Address of Y-Axis LSB Data
DATAY1 = 0x35; // Address of Y-Axis MSB Data
DATAZ0 = 0x36; // Address of Z-Axis LSB Data
DATAZ1 = 0x37; // Address of Z-Axis MSB Data
}
void Accelerometer::writeToAccelerometer(char address, char value)
{
Wire.beginTransmission(ADXL345); // start transmission to ADXL345
Wire.write(address); // send register address
Wire.write(value); // send value to write
Wire.endTransmission(); // end transmission
}
void Accelerometer::readFromAccelerometer(char address, int numOfBytes)
{
Wire.beginTransmission(ADXL345); // start transmission to ADXL345
Wire.write(address); // send register address
Wire.endTransmission(); // end transmission
Wire.beginTransmission(ADXL345); // start transmission to ADXL345
Wire.requestFrom(ADXL345, numOfBytes); // request some bytes from device
int i = 0;
while(Wire.available()) // while there is data
{
buffer[i] = Wire.read(); // receive a byte and save it in the buffer
i++;
}
Wire.endTransmission(); // end transmission
}
Acceleration Accelerometer::readData()
{
Acceleration R;
readFromAccelerometer(DATAX0, 6); // Read data from sensor
// Merge of data and conversion to int format
int x_acceleration = (((int)buffer[1]) << 8) | buffer[0];
int y_acceleration = (((int)buffer[3]) << 8) | buffer[2];
int z_acceleration = (((int)buffer[5]) << 8) | buffer[4];
R.x = x_acceleration*0.0078;
R.y = x_acceleration*0.0078;
R.z = x_acceleration*0.0078;
return R;
}
I'm using this library im my Arduino sketch with this code:
#include <Wire.h>
#include "Accelerometer.h"
Accelerometer accel;
Acceleration Racc;
void setup()
{
Serial.begin(9600); // Start serial for outbut at 9600 bps
}
void loop()
{
Racc = accel.readData();
// Print data
Serial.print("ACCELERATION - X: ");
Serial.print( Racc.x );
Serial.print(" G / Y: ");
Serial.print( Racc.y );
Serial.print(" G / Z: ");
Serial.print( Racc.z );
Serial.print(" G\n");
delay(100);
}
This code is compilated without erros. However, because of the instantiation of Accelerometer class, Serial communication is not working (I can't see any text in Serial Monitor). When I remove the class instance of the code (letting just Serial communication in code), I can see what is print in Serial Monitor.
Does anybody have any idea about what is going on? I would appreciate any help.
As the ADXL345 can operate either SPI or I2C it needs to be configured, as such. In your case of trying to use it as I2C, the CS of the ADXL345 need to be tied to 3.3V
I2C mode is enabled if the CS pinis tied high to VDD I/O. The CS pin
should always be tied high to VDD I/O or be driven by an external
controller because there is nodefault mode if the CS pin is left
unconnected. Therefore, not taking these precautions may result in an
inability to ommunicatewith the part.
Where in your above code I do not see any pins configured as output to drive the CS.
It has been my experience that when the I2C (wire.h) library is not properly connected to a device it can be blocking. Which is similar to your described symptom here.
You may want to reference your code against others, such as https://github.com/jenschr/Arduino-libraries/tree/master/ADXL345. which appears to fully support the many features of the ADXL345 over I2C