Add -t none or -t binary

I’d like to build the .bin, but without uploading it anywhere.

at the moment using platformio run, without target (at least on ESP) stops at the .elf, and doesn’t generate the binary.

is it posible to build the binary as well, or have a target binary to build the image…

or is there another way, I’m missing?


ok, ignore that.

run does generate it… i missed the -o flag in esptool. sorry


Just trying to do the same thing.
Are you doing this from the CLI? or the IDE?

Mind sharing some more info?


Guys, what do you mean? PlatformIO generates .elf and .bin by default. They are located in the .pioenvs/%ENV%/firmware.*.

See wiring example:

tree -La 3 examples/wiring-blink
├── .clang_complete
├── .gcc-flags.json
├── .gitignore
├── .pioenvs
│   ├── .sconsign.dblite
│   ├── nodemcu
│   │   ├── FrameworkArduino
│   │   ├── FrameworkArduinoVariant
│   │   ├── firmware.bin
│   │   ├── firmware.elf
│   │   ├── generic
│   │   ├── libFrameworkArduino.a
│   │   ├── libFrameworkArduinoVariant.a
│   │   └── src
│   └── structure.hash
├── .travis.yml
├── README.rst
├── lib
│   └── readme.txt
├── platformio.ini
└── src
    └── main.cpp

8 directories, 14 files

I know this is an old post but I can’t find firmware.bin anywhere. The project(s) I am working on are a variant of Teensy 3.1 (custom design using the NXP Kinetis MK20DX128… called teensy3x). My .pioenvs folder(s) don’t have a nodemcu folder and only have firmware.elf and firmware.hex in the .pioenvs folder.

What I have is all I typically need but I am looking at how to use utasker as a bootloader and I need binary files for that. I am using a JLINK as HEX file uploader and debugger and that is working OK.

The previous post by Ivan states the firmware.bin file is created by default but it looks like for Teensy this isn’t the case. What do I need to change to get the binary file?

My platformio.ini,, teensy3x.json, and mk20dx128V.ld files follow:


platform = teensy
framework = arduino
board = teensy3x
board_build.f_cpu = 96000000L
monitor_speed = 115200
extra_scripts =
debug_tool = custom
debug_init_break =  
debug_server =
  -if  ;was ifls?  

from os import makedirs
from os.path import isdir, join

# Optional block, only for Teensy
    env.VerboseAction(" ".join([
        "sed", "-i.bak",
    ]), "Fixing $BUILD_DIR/firmware.hex secure flash flags"))

def _jlink_cmd_script(env, source):
    build_dir = env.subst("$BUILD_DIR")
    if not isdir(build_dir):
    script_path = join(build_dir, "upload.jlink")
    commands = ["h", "loadbin %s,0x0" % source, "r", "q"]
    with open(script_path, "w") as fp:
    return script_path

        "-device", "MK20DX128xxx7",
        "-speed", "4000",
        "-if", "swd",
        "-autoconnect", "1"
    UPLOADCMD='"$UPLOADER" $UPLOADERFLAGS -CommanderScript ${__jlink_cmd_script(__env__, SOURCE)}'


  "build": {
    "core": "teensy3", 
    "cpu": "cortex-m4", 
    "extra_flags": "-D__MK20DX256__ -DTEENSY31", 
    "f_cpu": "96000000L", 
    "ldscript": "mk20dx128V.ld", 
    "mcu": "mk20dx128"
  "debug": {
    "jlink_device": "MK20DX128xxx7"
  "frameworks": [
  "name": "teensy3x", 
  "upload": {
    "maximum_ram_size": 16384, 
    "maximum_size": 131072,
  "protocols": [
   "protocol": "jlink"
  "url": "", 
  "vendor": "Teensy"


    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
    RAM  (rwx) : ORIGIN = 0x1FFFC000, LENGTH = 32K

	.text : {
		. = 0;
		/* TODO: does linker detect startup overflow onto flashconfig? */
		. = 0x400;
		. = ALIGN(4);
		. = ALIGN(4);
		__preinit_array_start = .;
		KEEP (*(.preinit_array))
		__preinit_array_end = .;
		__init_array_start = .;
		KEEP (*(SORT(.init_array.*)))
		KEEP (*(.init_array))
		__init_array_end = .;
	} > FLASH = 0xFF

	.ARM.exidx : {
		__exidx_start = .;
		*(.ARM.exidx* .gnu.linkonce.armexidx.*)
		__exidx_end = .;
	} > FLASH
	_etext = .;

The files generated will be board / environment specific. Since you’re not compiling for a nodemcu/esp8266 target, you won’t have that folder. What is in your .pioenvs/jlink_debug_and_upload folder, since whatever files are generated are located in:

When I tried against teensy30, what was compiled was a .hex and .elf.


That is consistent to what I am seeing, i.e. no firmware.bin file. Here is what my .pioenvs has:


and upload.jlink has this:

I could create a firmware.bin file if I knew where the script is that results from pio run. Specifically, unless there is a better way, I could add another call to arm-none-eabi-objcopy bit to create a binary file from firmware.elf

However, if the binary file is created and then deleted, where can I stop its deletion?

I’m reading thru the custom scripts section but the answer isn’t readily obvious.

Any advice?

Looking at platform-teensy/ at develop · platformio/platform-teensy · GitHub and platform-teensy/ at develop · platformio/platform-teensy · GitHub can you maybe add a call to to the ElfToBin like env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)? (you have this Script in C:\Users\<user>\.platformio\platforms\teensy\builder)

Or actually a PostBuild action like you did with the firmware.hex file to replace secure flash flags should also work there?

I actually have been trying to add a Post Build actin and can’t get it work. I also just came across the ElfToBin function and was about to try that. Thanks for the location of as I was having trouble finding it.

OK… so I am finding there are many files associated with PIO. In the folder C:\Users&lt;user>.platformio\platforms\teensy\builder there is one but also another .platform folder so a in C:\Users&lt;user>.platformio.platformio\platforms\teensy\builder. I added a print statement and a call to ElftoBin in both of those and it didn’t have an effect.

Any clues as to where the real would be located?

The content of the folder C:\Users\<user>\.platformio\platforms\teensy is exactly the clone of the repository GitHub - platformio/platform-teensy: Teensy: development platform for PlatformIO so I’m not sure which you mean?

That is exactly the question since I have in both two folders under <users…>/.platformio like this:

I don’t know why I have two folders like that but I’ll fix it later. I determined that the that is executing is the one in the highlighted builder folder. However, I have tried many things and cannot get a binary file. Here is what I have (among others) and it doesn’t work.

# Target: Build executable and linkable firmware

target_elf = None
if "nobuild" in COMMAND_LINE_TARGETS:
    target_firm = join("$BUILD_DIR", "${PROGNAME}.hex")
    target_elf = env.BuildProgram()
    target_firm = env.ElfToHex(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
#added by Al *************************
    target_firm_bin = join("$BUILD_DIR", "${PROGNAME}.bin")
    print "****** Creating binary file... (maybe!!!)***********"
    target_firm_bin = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
    print "************* binary file should be created ***************"

Well I just found the easy fix: replace

target_firm = env.ElfToHex(join("$BUILD_DIR", "${PROGNAME}"), target_elf)


target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)

Then pio run -t clean && pio run and you now have the firmware.bin.

C:\Users\Maxi\Desktop\teensy_test>file .pioenvs\teensy31\firmware*
.pioenvs\teensy31\firmware.bin; data
.pioenvs\teensy31\firmware.elf; ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped

I’ll check if I can construct a post-build script for a more general solution.

1 Like

Well that was easy. Just copy the VerboseAction that the ElfToBin function would execute.


platform = teensy
board = teensy31
framework = arduino
extra_scripts =

from os.path import join
Import("env", "projenv")

# Custom BIN from ELF
	env.VerboseAction(" ".join([
			]), "Building $TARGET"))
C:\Users\Maxi\Desktop\teensy_test>pio run
====================================================================== [SUCCESS] Took 3.96 seconds ======================================================================

C:\Users\Maxi\Desktop\teensy_test>file .pioenvs\teensy31\*
.pioenvs\teensy31\firmware.bin;          data
.pioenvs\teensy31\firmware.elf;          ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped
.pioenvs\teensy31\firmware.hex;          ASCII text, with CRLF line terminators
.pioenvs\teensy31\FrameworkArduino;      directory
.pioenvs\teensy31\libFrameworkArduino.a; current ar archive
.pioenvs\teensy31\src;                   directory

EDIT: Changed $SOURCES to the ELF file, otherwise this only translates the main.cpp.o file binary…


That’s great! Thanks for helping with this.

What you have is sort of what I did except I tried to get both files but mine didn’t work? I tried that again after a Clean but it still failed to produce the bin file. I then tried what you did and it worked. However, I still need the hex file.

I also tried added this to my and it doesn’t seem to run

    env.VerboseAction(" ".join([
        "$OBJCOPY", "-O", "binary", "-R", ".eeprom",
        "$BUILD_DIR/firmware.elf", "$BUILD_DIR/firmware.bin"
    ]), "Where is the binary file???"))
#        ]), "Building $BUILD_DIR/firmware.bin"))

The creation of the hex file is untouched since it’s just adding another Post action to the firmware.elf build step. Maybe you didn’t revert the modification in the

Our posts/replies have gotten out of time sync. What I had shown earlier for a PostAction was very close but wouldn’t run. The PostAction you posted works great. One thing to suggest is to change the last line to “Building ${PROGNAME}.bin” else it prints out Building firmware.elf" Many thanks!

PS I just did a similar thing and created an S-record file!

PSS Now what I need to do is to rebuild the uTasker bootloader for my configuration (custom K20 board) but
using PIO (and not Codewarrior or GCC toolchain directly!)

Thanks again for your help with this.