

Arduino IDE and PlatformIO – Part 1
How to structure a project to be compilable by both

Table of Contents
Introduction
The goal of this post is to provide hints on how to structure an Arduino project to be able to compile successfully on both Arduino IDE and PlatformIO (through VSCode). Each compiler / IDE brings its own limitations causing little headaches to developers that want to apply simple software development practices on an Arduino project. That is, code separation in folders, third-party library management, common use of source code by multiple example projects etc.
The project and the initial structure
The project on which we are going to work on is a stripped down example of a device that reads a value from a sensor, gets internet connectivity and uploads the measurement. For better versatility, the project will support WiFi and NBIoT connectivity. This leads us to create two example sketches, one for each connectivity type. The initial set of header and sketch files are:

1 2 3 4 5 6 | senseAndUpload/ ├── nbiot.h ├── wifi.h ├── sensor.h ├── exampleWifi.ino └── exampleNBIoT.ino |
- nbiot.h: Supports function “NBIoT::connect”, “NBIoT::upload”
- wifi.h: Supports function “WiFi::connect”, “WiFi::upload”
- sensor.h: Supports function “Sensor::readSensorLevel”
- exampleWifi.ino: example sketch
1 2 3 4 5 6 7 8 9 | #include "sensor.h" #include "wifi.h" void setup() {} void loop() { int val = Sensor::readSensorLevel(); WiFi::connect(); WiFi::upload; } |
- exampleNBIoT.ino: example sketch
1 2 3 4 5 6 7 8 9 | #include "sensor.h" #include "nbiot.h" void setup() {} void loop() { int val = Sensor::readSensorLevel(); NBIoT::connect(); NBIoT::upload; } |
Setting up folder structure based on Arduino Documentation
Each folder must contain at most 1 .ino file
1 2 3 4 5 6 7 8 | senseAndUpload/ ├── nbiot.h ├── wifi.h ├── sensor.h ├── exampleWifi/ │ └── exampleWifi.ino └── exampleNBIoT/ └── exampleNBIoT.ino |
Arduino IDE does not handle relative paths to previous folders
1 2 3 4 5 6 7 8 9 10 | senseAndUpload/ ├── examples/ | ├── exampleWifi/ | │ └── exampleWifi.ino | └── exampleNBIoT/ | └── exampleNBIoT.ino └── src/ ├── nbiot.h ├── wifi.h └── sensor.h |
PlatformIO recognizes library headers only in subfolders
- senseAndUpload/examples/exampleWifi/platformio.ini
1 2 3 4 5 6 7 8 9 10 11 | [env] lib_extra_dirs = ../../src [platformio] src_dir = . [env:mkrwifi1010] platform = atmelsam board = mkrwifi1010 framework = arduino |
- senseAndUpload/examples/exampleNBIoT/platformio.ini
1 2 3 4 5 6 7 8 9 10 11 | [env] lib_extra_dirs = ../../src [platformio] src_dir = . [env:mkrnb1500] platform = atmelsam board = mkrnb1500 framework = arduino |
1 2 3 4 5 6 7 8 9 10 11 12 | senseAndUpload/ ├── examples/ | ├── exampleWifi/ | │ └── exampleWifi.ino | └── exampleNBIoT/ | └── exampleNBIoT.ino └── src/ ├── device/ | └── sensor.h └── networking/ ├── nbiot.h └── wifi.h |
Arduino IDE recognizes library headers only at the base folder of each library
Since Arduino IDE detects only header files at the “src” folder, lets create one helper header file that will be including all the headers needed from the subfolders.
- senseAndUpload/src/customLibs.h
1 2 3 | #include <device/sensor.h> #include <networking/nbiot.h> #include <networking/wifi.h> |
- exampleWifi.ino:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //#define ENABLE_ARDUINO_IDE_SUPPORT #ifdef ENABLE_ARDUINO_IDE_SUPPORT #include #else #include #include #endif void setup() {} void loop() { int val = Sensor::readSensorLevel(); WiFi::connect(); WiFi::upload; } |
Summary
The correct design of a maintainable and expandable project structure in the Arduino world is an ongoing trial & error based on the capabilities and limitations of the underlying tools. This task gets even trickier when combining the limitations of more than one tools, in our case Arduino IDE and PlatformIO.
Until now we have seen that:
- Generic
- Each folder must contain at most 1 .ino file
- Arduino IDE only
- inherently identifies “src” and “examples” folders.
- does not handle relative paths to previous folders
- recognizes library headers only at the base folder of each library
- PlatformIO only
- recognizes library headers only in subfolders
In the next post, we explain how cpp files affect the source code and/or the folder structure and will conclude with a fully working template code that will contain all the recommendations of this post series.