Undefined reference to `tcaselect(int)'

I wanted to move some of my code from the INO to a library file(s). I keep getting undefined reference errors. When I hover over the ino, I see the reference, and right click, goto definition and it opens the dot h file in the ide. What am I doing wrong..?

h and cpp files below...

LoadCell.h

#ifndef LoadCell_h
#define LoadCell_h
#include "Wire.h"
#define TCAADDR 0x70 //TCA8574 8 channel i2c mux
#include <Adafruit_NAU7802.h> // i2c load cell adc
Adafruit_NAU7802 PitchCell;
Adafruit_NAU7802 RollCell;
#include <Adafruit_PCF8574.h> // 8 channel digital IO
#define PCFADDR 0x20 
Adafruit_PCF8574 pcf;
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);

void tcaselect(int i);




#endif

LoadCell.cpp

#include LoadCell.h

void tcaselect(int i) 
{
  if (i > 7) return;
 
  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

Error Message:

C:\Users\xps\AppData\Local\arduino\sketches\CC5DD8057351E09086B2CD34A4EEE4E8\sketch\LoadCellTest_i2c_getValues.ino.cpp.o: In function `setup':
C:\Users\xps\Documents\Arduino\LoadCellTest_i2c_getValues/LoadCellTest_i2c_getValues.ino:76: undefined reference to `tcaselect(int)'
C:\Users\xps\Documents\Arduino\LoadCellTest_i2c_getValues/LoadCellTest_i2c_getValues.ino:103: undefined reference to `tcaselect(int)'
C:\Users\xps\AppData\Local\arduino\sketches\CC5DD8057351E09086B2CD34A4EEE4E8\sketch\LoadCellTest_i2c_getValues.ino.cpp.o: In function `loop':
C:\Users\xps\Documents\Arduino\LoadCellTest_i2c_getValues/LoadCellTest_i2c_getValues.ino:154: undefined reference to `tcaselect(int)'
C:\Users\xps\Documents\Arduino\LoadCellTest_i2c_getValues/LoadCellTest_i2c_getValues.ino:148: undefined reference to `tcaselect(int)'
C:\Users\xps\Documents\Arduino\LoadCellTest_i2c_getValues/LoadCellTest_i2c_getValues.ino:150: undefined reference to `tcaselect(int)'
C:\Users\xps\AppData\Local\arduino\sketches\CC5DD8057351E09086B2CD34A4EEE4E8\sketch\LoadCellTest_i2c_getValues.ino.cpp.o:C:\Users\xps\Documents\Arduino\LoadCellTest_i2c_getValues/LoadCellTest_i2c_getValues.ino:164: more undefined references to `tcaselect(int)' follow
collect2.exe: error: ld returned 1 exit status
exit status 1

Compilation error: exit status 1

TIA...

In the INO, I try: (line 76 in setup, and various places in loop)

tcaselect(0);

// #include "Wire.h"
// #define TCAADDR 0x70 //TCA8574 8 channel i2c mux
// #include <Adafruit_NAU7802.h> // i2c load cell adc
// Adafruit_NAU7802 PitchCell;
// Adafruit_NAU7802 RollCell;
// #include <Adafruit_PCF8574.h> // 8 channel digital IO
// #define PCFADDR 0x20 
// Adafruit_PCF8574 pcf;
// #include <Adafruit_PWMServoDriver.h>
// Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);

// void tcaselect(uint8_t i) {
//   if (i > 7) return;
 
//   Wire.beginTransmission(TCAADDR);
//   Wire.write(1 << i);
//   Wire.endTransmission();  
// }

#include <LoadCell.h>
float pitch, roll;

const int IN1 = 0; // i2c digitial IO
const int IN2 = 1; // i2c digitial IO
const int ENA = 0; // pwm channel 0
const int IN3 = 2; // i2c digitial IO
const int IN4 = 3; // i2c digitial IO
const int ENB = 1; // pwm channel 1
int slow = 512;
int pwm1 = slow;
int pwm2 = slow;
int fast = 4095;

float min1 = 2;
float minPitch = 15;
float minRoll = 30;
float norm = 150;
float normin = 100;
float normout = 100;
bool react = false;
bool lock = false;

void setup() {
  while (!Serial);
    delay(1000);
    Wire.begin();
    Serial.begin(115200);
    board1.begin();
  // if(!board1.begin()){
  //   Serial.println("Couldn't find servo driver board");
  //   while (1);
  // }
  board1.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
  Serial.println();
  delay(1000);
  if (!pcf.begin(0x20, &Wire)) {
    Serial.println("Couldn't find i2c mux board");
    while (1);
  }
  for (uint8_t p=0; p<8; p++) {
    pcf.pinMode(p, OUTPUT);
    pcf.digitalWrite(p, LOW);
  }
  pcf.digitalWrite(0, LOW);
  pcf.digitalWrite(1, LOW);
  pcf.digitalWrite(2, LOW);
  pcf.digitalWrite(3, LOW);
  
  pcf.pinMode(IN1, OUTPUT);
  pcf.pinMode(IN2, OUTPUT);
  pcf.pinMode(ENA, OUTPUT);
  pcf.pinMode(IN3, OUTPUT);
  pcf.pinMode(IN4, OUTPUT);
  pcf.pinMode(ENB, OUTPUT);
  delay(100);
  tcaselect(0);
  delay(100);
  Serial.print("----- PitchCell ");
  if (! PitchCell.begin()) {
    Serial.println("Failed to find NAU7802");
    while (1) delay(10);  // Don't proceed.
  }
  Serial.println("Found NAU7802");
  PitchCell.setLDO(NAU7802_3V0);
  PitchCell.setGain(NAU7802_GAIN_128);
  PitchCell.setRate(NAU7802_RATE_80SPS);
  // Take 10 readings to flush out readings
  for (uint8_t i=0; i<10; i++) {
    while (! PitchCell.available()) delay(10);
    PitchCell.read();
  }
  while (! PitchCell.calibrate(NAU7802_CALMOD_INTERNAL)) {
    Serial.println("Failed to calibrate internal offset, retrying!");
    delay(1000);
  }
  Serial.println("Calibrated internal offset");

  while (! PitchCell.calibrate(NAU7802_CALMOD_OFFSET)) {
    Serial.println("Failed to calibrate system offset, retrying!");
    delay(1000);
  }
  Serial.println("Calibrated system offset");
  tcaselect(1);
  Serial.print("----- RollCell ");
  if (! RollCell.begin()) {
    Serial.println("Failed to find NAU7802");
    while (1) delay(10);  // Don't proceed.
  }
  Serial.println("Found NAU7802");
  RollCell.setLDO(NAU7802_3V0);
  RollCell.setGain(NAU7802_GAIN_128);
  RollCell.setRate(NAU7802_RATE_80SPS);
  // Take 10 readings to flush out readings
  for (uint8_t i=0; i<10; i++) {
    while (! RollCell.available()) delay(10);
    RollCell.read();
  }
  while (! RollCell.calibrate(NAU7802_CALMOD_INTERNAL)) {
    Serial.println("Failed to calibrate internal offset, retrying!");
    delay(1000);
  }
  Serial.println("Calibrated internal offset");

  while (! RollCell.calibrate(NAU7802_CALMOD_OFFSET)) {
    Serial.println("Failed to calibrate system offset, retrying!");
    delay(1000);
  }
  Serial.println("Calibrated system offset");
  Wire.begin();

}

void loop() {
  int val;
  if (Serial.available()){
    char ch = Serial.read();
    if(ch == 'f' || ch == 'F'){
      react = false;
      lock = false;
    }
    if(ch == 'r' || ch == 'R'){
      react = true;
      lock = false;
    }
    if(ch == 'l' || ch == 'L'){lock = true;}
    if(ch == 'u' || ch == 'U'){lock = false;}
    if(ch == 'c' || ch == 'C'){
      tcaselect(0);
      PitchCell.calibrate(NAU7802_CALMOD_OFFSET);
      tcaselect(1);
      RollCell.calibrate(NAU7802_CALMOD_OFFSET);
    }
  }
  tcaselect(0);
  while (! PitchCell.available()) {
    delay(1);
  }
  val = PitchCell.read();
  if (abs(val / 1000) > minPitch){
    Serial.print("Pitch Read "); Serial.println(val/1000);
    delay(100);
    pitch = val/1000;
  }
  tcaselect(1);
   while (! RollCell.available()) {
    delay(1);
  }
  val = RollCell.read();
  if (abs(val / 1000) > minRoll){
    Serial.print("Roll Read "); Serial.println(val/1000);
    delay(100);
    roll = val/1000;
  }
  if(pitch < minPitch * -1){
    if(react){
      pcf.digitalWrite(IN1,HIGH);
      pcf.digitalWrite(IN2,LOW);
    }
    else{
      pcf.digitalWrite(IN1,LOW);
      pcf.digitalWrite(IN2,HIGH);
    }
    pwm1 = slow;
  }
  if(roll < minRoll * -1){
    if(react){
      pcf.digitalWrite(IN4,HIGH);
      pcf.digitalWrite(IN3,LOW);
    }
    else{
      pcf.digitalWrite(IN4,LOW);
      pcf.digitalWrite(IN3,HIGH);
    }
    pwm2 = slow;
  }
  
  if(pitch < normout * -1){
    pwm1 = fast;
  }
  if(roll < normout * -1){
    pwm2 = fast;
  }
  
  if(pitch > minPitch){
    if(react){
      pcf.digitalWrite(IN1,LOW);
      pcf.digitalWrite(IN2,HIGH);
    }
    else{
      pcf.digitalWrite(IN1,HIGH);
      pcf.digitalWrite(IN2,LOW);
    }
    pwm1 = slow;
  }
  if(roll > minRoll){
    if(react){
      pcf.digitalWrite(IN4,LOW);
      pcf.digitalWrite(IN3,HIGH);
    }
    else{
      pcf.digitalWrite(IN4,HIGH);
      pcf.digitalWrite(IN3,LOW);
    }
    pwm2 = slow;
  }
  
  if(pitch > normin){
    pwm1 = fast;
  }
  if(roll > normin){
    pwm2 = fast;
  }
  
  if(abs(pitch) < minPitch or lock){
    pcf.digitalWrite(IN1,LOW);
    pcf.digitalWrite(IN2,LOW);
    pwm1 = 0;
  }
  if(abs(roll) < minRoll or lock){
    pcf.digitalWrite(IN3,LOW);
    pcf.digitalWrite(IN4,LOW);
    pwm2 = 0;
  }
  
  board1.setPWM(ENA, 0, pwm1);
  board1.setPWM(ENB, 0, pwm2);
  
}

Try

#include "LoadCell.h"

I can not get the "undefined reference error" but the program with the three files has several problems.

LoadCell.cpp

#include LoadCell.h

ino file
As already pointed out, use double quotes if the include file is in the same directory as the ino file

Compiling
After fixing the above I compiled which gave the following error

C:\Users\bugge\AppData\Local\arduino\sketches\D677FC5790B63E941705AF57659C0314\sketch\LoadCell.cpp.o (symbol from plugin): In function `PitchCell':
(.text+0x0): multiple definition of `PitchCell'
C:\Users\bugge\AppData\Local\arduino\sketches\D677FC5790B63E941705AF57659C0314\sketch\1447947.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

The reason is that you have included the LoadCell.h file twice, once in the ino file and once in LoadCell.cpp.

At this moment the only reason that I can see why you included LoadCell.h in LoadCell.cpp is that you wanted access to the Wire library.
Personally I would just include Wire.h in LoadCell.cpp.

That made no difference... Thank you.

That didn't work. What do you mean by "same directory? They are all in my documents / arduino folder, but the h and cpp file are in the Libraries folder, not same as INO...

Didn't work either way... Thank you.

The only way I can replicate your error is to remove the Loadcell.cpp file, otherwise I get a compile error because of the first line, as has been pointed out the file name needs quotes around it:

#include "LoadCell.h"

Are the files in the libraries folder, or within a folder that is located in the libraries folder? Usual convention would be to have a folder named LoadCell, and the .h and .cpp files would be inside that folder.

At that point you still have the problem that PitchCell, RollCell, pcf, and board1 are defined multiple times, once when the header file is included in the sketch file, and again when the header file is included in the cpp file. The header file should not create instances of the libraries, instead it should declare external references to those instances, which are then created in the cpp file.

LoadCell.h

#ifndef LoadCell_h
#define LoadCell_h
#include "Wire.h"
#define TCAADDR 0x70 //TCA8574 8 channel i2c mux
#include <Adafruit_NAU7802.h> // i2c load cell adc
extern Adafruit_NAU7802 PitchCell;
extern Adafruit_NAU7802 RollCell;
#include <Adafruit_PCF8574.h> // 8 channel digital IO
#define PCFADDR 0x20
extern Adafruit_PCF8574 pcf;
#include <Adafruit_PWMServoDriver.h>
extern Adafruit_PWMServoDriver board1;

void tcaselect(int i);

#endif

LoadCell.cpp

#include "LoadCell.h"

Adafruit_NAU7802 PitchCell;
Adafruit_NAU7802 RollCell;
Adafruit_PCF8574 pcf;
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);

void tcaselect(int i)
{
  if (i > 7) return;

  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();
}

In the libraries folder I have a folder called Robot that contains LoadCell h and cpp files. I intend adding more files to this folder later. Currently focussed on the bits I need for the leg motors and sensors (servos, linear actuators, rotary encoders and load cells).

I just checked and the cpp file was not in the libraries foler at all. I had put it in my 3d models folder by mistake. I found a bunch of other problems that are now resolved. I had to to include wire.h and define the TCAADDR in the cpp file then take them out of the h file. not sure if I need wire.h in both files though, also removed include loadcell from the cpp.

LoadCell.h

#ifndef LoadCell_h
#define LoadCell_h
//#include "Wire.h"
//#define TCAADDR 0x70 //TCA8574 8 channel i2c mux
#include <Adafruit_NAU7802.h> // i2c load cell adc
Adafruit_NAU7802 PitchCell;
Adafruit_NAU7802 RollCell;
#include <Adafruit_PCF8574.h> // 8 channel digital IO
#define PCFADDR 0x20 
Adafruit_PCF8574 pcf;
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);

void tcaselect(int i);




#endif

LoadCell.cpp

//#include "LoadCell.h"
#include "Wire.h"
#define TCAADDR 0x70 //TCA8574 8 channel i2c mux

void tcaselect(int i) 
{
  if (i > 7) return;
 
  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

I'm struggling with "extern". I have not used it before. I'm fairly new to all this, but had years of coding experience with python and other code. My c++ coding knowledge is mostly borrowed and I'm still getting to grips with some of the syntax. Thank you...

In that case you need to include Wire.h in the cpp file. You can simply try, remove the include <Wire.h> and compile.

The only thing that you do with extern (in this scenario) is to tell the compiler that you have defined a variable somewhere in a file that is called ....
It's purpose is the same as the use of a function prototype in your .h file where you tell the compiler that there will be somewhere a function called ... and the compiler now knows which arguments (and type of them) that function needs.


What we often refer to as compiling actually consists of 3 steps

  1. For every source file
    • Pre-processing (not to be confused with what the Arduino IDE does with the builder); this e.g. replace a line with #include by the actual content of the include file and looks for #defines and replace every occurence of that what you defined with the actual text (e.g. Wire.beginTransmission(TCAADDR) becomes Wire.beginTransmission(0x70)).
    • Compiling. This does the actual compiling of the pre-processed source file; the result will be a object file (.o)
  2. Linking. This takes all object files from the above and links them together; it will also link in some other object files that are outside your control.