persistent_commands

Example of the component adhuldas/edgecommand v0.1.1
# EdgeCommand: Persistent Commands Example

Demonstrates that command records — and therefore deduplication — survive
an engine restart, because they are backed by NVS rather than RAM.

## Purpose

- Submit a command and let it complete.
- Tear the engine down completely (`edgecommand_deinit()`), which frees the
  entire in-memory slot table.
- Bring up a brand-new engine instance against the same NVS namespace,
  standing in for a device reboot.
- Show that `edgecommand_get_status()` still finds the finished command
  (loaded from NVS, not RAM) and that re-submitting the same command ID is
  rejected with `EDGECOMMAND_ERR_DUPLICATE`.

A real power-cycle reboot demonstrates the same persistence even more
directly — nothing here depends on staying in the same process, this
in-process teardown/recreate is just easier to observe in a single log
capture. To see it across an actual power cycle instead, submit the
command, reset the board, and re-run only the second half of `main.c`
(query + re-submit the same ID).

## Requirements

- ESP-IDF v5.0 or newer.
- Any ESP-IDF-supported target.
- No external hardware required.

## Build and flash

```sh
cd examples/persistent_commands
idf.py set-target esp32
idf.py build flash monitor
```

## Expected output

```
I (320) persistent_example: recording telemetry for command persist-demo-0001
I (420) persistent_example: before restart: command persist-demo-0001 finished as COMPLETED
I (425) persistent_example: --- simulating restart: creating a fresh engine instance ---
I (430) persistent_example: after restart: edgecommand_get_status(persist-demo-0001) -> ESP_OK, state=COMPLETED
I (435) persistent_example: after restart: re-submitting persist-demo-0001 -> EDGECOMMAND_ERR_DUPLICATE (expected EDGECOMMAND_ERR_DUPLICATE)
I (540) persistent_example: recording telemetry for command persist-demo-0002
I (640) persistent_example: new command persist-demo-0002 finished as COMPLETED
```

## What's happening

- `edgecommand_submit()` persists a record to NVS at every lifecycle
  transition (`RECEIVED` → `PERSISTED` → `PENDING` → ... → terminal), not
  only at the end. See `RELIABILITY.md` for exactly what's durable at each
  point and the crash window that still exists around the physical action a
  handler performs.
- `edgecommand_deinit()` frees the in-memory slot table but never touches
  already-persisted NVS records — persistence is intentionally decoupled
  from process/engine lifetime.
- The second `edgecommand_init()` call creates a fresh in-memory slot table
  (empty) but reuses the same underlying NVS namespace (`"edgecmd"` by
  default), so `edgecommand_dedup_check()` still finds the old record via
  `storage->load()` even though it's absent from the new engine's RAM.
- See `examples/recovery` for what happens when a restart interrupts a
  command that was still `EXECUTING` or `PENDING`, which is a different
  (and more consequential) scenario than the already-`COMPLETED` command
  shown here.

To create a project from this example, run:

idf.py create-project-from-example "adhuldas/edgecommand=0.1.1:persistent_commands"

or download archive (~3.62 KB)