New in version 3.4.1.
Warning
Advanced Scripting is recommended for Advanced Users and requires Python language knowledge.
We highly recommend to take a look at Dynamic build flags option where you can use any programming language. Also, this option is useful if you need to apply changes to the project before building/uploading process:
Macro with the latest VCS revision/tag “on-the-fly”
Generate dynamic headers (*.h
)
Process media content before generating SPIFFS image
Make some changes to source code or related libraries
More details Dynamic build flags.
PlatformIO Build System allows to launch custom extra_scripts (based on SCons software construction tool) while processing environment. For more details please follow to “Construction Environments” section of SCons documentation.
Warning
You can not run/debug these scripts directly with Python Interpreter. They will be loaded automatically when you processing project environment using platformio run command.
There are 2 type of extra scripts:
PRE - executes before a main script of Development Platforms
POST - executes after a main script of Development Platforms
Multiple extra scripts are allowed. Please split them via “, ” (comma + space) in the same line or use multi-line values.
For example, Project Configuration File platformio.ini
[env:my_env_1]
platform = ...
; without prefix, POST script
extra_scripts = post_extra_script.py
[env:my_env_2]
platform = ...
extra_scripts = pre:pre_extra_script1.py, pre:pre_extra_script2.py
[env:my_env_3]
platform = ...
extra_scripts =
pre:pre_extra_script.py
post:post_extra_script1.py
post_extra_script2.py
This option can be set by global environment variable
PLATFORMIO_EXTRA_SCRIPTS
.
Take a look at the multiple snippets/answers for the user questions:
platformio.ini
¶platformio.ini
:
[env:my_env]
platform = ...
extra_scripts = extra_script.py
custom_option1 = value1
custom_option2 = value2
extra_script.py
:
from platformio import util
config = util.load_project_config()
value1 = config.get("my_env", "custom_option1")
value2 = config.get("my_env", "custom_option2")
platformio.ini
:
[env:my_env]
platform = ...
extra_scripts = extra_script.py
extra_script.py
(place it near platformio.ini
):
Import("env")
# General options that are passed to the C and C++ compilers
env.Append(CCFLAGS=["flag1", "falg2"])
# General options that are passed to the C compiler (C only; not C++).
env.Append(CFLAGS=["flag1", "flag2"])
# General options that are passed to the C++ compiler
env.Append(CXXFLAGS=["flag1", "flag2"])
-Wl,
prefix¶Sometimes you need to pass extra flags to GCC linker without Wl,
. You could
use build_flags option but it will not work. PlatformIO
will not parse these flags to LINKFLAGS
scope. In this case, simple
extra script will help:
platformio.ini
:
[env:env_extra_link_flags]
platform = windows_x86
extra_scripts = extra_script.py
extra_script.py
(place it near platformio.ini
):
Import("env")
#
# Dump build environment (for debug)
# print env.Dump()
#
env.Append(
LINKFLAGS=[
"-static",
"-static-libgcc",
"-static-libstdc++"
]
)
You can override default upload command of development platform using extra
script. There is the common environment variable UPLOADCMD
which PlatformIO
Build System will handle when you platformio run -t upload.
Please note that some development platforms can have more than 1 upload command.
For example, Atmel AVR has UPLOADHEXCMD
(firmware) and UPLOADEEPCMD
(EEPROM data).
See examples below:
platformio.ini
:
[env:my_custom_upload_tool]
platform = ...
# place it into the root of project or use full path
extra_scripts = extra_script.py
upload_protocol = custom
upload_flags = -arg1 -arg2 -argN
extra_script.py
(place it near platformio.ini
):
Import("env")
# please keep $SOURCE variable, it will be replaced with a path to firmware
# Generic
env.Replace(
UPLOADER="executable or path to executable"
UPLOADCMD="$UPLOADER $UPLOADERFLAGS $SOURCE"
)
# In-line command with arguments
env.Replace(
UPLOADCMD="executable -arg1 -arg2 $SOURCE"
)
# Python callback
def on_upload(source, target, env):
print source, target
firmware_path = str(source[0])
# do something
env.Execute("executable arg1 arg2")
env.Replace(UPLOADCMD=on_upload)
See project example https://github.com/platformio/bintray-secure-ota
Sometime is useful to have a different firmware/program name in build_dir.
platformio.ini
:
[env:env_custom_prog_name]
platform = espressif8266
board = nodemcuv2
framework = arduino
build_flags = -D VERSION=13
extra_scripts = pre:extra_script.py
extra_script.py
:
Import("env")
my_flags = env.ParseFlags(env['BUILD_FLAGS'])
defines = {k: v for (k, v) in my_flags.get("CPPDEFINES")}
# print defines
env.Replace(PROGNAME="firmware_%s" % defines.get("VERSION"))
There is a list with built-in targets which could be processed using
platformio run --target
option. You can create unlimited number of
the own targets and declare custom handlers for them.
Let’s create a simple ping
target and process it with
platformio run --target ping
command:
platformio.ini
:
[env:env_custom_target]
platform = ...
...
extra_scripts = extra_script.py
custom_ping_host = google.com
extra_script.py
:
from platformio import util
from SCons.Script import AlwaysBuild
Import("env")
config = util.load_project_config()
host = config.get("env_custom_target", "custom_ping_host")
def mytarget_callback(*args, **kwargs):
print "Hello PlatformIO!"
env.Execute("ping " + host)
AlwaysBuild(env.Alias("ping", "", mytarget_callback))
PlatformIO Build System has rich API that allows to attach different pre-/post
actions (hooks) using env.AddPreAction(target, callback)
or
env.AddPreAction(target, [callback1, callback2, ...])
function. A first
argument target
can be a name of target that is passed using
platformio run --target
command, a name of built-in targets
(buildprog, size, upload, program, buildfs, uploadfs, uploadfsota) or path
to file which PlatformIO processes (ELF, HEX, BIN, OBJ, etc.).
Examples
extra_script.py
file is located on the same level as platformio.ini
.
platformio.ini
:
[env:pre_and_post_hooks]
extra_scripts = post:extra_script.py
extra_script.py
:
Import("env", "projenv")
# access to global build environment
print env
# access to project build environment (is used source files in "src" folder)
print projenv
#
# Dump build environment (for debug purpose)
# print env.Dump()
#
#
# Change build flags in runtime
#
env.ProcessUnFlags("-DVECT_TAB_ADDR")
env.Append(CPPDEFINES=("VECT_TAB_ADDR", 0x123456789))
#
# Upload actions
#
def before_upload(source, target, env):
print "before_upload"
# do some actions
# call Node.JS or other script
env.Execute("node --version")
def after_upload(source, target, env):
print "after_upload"
# do some actions
print "Current build targets", map(str, BUILD_TARGETS)
env.AddPreAction("upload", before_upload)
env.AddPostAction("upload", after_upload)
#
# Custom actions when building program/firmware
#
env.AddPreAction("buildprog", callback...)
env.AddPostAction("buildprog", callback...)
#
# Custom actions for specific files/objects
#
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", [callback1, callback2,...])
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", callback...)
# custom action before building SPIFFS image. For example, compress HTML, etc.
env.AddPreAction("$BUILD_DIR/spiffs.bin", callback...)
# custom action for project's main.cpp
env.AddPostAction("$BUILD_DIR/src/main.cpp.o", callback...)
# Custom HEX from ELF
env.AddPostAction(
"$BUILD_DIR/${PROGNAME}.elf",
env.VerboseAction(" ".join([
"$OBJCOPY", "-O", "ihex", "-R", ".eeprom",
"$BUILD_DIR/${PROGNAME}.elf", "$BUILD_DIR/${PROGNAME}.hex"
]), "Building $BUILD_DIR/${PROGNAME}.hex")
)