1. Today’s topic

The architecture of safe OTA:

text
update command
→ check conditions
→ obtain manifest
→ download into the inactive OTA-slot
→ verify the image
→ switch boot partition
→ reboot
→ quick self-test
→ confirm the new version or rollback

The central idea: OTA finishes not when the file has been written to Flash, but when the new firmware has booted, passed self-diagnostics, and retained the ability to perform the next update.

2. Why this matters

The EC25 LTE connection can break at any moment:

text
- coverage was lost;
- EC25 re-registered;
- PDP context closed;
- the operator dropped TCP;
- supply voltage sagged during transmission.

An incomplete image must not become bootable. After an update, safe outputs, the input path, watchdog, fault manager, and local diagnostics must continue working. Losing LTE/MQTT after reboot is not always a reason for rollback: the external network may be temporarily unavailable.

3. Theory

A/B OTA partitioning

Typical structure:

text
bootloader
partition table
NVS
otadata
ota_0
ota_1
coredump
storage

While ota_0 is running, the new image is written into ota_1. Current firmware remains untouched. After verification, otadata changes. Example for 4 MiB Flash:

markdown
# Name,      Type, SubType,  Offset,   Size
nvs,         data, nvs,      0x9000,   0x6000
otadata,     data, ota,      0xF000,   0x2000
phy_init,    data, phy,      0x11000,  0x1000
coredump,    data, coredump, 0x12000,  0x10000
nvs_keys,    data, nvs_keys, 0x22000,  0x1000
ota_0,       app,  ota_0,    0x30000,  0x1D0000
ota_1,       app,  ota_1,    0x200000, 0x1D0000
storage,     data, fat,      0x3D0000, 0x20000

The image must comfortably fit its slot; preferably leave 10-15% spare capacity.

Rollback

Enable:

text
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y

States:

text
ESP_OTA_IMG_NEW
→ first boot
→ ESP_OTA_IMG_PENDING_VERIFY
→ self-test
   ├─ success → ESP_OTA_IMG_VALID
   └─ failure  → ESP_OTA_IMG_INVALID → previous firmware

Self-test after OTA

Check quickly and locally:

text
- config load/migration/validation;
- safe outputs;
- input service;
- health supervisor;
- fault manager;
- watchdog;
- critical tasks were created.

Do not wait several minutes for an MQTT broker or EC25 registration. The external network may be unavailable while the firmware still works. Skeleton:

c
static bool ota_critical_self_test(void)
{
    if (!config_load_migrate_validate()) return false;
    if (!safe_outputs_apply()) return false;
    if (!input_service_start_and_self_test()) return false;
    if (!health_supervisor_start()) return false;
    if (!fault_manager_start()) return false;
    if (!modem_transport_init()) {
        system_enter_degraded_mode(DEGRADED_NO_MODEM);
    }
    return true;
}

Manifest

A URL alone is insufficient. A manifest is needed:

c
{
  "schema": 1,
  "project": "traffic-controller",
  "version": "1.8.0",
  "build_number": 184,
  "chip": "esp32",
  "hw_rev_min": 2,
  "hw_rev_max": 4,
  "image_size": 1327104,
  "sha256": "8a7c...",
  "url": "https://fw.example.com/controller/1.8.0/app.bin"
}

Check project, chip, hardware revision, size, downgrade policy, SHA-256, and TLS policy.

OTA through EC25

Option A — PPPoS: ESP32 establishes PPP through EC25 UART, then uses normal HTTPS OTA. Option B — EC25 HTTP AT: the modem obtains the file, ESP32 reads it in chunks, and writes it through esp_ota_write(). Binary data, URC, lost bytes, UART overflow, and writing a block twice are particularly dangerous here. Prefer MQTT for the command/trigger and HTTPS for transferring the image itself.

4. Common mistakes

  • Confirming firmware at the start of app_main().
  • Making MQTT a mandatory self-test.
  • Using HTTPS without certificate verification.
  • Updating the partition table remotely without a separate recovery architecture.
  • Not checking the hardware revision.
  • Writing progress to NVS after every block.
  • Enabling security anti-rollback too early.

5. Practical assignment

  1. Create partitions_ota_4mb.csv.
  2. Enable rollback.
  3. Implement ota_confirm_if_pending().
  4. Add ota status.
  5. Create a HIL rollback test: firmware B runs once, self-test returns false, and A boots after reboot.

CLI example:

text
ota status:
state=DOWNLOAD
running=ota_0
target=ota_1
current_version=1.7.4
target_version=1.8.0
written=786432
total=1327104
progress=59%
transport=EC25_PPP
last_error=OK

6. What to try next

  • Power-cut tests at 10%, 50%, and 99% of the download.
  • LTE disconnect during download.
  • Incorrect manifest/hash/project/hw_rev.
  • Crash before OTA confirmation.
  • MQTT unavailable while the main function is healthy.

Exercise

A new image boots correctly, but the MQTT broker is unavailable. Define a confirmation decision that distinguishes local firmware health from an external network outage.

Self-check criteria: List local checks and explain why external connectivity is a separate fault domain. Do not confirm before local self-test.

Show the supplied answer

Use the local self-test to validate configuration, safe outputs, the input service, supervisor, fault manager, watchdog, and critical tasks. Do not require external MQTT connectivity merely to confirm a locally healthy image; report the network outage separately.

Exercise

Design a power-loss OTA test that proves an incomplete image cannot replace the running firmware.

Self-check criteria: Observe both image completeness and boot selection. A download progress log alone does not prove safe recovery.

Show the supplied answer

Interrupt download into the inactive slot, reboot, and verify that the previously valid firmware boots. Record the target slot, written size, verification outcome, and boot selection; then test a fully written image that fails local self-test and rolls back.