Skip to content

Set Up OTA Updates

Over-the-air updates let you deploy new firmware to ESP devices over WiFi without physical access to the serial port. This guide walks through creating the partition layout, building an OTA package, deploying it, and recovering with rollback if something goes wrong.

OTA requires at least two application slots (ota_0 and ota_1) plus an otadata partition that tracks which slot is active. The esp_partition_create_ota tool generates this layout automatically.

  1. Generate the partition table CSV:

    esp_partition_create_ota({
    "flash_size": "4MB",
    "app_size": "1MB"
    })

    This produces a table with nvs, otadata, phy_init, ota_0, ota_1, and a storage partition using whatever space remains.

  2. Convert the CSV to binary using ESP-IDF’s partition tool:

    Terminal window
    python $IDF_PATH/components/partition_table/gen_esp32part.py \
    partitions.csv partitions.bin
  3. Flash the partition table to the device:

    esp_flash_firmware({
    "port": "/dev/ttyUSB0",
    "firmware_path": "partitions.bin",
    "address": "0x8000"
    })

The esp_ota_package_create tool bundles your firmware binary with a manifest containing version, SHA-256 hash, and timestamp into a zip archive.

esp_ota_package_create({
"firmware_path": "build/my_app.bin",
"version": "1.2.0",
"output_path": "releases/my_app-1.2.0-ota.zip"
})

The response includes the manifest contents and package size. The SHA-256 hash in the manifest can be used by the device to verify the firmware before applying it.

Your device must be running an HTTP OTA server — either the ESP-IDF esp_https_ota component or a custom HTTP handler that accepts firmware binary data via POST.

  1. Ensure the device is connected to the network and its OTA endpoint is reachable.

  2. Deploy the package:

    esp_ota_deploy({
    "package_path": "releases/my_app-1.2.0-ota.zip",
    "target_url": "http://192.168.1.100/ota/update"
    })
  3. The tool extracts firmware.bin from the zip and POSTs it to the target URL. A 2xx HTTP response indicates the device accepted the update.

  4. The device typically reboots into the new firmware automatically. Monitor the serial output to confirm:

    esp_serial_monitor({
    "port": "/dev/ttyUSB0",
    "baud_rate": 115200,
    "duration_seconds": 10,
    "reset_on_connect": false
    })

If the new firmware is broken or the device becomes unresponsive over the network, you can force a rollback via the serial port.

esp_ota_rollback({
"port": "/dev/ttyUSB0"
})

The tool reads the partition table from the device to locate the otadata partition, then erases that region. On the next boot:

  1. The bootloader checks the otadata partition for a valid boot selection.
  2. Finding all 0xFF (erased state), it falls back to the factory app if one exists, or ota_0 otherwise.
  3. The device boots the fallback firmware.

For more precise control over which partition to erase, use esp_partition_analyze to find the otadata offset and esp_flash_erase to clear it manually.