Understanding the Arduino .ino.merged.bin File

If you have ever dug into the temporary build folders of the Arduino IDE after compiling a sketch for an ESP32 or ESP8266, you likely noticed a file named .ino.merged.bin alongside the standard .ino.bin and .ino.elf files. For makers and firmware engineers, the Arduino .ino.merged.bin file is a critical artifact. It represents a fully concatenated, ready-to-flash firmware image that includes not just your application code, but the bootloader, partition table, and sometimes even the filesystem image.

In 2026, with the widespread adoption of the ESP32-S3, ESP32-C3, and the maturation of Arduino IDE 2.x and 3.x, understanding how to locate, manipulate, and flash this merged binary is essential for over-the-air (OTA) deployment, mass production, and web-based flashing workflows.

Quick Reference: Binary File Types in Arduino

Before diving into flashing procedures, it is crucial to understand how the .ino.merged.bin differs from other compiled outputs. Use this quick reference table to select the right file for your deployment method.

File Extension Contents Primary Use Case Flashing Address
.ino.bin Application firmware only (no bootloader/partitions). Standard OTA updates; flashing via Arduino IDE serial monitor. 0x10000 (App partition)
.ino.elf Executable and Linkable Format (includes debug symbols). Hardware debugging via JTAG/SWD; stack trace decoding. Not flashed directly via UART.
.ino.merged.bin Bootloader + Partition Table + App + (Optional) Filesystem. Factory flashing, web flashers, mass production, esptool CLI. 0x0 (or 0x1000 depending on chip)

How to Locate the Merged Binary in Arduino IDE 2.x

Unlike older versions of the IDE, Arduino IDE 2.x and the underlying arduino-cli store compiled assets in hidden temporary directories. To find your .ino.merged.bin file:

  1. Open File > Preferences and check Show verbose output during: compilation.
  2. Click the Verify (compile) button.
  3. Watch the black console window at the bottom of the IDE. Look for the final lines of the build process.
  4. You will see a path resembling /tmp/arduino-build-XXXXX/sketch.ino.merged.bin.
  5. Click the path or copy it into your file explorer to extract the file.
Expert Tip: If you are using the Arduino CLI in a CI/CD pipeline, append the --build-path ./build flag to your compile command. This forces the CLI to output the .ino.merged.bin directly into your project directory, bypassing the temporary folder entirely.

Anatomy of the ESP32 Merged Binary

Why does the Arduino ESP32 core generate a merged file? The ESP32 architecture requires multiple distinct binaries to be placed at exact memory offsets to boot successfully. The esptool merge process pads these binaries with empty bytes (0xFF) to align them perfectly.

  • Second Stage Bootloader: Placed at 0x1000. Initializes flash pins and loads the partition table.
  • Partition Table: Placed at 0x8000. Tells the bootloader where the app and filesystem (LittleFS) reside.
  • Application Binary: Placed at 0x10000. Your actual C++ Arduino sketch.
  • Filesystem (LittleFS): Placed at the end of the defined partition (e.g., 0x290000).

When the IDE invokes the merge command, it calculates the absolute offsets and creates a single continuous file starting from 0x0. This means the resulting Arduino .ino.merged.bin file must always be flashed to the absolute zero address of the flash chip.

Flashing the Merged Binary via esptool.py

For production environments or headless Raspberry Pi flashing stations, Python's esptool.py is the industry standard. According to the official Espressif esptool documentation, flashing a merged binary requires specific parameters to ensure the flash mode and frequency match the hardware.

Step-by-Step CLI Flashing

Open your terminal and execute the following command. Adjust the port and chip type based on your specific hardware (e.g., ESP32-S3 vs classic ESP32).

esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x0 sketch.ino.merged.bin

Crucial Parameter Breakdown

  • --chip esp32: Change to esp32s3 or esp32c3 if using newer variants. The memory maps differ slightly.
  • --baud 921600: Modern ESP32 chips handle high-speed UART transfers easily. If you experience 'Invalid head of packet' errors, drop this to 115200.
  • --flash_mode dio: Dual I/O is the safest default for most generic dev boards. Using qio (Quad I/O) on boards with PSRAM wired to the same pins can cause bootloops.
  • 0x0: The target memory address. Because the merged binary includes padding from zero, you must write it to 0x0. Flashing it to 0x10000 will overwrite your app partition with bootloader data, instantly bricking the device.

Deploying via ESP Web Tools (Browser Flashing)

If you are distributing your firmware to end-users, requiring them to install Python and esptool is a massive friction point. Instead, use the ESP Web Tools JavaScript library to flash the .ino.merged.bin directly from a Chrome or Edge browser via WebSerial.

Create a manifest.json file in your web directory:

{
  "name": "My Smart Home Sensor",
  "version": "2.1.0",
  "builds": [
    {
      "chipFamily": "ESP32",
      "parts": [
        { "path": "sketch.ino.merged.bin", "offset": 0 }
      ]
    }
  ]
}

Because the offset is set to 0, the web flasher will write the entire merged payload correctly, effectively unbricking or factory-resetting the device in seconds.

FAQ & Troubleshooting Common Errors

Why is my .ino.merged.bin file so much larger than the .ino.bin?

The standard .ino.bin only contains your compiled application code, which might be 800KB. The .ino.merged.bin includes the bootloader, partition table, and potentially a pre-formatted LittleFS filesystem partition. If you have allocated a 2MB partition for LittleFS, the merged binary will be padded to at least 2MB+ to ensure the filesystem boundaries are preserved during the flash process.

I flashed the merged binary, but the ESP32 is stuck in a bootloop.

This is almost always caused by a flash mode mismatch or an incorrect starting address. Solution: Open the Serial Monitor at 115200 baud. If you see rst:0x10 (RTCWDT_RTC_RESET) followed by flash read err, 1000, you likely flashed the merged file to 0x10000 instead of 0x0. Re-flash using 0x0. If you see invalid header: 0xffffffff, your flash mode is wrong. Recompile ensuring your boards.txt or IDE menu is set to 'DIO' rather than 'QIO', or vice versa.

Can I use the .ino.merged.bin for OTA (Over-The-Air) updates?

No. OTA update mechanisms (like ArduinoOTA or ElegantOTA) only have permission to write to the specific application partition (usually starting at 0x10000). If you attempt to push a merged binary via OTA, the bootloader data contained within the first few kilobytes of the merged file will be written into the middle of your flash memory, corrupting the application and bricking the device until a physical serial re-flash is performed. Always use the standard .ino.bin for OTA.

How do I extract the application binary from a merged file?

If you have lost the original .ino.bin but possess the .ino.merged.bin, you can extract it using the dd command in Linux/macOS. Assuming your app partition starts at 0x10000 (65536 in decimal):

dd if=sketch.ino.merged.bin of=extracted_app.bin bs=1 skip=65536

This strips away the bootloader and partition table padding, leaving you with a clean binary suitable for OTA deployment.