I have no idea if this will prove useful to those hoping for an example of converting an Arduino Sketch to PlatformIO, but maybe it will help Arduino converts to understand why things are not as simple as at first expected.
The Arduino system does a “shed load” (technical term) of stuff in the background. I have a book on the internals of the Arduino Software - which I’m not going to mention here - if you need further information.
The IDE will open all *.ino
, *.cpp
, *.h
and *.c
files that it finds in the sketch directory. These files will be opened on separate tabs in the editor. When you compile a sketch the following happens:
Preprocessing:
A temporary directory is created, on the sketch’s very first compilation, in the Operating System’s “temp” area. This is /tmp
on Linux (and MacOS?) or something like c:\users\YOUR_NAME\AppData\Local\Temp\arduino_build_SOME_NUMBER
on Windows. From here on in, I shall refer to this location as $TMP
.
If the sketch has a number of *.ino
files, all of them are concatenated into a single file with the extension .ino.cpp
in the $TMP/Sketch
subdirectory. The first file added to this working file, is the main *.ino
- the one with the same name as the sketch’s own directory.
The remainder of the *.ino
files are appended to the working file in alphabetical order.
If the sketch only has one *.ino
file, it is processed as above, by itself, into the working file.
At the top of the working file, #include <Arduino.h>
is added, if it was not already found in the original sketch.
Any libraries used by the sketch are detected and the include path for those are discovered. This is done by running a dummy compilation, discarding the output - to the nul
device on Windows or the /dev/null
on Linux - but processing any relevant error messages.
Function prototypes are generated for all the functions found in the working *.ino.cpp
file. Occasionally, this is not possible for all functions, so the programmer has to add one, manually, to the main sketch *.ino
file, to get it to compile.
The *.ino.cpp
working file is preprocessed to add various #line
and #file
directives so that any subsequent error messages will reference the correct source file and line numbers within, rather than referencing the full, working *.ino.cpp
file’s name and line numbers.
The Arduino Preprocessor carries out all these actions and it can be found on GitHub at GitHub - arduino/arduino-preprocessor: Parses an Arduino Sketch and converts it into valid C++ source code - if you are interested.
Build:
After preprocessing, the Arduino Build tool takes over. It too lives on GitHub at GitHub - arduino/arduino-builder: A command line tool for compiling Arduino sketches - again, if you are interested.
The build process starts by compiling the working *.ino.cpp
file in the temporary directory created earlier. This is compiled into an object file named *.ino.cpp.o
and stored/written to the $TMP/Sketch
subdirectory.
Any additional *.c
and *.cpp
files in the Sketch’s original directory are now compiled into separate object files in the $TMP/Sketch
subdirectory. This may not be necessary if the various source files have not been edited since the previous compilation. The existing *.o
files will be reused.
All the libraries used will be compiled into multiple *.o
files and written to the $TMP/Libraries
subdirectory.
Next up, all the Arduino “core” files are compiled as *.o
files and written to the $TMP/core
subdirectory. One of the files compiled here is the one which supplies the main()
function. You never need to write a main()
function in a sketch.
After compiling all the source files, library files and core files, a static library, core.a
, is built in the $TMP/core
subdirectory.
An ELF format file is then created by linking all the *.o
files in $TMP/Sketch
and $TMP/Libraries
with the core.a
static library created above. This file is written to the $TMP
directory and named as SKETCH_NAME.ino.elf
.
SKETCH_NAME.ino.elf
is then processed to create SKETCH_NAME.ino.eep
which holds any data defined as being required to be written to the AVR’s EEPROM. Sadly, the Arduino IDE cannot (yet) upload *.eep
files to the EEPROM, so this is a bit of a waste of time.
SKETCH_NAME.ino.elf
is then processed again to create SKETCH_NAME.ino.hex
which is, finally, the code that will be uploaded to the Arduino.
So, there you have it. A lot of work goes on in the background and is hidden from you by the Arduino system. Real C++ programs require you to declare function prototypes before use so that the compiler can set up the correct stack frames to call and return from same, and to return the correct data types from non void functions. The Arduino attempts to do all this for you so that you don’t have to worry about it.
Edited to correct abysmal spelling!
HTH
Cheers,
Norm.