Understanding the Safeboot Partition Scheme for Tasmota Factory Images in ESP Flasher

The safeboot partition scheme reserves offset 0x10000 for a minimal recovery firmware that allows ESP32 devices to recover from corrupted user flashes, which ESP Flasher automatically detects and deploys by downloading the appropriate safeboot binary from the Tasmota OTA server and flashing it alongside the bootloader, partition table, and user firmware.

Tasmota v13+ introduced a robust safeboot partition layout for ESP32-class devices to prevent devices from becoming unrecoverable after failed over-the-air updates. The jason2866/esp_flasher repository implements automated handling of this scheme, detecting chip variants and orchestrating multi-partition flashes without manual intervention. This article examines how the safeboot partition scheme works and how ESP Flasher manages it for Tasmota factory images.

What Is the Safeboot Partition Scheme?

The safeboot partition scheme splits the firmware across distinct regions instead of using a single monolithic image. The layout reserves specific offsets for critical components:

Offset Content Purpose
0x0000 Bootloader Initial boot code (may be 0 B on some chips)
0x8000 Partition table Defines flash organization
0xe000 OTA data (otadata.bin) Over-the-air update state
0x10000 Safeboot factory image Minimal recovery bootloader
0xe0000 User firmware Primary Tasmota application

The safeboot binary at 0x10000 contains a minimal environment capable of re-flashing the user firmware region if a standard OTA update corrupts the primary firmware. This dual-partition approach ensures devices remain recoverable even when the main 0xe0000 region contains invalid code.

How ESP Flasher Automates Safeboot Deployment

ESP Flasher handles the entire safeboot workflow automatically, from chip detection to coordinated flashing. The implementation resides primarily in esp_flasher/common.py.

Detecting Factory Images via Magic Bytes

The tool determines whether a supplied binary is a factory image by inspecting offset 0x10000 for the ESP image magic. In esp_flasher/common.py, the read_firmware_info function performs this check between lines 189-197:

def read_firmware_info(firmware):
    firmware.seek(0x10000)                # Check for safeboot image

    header = firmware.read(4)
    magic, _, flash_mode_raw, flash_size_freq = struct.unpack("BBBB", header)
    if magic == esptool.ESPLoader.ESP_IMAGE_MAGIC:
        # Factory (safeboot) image found

        ...

When the magic byte matches esptool.ESPLoader.ESP_IMAGE_MAGIC, the function sets flag_factory = True, triggering the safeboot flash path.

Selecting Chip-Specific Safeboot Binaries

For regular (non-factory) images, configure_write_flash_args identifies the ESP32 variant and selects the corresponding safeboot file. Lines 269-322 in common.py implement this logic:

if "ESP32-C2" in info.model:
    model = "esp32c2"
    safeboot = "tasmota32c2-safeboot.bin"
elif "ESP32-C3" in info.model:
    model = "esp32c3"
    safeboot = "tasmota32c3-safeboot.bin"
...
elif "ESP32-P4" in info.model:
    # Special handling for revision 3.0+

    ...
    safeboot = "tasmota32p4rev3-safeboot.bin"
else:
    model = "esp32"
    safeboot = "tasmota32solo1-safeboot.bin"

This ensures the safeboot binary matches the specific silicon revision and capabilities of the target device.

Downloading from the Tasmota OTA Server

If no custom safeboot file is provided via --safeboot, ESP Flasher constructs the download URL automatically. The constant ESP32_SAFEBOOT_SERVER defined in esp_flasher/const.py (lines 17-20) points to the official Tasmota OTA host:

if not factory_firm_path:
    factory_firm_path = ESP32_SAFEBOOT_SERVER + safeboot

The open_downloadable_binary function (lines 21-40 in common.py) handles the actual HTTP retrieval, allowing transparent use of remote URLs without manual downloading.

Coordinating Multi-Partition Flash Writes

The tool hard-codes the critical offsets for the safeboot layout in lines 264-268 of common.py:

ofs_partitions   = 0x8000
ofs_otadata      = 0xe000
ofs_factory_firm = 0x10000
ofs_firmware     = 0xe0000

During the flash preparation phase, configure_write_flash_args assembles these into an address-filename list passed to esptool. Lines 375-380 append each component:

addr_filename.append((ofs_bootloader, bootloader))
addr_filename.append((ofs_partitions, partitions))
addr_filename.append((ofs_otadata, otadata))
addr_filename.append((ofs_factory_firm, factory_firm))
addr_filename.append((ofs_firmware, firmware))

esptool.write_flash receives this list and writes all five components to their respective offsets in a single operation.

Flashing Tasmota with Safeboot Support

ESP Flasher requires no manual partition table editing to deploy safeboot images. The following command automatically downloads the correct safeboot binary for the detected chip and flashes the complete set:

esp_flasher \
    --port /dev/ttyUSB0 \
    --firmware tasmota32.bin \
    --bootloader auto \
    --partitions auto \
    --otadata auto

To override the automatic download with a locally patched safeboot binary, use the --safeboot flag:

esp_flasher \
    --port /dev/ttyUSB0 \
    --firmware tasmota32.bin \
    --safeboot ./custom-safeboot.bin \
    --bootloader auto \
    --partitions auto \
    --otadata auto

Summary

  • The safeboot partition scheme places a recovery bootloader at offset 0x10000 to enable firmware recovery from corrupted user flashes at 0xe0000.
  • ESP Flasher detects safeboot images by checking for ESP image magic bytes at 0x10000 via read_firmware_info in esp_flasher/common.py.
  • The tool automatically selects chip-specific safeboot binaries (e.g., tasmota32c3-safeboot.bin) based on the detected ESP32 model.
  • Safeboot binaries download automatically from the Tasmota OTA server defined in esp_flasher/const.py unless overridden by the --safeboot argument.
  • All components—bootloader, partition table, otadata, safeboot, and user firmware—flash simultaneously using hard-coded offsets in a single esptool invocation.

Frequently Asked Questions

What happens if the safeboot partition is corrupted?

If the safeboot region at 0x10000 becomes corrupted, the device may lose the ability to recover automatically from a failed user firmware update. In this scenario, physical reflashing via USB/serial using ESP Flasher is required to restore the safeboot binary and partition table.

Can I use ESP Flasher with non-Tasmota firmware that uses safeboot?

Yes, though ESP Flasher is optimized for Tasmota. You can supply any compatible safeboot binary using the --safeboot argument. The tool will flash it to offset 0x10000 according to the standard safeboot partition scheme, but you must ensure the binary is compatible with your specific ESP32 variant and partition table.

Why does ESP Flasher check offset 0x10000 specifically for safeboot detection?

Offset 0x10000 is the standardized location for the factory/safeboot image in the Tasmota safeboot partition scheme. The read_firmware_info function seeks to this address to check for the ESP image magic (ESPLoader.ESP_IMAGE_MAGIC), distinguishing factory images from single-partition user firmware that typically starts at 0xe0000.

How does ESP Flasher handle ESP32-P4 revision-specific safeboot binaries?

For ESP32-P4 devices, the code in configure_write_flash_args (lines 269-322 of common.py) includes special handling to identify silicon revision 3.0+. It selects tasmota32p4rev3-safeboot.bin for newer revisions and tasmota32p4-safeboot.bin for earlier silicon, ensuring compatibility with hardware-specific boot requirements.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →