PlatformIOで複数のsetup(), loop()を定義してビルド時に切り替える

背景

PlatformIOで複数のサンプルコードを用意したり、 試行錯誤している際に、 似たような構成のプロジェクトを複数用意することになります。

そのような時に、似たようなプロジェクトがいくつもできてしまうのを避けるために、 ビルド時に setup()loop() などのメインのコードだけを差し替えることで、 同じプロジェクトを使い回す方法を調査したので書いていこうと思います。

解決方法1(platformio.iniでビルド対象のファイルを切り変える)

platformio.inibuild_src_filterを 設定することでビルド対象のファイルをフィルタリングすることができます。 なので、環境ごとにビルド対象のファイルを変えることでメインのファイルを切り替えます。

具体的には以下のような構成になります。 ここではESP32の例を載せています。 また、この記事では PlatformIO Core 6.1.18 を使用しています。

.
├── platformio.ini
└── src
    ├── main1.cpp
    └── main2.cpp

platformio.ini

[platformio]
default_envs=esp32dev1 ;ここでビルドする環境を切り替える

; 環境共通の設定
[env]
platform = espressif32
board = esp32dev
framework = arduino
build_src_filter =
	-<**/main*.cpp> ;ここで **/main*.cpp をビルド対象から除外する
lib_deps = 
	z3t0/IRremote@^4.4.1 ;ここに共通で使うライブラリを記述する

; 環境1(esp32dev1)
[env:esp32dev1]
build_src_filter =
	${env.build_src_filter}
	+<main1.cpp> ;ここで main1.cpp をビルド対象とする
lib_deps = 
	${env.lib_deps}
	hideakitai/ArduinoOSC@^0.5.1 ;ここに環境固有のライブラリを記述する

; 環境2(esp32dev2)
[env:esp32dev2]
build_src_filter =
	${env.build_src_filter}
	+<main2.cpp> ;ここで main2.cpp をビルド対象とする
lib_deps = 
	${env.lib_deps}

main1.cpp

#include <Arduino.h>

void setup() {
    Serial.begin(115200);
    Serial.println("main1.cpp");
}

void loop() {}

main2.cpp

#include <Arduino.h>

void setup() {
    Serial.begin(115200);
    Serial.println("main2.cpp");
}

void loop() {}

解決方法2(extra_scriptsでbuild_src_filterを更新する)

もう少しスマートに解決する方法として、 ビルド前にスクリプトを実行して build_src_filter を更新する方法が考えられます。

この方法のメリットは、 platform.ini がすっきりする点と、 ビルド対象のファイル名と環境名を関連付けられる点です。

以下に具体的な構成を載せます。

.
├── platformio.ini
├── pre_extra_script.py
└── src
    ├── esp32dev1_main.cpp
    └── esp32dev2_main.cpp

platform.ini

platform.ini では build_src_filter で全ての **/main*.cpp をビルド対象から除外し、 extra_scripts のみを記述します。

[platformio]
default_envs=esp32dev1 ;ここでビルドする環境を切り替える

; 環境共通の設定
[env]
platform = espressif32
board = esp32dev
framework = arduino
build_src_filter =
	-<**/main*.cpp> ;ここで **/main*.cpp をビルド対象から除外する
lib_deps = 
	z3t0/IRremote@^4.4.1 ;ここに共通で使うライブラリを記述する
extra_scripts =
	pre:pre_extra_script.py ;ビルド前に実行するスクリプトを指定する

; 環境1(esp32dev1)
[env:esp32dev1]
lib_deps = 
	${env.lib_deps}
	hideakitai/ArduinoOSC@^0.5.1 ;ここに環境固有のライブラリを記述する

; 環境2(esp32dev2)
[env:esp32dev2]
lib_deps = 
	${env.lib_deps}

pre_extra_script.py

ビルド前に実行される pre_extra_script.py は以下のようになります。 platfomio.ini で記述されている build_src_filter${環境名}_main.cpp を追加しています。

Import('env')

env.Replace(SRC_FILTER=env['SRC_FILTER'] + [f'+<{env["PIOENV"]}_main.cpp>'])

esp32dev1_main.cpp

#include <Arduino.h>

void setup() {
    Serial.begin(115200);
	Serial.println("esp32dev1_main.cpp");
}

void loop() {
}

esp32dev2_main.cpp

#include <Arduino.h>

void setup() {
    Serial.begin(115200);
	Serial.println("esp32dev2_main.cpp");
}

void loop() {
}

まとめ

とりあえず思いついた方法はこんな感じです。 やりすぎかな?と思いつつも、個人的には二番目の方法がスマートでいいなと思います。

extra_scriptsは初めて書いたのですが env.Dump() でキーの名前を調べたり、 デバッガーを使って調査できなかったり少し大変だなーと思いました。 ですが使い慣れると便利そうです。 この辺はそのうち使うかもしれないのでメモしておきます。

参考


PlatformIOで複数のsetup(), loop()を定義してビルド時に切り替える

By Katsuya Endoh, 2025-03-16