on_demand

# OTA API On-Demand Example

Updating when the application decides to, not when it boots.

The device connects to the network and then just runs, exposing a small console
on the serial port. An update happens only when you ask for one, through
`ota_api_update()` — the **blocking** half of the API, which returns an
`esp_err_t` to the caller instead of rebooting on its own.

That difference is the whole example:

| | [basic](../basic) (`ota_api_start_task`) | this example (`ota_api_update`) |
| --- | --- | --- |
| Trigger | at boot | console command, any time |
| On failure | task logs and exits | returns an error, device keeps running, retry allowed |
| Reboot | done by the component | done by the application, after its own cleanup |

## Console commands

```text
ota>  help
ota>  version
ota>  ota
ota>  ota https://192.168.0.3:8070/my_firmware.bin
```

- `ota` without an argument uses **Example Configuration -> default firmware
  upgrade url endpoint**.
- `ota <url>` overrides it at runtime — no rebuild, no `menuconfig`.
- `version` prints the running project name, version, build date and partition,
  which is how you confirm an update actually took effect.

The update runs on a dedicated worker task: the console REPL task's stack is
nowhere near enough for a TLS download. A second `ota` while one is in flight
is rejected rather than queued.

## Configuration

```bash
idf.py menuconfig
```

- **Example Connection Configuration** — Wi-Fi SSID/password or Ethernet.
- **Example Configuration -> default firmware upgrade url endpoint** — URL used
  by a bare `ota` command.
- **Example Configuration -> Skip server certificate CN fieldcheck** — leave
  off. The certificate generated by `ota_server.py` carries the server IP in
  `subjectAltName`, so validation succeeds without it.

The console is the standard UART REPL. On a board configured for USB Serial
JTAG instead, switch **Component config -> ESP System Settings -> Channel for
console output** back to UART, or the prompt will not appear.

## Local HTTPS server

```bash
python ../common/ota_server.py
```

Serves `examples/common/ota/` on port `8070`, prints the exact URL to paste
after `ota`, and generates the self-signed certificate itself — no `openssl`
required, on Windows or Linux. See
[examples/common/README.md](../common/README.md).

Rebuild and reflash whenever the script reports a newly generated certificate,
since it is baked into the binary at build time.

## Build and Flash

```bash
idf.py -p PORT flash monitor
```

(Replace `PORT` with the serial port name, e.g., `/dev/ttyUSB0` or `COM3`.
To exit the serial monitor, type `Ctrl-]`.)

## Expected session

```text
I (...) ota_on_demand: Console ready. Type 'help' for the command list, or 'ota' to update now
ota> version
project     : ota_api_on_demand_example
version     : 1
partition   : ota_0 at offset 0x00010000
ota> ota https://192.168.0.3:8070/hello_world.bin
Requesting update from https://192.168.0.3:8070/hello_world.bin
I (...) ota-api: Downloading update from https://192.168.0.3:8070/hello_world.bin
I (...) ota-api: Update written, new firmware boots on next restart
I (...) ota_on_demand: Update written, shutting the connection down before rebooting
```

Point `ota` at a URL that does not exist to see the other half — the error is
reported and the prompt comes back:

```text
ota> ota https://192.168.0.3:8070/nope.bin
E (...) ota_on_demand: Update failed (ESP_ERR_HTTP_CONNECT). Still running, type 'ota <url>' to retry
ota>
```

## Going further

[advanced](../advanced) builds on this one: same console-driven trigger, plus a
live percentage bar, `abort` to cancel a download in flight, a version check
that refuses firmware already installed, and a rollback you confirm by hand.

## Project Structure

```text
examples/on_demand/
├── CMakeLists.txt
├── sdkconfig.defaults          # OTA partition table + certificate bundle
├── sdkconfig.defaults.esp32h2
├── README.md
└── main/
    ├── CMakeLists.txt
    ├── idf_component.yml       # ota-api + protocol_examples_common deps
    ├── Kconfig.projbuild       # Example Configuration menu
    └── main.c
```

The HTTPS server, certificates and firmware images live in
[examples/common/](../common), shared with the other examples.

To create a project from this example, run:

idf.py create-project-from-example "pedroluisdionisiofraga/ota-api=0.2.0:on_demand"

or download archive (~6.92 KB)