I'm starting a project in ESP8266 NodeMcu and it's going to have some lines including WifiManager,TelegramBot,NTPClock, GPIOs, SPIFF and validation, so, I was thinking about the following structure to avoid syntax, compiling and malfunction problems:
Create a src folder into my sketch folder, put all my libraries (.h and .cpp) there and call them using #include "src/example1.h"; #include "src/example2.h".
The libraries will have the code to each functionality, including their own global variable to be easily passed between them: setBot.h will create a global UniversalTelegramBot variable that can be accessed by the setValidation.h or other library.
I read how to create library using class but I don't think I will use it because I just need the functions created by the "includes", it's just to avoid put all the stuff in the main.ino.
The libraries are going to be like:
#include "sum.h"
void sum(int a, int b){
int result = a+b;
}
main.ino:
#include "src/sum.h"
sum(1,2);
print(result);
Will it work ? Can I use extern, .h, .cpp like that ? Is there anything to add to prevent problems while compiling or running for a while in the board ?
Yes, it will work... But you need to tweak it slightly by declaring the result inside the .cpp file aswell.
So marking it as extern just says 'Hey, by the way.... go looking elsewhere to find this'. Currently, you have int result = a+b; which defines result as a local variable inside the sum function.
So if you change your sum.cpp file to:
#include "sum.h"
int result;
void sum(int a, int b) {
result = a + b;
}
it should work. Do you definitely want to just set the result inside the sum function? Usually those types of functions would look more like:
int sum(int a, int b) {
return a + b;
}
So the full code would be:
main.ino
#include "sum.h"
int result;
void setup() {
Serial.begin(9600);
result = sum(1, 2);
Serial.print(result);
}
void loop() {
// put your main code here, to run repeatedly:
}
sum.h
#ifndef sum_h
#define sum_h
int sum(int a, int b);
#endif
sum.cpp
#include "sum.h"
int sum(int a, int b) {
return a + b;
}
This defines a local variable so the linking will fail. The global variable "int result;" has to be defined somewhere. It can be in main.ino or sum.cpp. DON'T define it in sum.h because then it will be defined in both main.ino and sum.cpp and you will get a compile error.
The closest I can come to your sketch that will work:
sum.h:
#ifndef sum_h
#define sum_h
extern int result;
void sum(int a, int b);
#endif
sum.cpp:
#include "sum.h"
int result;
void sum(int a, int b)
{
result = a + b;
}
"DON'T define it in sum.h because then it will be defined in both main.ino and sum.cpp and you will get a compile error." So, in .h files should I never define or implement functions ? Is it just to prototype functions or create classes and struct ?
What if I create global variables in each tab ? It was like I had defined them in the middle of the file ?
So, the IDE compiles everything as I had copied and paste in one single file ? Then It looks for the tab with setup and loop to follow.
Is there some tab order that the compiler follow ? I mean if I use in tab2 the function created in tab5 ?
All .ino and .pde files in the sketch folder (shown in the Arduino IDE as tabs with no extension) are concatenated together, starting with the file that matches the folder name followed by the others in alphabetical order. The .cpp filename extension is then added to the resulting file.
if I have any reason to create globals in a separte tab, I add a tab with the extension .h , put the globals there and <include "mytab.h" at the top of the main sketch.
Furthermore I often name the tabs with leading number, so I have a little easier control about the order what tab is concatinated when.
There are times when it is necessary or appropriate but as a rule, you should not define globals in a header file. In general, declare 'extern' variables and prototype global functions in the header file and define them in the associated .cpp file.
Only for .ino files. If you define the same global variable in two .ino files the compiler should complain. For .cpp files or a mix of .cpp and .ino files it is the linker that will complain when it finds the same global name declared in two separate object (.o) files.
Only the .ino files. The .cpp files are compiled separately and a double-definition error isn't discovered until the linker finds two definitions.
For .ino files, the one that matches the directory name is always first, followed by the rest alphabetically. For functions, the automatic generation of function prototypes will probably save you from having functions declared after they are used. For global variables, I think you have to declare then in or before the first .ino where they are used.
Don't worry. The compiler and linker will generate error messages if a variable or function is not declared or is defined twice.
I cannot use a computer right now to test the following:
tab1.ino
int global1 = 1;
print (global2);
tab2.ino
int global2 = 2;
global3 = 3;
print (global3)
tab3.ino
int global3;
I am declaring and defining the globals in separate tabs and trying to use them among the tabs, and in global3 I have just declared it and defined it in other tab.
Can I do that ? Will the compiler find and load all the global variable and its values firstly ?
As variables the same is applied to global objects created in specific tabs ?
I know it's better to put all of them together in some .h, as mentioned above, or in the first tab to be compiled (the folder name tab), but I would try that, the reason is to keep definitely all the code and its global variables to the same tab.
If "tab1.ino" is the main sketch, it can't "print(global2)" because global2 isn't declared yet. You have to add an "extern int global2;" before "print(global2)".
Similarly tab2.ino can't assign a value to global3 or print(global3) because it has not been declared yet. You have to add an "extern int global3;" if you want to use the variable before it is declared.
This corrected sketch displays "23":
// tab1.ino
int global1 = 1;
extern int global2;
void pt1()
{
Serial.print(global2);
}
void setup()
{
Serial.begin(115200);
delay(200);
pt1();
pt2();
}
void loop() {}
// tab2.ino
int global2 = 2;
extern int global3;
void pt2()
{
global3 = 3;
Serial.print(global3);
}
// tab3.ino
int global3;
That's interesting ! But would you do that or rely on it after some code lines ? What is the problem with that ? Maybe more processing time ?
So, the trick is the 'extern', without it the compiler doesn't know that the variable exist, after you add it the compiler knows that it exists in someplace.
Behind the curtains:
When it comes to "extern int global2" the compiles just knows it exists but it won't start looking for yet.
But in "Serial.print(global2)" it starts looking for to link, when it finds the definition, the memory is allocated only once, so, if I use the global2 again it won't keep looking for again.
It makes the code harder to understand. Global variables can be changed ANYWHERE in the code. There are far better ways to share data between parts of a program. Particularly function argments.
The compiler doesn't 'go looking' for definitions. If the variable is not declared before it is used, you get an error. If the variable is declared, that's all the compiler needs to know. It references the variable by name and the linker fills in the address later.