Undefined Reference to Variable error (global variable) [Timers && ISR's on the ATMega328p]

I need to use a few global variables in order to update ADC read values whenever a triggered interrupt service routine occurs. The interrupt routine is using the standard pre-processor ISR(vector) function in the default Arduino.h library. Timers 0, 1, 2 on the ATMega328p chip (basically all PWM capable compare registers) are already enabled and the ISR have been tested in advanced.

Interrupts.cpp

#include "Interrupts.h"

ISR(TIMER0_COMPA_vect)
{
    // Update Global Variable (#include of Globals.h is performed in Interrupts.h)
    PIN_A0_READ = analogRead(5);
    TIFR0 |= (0 << OCF0A);
}

ISR(TIMER0_COMPB_vect)
{
    PIN_A1_READ = analogRead(6);
    TIFR0 |= (0 << OCF0B);
}

ISR(TIMER1_COMPA_vect)
{
    TIFR1 |= (0 << OCF1A);
}

ISR(TIMER1_COMPB_vect)
{
//    std::cout << "Interrupt Service Routine 1B tripped!!!" << std::endl;
    TIFR1 |= (0 << OCF1B);
}

ISR(TIMER2_COMPA_vect){
    TIFR2 |= (0 << OCF2A);
}

ISR(TIMER2_COMPAB_vect){
    TIFR2 |= (0 << OCF2B);
}

In a separate header file, I have a class which instantiates a Thermistor object. The constructor accepts a few arguments. One of which, is the analog input pin to specify which physical port a Thermistor probe is attached to. The constructor checks this argument to direct the private class member adcValue to the space in memory from which it should be updated from (i.e address reference). The idea of this approach was to allow the Thermistor object to continuously update in the background after instantiation by utilizing the interrupt service routines in order to monitor/update temperatures without blocking the main program through various and frequent class-method calls.

Thermistor.h

#ifndef THERMISTOR_H
#define THERMISTOR_H
#include <Arduino.h>
#include <ArduinoSTL.h>
#include <Globals.h>

class Thermistor
{
private:
    unsigned int * adcValue = nullptr;
    int * logicalPinAddress = nullptr;
    long * nominalResistance = nullptr;
    int * beta = nullptr;
    float * supplyVoltage = nullptr;
    float * vo = nullptr;
    float * r1 = nullptr;
    float * r2 = nullptr;
    float * temperatureCelsius = nullptr;
    float * temperatureFahrenheit = nullptr;

public:
    Thermistor(){};

    Thermistor(int pinAddress, long int nominalResistance, int beta, float supplyVoltage, float R1) :
            logicalPinAddress(&pinAddress), nominalResistance(&nominalResistance), beta(&beta), supplyVoltage(&supplyVoltage), r1(&R1) {

        if (*logicalPinAddress == PIN_A0)
            this->adcValue = &PIN_A0_READ;

        if (*logicalPinAddress == PIN_A1)
            this->adcValue = &PIN_A1_READ;

        if (*logicalPinAddress == PIN_A2)
            this->adcValue = &PIN_A2_READ;;

        if (*logicalPinAddress == PIN_A3)
            this->adcValue = &PIN_A3_READ;

        if (*logicalPinAddress == PIN_A4)
            this->adcValue = &PIN_A4_READ;

        if (*logicalPinAddress == PIN_A5)
            this->adcValue = &PIN_A5_READ;
    };
};


#endif //ARDUINO_TEMPERATURE_CONTROLLER_THERMISTOR

I cannot declare the global variables in the Main.cpp file, because if I needed to access it from my Interrupts.h header, then the #include main.cpp would cause a circular dependency. I read up online a better approach would be to use a dedicated .h file (“Globals.h”) to hold global variables and then include them into other headers as needed.

Globals.h

extern unsigned int PIN_A0_READ;
extern unsigned int PIN_A1_READ;
extern unsigned int PIN_A2_READ;
extern unsigned int PIN_A3_READ;
extern unsigned int PIN_A4_READ;
extern unsigned int PIN_A5_READ;

Upon compiling my code I get a Undefined Reference to Variable error as seen below in the console output. I am not sure what could be causing this, as my Linter/Intellisense all checks out. Are there any specific instances I would not be able to reference a Global variable by address?

Compiler Output:

====================[ Build | Production | uno ]================================
C:\Users\Aspen\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\202.7660.37\bin\cmake\win\bin\cmake.exe --build "C:\Users\Aspen\Documents\PlatformIO\Projects\Arduino Temperature Controller\cmake-build-uno" --target Production -- -j 6
Processing uno (platform: atmelavr; board: nanoatmega328; framework: arduino)
--------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/nanoatmega328.html
PLATFORM: Atmel AVR (3.0.0) > Arduino Nano ATmega328
HARDWARE: ATMEGA328P 16MHz, 2KB RAM, 30KB Flash
DEBUG: Current (avr-stub) On-board (avr-stub, simavr)
PACKAGES: 
 - framework-arduino-avr 5.1.0 
 - toolchain-atmelavr 1.50400.190710 (5.4.0)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 11 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <ArduinoSTL> 1.1.0
|-- <Arduino-MemoryFree> 0.0.0+20201021195712.sha.aed5cc5
|-- <Interrupts>
|   |-- <Probes>
|   |   |-- <ArduinoSTL> 1.1.0
|   |   |-- <Thermistor>
|   |   |   |-- <ArduinoSTL> 1.1.0
|   |   |   |-- <Globals>
|   |   |-- <Arduino-MemoryFree> 0.0.0+20201021195712.sha.aed5cc5
|-- <Probes>
|   |-- <ArduinoSTL> 1.1.0
|   |-- <Thermistor>
|   |   |-- <ArduinoSTL> 1.1.0
|   |   |-- <Globals>
|   |-- <Arduino-MemoryFree> 0.0.0+20201021195712.sha.aed5cc5
|-- <Thermistor>
|   |-- <ArduinoSTL> 1.1.0
|   |-- <Globals>
Building in release mode
Compiling .pio\build\uno\src\main.cpp.o
Compiling .pio\build\uno\libcfd\Interrupts\Interrupts.cpp.o
In file included from src\main.cpp:13:0:
.pio\libdeps\uno\Arduino-MemoryFree/pgmStrToRAM.h:5:39: warning: '__progmem__' attribute ignored [-Wattributes]
 char *pgmStrToRAM(PROGMEM const char *theString);
                                       ^
src\main.cpp: In function 'void coutLong(std::string)':
src\main.cpp:185:23: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
     for (int i = 0; i < s.length(); ++i)
                       ^
In file included from C:\Users\Aspen\.platformio\packages\framework-arduino-avr\cores\arduino/Arduino.h:30:0,
                 from lib\Interrupts\Interrupts.h:8,
                 from lib\Interrupts\Interrupts.cpp:5:
lib\Interrupts\Interrupts.cpp: In function 'void TIMER2_COMPAB_vect()':
lib\Interrupts\Interrupts.cpp:41:5: warning: 'TIMER2_COMPAB_vect' appears to be a misspelled signal handler, missing __vector prefix [-Wmisspelled-isr]
 ISR(TIMER2_COMPAB_vect){
     ^
Archiving .pio\build\uno\libcfd\libInterrupts.a
Indexing .pio\build\uno\libcfd\libInterrupts.a
Linking .pio\build\uno\firmware.elf
C:\Users\Aspen\AppData\Local\Temp\ccEIKXAw.ltrans0.ltrans.o: In function `main':
<artificial>:(.text.startup+0x354): undefined reference to `PIN_A0_READ'
<artificial>:(.text.startup+0x356): undefined reference to `PIN_A0_READ'
<artificial>:(.text.startup+0x3f0): undefined reference to `PIN_A1_READ'
<artificial>:(.text.startup+0x3f2): undefined reference to `PIN_A1_READ'
<artificial>:(.text.startup+0x48c): undefined reference to `PIN_A2_READ'
<artificial>:(.text.startup+0x48e): undefined reference to `PIN_A2_READ'
<artificial>:(.text.startup+0x500): undefined reference to `PIN_A3_READ'
<artificial>:(.text.startup+0x502): undefined reference to `PIN_A3_READ'
<artificial>:(.text.startup+0x582): undefined reference to `PIN_A4_READ'
<artificial>:(.text.startup+0x584): undefined reference to `PIN_A4_READ'
<artificial>:(.text.startup+0x5fc): undefined reference to `PIN_A5_READ'
<artificial>:(.text.startup+0x5fe): undefined reference to `PIN_A5_READ'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\uno\firmware.elf] Error 1

========================== [FAILED] Took 1.97 seconds ==========================
Environment    Status    Duration
-------------  --------  ------------
uno            FAILED    00:00:01.967
==================== 1 failed, 0 succeeded in 00:00:01.967 ====================
mingw32-make.exe[3]: *** [CMakeFiles/Production] Error 1
mingw32-make.exe[2]: *** [CMakeFiles/Production.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles/Production.dir/rule] Error 2
mingw32-make.exe: *** [Production] Error 2
CMakeFiles\Production.dir\build.make:76: recipe for target 'CMakeFiles/Production' failed
CMakeFiles\Makefile2:98: recipe for target 'CMakeFiles/Production.dir/all' failed
CMakeFiles\Makefile2:105: recipe for target 'CMakeFiles/Production.dir/rule' failed
Makefile:137: recipe for target 'Production' failed

I think you have declared your PIN_An_READ variales as extern in globals.h but you have not declared them anywhere else in your code. The linker is therefore complaining.

Somewhere you need to:

unsigned int PIN_A0_READ;
unsigned int PIN_A1_READ;
unsigned int PIN_A2_READ;
unsigned int PIN_A3_READ;
unsigned int PIN_A4_READ;
unsigned int PIN_A5_READ;

And maybe initialise them all? Most likely in the file with the main() function?

Cheers,
Norm.

1 Like