PlatformIO Community

Multiple definition of error. Teensy 4.1

Hello I am new to platformio and arduino and I have no clue how to fix this error.

.pio\build\teensy41\src\main.cpp.o: In function `EncoderTool::Encoder::Encoder()':
main.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): multiple definition of `EncoderTool::Encoder::Encoder()'
.pio\build\teensy41\src\Encoders.cpp.o:Encoders.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): first defined here
c:/users/e/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions
.pio\build\teensy41\src\main.cpp.o: In function `EncoderTool::Encoder::Encoder()':
main.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): multiple definition of `EncoderTool::Encoder::Encoder()'
.pio\build\teensy41\src\Encoders.cpp.o:Encoders.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): first defined here

This is the Encoders.cpp code:

#include "Encoders.h"

Encoder enc1;
Encoder enc2;
Encoder enc3;
Encoder enc4;
Encoder enc5;

void Encoders::begin()
{
    enc1.begin(28, 27, CountMode::half);
    enc2.begin(25, 24, CountMode::half);
    enc3.begin(11, 10, CountMode::half);
    enc4.begin(8, 7, CountMode::half);
    enc5.begin(5, 4, CountMode::half);
}

void Encoders::value()
{
    if (enc1.valueChanged() || enc2.valueChanged() || enc3.valueChanged() || enc4.valueChanged() || enc5.valueChanged())
    {

        Serial.print("Enc1 = ");
        Serial.print(enc1.getValue());
        Serial.print(", Enc2 = ");
        Serial.print(enc2.getValue());
        Serial.print(", Enc3 = ");
        Serial.print(enc3.getValue());
        Serial.print(", Enc4 = ");
        Serial.print(enc4.getValue());
        Serial.print(", Enc5 = ");
        Serial.print(enc5.getValue());
        Serial.println();
    }
}

And this is the Encoders.h code:

#ifndef ENCODERS_H
#define ENCODERS_H

#include "EncoderTool.h"
#include "Arduino.h"

using namespace EncoderTool;

extern Encoder enc1;
extern Encoder enc2;
extern Encoder enc3;
extern Encoder enc4;
extern Encoder enc5;

class Encoders
{
public:
  void value();
  void begin();
};

#endif

And this is the main.cpp code:

#include "Arduino.h"
#include "SPI.h"

#include "Display.h"
Display display;
#include "Encoders.h"
Encoders encoders;
#include "Sd.h"
SD sD;

void setup()
{
    display.begin();
    sD.begin();
    encoders.begin();
}

void loop()
{
    encoders.value();
}

And what happens in this file?

It is a library file but this is the code in EncoderTool.h

#pragma once

#include "Multiplexed/EncPlex74165.h"
#include "Multiplexed/EncPlex4067.h"
#include "Multiplexed/EncPlex4051.h"
#include "Single/Encoder.h"
#include "Single/PolledEncoder.h"

And in this one?

Where is the constructor Encoder::Encoder() implemented?

This one? Encoder.h

#pragma once

#include "../EncoderBase.h"
//#include "../HAL/iPin_t.h"
#include "../HAL/directReadWrite.h"
#include "../HAL/pinInterruptHelper.h"

namespace EncoderTool
{
    /***********************************************************************
     *  Simple interupt based encoder implementation which reads
     *  phase A and B from two interupt capable pins
     ************************************************************************/
    class Encoder : public EncoderBase
    {
     public:
        Encoder();
        inline ~Encoder();

        inline bool begin(int pinA, int pinB, CountMode = CountMode::quarter, int inputMode = INPUT_PULLUP);
        inline bool begin(int pinA, int pinB, encCallback_t cb, CountMode = CountMode::quarter, int inputMode = INPUT_PULLUP);

        void doUpdate() { update(HAL::directRead(A), HAL::directRead(B)); }

     protected:
        HAL::pinRegInfo_t A, B;

        using iHelper = HAL::PinInterruptHelper<Encoder, &Encoder::doUpdate>;
    };

    // Inline implementation ===============================================

    Encoder::Encoder()
    {
        // Pin::setCallbackMember(&Encoder::doUpdate);
    }

    bool Encoder::begin(int pinA, int pinB, encCallback_t cb, CountMode countMode, int inputMode)
    {
        bool ret = begin(pinA, pinB, countMode, inputMode);
        attachCallback(cb);
        return ret;
    }

    bool Encoder::begin(int pinA, int pinB, CountMode countMode, int inputMode)
    {
        using namespace HAL;

        if (!iHelper::hasInterrupt(pinA) || !iHelper::hasInterrupt(pinB))
            return false;

        A = pinRegInfo_t(pinA);
        B = pinRegInfo_t(pinB);

        pinMode(A.pin, inputMode);
        pinMode(B.pin, inputMode);

        setCountMode(countMode);
        EncoderBase::begin(directRead(A), directRead(B)); // set start state

        iHelper::attachInterrupt(A.pin, this, CHANGE);
        iHelper::attachInterrupt(B.pin, this, CHANGE);
        return true;
    }

    Encoder::~Encoder()
    {
        iHelper::detachInterrupt(A.pin);
        iHelper::detachInterrupt(B.pin);
    }
} // namespace EncoderTool

And if you move this code into a .cpp file to garuantee it only exists in one object file? (With the appropriate namepace declaration of course.

where should I move it or should I rename it to Encoder.cpp

This is where the files are:

EncoderTool.h - E-1 - Visual Studio Code Administrator 7_19_2022 9_51_14 PM (2)

I would create a file called Encoder.cpp and put the implementation in there, in the same namespace.

I see you’re actually using https://github.com/luni64/EncoderTool. Let me try and reproduce the issue.

This is a library issue. It’s very easily reproducable by using

[env:teensy41]
platform = teensy
board = teensy41
framework = arduino
lib_deps = https://github.com/luni64/EncoderTool.git

with src/main.cpp

#include <Arduino.h>
#include <EncoderTool.h>

using namespace EncoderTool;
Encoder enc1;
extern Encoder enc2;

void setup() {
    enc1.begin(1, 2);
    enc2.begin(3, 4);
}

void loop() {}

and src/helper.cpp

#include <Arduino.h>
#include <EncoderTool.h>

using namespace EncoderTool;
Encoder enc2;

void some_func() { }

already leading to

.pio\build\teensy41\src\main.cpp.o: In function `EncoderTool::Encoder::Encoder()':
main.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): multiple definition of `EncoderTool::Encoder::Encoder()'
.pio\build\teensy41\src\helper.cpp.o:helper.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): first defined here
c:/users/max/.platformio/packages/toolchain-gccarmnoneeabi@1.50401.190816/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions        
.pio\build\teensy41\src\main.cpp.o: In function `EncoderTool::Encoder::Encoder()':
main.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): multiple definition of `EncoderTool::Encoder::Encoder()'
.pio\build\teensy41\src\helper.cpp.o:helper.cpp:(.text._ZN11EncoderTool7EncoderC2Ev+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

This library is only meant for exactly one .cpp file every doing a #include <EncoderTool.h> – so, written only with the Arduino IDE in mind which when there’s one or multiple .ino files which are all combined into one before compilation.

I will do a quick fix of that library…

Try using

lib_deps =
   https://github.com/maxgerhardt/EncoderTool/archive/refs/heads/master.zip

instead of the old EncoderTool library. I have fixed at least the Encoder class to be usable by more than one cpp file. (Not any of the other classes… this same pattern is repeated over and over again in the library).

It works now! Thanks

This is tracked in https://github.com/luni64/EncoderTool/issues/22 now.

@maxgerhardt: Thanks for opening the GitHub issue. Should be fixed in v3.0.3.

1 Like