split ino code to many files

My project (.ino file) started to become really big so i decided to split it in many files..
I followed this guide but my code started giving compiler errors. Although i fixed them i am not sure i know how this really works.
My code goes like this:

ino ("main") file:

#include "mySPIFFS.h"
void setup(){}

void loop(){}

mySPIFFS.h file:

void listDir(fs::FS &fs, const char * dirname, uint8_t levels);

mySPIFFS.cpp file:

#include "Arduino.h"
#include "FS.h"
#include "mySPIFFS.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
//do stuff
}

The compiler gives errors like "fs" has not been declared..

If i change mySPIFFS.h to:

#include "FS.h"
void listDir(fs::FS &fs, const char * dirname, uint8_t levels);

It compiles fine.

I supposed that included FS.h from mySPIFFS.cpp will "work" for mySPIFFS.h and dont want to be again.

So, do i have to include all these libraries in both cpp and h files?

  1. Let's say i have 10 myFile.cpp, myFile.h files, and i include libraries like FS.h, Wire.h etc..
    where is better to place them? in my main .ino file or in every cpp and h file?

  2. I have to place #include "Arduino.h" in every cpp file. Will be included for every cpp multiple times or one time? (so, compiler knows that its already included), or i have to use #ifndef.. etc?

uzer123:
I supposed that included FS.h from mySPIFFS.cpp will “work” for mySPIFFS.h

It won’t. The .cpp file is a separate translation unit.

uzer123:
So, do i have to include all these libraries in both cpp and h files?

No. This line:

#include "mySPIFFS.h"

essentially just copy/pastes the contents of mySPIFFS.h into mySPIFFS.cpp. So if you move this line:

#include "FS.h"

to mySPIFFS.h then the declarations in FS.h will be seen by both mySPIFFS.h and mySPIFFS.cpp.

uzer123:
2) Let’s say i have 10 myFile.cpp, myFile.h files, and i include libraries like FS.h, Wire.h etc…
where is better to place them? in my main .ino file or in every cpp and h file?

Put them in each translation unit that needs the declarations from those header files. Note that the .ino file is also a separate translation unit (at the start of the compilation process it undergoes some minimal preprocessing to make it valid C++ and is then renamed with the .cpp file extension and compiled as C++). If you have multiple .ino files, they are concatenated together during compilation and compiled as a single translation unit.

uzer123:
3) I have to place #include “Arduino.h” in every cpp file.

You only need to do that if the file uses the Arduino core library’s API (e.g., digitalWrite(), digitalRead(), byte).

uzer123:
Will be included for every cpp multiple times or one time? (so, compiler knows that its already included), or i have to use #ifndef… etc?

No. The file has an include guard that prevents multiple inclusions in a single translation unit:

#ifndef Arduino_h
#define Arduino_h
// declarations
#endif

What's this mean? Never seen anything like it.

fs::FS &fs

-jim lee

jimLee:
What's this mean? Never seen anything like it.

fs::FS &fs

-jim lee

It is quite cryptic. You found that in this function prototype.

void listDir( fs::FS &fs, const char * dirname, uint8_t levels ) ;

The first reference to fs ( fs:: ) is a namespace in which the class FS is defined. '::' is a scope qualifier.
&fs indicates that the argument required is a pointer. Confusingly, they have also used fs as the name of the argument but that name is not used anywhere.

So, if you call listDir(), the first argument has to be a pointer to an object of type fs::FS .

FS is defined here: Arduino/FS.h at master · esp8266/Arduino · GitHub

I think:

6v6gt:
It is quite cryptic. You found that in this function prototype.

void listDir( fs::FS &fs, const char * dirname, uint8_t levels ) ;

The first reference to fs ( fs:: ) is a namespace in which the class FS is defined. '::' is a scope qualifier.
&fs indicates that the argument required is a pointer reference. Confusingly, they have also used fs as the name of the argument but that name is not used anywhere.

So, if you call listDir(), the first argument has to be a pointer to an object of type fs::FS .

FS is defined here: Arduino/FS.h at master · esp8266/Arduino · GitHub

uzer123:
I supposed that included FS.h from mySPIFFS.cpp will "work" for mySPIFFS.h and dont want to be again.

Yes, the '#include "FS.h"' in mySPIFFS.cpp worked fine for the mySPIFFS.h that is included after it but it does nothing for the mySPIFFS.h included in main.ino. Putting the '#include "FS.h"' at the top of mySPIFFS.h works for both.

I've spent a day reading about good practices etc about splitting c++ code.
And i found my final adjustments to go along with your comments.
So, for future users, i would like to point the basic principles i followed:

  1. to make the cpp and ino relatively independent #include necessary libraries to each cpp file and not put everything in the ino file.
    In my example i put #include "FS.h" in both mySPIFFS.cpp and mySPIFFS.h and #include "mySPIFFS.h" in the "main" ino file.
    So, if you make another ino project, you just include the mySPIFFS.h and your ready to go.

  2. include only the absolutely necessary libraries in each .h file. So i use #include "FS.h" in mySPIFFS.h, but
    #include "FS.h", #include "SPIFFS.h" in mySPIFFS.cpp

  3. i put global variables in the cpp file that was more relevant

  4. put #include "Arduino.h" in h files (maybe your dont need it in every cpp but keep that in mind)

  5. use

#ifndef MYSPIFFS_H
#define MYSPIFFS_H

//your code

#endif

That way you dont need to worry about this:

main ino file:

#include "mySPIFFS.h"
#include "anotherLibrary.h"

and your anotherLibrary.h" is:

#include "mySPIFFS.h"
....

So now you try to #include "mySPIFFS.h" twice, which is a problem..#ifndef include it only once.

Thats why libraries like #include "Arduino.h" doesnt have this problem..
it contains #ifndef ARDUINO_H

A simpler (non-standard but widely supported) way to do #5 is to put "#pragma once" at the top of your include file.

I didn't know the Arduino IDE supported #pragma once. The old MetroWerks compilers did. I loved that. Solved a LOT of bizarre issues. There was another #something that would put the defined text on a menu for navigating your source file. #somethingIForgetWhat - would give a menu break. #somethingIForgetWhat someText Would give a menu choice text.

Then, when editing, you could click your popup menu and go the the different sections of your code. I miss that.

-jim lee

It's not a matter of the Arduino IDE supporting it. It's a matter of the compiler supporting it. Most platforms use GCC, which does support #pragma once, but it's not guaranteed that every platform's compiler will support it.

I really don't see the benefit of #pragma once. The working of standard include guards are obvious at a glance and only take two extra lines of code. The include guard macro can be useful for other purposes too.

Well, at least we have the standard guards. I do really miss the navigation menu though.

-jim lee