Multiple, unique, programs in a Project

This is NOTt about multiple .cpp and .h files under a single src directory - all needed to compile one program.

I’m very new to PlatformIO (less than a week), trying to migrate from Arduino IDE. One thing I found easy in Arduino but can’t seem to do in Platform IO is to have several programs under a single project directory. The idea is that they could share libraries I have added to the project directory and other common things like platformio,ini. But, they could be separately compiled and uploaded…

As a test, I created a project and created programA.cpp, which I compiled and ujploaded to my device. I then added a folder under the project and added a src folder under that. I then created programB.cpp, under that new src folder and tried to compile and upload that new program. Problem is it compiled and uploaded programA.cpp rather than programB.cpp.

Is there a way I can accomplish what I am trying to do. I sure would find it inconvenient to start a new project for each of these related programs. Thank you

1 Like

I don’t quite get this, can you show an example? Is “several programs under a single project directory” equal to “multiple .INO sketches located in the same folder”?

Thanks for your response,

No. Each .ino file would be in its separate folder, but all under the same overarching folder.

These could be different versions of the same sketch, or related sketchs, often stuff I just want to try out but related to the same project. I know Arduino IDE does not have a project concept, per se, For me the folder just under the Arduino folder is like the project designation. All my .ino files that are related to each other are in folders under that folder.

It then is easy, and fast, to go from one of these sketches to another.

I really like the idea that in PlatformIO I can put project related libraries in the project folder. Easy to disseminate your project to others. Would be nice if all of my related PlatformIO programs could share libraries without having to have a separate copy go along with each of program, or by sticking the libraries in the global library space.

1 Like
  • In VSCode you can have multiple workspaces open at once. See the documentation. That of course requires you to duplicate your project once you work on a new variant of the project. You can copy-paste the folder and then execute the “Rebuild Intellisense” so you don’t have to go through the project creation wizard. Also, libraries can more easily be declared via the lib_deps declaration in the platformio.ini, so to keep project’s in sync, you just need to keep the platformio.ini in sync.
  • In the same environment, you can create multiple environments.See the documentation. By additionally using the src_filter, you can control which files are compiled in which environment by excluding specific files. This would allow you to have one “real” project with one shared platformio.ini and lib folder , with the src folder containing multiple cpp/h files which you manually select in each environment
  • there is the src_folder option, by which you can chooce the folder names where the source files are located. You can write a simple python script which, based on the used environment or some other variable, rewrites the src_folder or PLATFORMIO_SRC_DIR variable before build. This is just a fancier version of the thing above but now the files don’t have to be in the same src/ folder.

Ok, so we’re talking about something like the Arduino sketchbook folder, where you would have subfolders for each program, and then the .ino file(s) in each of the subfolders, but with one common folder for libraries?

Maybe the platformio.ini lib-extra-dirs parameter?

Thank you very much. I’m anxious to try what you suggest.

1 Like

Yes, that is exactly what I mean. To me a 'project" would include all versions, and all related programs. But, that is just my idea and comes from the way I work. I’ll definitely look into parameter you suggest. Thanks for your help.

1 Like

@piGuy, how did you get on? I’m in a similar position to yourself. My project uses two Arduino Unos, so I’m not sure how I can separate the environments.

I had this all working fine yesterday, but today on start up, PlatformIO extension would not activate because the platformio.ini was not in the root folder.

Open a new window (File -> New Window) , and add the Sensor Hub and Gateway folders separately the workspace (File -> Add Folder to Workspace for each). This will now be a multi-root workspace, and as far as PlatformIO is concerned, two separate project folders. Save the workspace (File -> Save Workspace As) if you want to be able to save the configuration to a file so you can re-open it at a later date. Workspace can also store other configuration into such as installed plugins, editor settings, etc.


Thanks, I tried that before you replied and got it working again. Once you know, you know!


platformio.ini can manage multiple programs with environments, notice [env:main] specifies build_src_filter = +<main.cpp> also notice that .pio/build has a build folder for main and test_thru

Do you have more ideas to solve this?

I have the exact same problem as the OP.

I’m working in some project, and I have a bunch of folders, each of which have some test code that I want to compile and run separately. I was working with Arduino, and that was easy with .ino files. I just want to be able to easily build and run each of those test codes separately when I want to.

The second solution of maxgerhardt works, but is cumbersome. I have to change the build_src_filter value when I change the test code I want to try, and that is very awful. In Arduino I just opened the corresponding .ino file in the test folder and that’s it.

This second solution also mentioned a python script. This may work, but I don’t know how to aproach writing this script so that I can change the src_folder “dinamically” based on which test I want to build and run. How can this script know which test I want to run? And I would want this to be as easy as with Arduino. I could just change build_src_filter value manually each time but that is very awful and timeconsuming.

These test folders are just a bunch of code that is closely related to my main project and I want these test codes to share platformio.ini file, libraries and to be together so that I can track my project development using git

How about this:

  • Create a global environment ([env]) which applies to all other environments
  • Create an empty project environment ([env:project]) which just inherits all settings from the global environment
  • For each test create a test environment ([env:test1], [env:test2] and so on)
  • The test-environments inherits all settings from the global environment ([env])
    The only setting in the test environment is build_src_filter which points to the folder which contains the test code.

Then use the project environment switcher at the bottom bar to switch between the project and the defined test projects:





default_envs = project

platform = espressif32
board = esp32dev
framework = arduino
lib_deps = 


build_src_filter = +<../mytests/test1/*>

build_src_filter = +<../mytests/test2/*>

The [platformio] environment is optional.
If you delete this, all projects (main projects and all tests) will be build one after each other if the Default environment is selected.

lib_deps = bblanchon/ArduinoJson is just an example to proove that this also applies to all test projects.

File and folder structure:

├── include
├── lib
├── mytests
│   ├── test1
│   │   └── test1.cpp
│   └── test2
│       └── test2.cpp
├── src
│   └── main.cpp
├── test
└── platformio.ini
1 Like

Thank you sivar2311!

This seems like a great solution. I will test this tomorrow. I’m a platformio newbie so that the idea of environments is not very clear for me yet.

I also arrived at another solution using a python script. For this solution I need to have all my test folders in the src folder. Then I need to type the test folder name before compilation.

This is the script


def prRed(skk): print("\033[91m{}\033[00m".format(skk))
def prGreen(skk): print("\033[92m{}\033[00m".format(skk))

folder_name = ""
test_input  = ""
is_valid    = False
testing     = False

# Do not run a script when external applications, such as IDEs,
# dump integration data. Otherwise, input() will block the process
# waiting for the user input
if env.IsIntegrationDump():
    # stop the current script execution

while True:
    print("Testing? (y/n):")
    test_input = input()

    if (test_input != 'y') and (test_input != 'n'):
        is_valid = False
        is_valid = True

    if is_valid:
    prRed("Not a valid option...")

if test_input == 'y':
    testing = True
    testing = False

if testing:
    print("Test folder name:")
    folder_name = input()

    env['PROJECT_SRC_DIR'] = env['PROJECT_DIR'] + "\\src\\" + folder_name

prGreen("Setting the source directory to: {}".format(env['PROJECT_SRC_DIR']))
print("-" * 100)

The drawback here is that I cannot organize my test folders the way I want to, but this is much better than changing build_src_filter again and again.

Take a look at the documentation platformio.ini / sections.
This explains environments very well.

If you want to write real tests, “Unit Testing” is the right choice.
There are good tutorials on this: Unit Testing with PlatformIO: Part 1. The Basics

But this will be a complete different topic :wink:

It should be possible to change the script so that “src” is not used as the root directory but, for example, “mytest”.

Unfortunately, I am not very good at reading and writing Python scripts.
Typing in the directory name manually would be too annoying for me.

This is much easier to handle with the Project Environment Switcher (click and run…).

In my solution, you must of course create the corresponding entries in platformio.ini. But these are done quickly.

1 Like

Hello. Good evening. I tried your solution and it worked fine for me. The setup is pretty fast as you said and only done once :grinning:

I think this is superior to the python script approach if I wanted to customize more options for each environment, I still don’t fully grasp to which extent this multi-environment approach is powerful.

I still have some issues that I’m gonna state in other post. But these issues are common to both approaches.

I also enhanced my script so that it uses a menu in terminal. This is the updated script


import os

# For aesthetic purposes
def newline(): print()

# Colored text functions
def prRed(skk):         print("\033[91m {}\033[00m" .format(skk))
def prGreen(skk):       print("\033[92m {}\033[00m" .format(skk))
def prYellow(skk):      print("\033[93m {}\033[00m" .format(skk))
def prLightPurple(skk): print("\033[94m {}\033[00m" .format(skk))
def prPurple(skk):      print("\033[95m {}\033[00m" .format(skk))
def prCyan(skk):        print("\033[96m {}\033[00m" .format(skk))
def prLightGray(skk):   print("\033[97m {}\033[00m" .format(skk))
def prBlack(skk):       print("\033[98m {}\033[00m" .format(skk))

# Test folders directory
src = "lab"

# All folders in src
src_folders = [
    f for f in os.listdir(src) if os.path.isdir(os.path.join(src, f))

yesno = ["Yes", "No"]

testing         = False
test_folder     = ""

# Do not run a script when external applications, such as IDEs,
# dump integration data. Otherwise, input() will block the process
# waiting for the user input
if env.IsIntegrationDump():
    # stop the current script execution

for i, option in enumerate(yesno):
    prYellow("\t{}: {}".format(i, option))

# Requesting input
if int(input()) == 0:
    testing = True
    testing = False


if testing:
    prCyan("Test folder name...")
    for i, option in enumerate(src_folders):
        prYellow("\t{}: {}".format(i, option))
    folder_id = int(input())

    # Change default src dir
    env['PROJECT_SRC_DIR'] = '{}\\{}\\{}'.format(env['PROJECT_DIR'], src, src_folders[folder_id])

prGreen("Setting the source directory to: {}".format(env['PROJECT_SRC_DIR']))
print("-" * 100)

All test folders need to be in a “lab” folder in the main directory, but this can be customized by modifying the script.

I think this is good because I only need to put my test folders in ‘lab/’ and I don’t need to configure nothing manually, but manual configuration of environments is fast anyway.

I don’t know to which extent this approach can be considered good practice, though.

I’m going to stick to your solution for now on because I think I may need the extra flexibility it has in terms of configuring each environment independently easily, but here is the python script if someone find it useful.

Thank you for your help!