Here is a simple way to have many small "main" apps

Here is a very simple way to reuse code placed in lib/ from different single-file main apps. My file tree looks as follows:

$ tree
.
├── lib
│   └── ...
├── platformio.ini
└── src
    ├── board.h
    ├── main-blink.cpp
    ├── main-getiq.cpp
    ├── main-lcd.cpp
    ├── main-psram.cpp
    ├── main-qspi.cpp
    ├── main-wifi.cpp
    └── memtest.h

And the platformio.ini has something like this:

[env]
platform = ststm32
framework = cmsis
board = disco_f723ie
src_filter = +<*.h> +<main-${PIOENV}.cpp>
...

[env:blink]
[env:getiq]
[env:lcd]
[env:psram]
[env:qspi]
[env:wifi]

Note that the [env:xyz] sections can be left empty.

It’s by far the simplest way I’ve found so far to keep a collection of little test & demo apps in a single place, all compiled in the same way and using the same library code. A quick check that all the apps compile is a matter of typing pio run.

To build and upload say the blink app, type: pio run -t upload -e blink - easy!

(and if you define alias prue='pio run -t upload -e', then this becomes prue blink)

Nicely done!

Cheers,
Norm.

Hi there!

Where are you typing the commands at in VSCode? For me, typing pio into the terminal, set to a PlatformIO CLI, I get the standard list of commands help menu printed. Typing in pio env results in a Error: No such command "env"

Looking for a way to mimic Arduino library folder, allowing me to work on code in ‘examples’ folder while still being able to work on the library itself.

Thanks,
Randy

pio env -t upload -e blink - easy!

I suspect this should be pio run -t upload -e blink

The “env” is supplied by the -e option.

HTH

Cheers,
Norm.

Awww … good catch, fixed, thx.

Thanks, that’s great! Just to check, should the source filter include lib, i.e. +<*.h> +<lib/*> +<main-${PIOENV}.cpp>

No, src_filter is for files in src/, the include/ and lib/ areas are always used.

Lovely, thanks! Last question - is there a way to use other variables in +<main-${PIOENV}.cpp>? Could I set e.g. SRCNAME=default in the default env, use +<main-${SRCNAME}.cpp> in the src_filter, and then have some environments that default to main-default.cpp, but allow others to set a new value, e.g.

[env]
src_filter = +<*.h> +<main-${SRCNAME}.cpp>
SRCNAME="default"

[env:usesdefault]
[env:special]
SRCNAME="special"

See “platformio.ini” (Project Configuration File) — PlatformIO latest documentation
There’s no single answer, best option is to just try it and see what works best for you.

@jcw This is an awesome find; it is a big time saver. With all the connectivity (ble, wifi, lora) it is rare to have a single device in an application these days. Using your technique I was able to get multiple devices sharing code and playing nice all in one PlatformIO project.

env]
platform = espressif32
framework = arduino
board = ttgo-lora32-v2
lib_ignore = examples
monitor_speed = 115200
board_build.partitions = assets/min_spiffs.csv
build_src_filter = 
	+<*.h> 
	-<.git/> 
	-<.svn/>
	+<shared/>
	+<${PIOENV}/>
	+<main-${PIOENV}.cpp>

lib_ldf_mode=deep
lib_deps = 
	Spi
	Wire
build_flags = 
	-D OTA_ENABLE=flase
	-D RX2=14
	-D TX2=12

[env:base]
lib_deps = 
	${env.lib_deps}
build_flags = 
	${env.build_flags}
	-D BASE

[env:blaster]
lib_deps = 
	${env.lib_deps}
	adafruit/Adafruit BusIO@^1.13.2
	adafruit/Adafruit SSD1306 @ ^2.5.7
	sandeepmistry/LoRa@^0.8.0
	crankyoldgit/IRremoteESP8266@^2.8.2
	lennarthennigs/Button2@^2.0.3
	majicdesigns/MD_Menu@^2.1.3
	adafruit/Adafruit Soundboard library@^1.1.0
	bodmer/TFT_eSPI@^2.4.78
build_flags = 
	${env.build_flags}
	-D BLASTER
	-D BAND=915E6
	-D L_CS=18
	-D L_RST=14
	-D L_IRPT=26
	-D INC_PIN=36
	-D DEC_PIN=39
	-D SEL_PIN=34

[env:target]
lib_deps = 
	${env.lib_deps}
build_flags = 
	${env.build_flags}
	-D TARGET

[env:scratchpad]
lib_deps = 
	${env.lib_deps}
	bodmer/TFT_eSPI@^2.4.78
build_flags = 
	${env.build_flags}
	-D SCRATCH

Thanks again!

Thanks this was very helpful! Another tip - if you want to use the GUI checkmark to build or upload, you can use the “Project Selector” at the bottom of the screen to choose a specific environment. (“Penumbra_Agent”) in this image.

Very helpful. So far I used the Project Selector and this whole mechanism for choosing between different target boards.
Is there a way to have both board and “app” selectable? i.e. without writing down all possible combinations in the .ini file…
Would be sufficient to make hardware selection by text in the .ini file and select the “app” in the UI.

No: an environment is a build for a specific board and specific source files.

But you can include something like:

[platformio]
extra_configs = pio-config.ini

And then create a pio-config.ini file with something like:

[env]
board = ...

To switch boards, change that config file. Be sure to omit board = ... from platformio.ini as it’ll take precedence.

This way, the board choice remains separate from the main PIO project info.

Thank you for illustrating that this was still the preferred method. I had only one issue with the external libraries. In 2022 I could just add all the external libraries in the global configuration. I have put all the other libraries in the ./tools/ directory and that keeps everything clean. In 2026, it seems that I need to specify the needed external libraries per tool. Since Platformio can’t find the right dependencies if they’re not compiled with them.

Which makes updating all the libraries tedious.

Project link: Jelle Haandrikman / house2 · GitLab

My platformio.ini file that now works:

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[platformio]
default_envs = house2

[esp32]
platform = espressif32
board = nodemcu-32s
framework = arduino
upload_port = /dev/ttyUSB0
monitor_speed = 115200
monitor_port = /dev/ttyUSB0
extra_scripts = pre:extra_script.py

[env:house2]
extends = esp32
lib_deps = 
    budryerson/TFLI2C@^0.2.0
    adafruit/Adafruit PWM Servo Driver Library@^3.0.3
    robtillaart/MCP23017@^0.9.3
    sparkfun/SparkFun VL53L1X 4m Laser Distance Sensor@^1.2.12

[env:native]
platform = native
lib_ignore = 
    distance_hal
    house2
    mcp_gpio_hal
    sensorbox_hal
    servoshield_hal
build_flags = 
    -D UNITY_INT_WIDTH=32 
    -D TEST_DEBUG

[env:proximity_check]
extends = esp32
build_flags = -DDEBUG
build_src_filter = +<../tools/proximity_check/proximity_check.cpp>
lib_deps = 
    sparkfun/SparkFun VL53L1X 4m Laser Distance Sensor@^1.2.12
    robtillaart/MCP23017@^0.9.3

# Update to new API
[env:io_extender]
extends = esp32
build_src_filter = +<../tools/io_extender/io_extender.cpp>
lib_deps = 
    robtillaart/MCP23017@^0.9.3

[env:i2c_scan]
extends = esp32
build_src_filter = +<../tools/i2c_scan/i2c_scan.cpp>
lib_deps = 
    sparkfun/SparkFun VL53L1X 4m Laser Distance Sensor@^1.2.12

[env:luna_config]
extends = esp32
build_src_filter = +<../tools/luna_config/luna_config.cpp>
lib_deps = 
    budryerson/TFLI2C@^0.2.0

[env:servoshield]
extends = esp32
build_src_filter = +<../tools/servoshield/servoshield.cpp>
lib_deps = 
    adafruit/Adafruit PWM Servo Driver Library@^3.0.3

[env:luna_array]
extends = esp32
build_src_filter =  +<../tools/luna_array/luna_array.cpp>
lib_deps = 
    budryerson/TFLI2C@^0.2.0

[env:status_leds]
extends = esp32
build_src_filter =  +<../tools/status_leds/status_leds.cpp>

[env:servo_test]
extends = esp32 
build_src_filter =   +<../tools/servo_test/servo_test.cpp>
lib_deps = 
    adafruit/Adafruit PWM Servo Driver Library@^3.0.3

[env:servo_trajectory]
extends = esp32 
build_src_filter =   +<../tools/servo_trajectory/servo_trajectory.cpp>
lib_deps = 
    adafruit/Adafruit PWM Servo Driver Library@^3.0.3

[env:servo_config]
extends = esp32 
build_src_filter =   +<../tools/servo_config/servo_config.cpp>
lib_deps = 
    adafruit/Adafruit PWM Servo Driver Library@^3.0.3

# Update to new API
[env:audio_test]
extends = esp32 
build_src_filter =   +<../tools/audio_test/audio_test.cpp>
lib_deps = 
    robtillaart/MCP23017@^0.9.3

[env:vl53_test]
extends = esp32 
build_src_filter =   +<../tools/vl53_test/vl53_test.cpp>
lib_deps = 
    sparkfun/SparkFun VL53L1X 4m Laser Distance Sensor@^1.2.12