Hi guys! For this quarantine I'm trying to understand libraries and how to create them.
I'm using eclipse with arduino add-on.
First, I've created a simple led blinking library and all works fine.
Now I would like to implement a sort of chronometer library which uses NTPClient.
My code:
Cronometro.h
#ifndef Cronometro_h
#define Cronometro_h
#include <Arduino.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
WiFiUDP chronometer_wifi_udp;
NTPClient chronometer_ntp(chronometer_wifi_udp, "pool.ntp.org", 3600);
class Cronometro{
private:
unsigned long int start_time, pause_time, stop_time, total_time, temp_time;
byte status_pause;
public:
void init(void);
void start(void);
void pause(void);
void resume(void);
unsigned long int stop();
unsigned long int status();
};
#endif
Cronometro.cpp
#include "Cronometro.h"
void Cronometro::init(){
chronometer_ntp.begin();
chronometer_ntp.setTimeOffset(3600); //GMT+1h
}
unsigned long int Cronometro::status(){
if(status_pause)
return total_time;
else{
temp_time = total_time + chronometer_ntp.getEpochTime() - start_time;
return temp_time;
}
}
void Cronometro::start(){
total_time = 0;
status_pause = 0;
start_time = chronometer_ntp.getEpochTime();
}
void Cronometro::pause(){
total_time += chronometer_ntp.getEpochTime() - start_time;
status_pause = 1;
}
void Cronometro::resume(){
start_time = chronometer_ntp.getEpochTime();
status_pause = 0;
}
unsigned long int Cronometro::stop(){
if(status_pause)
return total_time;
else{
total_time += chronometer_ntp.getEpochTime() - start_time;
return total_time;}
}
When I write "include "Cronometro.h" into the main cpp file (where loop void is located) and try to compile, I get this error:
Building Test_Librerie
"C:/Users/Roberto/.arduinocdt/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-g++" -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-IC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/include" "-IC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/lwip2/include" "-IC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/libc/xtensa-lx106-elf/include" "-I./core" -c -w -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections -DF_CPU=80000000L -DLWIP_OPEN_SRC -DTCP_MSS=536 -DARDUINO=10608 -DARDUINO_ESP8266_NODEMCU -DARDUINO_ARCH_ESP8266 -DARDUINO_BOARD="ESP8266_NODEMCU" -DESP8266 -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/cores/esp8266" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/variants/nodemcu" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/libraries/ESP8266WiFi/src" -I"C:/Users/Roberto/.arduinocdt/libraries/NTPClient/3.2.0" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/libraries/Ticker" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/libraries/Wire" "../../Cronometro.cpp" -o "project/Cronometro.cpp.o"
"C:/Users/Roberto/.arduinocdt/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-g++" -D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-IC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/include" "-IC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/lwip2/include" "-IC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/libc/xtensa-lx106-elf/include" "-I./core" -c -w -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections -DF_CPU=80000000L -DLWIP_OPEN_SRC -DTCP_MSS=536 -DARDUINO=10608 -DARDUINO_ESP8266_NODEMCU -DARDUINO_ARCH_ESP8266 -DARDUINO_BOARD="ESP8266_NODEMCU" -DESP8266 -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/cores/esp8266" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/variants/nodemcu" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/libraries/ESP8266WiFi/src" -I"C:/Users/Roberto/.arduinocdt/libraries/NTPClient/3.2.0" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/libraries/Ticker" -I"C:/Users/Roberto/.arduinocdt/packages/esp8266/hardware/esp8266/2.4.1/libraries/Wire" "../../Test_Librerie.cpp" -o "project/Test_Librerie.cpp.o"
"C:/Users/Roberto/.arduinocdt/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin/xtensa-lx106-elf-gcc" -g -w -Os -nostdlib -Wl,--no-check-sections -u call_user_start -u _printf_float -u _scanf_float -Wl,-static "-LC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/lib" "-LC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/ld" "-LC:\Users\Roberto\.arduinocdt\packages\esp8266\hardware\esp8266\2.4.1/tools/sdk/libc/xtensa-lx106-elf/lib" "-Teagle.flash.4m1m.ld" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read -o "./Test_Librerie.elf" -Wl,--start-group project/Cronometro.cpp.o project/Test_Librerie.cpp.o libraries/ESP8266WiFi/src/ESP8266WiFi.cpp.o libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp.o libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp.o libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp.o libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp.o libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp.o libraries/ESP8266WiFi/src/WiFiClient.cpp.o libraries/ESP8266WiFi/src/WiFiClientSecure.cpp.o libraries/ESP8266WiFi/src/WiFiServer.cpp.o libraries/ESP8266WiFi/src/WiFiServerSecure.cpp.o libraries/ESP8266WiFi/src/WiFiUdp.cpp.o libraries/NTPClient/3.2.0/NTPClient.cpp.o libraries/Ticker/Ticker.cpp.o libraries/Wire/Wire.cpp.o "./arduino.ar" -lhal -lphy -lpp -lnet80211 -llwip2 -lwpa -lcrypto -lmain -lwps -laxtls -lespnow -lsmartconfig -lairkiss -lwpa2 -lstdc++ -lm -lc -lgcc -Wl,--end-group "-L."
project/Test_Librerie.cpp.o:(.bss.chronometer_wifi_udp+0x0): multiple definition of `chronometer_wifi_udp'
project/Cronometro.cpp.o:(.bss.chronometer_wifi_udp+0x0): first defined here
project/Test_Librerie.cpp.o:(.bss.chronometer_ntp+0x0): multiple definition of `chronometer_ntp'
project/Cronometro.cpp.o:(.bss.chronometer_ntp+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
make: *** [Test_Librerie.elf] Error 1
makefile:101: recipe for target 'Test_Librerie.elf' failed
Anybody could help me please? Where is my error?
1.) You can use #pragma once instead of #ifndef and #endif
2.) If you only need one instance of both WiFiUDP and NTPClient classes, you should make them static members of the Cronometro class instead of global instances (as you currently have them)
On second thought, it's probably better to specify a constructor for your Cronometro class where there are two input parameters: a reference to a WiFiUDP class and a reference to an NTPClient class. These references can then be saved in the Cronometro class for usage. It would make your code less prone to errors, much cleaner, and easier to understand
The problem is that you've instantiated objects in your .h file. Instantiations (aka Definitions) don't belong in header files for just the reason you've discovered: when the header file gets #include(d) in multiple source files, you end up with multiple definitions with the same name. This upsets the linker greatly.
In your case, Cronometro.h is #include(d) in both Cronometro.cpp and "the main cpp file (where loop void is located)"
BTW, first lesson in NOT being recognized as a Clueless Newbie -- loop() is not a "void", it's a FUNCTION. The 'void' keyword signifies that the function returns no value.
But, I digress. Generally speaking there are 3 methods (that I know of) for incorporating the functionality of one class into another. The one you choose depends on which of the following statements is the most accurate:
-
My "Cronometro" class HAS an NTPClient object.
-
My "Cronometro" class IS A TYPE OF NTPClient object.
-
My "Cronometro" class KNOWS an NTPClient object.
For #1 you declare that the Cronometro class contains a (most likely private) member that is of the type NTPClient.
For #2 you have the Cronometro class inherit from the NTPClient class.
For #3 you create the NTPClient object at a higher level and pass a pointer (or reference) to it into your Cronometro class.
Thanks for all your answers 
I made some changes and it seems to work properly:
Cronometro.h
#pragma once
#include <Arduino.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
//WiFiUDP chronometer_wifi_udp;
//NTPClient chronometer_ntp(chronometer_wifi_udp, "pool.ntp.org", 3600);
class Cronometro{
private:
unsigned long int start_time, pause_time, stop_time, total_time, temp_time;
byte status_pause;
WiFiUDP chronometer_wifi_udp;
NTPClient* chronometer_ntp;
public:
void init(void);
void start(void);
void pause(void);
void resume(void);
unsigned long int stop();
unsigned long int status();
};
Cronometro.cpp
#include "Cronometro.h"
void Cronometro::init(){
chronometer_ntp = new NTPClient(chronometer_wifi_udp, "pool.ntp.org", 3600);
chronometer_ntp->begin();
chronometer_ntp->setTimeOffset(3600); //GMT+1h
}
unsigned long int Cronometro::status(){
if(status_pause)
return total_time;
else{
temp_time = total_time + chronometer_ntp->getEpochTime() - start_time;
return temp_time;
}
}
void Cronometro::start(){
total_time = 0;
status_pause = 0;
start_time = chronometer_ntp->getEpochTime();
}
void Cronometro::pause(){
total_time += chronometer_ntp->getEpochTime() - start_time;
status_pause = 1;
}
void Cronometro::resume(){
start_time = chronometer_ntp->getEpochTime();
status_pause = 0;
}
unsigned long int Cronometro::stop(){
if(status_pause)
return total_time;
else{
total_time += chronometer_ntp->getEpochTime() - start_time;
return total_time;}
}
BTW I don't understand why if I write directly "NTPClient(chronometer_wifi_udp, "pool.ntp.org", 3600);" instead "NTPClient* chornometer_ntp;" in the header file, it doesn't compile.
neorob85:
BTW I don't understand why if I write directly "NTPClient(chronometer_wifi_udp, "pool.ntp.org", 3600);" instead "NTPClient* chornometer_ntp;" in the header file, it doesn't compile.
You have to use an Initializer List.
I think I got it, maybe!
Cronometro.h
#pragma once
#include <Arduino.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
class Cronometro{
private:
unsigned long int start_time, pause_time, stop_time, total_time, temp_time;
byte status_pause;
WiFiUDP chronometer_wifi_udp;
NTPClient chronometer_ntp;
public:
Cronometro();
void start(void);
void pause(void);
void resume(void);
unsigned long int stop();
unsigned long int status();
};
Cronometro.cpp
#include "Cronometro.h"
Cronometro::Cronometro() : chronometer_ntp(chronometer_wifi_udp, "pool.ntp.org", 3600)
{
start_time = 0;
pause_time = 0;
stop_time = 0;
total_time = 0;
temp_time = 0;
status_pause = 0;
chronometer_ntp.begin();
chronometer_ntp.setTimeOffset(3600); //GMT+1h
}
unsigned long int Cronometro::status(){
if(status_pause)
return total_time;
else{
temp_time = total_time + chronometer_ntp.getEpochTime() - start_time;
return temp_time;
}
}
void Cronometro::start(){
total_time = 0;
status_pause = 0;
start_time = chronometer_ntp.getEpochTime();
}
void Cronometro::pause(){
total_time += chronometer_ntp.getEpochTime() - start_time;
status_pause = 1;
}
void Cronometro::resume(){
start_time = chronometer_ntp.getEpochTime();
status_pause = 0;
}
unsigned long int Cronometro::stop(){
if(status_pause)
return total_time;
else{
total_time += chronometer_ntp.getEpochTime() - start_time;
return total_time;}
}
The code above seems to work properly 
Another question: when I create new objects of Cronometro's class in the main program, am I creating also new WiFiUDP and NTPClient objects?
If yes, how can I make them static?
neorob85:
Another question: when I create new objects of Cronometro's class in the main program, am I creating also new WiFiUDP and NTPClient objects?
Yes.
neorob85:
If yes, how can I make them static?
This might help. Personally, I think it would be easier to pass references of each of the two objects as parameters to your Cronometro class's constructor, or even its begin() method.
Power_Broker:
Personally, I think it would be easier to pass references of each of the two objects as parameters to your Cronometro class's constructor, or even its begin() method......
Answer me these questions three...... then you'll know ...... Replly #2.
Power_Broker:
Yes.
This might help. Personally, I think it would be easier to pass references of each of the two objects as parameters to your Cronometro class's constructor, or even its begin() method.
In this way I should declare (and configure) two objects (WiFiUDP and NTPClient) just for the class Cronometro in the main program, right?
I edited to make WiFiUDP and NTPClient objects static. The source code is compiled without errors, but it doesn't work... Nodemcu continues to reboot:
ets Jan 8 2013,rst cause:2, boot mode:(3,2)
load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v614f7c32
~ld
What's wrong?
Cronometro.h
#pragma once
#include <Arduino.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
class Cronometro{
private:
unsigned long int start_time, pause_time, stop_time, total_time, temp_time;
bool status_pause;
static bool first_time;
static WiFiUDP chronometer_wifi_udp;
static NTPClient chronometer_ntp;
public:
Cronometro();
void start(void);
void pause(void);
void resume(void);
unsigned long int stop();
unsigned long int status();
};
Cronometro.cpp
#include "Cronometro.h"
WiFiUDP Cronometro::chronometer_wifi_udp;
NTPClient Cronometro::chronometer_ntp(chronometer_wifi_udp);
bool Cronometro::first_time = true;
Cronometro::Cronometro()
{
start_time = 0;
pause_time = 0;
stop_time = 0;
total_time = 0;
temp_time = 0;
status_pause = false;
if(first_time){
chronometer_ntp.begin();
chronometer_ntp.setTimeOffset(3600); //GMT+1h
first_time = false;
}
}
unsigned long int Cronometro::status(){
if(status_pause)
return total_time;
else{
temp_time = total_time + chronometer_ntp.getEpochTime() - start_time;
return temp_time;
}
}
void Cronometro::start(){
total_time = 0;
status_pause = false;
start_time = chronometer_ntp.getEpochTime();
}
void Cronometro::pause(){
total_time += chronometer_ntp.getEpochTime() - start_time;
status_pause = true;
}
void Cronometro::resume(){
start_time = chronometer_ntp.getEpochTime();
status_pause = false;
}
unsigned long int Cronometro::stop(){
if(status_pause)
return total_time;
else{
total_time += chronometer_ntp.getEpochTime() - start_time;
return total_time;}
}