Weird multiple definition message

While moving code into a library I receive weird messages:
Linking .pio/build/esp32dev/firmware.elf
.pio/build/esp32dev/src/rotEncoder.cpp.o:(.data.mux+0x0): multiple definition of mux' .pio/build/esp32dev/src/main.cpp.o:(.data.mux+0x0): first defined here .pio/build/esp32dev/src/rotEncoder.cpp.o:(.data.encSwitch+0x0): multiple definition of encSwitch’
.pio/build/esp32dev/src/main.cpp.o:(.data.encSwitch+0x0): first defined here
.pio/build/esp32dev/src/rotEncoder.cpp.o:(.bss.deBounceTimer+0x0): multiple definition of `deBounceTimer’
.pio/build/esp32dev/src/main.cpp.o:(.bss.deBounceTimer+0x0): first defined here
collect2: error: ld returned 1 exit status

These definitions were in the previous version of main.cpp, but are now moved to a header file.

Does PIO ‘remember’ the previous definition somewhere in cache?
If so:
How can I clear this cache?

The most probable cause of this is that you’ve written your header file like

#ifndef SOME_HEADER_H_
#define SOME_HEADER_H_

#include <some_lib.h>
someType encSwitch;

#endif

Which is C++ code to create a global variable called encSwitch. Thus if you include that header from 2 separate .cpp files, both .cpp files are trying to create the same global variable – thus a multiple definition.

You’ll need to use extern someType encSwitch; in the header to only convey the information that this variable exists somewhere (declaration), and then only in one .cpp file instantiate the global variable with someType encSwitch (definition).

Refer e.g.

1 Like

I’v tried several insertion of “extern” and extern “C”, but the compiler refuses to accept them.
This is my code:

File: main.cpp

#include <Arduino.h>
#include <rotaryCoder.h>

RotaryCoder rotaryCoder;

void IRAM_ATTR switchHandler()
{
  rotaryCoder.buttonHandler();
}

void setup() 
{
  Serial.begin(115200); delay(500);
  Serial.printf("setup=========== complete: %u\n",
                encSwitch.deBounceTime);
  pinMode(encSwitch.pinSW,  INPUT_PULLUP);
  attachInterrupt(encSwitch.pinSW,  switchHandler, FALLING);
  encSwitch.maxEncCount = stations.getMaxEncoder() - 1;
}

File: rotaryCoder.h

#ifndef rotaryCoder_h
#define rotaryCoder_h

#include <Arduino.h>

struct EncSwitch
{
  const uint8_t pinSW  = 13; 
  const uint8_t pinCLK = 26; 
  const uint8_t pinDT  = 27; 
  uint16_t totalPress  = 0; 
  uint16_t maxEncCount = 0; 
  volatile uint16_t curEncCount  = 0;
  volatile uint16_t oldEncCount  = 0; 
  volatile uint16_t deBounceTime = 300;  
  bool swPressed = false;
} encSwitch;

volatile uint32_t deBounceTimer = 0;       // Store time in millis()
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

class RotaryCoder
{
    public:
      void buttonHandler(void);  
};  // end class RotaryCoder

#endif

What do I have to do, please?

Remove encSwitch to as to not define a global variable there.

Write extern at the start of this line and remove = 0 at the end of it.

Then in main.cpp change

#include <Arduino.h>
#include <rotaryCoder.h>

to

#include <Arduino.h>
#include <rotaryCoder.h>

EncSwitch encSwitch;
volatile uint32_t deBounceTimer = 0;

That restructures the code to only use structure declarations and a variable declaration, and do the definition in one cpp file.

Maximilian,
First of all thanks for the respons!
You said:

Do I understand this well as to make the struct EncSwitch a member of class RotaryCoder?
This is what I deduct from the statement in main.cpp: EncSwitch encSwitch;

You didn’t mention anything about:

Do I have to handle that the same way as:

No, just remove the text encSwitch after the struct declaration as to not create a variable of that struct directly.

struct EncSwitch
{
  const uint8_t pinSW  = 13; 
  const uint8_t pinCLK = 26; 
  const uint8_t pinDT  = 27; 
  uint16_t totalPress  = 0; 
  uint16_t maxEncCount = 0; 
  volatile uint16_t curEncCount  = 0;
  volatile uint16_t oldEncCount  = 0; 
  volatile uint16_t deBounceTime = 300;  
  bool swPressed = false;
} encSwitch;

^ declares the structure and directly instantiates one with the name encSwitch → if included from multiple .cpp files, will create multiple definitions → bad

struct EncSwitch
{
  const uint8_t pinSW  = 13; 
  const uint8_t pinCLK = 26; 
  const uint8_t pinDT  = 27; 
  uint16_t totalPress  = 0; 
  uint16_t maxEncCount = 0; 
  volatile uint16_t curEncCount  = 0;
  volatile uint16_t oldEncCount  = 0; 
  volatile uint16_t deBounceTime = 300;  
  bool swPressed = false;
};

^ just declares the EncSwitch structure, which is what you need.

You’re right, this has to be treated the same way. extern in the header and without init value, and then no extern and with init value in one .cpp file.

Thank you.
But just removing the instantiation from the declaration; like so:
struct EncSwitch
{

};
in file rotaryEncoder.h
and than instatiate the struct in file main.ccp; like this:
EncSwitch encSwitch;

Is not what my compilers likes:
Compiling .pio/build/esp32dev/src/rotaryCoder.cpp.o
src/rotaryCoder.cpp: In member function ‘void RotaryCoder::buttonHandler()’:
src/rotaryCoder.cpp:6:21: error: ‘encSwitch’ was not declared in this scope
if ((millis() - encSwitch.deBounceTime) >= deBounceTimer)

I’ll try to declare encSwitch as a member variable of class RotaryCoder.
Coming to think about this, it may even be neseccarily to innitialize the struct
in the constructor of class RotaryEncoder.

Then place a declaration of

extern EncSwitch encSwitch;

in the header file as well, after the declaration of the structure.

Maximilian,
Thank you.
After some thinking and trying I came to the following solution.
This way I can use a pointer to the struct, which avoids the copying.
File: encoder.h

===============

#ifndef encoder_h
#define encoder_h

#include <Arduino.h>

struct Coder
{
  const uint8_t pinSW  = 13;               // GPIO 13 on ESP32
  const uint8_t pinCLK = 26;               // GPIO 26 on ESP32
  const uint8_t pinDT  = 27;               // GPIO 27 on ESP32
  uint16_t totalPress  = 0;                // Total times switch pressed
  uint16_t maxEncCount = 0;                // Total number of stations
  volatile uint16_t curEncCount  = 0;      // Current Rotary Encoder Counter
  volatile uint16_t oldEncCount  = 0;      // Previous Rotary Encoder Counter
  volatile uint16_t deBounceTime = 300;  
  bool swPressed = false;
};

extern volatile uint32_t deBounceTimer;    // Store time in millis()
extern portMUX_TYPE mux;

class Encoder
{
    public:
      void buttonHandler(void);  
      struct Coder sCoder;
      Coder *pCoder = &sCoder;
};  // end class Encoder

#endif

File: encoder.cpp

#include <encoder.h>

void Encoder::buttonHandler(void)
{
    portENTER_CRITICAL(&mux);   
    if ((millis() - sCoder.deBounceTime) >= deBounceTimer)
    {
        deBounceTimer = millis();
        sCoder.totalPress++;
        sCoder.swPressed = true;
    }
    portEXIT_CRITICAL(&mux);

}   // end Encoder::buttonHandler(void)

File: main.cpp

#include <Arduino.h>
#include <encoder.h>

volatile uint32_t deBounceTimer = 0;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;

Encoder encoder;    // instance of class Encoder

void IRAM_ATTR switchHandler()
{
  encoder.buttonHandler();
}


void setup() 
{
  Serial.begin(115200); delay(500);
  Serial.printf("setup=========== complete: %u\n",
                 encoder.pCoder->deBounceTime);
  pinMode(encoder.pCoder->pinSW,  INPUT_PULLUP);
  attachInterrupt(encoder.pCoder->pinSW,  switchHandler, FALLING);
}

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

I trust it will be of interest to others.