I'm trying to make a WiFi enabled egg-cooker. I have a working Arduino sketch and am now trying to port it to ESPHome so that it can interface with Home Assistant. ESPHome uses a yaml file (eggcooker.yaml) to generate a main.cpp file, and I can add custom components to the yaml which link to C++ files that I can program myself, and which are then used by that main.cpp. My problem seems to occur in one of my C++ files. Here's a link to the code: https://github.com/Amanoo/Smart-Egg-Cooker/tree/feature-esphome/eggslice
In this folder, run these commands:
python3 -m venv venv
source venv/bin/activate
pip3 install esphome
pip3 install tornado esptool
esphome run eggcooker.yaml
This will compile the whole thing. However, it will eventually throw an error:
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:105: multiple definition of `m_asFont'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:105: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:106: multiple definition of `m_asPage'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:106: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:104: multiple definition of `m_drv'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:104: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:103: multiple definition of `m_gui'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:103: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:110: multiple definition of `m_asPage1ElemRef'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:110: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:109: multiple definition of `m_asPage1Elem'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:109: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:112: multiple definition of `m_asPage2ElemRef'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:112: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:111: multiple definition of `m_asPage2Elem'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:111: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:114: multiple definition of `m_asPage3ElemRef'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:114: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:113: multiple definition of `m_asPage3Elem'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:113: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:116: multiple definition of `m_asKeypadAlphaElemRef'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:116: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:115: multiple definition of `m_asKeypadAlphaElem'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:115: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:118: multiple definition of `m_sListbox2'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:118: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:120: multiple definition of `m_acListboxBuf2'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:120: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:121: multiple definition of `m_sListScroll2'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:121: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:117: multiple definition of `m_sKeyPadAlpha'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:117: first defined here
/home/marco/.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pioenvs/eggcooker/src/main.cpp.o: in function `InitGUIslice_gen()':
/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:169: multiple definition of `InitGUIslice_gen()'; .pioenvs/eggcooker/src/eggslice.cpp.o:/home/marco/Desktop/eggcookeresphome/.esphome/build/eggcooker/src/eggslice_GSLC.h:169: first defined here
collect2: error: ld returned 1 exit status
*** [.pioenvs/eggcooker/firmware.elf] Error 1
I'm not sure why there are "multiple definitions". I just have a file that I import. It didn't throw this error when the same file was in my Arduino sketch. I checked my include guards, and they're there. I checked m_asFont for occurences in my files, but it only exists in eggslice_GSLC.h where it's defined once and used once. Definitely not multiple definitions. As best I can tell, there are no multiple definitions, it shouldn't even be possible.
In your header file eggslice_GSLC.h you have quite a few variables that you define rather than declare.
gslc_tsGui m_gui;
gslc_tsDriver m_drv;
gslc_tsFont m_asFont[MAX_FONT];
gslc_tsPage m_asPage[MAX_PAGE];
//<GUI_Extra_Elements !Start!>
gslc_tsElem m_asPage1Elem[MAX_ELEM_PG_MAIN_RAM];
gslc_tsElemRef m_asPage1ElemRef[MAX_ELEM_PG_MAIN];
gslc_tsElem m_asPage2Elem[MAX_ELEM_PG_WIFI_RAM];
gslc_tsElemRef m_asPage2ElemRef[MAX_ELEM_PG_WIFI];
gslc_tsElem m_asPage3Elem[MAX_ELEM_PG_PASSWD_RAM];
gslc_tsElemRef m_asPage3ElemRef[MAX_ELEM_PG_PASSWD];
gslc_tsElem m_asKeypadAlphaElem[1];
gslc_tsElemRef m_asKeypadAlphaElemRef[1];
gslc_tsXKeyPad m_sKeyPadAlpha;
gslc_tsXListbox m_sListbox2;
// - Note that XLISTBOX_BUF_OH_R is extra required per item
char m_acListboxBuf2[94 + XLISTBOX_BUF_OH_R];
gslc_tsXSlider m_sListScroll2;
Not coincidentally, these are the variables that the linker is complaining are multiply defined.
All that has to happen to cause that is for two .cpp files to include this file. Header file guards can't protect against this; all they protect against is a header file being included multiple times in a single source file.
The fix is easy; copy all of these definitions into a single .cpp file and change all of these definitions in the .h file to declarations by prefixing them with extern
.
When you build esphome, the build script generates a main.cpp file which includes all the header files you listed in eggcooker.yaml. That includes eggslice_GSLC.h so now it's included in multiple source files leading to the variables being multiply defined.
// Auto generated code by esphome
// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ===========
#include "esphome.h"
using namespace esphome;
using std::isnan;
using std::min;
using std::max;
using namespace text_sensor;
logger::Logger *logger_logger;
web_server_base::WebServerBase *web_server_base_webserverbase;
captive_portal::CaptivePortal *captive_portal_captiveportal;
wifi::WiFiComponent *wifi_wificomponent;
mdns::MDNSComponent *mdns_mdnscomponent;
ota::OTAComponent *ota_otacomponent;
api::APIServer *api_apiserver;
using namespace api;
web_server::WebServer *web_server_webserver;
using namespace sensor;
using namespace json;
preferences::IntervalSyncer *preferences_intervalsyncer;
template_::TemplateSensor *secs;
template_::TemplateTextSensor *state;
#define yield() esphome::yield()
#define millis() esphome::millis()
#define micros() esphome::micros()
#define delay(x) esphome::delay(x)
#define delayMicroseconds(x) esphome::delayMicroseconds(x)
#include "eggslice.h"
#include "eggslice_GSLC.h"
#include "FreeSans14pt7b.h"
#include "NotoMono24pt7b.h"
#include "NotoSansBold14pt7b.h"
#include "dosis_book12pt7b.h"
#include "dosis_book16pt7b.h"
#include "pijlGlyph.h"
// ========== AUTO GENERATED INCLUDE BLOCK END ==========="
You should only ever declare variables in a .h file, using extern
. When you define them in a header file you open yourself up to problems like this.