How the IDE organizes things
First thing, this is how the IDE organizes your "sketch":
- The main
.ino
file is the one of the same name as the folder it is in. So, for foobar.ino
in foobar
folder - the main file is foobar.ino.
- Any other
.ino
files in that folder are concatenated together, in alphabetic order, at the end of the main file (regardless of where the main file is, alphabetically).
- This concatenated file becomes a
.cpp
file (eg. foobar.cpp
) - it is placed in a temporary compilation folder.
- The preprocessor "helpfully" generates function prototypes for functions it finds in that file.
- The main file is scanned for
#include <libraryname>
directives. This triggers the IDE to also copy all relevant files from each (mentioned) library into the temporary folder, and generate instructions to compile them.
- Any
.c
, .cpp
or .asm
files in the sketch folder are added to the build process as separate compilation units (that is, they are compiled in the usual way as separate files)
- Any
.h
files are also copied into the temporary compilation folder, so they can be referred to by your .c or .cpp files.
- The compiler adds into the build process standard files (like
main.cpp
)
- The build process then compiles all the above files into object files.
- If the compilation phase succeeds they are linked together along with the AVR standard libraries (eg. giving you
strcpy
etc.)
A side-effect of all this is that you can consider the main sketch (the .ino files) to be C++ to all intents and purposes. The function prototype generation however can lead to obscure error messages if you are not careful.
Avoiding the pre-processor quirks
The simplest way of avoiding these idiosyncrasies is to leave your main sketch blank (and not use any other .ino
files). Then make another tab (a .cpp
file) and put your stuff into it like this:
#include <Arduino.h>
// put your sketch here ...
void setup ()
{
} // end of setup
void loop ()
{
} // end of loop
Note that you need to include Arduino.h
. The IDE does that automatically for the main sketch, but for other compilation units, you have to do it. Otherwise it won't know about things like String, the hardware registers, etc.
Avoiding the setup/main paradigm
You don't have to run with the setup/loop concept. For example, your .cpp file can be:
#include <Arduino.h>
int main ()
{
init (); // initialize timers
Serial.begin (115200);
Serial.println ("Hello, world");
Serial.flush (); // let serial printing finish
} // end of main
Force library inclusion
If you run with the "empty sketch" concept you still need to include libaries used elsewhere in the project, for example in your main .ino
file:
#include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>
This is because the IDE only scans the main file for library usage. Effectively you can consider the main file as a "project" file which nominates which external libraries are in use.
Naming issues
Don't name your main sketch "main.cpp" - the IDE includes its own main.cpp so you will have a duplicate if you do that.
Don't name your .cpp file with the same name as your main .ino file. Since the .ino file effectively becomes a .cpp file this also would give you a name clash.
Declaring a C++-style class in the same, single .ino file (have heard of, but never seen working - is that even possible?);
Yes, this compiles OK:
class foo {
public:
};
foo bar;
void setup () { }
void loop () { }
However you are probably best off to follow normal practice: Put your declarations in .h
files and your definitions (implementations) in .cpp
(or .c
) files.
A bit more detail here.