# Changelog All notable changes to the RelayX C++ Device SDK are documented here. ## [0.2.0] - 2026-06-18 ### Added - **Over-the-air firmware updates (`device.ota`).** A desired-state OTA client for ESP32. Two discovery paths feed one pipeline: a PUSH nudge (`firmware_update`, sent only when this rollout is the device's FIFO head) for immediacy, and an authoritative POLL (`job.poll`) on connect, every >=30 min, on manual `check()`, after every terminal status, and immediately before install. The SDK owns the download mechanics (presigned-URL fetch, streaming into the inactive A/B partition, SHA-256 verify, HTTP Range resume); the application owns install through four hooks: `on_download()`, `on_pre_install()`, `on_install()`, and `on_post_install()`. Reboot-spanning state (pending install, rollback report) is persisted in NVS, so the post-reboot verify/rollback sequence survives a power cycle. `check()` forces a discovery poll and `state()` returns a progress snapshot. - **Compile-time OTA configuration.** `RELAY_OTA_POLL_INTERVAL_MIN` (default 45, hard floor 30) tunes the poll cadence, and `RELAY_OTA_HTTP_TX_BUFFER` (floor 1024) sizes the request buffer for long presigned URLs. ### Changed - **OTA is opt-in and OFF by default.** Set `RELAY_ENABLE_OTA` to `1` to compile it in (the example does `#define RELAY_ENABLE_OTA 1` before including the SDK; a build-time compile definition works too). All OTA headers are `#if`-guarded so existing builds are unaffected. When enabled, the consumer must supply an OTA-capable partition table (`ota_0`/`ota_1`/`otadata`) and NVS. The component now always lists the OTA dependencies (`app_update`, `esp_http_client`, `nvs_flash`, `esp_app_format`) so `relay/ota.h` resolves even when the feature is compiled out. ### Notes - No breaking changes. With `RELAY_ENABLE_OTA` unset, the public API and runtime behaviour are identical to 0.1.7. ## [0.1.7] - 2026-06-09 ### Fixed - **TLS task stack overflow.** Raised the default stacks for the internal `relay_process` (4096 → 16384) and `relay_publisher` (4096 → 8192) tasks. The old 4096 default overflowed during the mbedTLS handshake under TLS, surfacing as a hardware "Stack protection fault" (`mcause 27` on RISC-V targets like the ESP32-C6/H2). Both are overridable via `RELAY_PROCESS_TASK_STACK_SIZE` / `RELAY_PUBLISH_TASK_STACK_SIZE`. - **Use-after-free when `process()` is driven from a second task.** `process()` now serializes itself with a try-lock, so calling it from application code in addition to the SDK's internal `relay_process` task can no longer race the reconnect path (which deletes/recreates the NATS client). Previously this caused a load-access fault (`mcause 5`) inside `connect_with_transport` under reconnect churn. - **Publisher stranding after a flaky reconnect.** The async publisher's connection gate (`BIT_CONNECTED`) is now kept in lockstep with the real connection state on every process tick. A desync could leave telemetry, logs, and events buffered indefinitely while RPC replies (a separate send path) kept working — i.e. "RPCs respond but nothing else publishes." That stranding is no longer possible. ### Notes - No API changes. You still do not need to call `device->process()` yourself — the SDK drives its own loop. Calling it remains safe but redundant.
idf.py add-dependency "relayx/device-cpp^0.2.0"