| Supported Targets | ESP32-P4 | ESP32-H2 |
|---|
This example demonstrates ESP-Hosted Transport OTA - updating slave firmware through the existing ESP-Hosted connection (SDIO/SPI/UART) without any additional hardware or physical access.
Key Features:
- No extra hardware needed - uses your existing ESP-Hosted transport
- Three OTA methods - choose what works best for your deployment
- Version checking - prevents unnecessary updates
There are a lot of ways that you can flash the slave firmware. To limit possibilities, two main approaches could be understood.
- Hardware: None (reuses existing SDIO/SPI/UART serial channel from ESP-Hosted)
- Use case: Production updates, remote deployment
- Access: Over-the-air OR through Host partition (Uses RPC calls to slave)
- Hardware: ESP-Prog or UART connection
- Use case: Initial slave FW setup, firmware recovery, development
- Access: Requires physical access to device
This example focuses entirely on Method 1
---
title: ESP-Hosted Co-processor (Slave) OTA Process
---
sequenceDiagram
participant Source as Firmware Source
participant Host as Host MCU
participant Slave as Slave ESP32
Note over Host,Slave: Pre-OTA<br/> Version compatibility check (optional)<br/> <Host version != Current Slave version>
Host->>Host: 1. Read firmware header from FW image
Host->>Host: 2. Validate FW image<br/>(magic number, header)
Host->>Host: 3. Compare <FW image version != Current slave version>
alt Slave needs update
Host->>Slave: 4. Send OTA_BEGIN command
Slave->>Slave: Prepare OTA partition
loop Transfer firmware in chunks
Source->>Host: 5. Read firmware chunk
Host->>Slave: 6. Send OTA_WRITE (chunk)
Slave->>Slave: Write to flash partition
end
Host->>Slave: 7. Send OTA_END command
Slave->>Slave: 8. Verify firmware integrity<br/>(CRC, signature)
Host->>Host: 9. Check slave FW version<br/>for activate support
alt Slave FW >= v2.6.0
Host->>Slave: 10. Send OTA_ACTIVATE command
Slave->>Slave: Mark new firmware as active
Note over Slave: ✅ Slave OTA Done
else Older slave FW
Note over Host: ✅ Slave OTA Done (activate API not required)
end
Note over Host: Restart Host, to avoid sync issues
else Slave already up-to-date
Note over Host: ✅ Slave OTA not required<br/>(firmware versions match/compatible)
end
The ESP-Hosted slave OTA functionality is built around 4 core APIs that handle the complete OTA process. These APIs are transport-agnostic and can work with any firmware source (LittleFS, Partition, HTTPS, SPIFFS, etc.).
esp_err_t esp_hosted_slave_ota_begin(void);- Purpose: Initializes the OTA process on the slave
- Arguments: None
- Returns:
ESP_OKon success, error code on failure - What it does:
- Prepares slave for firmware reception
- Allocates OTA buffers
- Sets up the OTA partition on slave
esp_err_t esp_hosted_slave_ota_write(const void *data, size_t size);- Purpose: Sends firmware data chunks to the slave
- Arguments:
data: Pointer to firmware data chunksize: Size of the data chunk (typically 1400-1500 bytes)
- Returns:
ESP_OKon success, error code on failure - What it does:
- Transmits firmware data over ESP-Hosted transport (SDIO/SPI/UART)
- Slave writes data to its OTA partition
- Can be called multiple times for large firmware
esp_err_t esp_hosted_slave_ota_end(void);- Purpose: Finalizes the OTA process
- Arguments: None
- Returns:
ESP_OKon success, error code on failure - What it does:
- Validates the complete firmware image on slave
- Calculates and verifies checksums
- Marks the new firmware as valid but not active
esp_err_t esp_hosted_slave_ota_activate(void);- Purpose: Activates the newly flashed firmware
- Arguments: None
- Returns:
ESP_OKon success, error code on failure - What it does:
- Switches slave's boot partition to the new firmware
- Triggers slave reboot with new firmware
- Note: After this call, slave will restart with new firmware
esp_err_t esp_hosted_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *version);- Purpose: Gets the currently running slave firmware version
- Arguments:
version: Pointer to structure to store version information
- Returns:
ESP_OKon success, error code on failure - Structure:
typedef struct { uint32_t major1; // Major version uint32_t minor1; // Minor version uint32_t patch1; // Patch version } esp_hosted_coprocessor_fwver_t;
This example demonstrates the complete OTA workflow:
- Version Check (optional): Uses
esp_hosted_get_coprocessor_fwversion()to get current slave version - Begin OTA: Calls
esp_hosted_slave_ota_begin()to initialize - Transfer Firmware:
- Reads firmware from source (LittleFS file/Partition/HTTPS download)
- Calls
esp_hosted_slave_ota_write()repeatedly with chunks
- Finalize: Calls
esp_hosted_slave_ota_end()to validate firmware - Activate: Calls
esp_hosted_slave_ota_activate()to switch to new firmware
Important: These APIs are not limited to the three methods shown. You can use:
- LittleFS (this example)
- Dedicated Partition (this example)
- HTTPS Download (this example)
- SPIFFS filesystem
- SD Card files
- NVS storage
- Custom network protocols
- Any other firmware source
The key requirement is that you can read the firmware data and feed it to esp_hosted_slave_ota_write() in chunks.
Choose the method that best fits your deployment:
| Method | Remarks |
|---|---|
| Slave OTA using LittleFS | Local deployment. One of Host partition formatted as LittleFS and pushed the slave fw as file |
| Slave OTA using Host Partition | Local deployment. Slave Fw flashed in one of Host partition directly |
| Slave OTA Using HTTPS | Needs internet access. Can reuse HTTPS server in the deployment |
Ease of Use: LittleFS > HTTPS > Partition
Pre-requisites:
- Please ensure the slave application firmware binary is located in the designated path specified below to prevent 'File not Found' build errors.
- For HTTPS-based updates, verify that the required certificates are properly installed to avoid connection establishment failures.
host_performs_slave_ota/
├── components/
│ ├── ota_partition/ # Slave OTA using Host Partition method
│ │ └── slave_fw_bin/ # Put slave .bin files here
│ ├── ota_littlefs/ # Slave OTA using LittleFS method
│ │ └── slave_fw_bin/ # Put slave .bin files here
│ └── ota_https/ # Slave OTA Using HTTPS method
│ ├── certs/ # SSL certificates
│ └── test_server/ # Local HTTPS server
├── partitions.csv # Universal partition table
└── main/ # Main application
nvs, data, nvs, 0x9000, 16K,
otadata, data, ota, 0xd000, 8K,
phy_init, data, phy, 0xf000, 4K,
ota_0, app, ota_0, 0x10000, 2M,
ota_1, app, ota_1, 0x210000, 2M,
storage, data, littlefs, 0x410000, 0x1E0000, # Used by Slave OTA using LittleFS
slave_fw, data, 0x40, 0x5F0000, 0x200000, # Used by Slave OTA using Host Partition
Best for: General use, dynamic updates, multiple firmware storage
- Slave firmware stored in LittleFS filesystem
- Build system creates filesystem image with firmware
- OTA mounts filesystem and reads firmware file
-
Build slave firmware: Please build the slave firmware referring the slave example.
-
Copy slave firmware:
cp slave/build/network_adapter.bin examples/host_performs_slave_ota/components/ota_littlefs/slave_fw_bin/
-
Configure (optional - already default):
idf.py menuconfig # ESP-Hosted Slave OTA Configuration → OTA Method → LittleFS OTA -
Build and flash:
idf.py -p <host_serial_port> build flash monitor
- Build system creates LittleFS image containing slave firmware
- LittleFS image flashed to
storagepartition - At runtime, OTA mounts filesystem, finds firmware, and compares versions
- Only updates if versions differ
Best for: Production, fastest updates, most reliable
- Slave firmware pre-flashed to dedicated partition
- OTA reads directly from partition (no filesystem overhead)
- Version comparison prevents unnecessary updates
-
Build slave firmware: Please build the slave firmware referring the slave example.
-
Copy slave firmware:
cp slave/build/network_adapter.bin examples/host_performs_slave_ota/components/ota_partition/slave_fw_bin/
-
Configure:
idf.py menuconfig # ESP-Hosted Slave OTA Configuration → OTA Method → Partition OTA # (Default) Set partition label to: slave_fw
-
Build and flash:
idf.py -p <host_serial_port> build flash monitor
- Build system detects slave firmware and shows notice
- During flash, slave firmware automatically flashed to
slave_fwpartition - At runtime, OTA reads firmware from partition and compares versions
- Only updates if versions differ
Best for: Remote updates, internet deployment
- Downloads slave firmware from HTTPS server
- Verifies image header while downloading
- Supports both self-signed and CA certificates
The Slave OTA Using HTTPS supports two certificate modes:
For real servers with CA-signed certificates, disable self signed testing.
ESP-Hosted Slave OTA Configuration
└── OTA Method
└── HTTPS OTA Config
└── Use self-signed certificate (Testing Only) ---> ❌ DISABLE
How it works:
- Uses ESP-IDF's built-in CA certificate bundle (
esp_crt_bundle_attach) - Automatically validates certificates from major CAs
- No additional certificate files needed
- Production-ready security
For local testing with self-signed certificates
- Enable self-signed certificates:
ESP-Hosted Slave OTA Configuration
└── OTA Method
└── HTTPS OTA Config
└── Use self-signed certificate (Testing Only) ---> ENABLE
-
Generate SSL certificates:
cd examples/host_performs_slave_ota/components/ota_https/test_server ./create_self_signed_certs.sh -
Start HTTPS server:
cd examples/host_performs_slave_ota/components/ota_https/test_server python3 create_https_server.py -
Copy slave firmware to server: Please build the slave firmware referring the slave example.
cp slave/build/network_adapter.bin examples/host_performs_slave_ota/components/ota_https/test_server/
-
Configure WiFi and URL:
idf.py menuconfig # ESP-Hosted Slave OTA Configuration → OTA Method → HTTPS OTA # Set HTTPS OTA URL to: https://<YOUR_IP>:8443/network_adapter.bin # Set WiFi SSID and password
-
Build and flash:
idf.py -p <host_serial_port> build flash monitor
- ESP32 connects to WiFi
- Downloads firmware over HTTPS with certificate verification
- Verifies image header during download
- Compares versions and only updates if different
ESP-Hosted Slave OTA Configuration
├── OTA Method
│ ├── HTTPS OTA (default)
│ │ │
│ │ ├── Wi-Fi Config
│ │ │ ├── SSID <--change-->
│ │ │ └── Password <--change-->
│ │ │
│ │ └── HTTPS OTA Config
│ │ ├── HTTPS OTA URL (https://...) <--change-->
│ │ └── Use self-signed certificate (Testing Only) <--ENABLE-to-test-using-self-certs>
│ │ └── Skip certificate Common Name check <--ENABLE-to-test-using-self-certs>
│ │
│ ├── LittleFS OTA
│ │ └── Delete OTA file from LittleFS once finished (y)
│ │
│ └── Partition OTA
│ └── Partition Label (for Partition Slave OTA) (slave_fw)
│
├── Host-Slave version compatibility check (y)
│
└── Skip OTA if slave firmware versions match (y)
I (1234) host_performs_slave_ota: ESP-Hosted initialized successfully
I (1235) host_performs_slave_ota: Using XXXX OTA method
I (1240) ota_XXXX: Current slave firmware version: 2.5.12
I (1242) ota_XXXX: New slave firmware version: 2.6.0
I (1245) ota_XXXX: Version differs - proceeding with OTA
I (5678) ota_XXXX: XXXX OTA completed successfully
I (5680) host_performs_slave_ota: OTA completed successfully
I (1240) ota_XXXX: Current slave firmware version: 2.6.0
I (1242) ota_XXXX: New slave firmware version: 2.6.0
W (1245) ota_XXXX: Versions match. Skipping OTA.
I (1246) host_performs_slave_ota: OTA not required
- "No .bin files found": Copy slave firmware to appropriate
slave_fw_bin/directory - "Partition not found": Check
partitions.csvincludes required partitions
- "Failed to initialize ESP-Hosted": Check hardware connections
- "OTA failed": Check version format mismatch, ensure slave firmware uses semantic versioning
- WiFi issues (HTTPS): Verify network credentials and connectivity
- SSL errors (HTTPS): Ensure certificate CN matches server IP address
- Slave firmware now uses semantic versioning (e.g.,
2.5.12) instead of git hashes - Host compares versions and skips OTA if versions match
- Check slave firmware version with
esp_hosted_get_coprocessor_fwversion()
- Older slave firmwares (<
2.15.12) exposed git commit asPROJECT_VER, instead ofX.Y.ZHosted slave firmware version. So, Comparison ofSlave firmware image versionVsSlave current FW versionwill always fail. So OTA will be triggered.
Note
This section is for reference only. The main focus of this example is ESP-Hosted Transport OTA above.
Note
ESP-Prog is only required if you want to flash firmware to the slave using the standard ESP Tools.
In following section, ESP32-P4-Function EV Board is considered as example, to showcase, how ESP-Prog is to be connected. Any ESP can be programmed with ESP-Prog, including Host as well.
The image below shows the board with an ESP-Prog connected to the header to communicate with the on-board ESP32-C6..
ESP32-P4-Function-EV-Board with ESP-Prog Connected to ESP32-C6
If you need to update the ESP-Hosted slave firmware on the on-board ESP32-C6 module using ESP-Prog, follow these steps:
- Check out the ESP-Hosted slave example project:
idf.py create-project-from-example "espressif/esp_hosted:slave"
- Set the target and start
Menuconfig:
idf.py set-target esp32c6
idf.py menuconfig-
Navigate and ensure SDIO is enabled. By default it should already be enabled.
Example Configuration └── Bus Config in between Host and Co-processor └── Transport layer └── Select "SDIO" -
Build the firmware:
idf.py build- Connect the Program Header on the ESP-Prog to the
PROG_C6header on the board. The connections are as follows:
| ESP-Prog | PROG_C6 | Notes |
|---|---|---|
| ESP_EN | EN | |
| ESP_TXD | TXD | |
| ESP_RXD | RXD | |
| VDD | - | Do not connect |
| GND | GND | |
| ESP_IO0 | IO0 |
- Flashing the firmware
The on-board ESP32-P4 controls the reset signal for the ESP32-C6. To prevent the P4 interfering with the C6 while flashing (by asserting the C6 Reset signal during the firmware download), set the P4 into Bootloader mode before flashing the firmware to the C6:
1. hold down the `BOOT` button on the board
2. press and release the `RST` button on the board
3. release the `BOOT` button
esptool.py -p <host_serial_port> --before default_reset --after no_reset runYou can now flash the firmware to the C6 (and monitor the console output):
idf.py -p <Serial Port> flash monitorYou can connect above ESP32-C6 (slave) GPIOs directly on ESP32-P4 (Host) GPIOs and use ESP-Serial-Flasher to flash the Slave firmware from Host.
Hardware Required: Host MCU UART connection (dedicated UART needed, cannot use ESP-Hosted bus)
Steps:
- Connect host UART to slave flashing pins
- Put host in bootloader mode (same as explained above)
- Use esp-serial-flasher library to flash over UART
