The Build System
For the core, or
for the Photon, in the top-level directory creates the bootloader and firmware binaries for your device, which are output to subdirectories of the
The top-level make is mainly a convenience to build
main projects. It
supports these targets:
clean - force the next build to be a full rebuild, and
all (default), build the artefact.
The Recipes and Tips section describes the most frequently used commands. The remaining sections describe the build system in detail.
The build system is organized as a number of build components. Each build component exists in it's own directory, with it's own makefiles and is responsible for build the artifacts that make up that component.
These are the primary components that produce executable code for a device:
- main (builds application firmware)
- modules (builds system+application firmware)
The other projects are libraries used by these primary projects.
When building firmware, it's a good idea to build from
main, since this offers
additional features compared to building in the root directory, such as
program-dfu to flash
the produced firmware to the device.
Updating System Firmware (Photon)
When building locally on the photon from the develop branch, it is necessary to update the system firmware to the latest version:
- put the Photon in DFU mode
make PLATFORM=photon clean all program-dfu
- You can optionally add
TESTvalues to the command above to build a specific application as you would when building
This will flash the latest system modules and the application to your device.
A key indicator that this is necessary is that the Photon doesn't run your application after flashing, due to a version mismatch. The onboard LED will breathe magenta to indicate Safe Mode when the application firmware isn't run.
all: the default target - builds the artefact for the project
clean: deletes all artefacts so the next build runs from a clean state
all program-dfu: (not bootloader) - builds and flashes the executable to a device via dfu
all st-flash: flashes the executable to a device via the st-link
make accepts variable definitions as part of the command invocation
v- verbose - set to 1 to trigger verbose output
PLATFORM_ID: specifies the target platform, either as a name or as an ID.
PRODUCT_ID: specifies the target product ID.
PRODUCT_FIRMWARE_VERSION: specifies the firmware version that is sent to the cloud. Value from 0 to 65535.
GCC_PREFIX: a prefix added to the ARM toolchain. Allows custom locations to be specified if the ARM tools are not in the path.
APP: builds the application stored in
user/applications/$(APP). (The default is to build the application code in
APPDIR: builds the application located in $(APPDIR). The directory specified should be outside of the firmware repo working directory, allowing 3rd party applications to be built. See
TESTbuilds the test application stored in
APPDIRis used this specifies the location of the makefile to include, relative to
APPDIR. The default is
DEBUG_BUILDdescribed in debugging
TARGET_NAME: sets the base name of the artefact file produced. E.g. setting
TARGET_NAME=whereyouwould produce the target named
whereyou.binThe default is the value of
TARGET_DIR: sets the directory where the target files are placed relative to the current directory.
The Platform ID describes the target platform. If you are targeting the Spark Core, you can skip this section. A list of supported platform IDs are listed in [platform-id.mk]((../build/platform-id.mk). The most common are listed here:
The platform is specified on the command line as
Would build the firmware for the Photon / P0.
To avoid repeatedly specifying the platform on the command line, it can be set as an environment variable.
In the commands that follow, we avoid listing the PLATFORM explicitly to keep the examples concise.
This clears all output files from the build so that all sources are recompiled.
Specifying custom toolchain location
Custom compiler prefix can be used via an environment variable
For example when you have installed a custom toolchain under
/opt/gcc-arm-embedded-bin you can use invoke make using that toolchain
The default value of
arm-none-eabi, which uses the ARM
version of the GCC toolchain, assumed to be in the path.
Alternatively, a path for the tools can be specified separately as
which, if specified should end with a directory separator, e.g.
By default the makefile is quiet - the only output is when an .elf file is produced to
show the size of the flash and RAM memory regions. To produce more verbose output, define
v (verbose) variable, like this:
Building individual modules
The top-level makefile builds all modules. Each module can be built on its own by executing the makefile in the module's directory. The make also builds any dependencies.
For example, executing
cd main make
Will build the main firmware, and all the modules the main firmware depends on.
By default, the build system targets the Spark Core (Product ID 0). If your product has been assigned product ID, you should pass this on the command line to specifically target your product. For example:
Builds the firmware for product ID 2.
Note that this method works only for the Core. On later platforms, the PRODUCT ID and version is specified in your application code via the macros:
Building an Application
To build a new application, first create a subdirectory under
You'll find the Tinker app is already there. Let's say we want to create a new
app, which we'll call
Then add the files needed for your application to that directory. These can be named freely,
but should end with
.cpp. For example, you might create these files:
myapplication.cpp mylibrary.cpp mylibrary.h
You can also add header files - your application subdirectory is on the include path.
To build this application, change directory to
main directory and run
This will build your application with the resulting
.bin file available in
Including Libraries in your Application
To include libraries in your application, copy or symblink the library sources into your application folder.
To importing libraries from the WebIDE:
- rename the
firmware folder to the same name as the library
- remove the examples folder
The library should then compile successfully
Changing the Target Directory
If you prefer the output to appear somewhere else than in the
you can define the
make APP=myapp TARGET_DIR=my/custom/output
This will place
main.bin (and the other output files) in
my/custom/output relative to the current directory.
The directory is created if it doesn't exist.
Changing the Target File name
It's also possible to specify the name of the output file, e.g. to revert to the
old naming convention of
make APP=myapp TARGET_FILE=core-firmware
This will build the firmware with output as
These can of course also be combined like so:
make APP=myapp TARGET_DIR=myfolder TARGET_FILE=core-firmware
Which will produce
Compiling an application outside the firmware source
If you prefer to separate your application code from the firmware code,
the build system supports this, via the
make APPDIR=/path/to/application/source [TARGET_DIR=/path/to/applications/output] [TARGET_FILE=basename]
APPDIR: The relative or full path to the directory containing the user application
TARGET_DIR: the directory where the build output should go. If not defined, output files willb e placed under a
targetdirectory of the application sources.
TARGET_FILE: the basename of the files created. If not defined, defaults to the name of the application sources directory.
APPDIR to build custom application sources, the build system
by default will build any
.cpp files found in the given directory
and it's subdirectories. You can override this and customize the build process by adding the file
a makefile to the root of the application sources.
The makefile should be placed in the root of the application folder. The default name for the file is:
- when building with
APP=myappthe default name is
- when building with
APPDIR=the default name is
The file should be a valid gnu make file.
To customize the build, append values to these variables:
CPPSRC: the c and cpp files in the build which are compiled and linked, e.g.
SRC += $(call target_files,,*.c) CPPSRC += $(call target_files,,*.cpp)
To add all files in the application directory and subdirectories.
INCLUDE_DIRS: the include path. Paths are relative to the APPDIR folder.
LIB_DIRS: the library search path
LIBS: libraries to link (found in the library search path). Library names are given without the
LIB_DEPS: full path of additional library files to include.
To use a different name/location for customization makefile file other than
USER_MAKEFILE to point to
your custom build file. The value of
USER_MAKEFILE is the location of your custom makefile relative to the application sources.
Integrated application.cpp with firmware
In previous versions of the make system, the application code was integrated with the firmware code at
This mode of building is still supported, however the location has changed to:
To build the default application sources, simply run
Platform Specific vs Platform Agnostic builds
Currently the low level hardware specific details are abstracted away in the HAL (Hardware Abstraction Layer) implementation.
By default the makefile will build for the Spark Core platform which will allow you to add direct hardware calls in your application firmware.
You should however try to make use of the HAL functions and methods instead of making direct hardware calls, which will ensure your code is more future proof!
To build the firmware as platform agnostic, first run
make clean, then simply include
SPARK_NO_PLATFORM=y in the make command.
This is also a great way to find all of the places in your code that make hardware specific calls, as they should generate an error when building as platform agnostic.
make APP=myapp SPARK_NO_PLATFORM=y
Build Output Directory
The build system uses an
out of source directory for all built artifacts. The
build/target/. In previous versions of the build system, artifacts
were placed under a local
build folder. If you would prefer to maintain this style of
working, you can create a symlink from
Then after building
main, the artifacts will be available in the
Flashing the firmware to the device via DFU
program-dfu target can be used when building from
main/ to flash
.bin file to the device using dfu-util. For this to work,
dfu-util should be
installed and in your PATH (Windows), and the device put in DFU mode (flashing yellow).
cd main make program-dfu
Enabling DFU Mode automatically
Normally, the device requires physical button presses to enter DFU mode. The build
also supports automatic DFU mode, where the device will automatically enter DFU
mode as part of running the
program-dfu target. To enable this, define the environment variable
PARTICLE_SERIAL_DEV to point to the name of the serial device. E.g.
PARTICLE_SERIAL_DEV=/dev/tty.usbmodem12345 make all program-dfu
the device will then automatically enter DFU mode and flash the firmware.
(Tested on OS X. Should work on other platforms that provide the
Flashing the firmware to the device via ST-Link
st-flash target can be used to flash all executable code (bootloader, main and modules)
to the device. The flash uses the
st-flash tool, which should be in your system path.
To enable JTAG debugging, add this to the command line:
and perform a clean build.
To enable SWD debugging only (freeing up 2 pins) add:
and perform a clean build. For more details on SWD-only debugging see https://github.com/spark/firmware/pull/337
Compilation without Cloud Support
To release more resources for applications that don't use the cloud, add SPARK_CLOUD=n to the make command line. This requires a clean build.
After compiling, you should see a 3000 bytes reduction in statically allocated RAM and 35k reduction in flash use.
Before the 0.4.0 firmware was released, we recommended the develop branch for early adopters to obtain the code. This is still fine for early adopters, and people that want the bleeding edge, although please keep in mind the code is untested and unreleased.
The released code is available in the
latest branch. This will eventually become the
branch once 0.4.4 is released for the Core.
To build the develop branch, follow these guidelines:
- export the environment variable PARTICLE_DEVELOP=1
- after pulling from the develop branch, be sure to build and flash the system firmware
Recipes and Tips
- The variables passed to make can also be provided as environment variables, so you avoid having to type them out for each build. The environment variable value can be overridden by passing the variable on the command line.
PARTICLE_DEVELOPcan be set in the environment when building from the
developbranch. (Caveats apply that this is bleeding edge!)
PLATFORMset in the environment if you mainly build for one platform, e.g. the Photon.
Here are some common recipes when working with the photon. Note that
PLATFORM=photon doesn't need to be present if you have
PLATFORM=photon already defined in your environment.
# Complete rebuild and DFU flash of latest system and application firmware firmware/modules$ make clean all program-dfu PLATFORM=photon # Incremental build and flash of latest system and application firmware firmware/modules$ make all program-dfu PLATFORM=photon # Build system and application for use with debugger (Programmer Shield) # APP/APPDIR can also be specified here to build the non-default application firmware/modules$ make clean all program-dfu PLATFORM=photon USE_SWD_JTAG=y # Incremental build and flash user application.cpp only (note the directory) firmware/main$ make all program-dfu PLATFORM=photon # Build an external application firmware/modules$ make all PLATFORM=photon APPDIR=~/my_app
For system firmware developers:
# Rebuild and flash the primary unit test application firmware/main$ make clean all program-dfu TEST=wiring/no_fixture PLATFORM=photon # Build the compilation test (don't flash on device) firmware/main$ make TEST=wiring/api PLATFORM=photon