Undefined reference error while linking

Hi, I bought a LED strip and programmed a slow "sunrise" (more functions will follow). To arrange the code more clearly and maybe to use it in similar projects, I moved it into an own library. Because in the corresponding libraray the LEDs are addressed as a class, I wrote an own class for my fading functions. To be able to use an interrupt (Timer), I adapted this example.

Some errors I could fix on my own, but now while trying to compile it, I get undefined reference to `NatureFadeIR' errors, and the collect2.exe: error: ld returned 1 exit status, where "NatureFadeIR"… is the instance name of my fading class inside the interrupt helping class cpp file (declared as "extern NatureFade NatureFadeIR;", just equal to the example "extern CTimer0 Timer0"). Curiously the errors don't occur in the helping class cpp, but in the main class cpp in a function that has nothing to do with the helping class :o Both cpp files include the other one like in the example...

So where do the errors come from?

If more code is needed, let me know.

Thanks in advance!

If more code is needed, let me know.

Please post your complete program and full error messages

Ok, let's go. I've attached the four library files. The sketch is irelevant (it also occurs in an empty sketch). And here are the error messages:

Arduino: 1.8.12 (Windows 7), Board: "Arduino Uno"

C:\Users...\AppData\Local\Temp\ccdtKWTD.ltrans0.ltrans.o: In function `FastSunFade':

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:92: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:93: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:94: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:97: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:99: undefined reference to `NatureFadeIR'

C:\Users...\AppData\Local\Temp\ccdtKWTD.ltrans0.ltrans.o:D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:99: more undefined references to `NatureFadeIR' follow

collect2.exe: error: ld returned 1 exit status

exit status 1

NatureFade.cpp (4.46 KB)

NatureFade.h (1.11 KB)

TimerInterrupt.cpp (688 Bytes)

TimerInterrupt.h (543 Bytes)

Because my last thread has oviously "sunken", another try:

I bought a LED strip and programmed a slow "sunrise" (more functions will follow later). To arrange the code more clearly and maybe to use it in similar projects, I moved it into an own library. Because in the corresponding libraray the LEDs are addressed as a class, I wrote an own class for my fading functions. To be able to use an interrupt (Timer), I adapted this example.

Some errors I could fix on my own, but now while trying to compile it, I get undefined reference to `NatureFadeIR' errors, and the collect2.exe: error: ld returned 1 exit status, where "NatureFadeIR"… is the instance name of my fading class inside the interrupt helping class cpp file (declared as "extern NatureFade NatureFadeIR;", just equal to the example "extern CTimer0 Timer0"). Curiously the errors don't occur in the helping class cpp, but in the main class cpp in a function that has nothing to do with the helping class :o Both cpp files include the other one like in the example...

So where do the errors come from?

Thanks in advance!

Here are the full error messages:

Arduino: 1.8.12 (Windows 7), Board: "Arduino Uno"

C:\Users\ \AppData\Local\Temp\ccdtKWTD.ltrans0.ltrans.o: In function `FastSunFade':

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:92: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:93: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:94: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:97: undefined reference to `NatureFadeIR'

D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:99: undefined reference to `NatureFadeIR'

C:\Users...\AppData\Local\Temp\ccdtKWTD.ltrans0.ltrans.o:D:\Dokumente\Arduino\libraries\NatureFade/NatureFade.cpp:99: more undefined references to `NatureFadeIR' follow

collect2.exe: error: ld returned 1 exit status

exit status 1

NatureFade.cpp (4.46 KB)

NatureFade.h (1.11 KB)

TimerInterrupt.cpp (688 Bytes)

TimerInterrupt.h (543 Bytes)

Post the code and don't attach the files. I'm not willing to download them on my mobile device.

MdE2020:
[...] (declared as "extern NatureFade NatureFadeIR;"[...]
So where do the errors come from?

You have declared the variable to the compiler (there is a variable of type NaturFade and it is called NatureFadeIR somewhere). The compiler then compiles happily, because it trusts you and your declaration. Then the linker comes in and wants to resolve the addresses necessary to use the variable, but it can't find the variables definition anywhere.

TL;DR: whenever you declare an extern(al) variable, you also need to define it somewhere:
a.cpp

extern int hello; // declaration -- declare to the compiler that a variable named "hello" exists somewhere

b.cpp

int hello; // definition

The two topics have been merged

@MdE2020 - be very careful that you don't get banned for starting a duplicate topic. If you wanted to bring attention to your original topic all you have to do was to add another post to it and it would have moved onto the first page

Please do not start duplicate topics in the future or action may be taken

LightuC:
Post the code and don't attach the files. I'm not willing to download them on my mobile device.

Maybe you should suggest #8 in How to use this forum - please read to be changed :wink:

LightuC:
You have declared the variable to the compiler (there is a variable of type NaturFade and it is called NatureFadeIR somewhere). The compiler then compiles happily, because it trusts you and your declaration. Then the linker comes in and wants to resolve the addresses necessary to use the variable, but it can't find the variables definition anywhere.

Ok, I guess I understand what you explain. But I thought, "NatureFade" is definied in the main class.

NatureFade.cpp:

/*
  NatureFade.h - Library für natürliche Fades mit Adafruit NeoPixels - implementation
  2020 © MH
*/

#include "Arduino.h"
#include "Adafruit_NeoPixel.h"
#include "TimerInterrupt.h"
#include "NatureFade.h"

Adafruit_NeoPixel LEDs;


NatureFade::NatureFade(byte _PinN, byte _Count) {    
  _LastFastMillis = millis();
  _LastSecond = _LastFastMillis / 1000;
  
  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);
  
  _NUMPIXELS = _Count;
  _PIN = _PinN;
}

void NatureFade::begin() {
  LEDs.updateType(NEO_GRBW + NEO_KHZ800);
  LEDs.updateLength(_NUMPIXELS);
  LEDs.setPin(_PIN);
  LEDs.begin();
}

void NatureFade::DoUpdate() {
  FastSunFade();
  
  _NowSecond = millis() / 1000;
  if (_NowSecond > _LastSecond) {
    _LastSecond = _NowSecond;
    
    SunRise();
  }
}

void NatureFade::AllowBreakActiveFade(bool _allow) {
  _BreakActiveFade = _allow;
}

bool NatureFade::NoFadingActive() {
  return !(_FastFading || _SunRising);
}

void NatureFade::StartSunRise(unsigned long _SRDuration) {
  if (_BreakActiveFade) {
    StopAllFades();  
  }
  if (NoFadingActive()) {
 _SunRiseDuration = _SRDuration * 60;
    _DestSecond = _LastSecond + _SunRiseDuration;
    _SunRising = true;
  }
}

void NatureFade::StartFastFade(bool _Direction, unsigned long _Pause) {
  if (_BreakActiveFade) {
    StopAllFades();  
  }
  if (NoFadingActive()) {
    _FastFIndex = 0;
    _FastFading = true;
    _FastFDirection = _Direction;
    _FastFPause = _Pause;
  }
}

void NatureFade::StopAllFades() {
  _SunRising = false;
  _FastFading = false;
  _FastFIndex = 0;
}

void NatureFade::FullBlank(bool _Blank) {
  StopAllFades();
  if (_Blank) {
    LEDs.fill(LEDs.Color(0, 0, 0, 0));
  }
  else {
    LEDs.fill(LEDs.Color(255, 255, 255, 255));
  }
  LEDs.show();
}

void NatureFade::FastSunFade() {
  if (_FastFIndex == 255) {
    _FastFIndex = 0;
    _FastFading = false;
  }
  byte _i;
  if (_FastFading) {
    unsigned long _NowFastMillis = millis();
    if (_NowFastMillis > _LastFastMillis + _FastFPause) {
      _LastFastMillis = _NowFastMillis;
      if (_FastFDirection) {
        _i = _FastFIndex;
      }
      else {
        _i = 255 - _FastFIndex;
      }
      LEDs.fill(LEDs.gamma32(LEDs.Color(_i, _i, _i, _i)));
      LEDs.show();
      _FastFIndex++;
    }
  }
}

void NatureFade::SunRise() {
  if (_NowSecond > _DestSecond) {
    _SunRising = false;
  }  
  if (_SunRising) {
    LEDs.fill(SunRiseColor(GetFactorWithGamma(GetQuotient(_SunRiseDuration))), 0, _NUMPIXELS);
    LEDs.show();    
  }
}

uint32_t NatureFade::SunRiseColor(int i) {
  byte gir, gig, gib, giw;
  switch (i) {
    case 0 ... 44:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8(i/3.0);
      return LEDs.Color(gir, gig, 0, 0);
      break;
    case 45 ... 99:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8(i/3.0);
      return LEDs.Color(gir, gig, 0, 1);
      break;
    case 100 ... 124:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8((i-100)*1.427586207+33);
      gib = LEDs.gamma8((i-100)*1.503448276);
      return LEDs.Color(gir, gig, gib, 2);      
      break;
    case 125 ... 154:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8((i-100)*1.427586207+33);
      gib = LEDs.gamma8((i-100)*1.503448276);
      giw = LEDs.gamma8((i-125)*1.946153846+2);
      return LEDs.Color(gir, gig, gib, giw);         
      break;   
    case 155 ... 199:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8((i-100)*1.427586207+33);
      gib = LEDs.gamma8((i-100)*1.503448276);
      giw = LEDs.gamma8((i-125)*1.946153846+2);
      return LEDs.Color(gir, gig, gib, giw);         
      break;    
    case 200 ... 244:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8((i-100)*1.427586207+33);
      gib = LEDs.gamma8((i-200)*2.111111111+155);
      giw = LEDs.gamma8((i-125)*1.946153846+2);
      return LEDs.Color(gir, gig, gib, giw);         
      break;
    case 245 ... 255:
      gir = LEDs.gamma8(i);
      gig = LEDs.gamma8(240);
      gib = LEDs.gamma8(245);
      giw = LEDs.gamma8((i-125)*1.946153846+2);
      return LEDs.Color(gir, gig, gib, giw);
      break;
    default:
      return 0;
      break;
    } 
}

byte NatureFade::GetFactorWithGamma(float _Quotient) {
  return round(_Quotient * 232.0 + 23);
}

float NatureFade::GetQuotient(unsigned long _Duration) {
  float _QNow = _Duration-(_DestSecond-(millis()/1000));
  float _QDest = _Duration;
  return _QNow / _QDest;  
}

NatureFade.h:

/*
  NatureFade.h - Library für natürliche Fades mit Adafruit NeoPixels - description
  2020 © MH
*/

#include "Adafruit_NeoPixel.h"
#include "TimerInterrupt.h"

#ifndef NatureFade_h
#define NatureFade_h

class NatureFade {
    friend class TimerInterrupt;
  private:
    int _NUMPIXELS, _PIN;

    bool _BreakActiveFade = true;
    bool _FastFading, _SunRising = false;

    unsigned long _LastSecond, _NowSecond, _DestSecond, _LastFastMillis = 0;
    unsigned long _SunRiseDuration, _SunDownDuration, _FastFPause;
    byte _FastFIndex = 0;
    bool _FastFDirection;
 
    void DoUpdate();
 
 void FastSunFade();
    void SunRise();
 
    uint32_t SunRiseColor(int i);
    byte GetFactorWithGamma(float _Quotient);
    float GetQuotient(unsigned long _Duration);
  public:
    NatureFade(byte _PPinN, byte _Count);
    void begin();
    void AllowBreakActiveFade(bool _allow);
    bool NoFadingActive();
    void StartSunRise(unsigned long _SRDuration);
    void StartFastFade(bool _Direction, unsigned long _Pause);
    void StopAllFades();
    void FullBlank(bool _Blank);
};

#endif

TimerInterrupt.cpp:

/*
  TimerInterrupt.cpp - implementation
  2008 © Ron Kreymborg
  from https://www.mail-archive.com/avr-libc-dev@nongnu.org/msg02454.html
  2020 adapted by MH
*/

#include "NatureFade.h"
#include "TimerInterrupt.h"

extern NatureFade NatureFadeIR;

//---------------------------------------------------------------
TimerInterrupt::TimerInterrupt()
{
  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);
}

//---------------------------------------------------------------
TimerInterrupt::~TimerInterrupt()
{
  TIMSK0 &= ~_BV(OCIE0A);
}

//---------------------------------------------------------------
void TimerInterrupt::OnInterrupt()
{
  NatureFadeIR.DoUpdate();
}

TimerInterrupt.h:

/*
  TimerInterrupt.cpp - implementation
  2008 © Ron Kreymborg
  from https://www.mail-archive.com/avr-libc-dev@nongnu.org/msg02454.html
  2020 adapted by MH
*/

#define STRINGIFY(name) #name
#define CLASS_IRQ(name, vector) \
    static void name(void) asm(STRINGIFY(vector)) \
    __attribute__ ((signal, __INTR_ATTRS))

#ifndef TimerInterrupt_h
#define TimerInterrupt_h

class TimerInterrupt {
  private:
    CLASS_IRQ(OnInterrupt, TIMER0_COMPA_vect);
  public:
    TimerInterrupt();
 ~TimerInterrupt();
};

#endif

UKHeliBob:
@MdE2020 - be very careful that you don't get banned for starting a duplicate topic.

I'm sorry I didn't know duplicate posting is a "capital offence" here, in spite of searching I wasn't even able to find the old thread, fortunately I had the draft on my computer. Thank you for merging (really).

But I thought, "NatureFade" is definied in the main class.

You need to do three things:

  • Define the class. (NatureFade)
  • Create an actual instance of the class (NatureFade NatureFadeIR;)
  • declare the type of of your instance in any module that would otherwise not have the variable in scope. (extern NatureFade NatureFadeIR;)

You've done 1, but in TimerInterrupt.cpp, where you presumably want to do this, you have "extern NatureFade NatureFadeIR;", which only says "the actual instance is defined somewhere else." If you remove the "extern" from that statment, it should work.
(Since you're not actually referencing NatureFadeIR from anywhere else, you don't actually need (3). At some point, you probably want to put the extern declaration in one of your .h files.)