|
| 1 | +# esp_cli Examples Plan |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Five examples covering the full API surface of `esp_cli`, `esp_cli_commands`, and `esp_linenoise` (as used by `esp_cli`). |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Example 1: `basic` — Basic REPL with Static Commands |
| 10 | + |
| 11 | +**Goal:** Minimal but complete setup showing the core lifecycle and the most common usage pattern. |
| 12 | + |
| 13 | +**API covered:** |
| 14 | + |
| 15 | +| Component | API / Feature | |
| 16 | +|---|---| |
| 17 | +| `esp_cli` | `esp_cli_create`, `esp_cli_start`, `esp_cli`, `esp_cli_stop`, `esp_cli_destroy` | |
| 18 | +| `esp_cli` | `CONFIG_ESP_CLI_HAS_QUIT_CMD` (built-in `quit` command) | |
| 19 | +| `esp_cli_commands` | `ESP_CLI_COMMAND_REGISTER` (static registration — linker section) | |
| 20 | +| `esp_cli_commands` | `esp_cli_commands_get_completion`, `esp_cli_commands_get_hint` (wired into linenoise callbacks) | |
| 21 | +| `esp_cli_commands` | Built-in `help` command (auto-registered, always available) | |
| 22 | +| `esp_cli_commands` | `cmd_args->write_func(cmd_args->out_fd, ...)` pattern for command output | |
| 23 | +| `esp_linenoise` | Instance creation/deletion, `completion_cb`, `hints_cb`, `free_hints_cb` | |
| 24 | +| I/O | UART/USB-CDC/USB-JTAG initialization boilerplate | |
| 25 | + |
| 26 | +**What the example does:** |
| 27 | + |
| 28 | +1. Initializes the console I/O (UART/CDC/JTAG depending on sdkconfig). |
| 29 | +2. Creates an `esp_linenoise` instance with tab-completion and hints callbacks wired to `esp_cli_commands` helpers. |
| 30 | +3. Registers 2–3 simple static commands via `ESP_CLI_COMMAND_REGISTER` (e.g. `hello`, `version`, `uptime`). |
| 31 | +4. Creates an `esp_cli` instance (no callbacks, no history persistence, no command set — accepts all commands). |
| 32 | +5. Runs `esp_cli()` in a dedicated FreeRTOS task. |
| 33 | +6. Calls `esp_cli_start()` from `app_main`. |
| 34 | +7. The user interacts: types commands, uses tab-completion, uses `help`, and types `quit` to exit. |
| 35 | +8. After `esp_cli()` returns, the task cleans up and deletes itself. |
| 36 | + |
| 37 | +**Key teaching points:** This is the "hello world" of esp_cli. Users learn the fundamental create → task → start → quit → destroy flow, and how the three components (`esp_linenoise`, `esp_cli_commands`, `esp_cli`) plug together. |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## Example 2: `lifecycle_hooks` — Lifecycle Callbacks and History Persistence |
| 42 | + |
| 43 | +**Goal:** Demonstrate all five callbacks and filesystem-based history persistence. |
| 44 | + |
| 45 | +**API covered:** |
| 46 | + |
| 47 | +| Component | API / Feature | |
| 48 | +|---|---| |
| 49 | +| `esp_cli` | `on_enter` callback (runs once when the REPL task enters the loop) | |
| 50 | +| `esp_cli` | `pre_executor` callback (runs before every command; can reject execution) | |
| 51 | +| `esp_cli` | `post_executor` callback (runs after every command; receives return values) | |
| 52 | +| `esp_cli` | `on_stop` callback (runs when `esp_cli_stop` is called) | |
| 53 | +| `esp_cli` | `on_exit` callback (runs when `esp_cli()` returns) | |
| 54 | +| `esp_cli` | `history_save_path` (automatic history save to filesystem after each command) | |
| 55 | +| `esp_linenoise` | `esp_linenoise_history_load` (restore history from file on startup) | |
| 56 | +| `esp_linenoise` | `esp_linenoise_set_prompt` (dynamic prompt change from `on_enter`) | |
| 57 | +| Filesystem | SPIFFS mount for persistent history storage | |
| 58 | + |
| 59 | +**What the example does:** |
| 60 | + |
| 61 | +1. Mounts a SPIFFS partition. |
| 62 | +2. Creates `esp_linenoise` and loads history from file if it exists (`esp_linenoise_history_load`). |
| 63 | +3. Configures `esp_cli` with all five callbacks: |
| 64 | + - **`on_enter`**: Logs "CLI session started", changes the prompt to include a session counter. |
| 65 | + - **`pre_executor`**: Logs every command line received; demonstrates rejecting a "forbidden" command by returning an error. |
| 66 | + - **`post_executor`**: Logs the command result (success/failure, return code); counts total commands executed. |
| 67 | + - **`on_stop`**: Logs "CLI stopping", performs any cleanup. |
| 68 | + - **`on_exit`**: Logs final statistics (number of commands executed), calls `esp_linenoise_history_save` one last time. |
| 69 | +4. Sets `history_save_path` to persist history across reboots. |
| 70 | +5. Registers a few static commands plus enables `quit`. |
| 71 | +6. Demonstrates that `pre_executor` returning an error code skips command execution (the user types a "blocked" command and sees it rejected). |
| 72 | + |
| 73 | +**Key teaching points:** Users learn how to hook into every stage of the REPL lifecycle for logging, validation, analytics, or access control. They also learn how to persist command history to flash. |
| 74 | + |
| 75 | +--- |
| 76 | + |
| 77 | +## Example 3: `command_management` — Dynamic Commands and Command Sets |
| 78 | + |
| 79 | +**Goal:** Show runtime command registration/unregistration and command set filtering. |
| 80 | + |
| 81 | +**API covered:** |
| 82 | + |
| 83 | +| Component | API / Feature | |
| 84 | +|---|---| |
| 85 | +| `esp_cli_commands` | `esp_cli_commands_register_cmd` (dynamic registration at runtime) | |
| 86 | +| `esp_cli_commands` | `esp_cli_commands_unregister_cmd` (dynamic unregistration) | |
| 87 | +| `esp_cli_commands` | `ESP_CLI_COMMANDS_CREATE_CMD_SET` with `ESP_CLI_COMMAND_FIELD_ACCESSOR(name)` | |
| 88 | +| `esp_cli_commands` | `ESP_CLI_COMMANDS_CREATE_CMD_SET` with `ESP_CLI_COMMAND_FIELD_ACCESSOR(group)` | |
| 89 | +| `esp_cli_commands` | `esp_cli_commands_concat_cmd_set` (merging two sets) | |
| 90 | +| `esp_cli_commands` | `esp_cli_commands_destroy_cmd_set` | |
| 91 | +| `esp_cli_commands` | `esp_cli_commands_find_command` (looking up a command by name) | |
| 92 | +| `esp_cli_commands` | `command_set_handle` in `esp_cli_config_t` (restricts visible commands per instance) | |
| 93 | +| `esp_cli_commands` | `hint_cb` and `glossary_cb` callbacks on command structs | |
| 94 | + |
| 95 | +**What the example does:** |
| 96 | + |
| 97 | +1. Statically registers several commands in two groups (e.g. group `"system"`: `reboot`, `info`; group `"network"`: `ping`, `ifconfig`). |
| 98 | +2. Creates a command set **by group** (`ESP_CLI_COMMAND_FIELD_ACCESSOR(group)`) to include only `"system"` commands. |
| 99 | +3. Creates a second command set **by name** for a specific whitelist. |
| 100 | +4. Concatenates both sets with `esp_cli_commands_concat_cmd_set`. |
| 101 | +5. Creates the `esp_cli` instance with this combined command set — only allowed commands are visible/executable. |
| 102 | +6. Registers a meta-command `plugin` that **dynamically registers** a new command (`custom_cmd`) at runtime using `esp_cli_commands_register_cmd`, and an `unplug` command that removes it with `esp_cli_commands_unregister_cmd`. |
| 103 | +7. Shows `esp_cli_commands_find_command` to check if a command exists before attempting operations. |
| 104 | +8. Demonstrates `hint_cb` and `glossary_cb` returning dynamic strings for dynamically registered commands. |
| 105 | + |
| 106 | +**Key teaching points:** Users learn how to build a modular CLI where commands come and go at runtime (plugin model), and how to restrict command visibility per CLI instance using command sets and group-based filtering. |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## Example 4: `multi_instance` — Multiple CLI Instances on Different Interfaces |
| 111 | + |
| 112 | +**Goal:** Run two independent CLI instances simultaneously, each on a different I/O interface, with separate command sets and custom read/write. |
| 113 | + |
| 114 | +**API covered:** |
| 115 | + |
| 116 | +| Component | API / Feature | |
| 117 | +|---|---| |
| 118 | +| `esp_cli` | Two `esp_cli_handle_t` instances running concurrently in separate tasks | |
| 119 | +| `esp_cli` | `esp_cli_stop` called from a different task (cross-task stop) | |
| 120 | +| `esp_cli` | `on_stop` callback to unblock a custom reader | |
| 121 | +| `esp_linenoise` | `read_bytes_cb` / `write_bytes_cb` (custom I/O functions) | |
| 122 | +| `esp_linenoise` | Two `esp_linenoise_handle_t` instances with different `in_fd` / `out_fd` | |
| 123 | +| `esp_linenoise` | Different prompts per instance (`"uart> "` vs `"jtag> "`) | |
| 124 | +| `esp_linenoise` | `esp_linenoise_get_out_fd` / `esp_linenoise_get_in_fd` / `esp_linenoise_get_read` / `esp_linenoise_get_write` | |
| 125 | +| `esp_cli_commands` | Per-instance command sets (e.g. admin commands only on JTAG) | |
| 126 | +| `esp_cli_commands` | `cmd_args->write_func(cmd_args->out_fd, ...)` — commands correctly output to the right interface | |
| 127 | + |
| 128 | +**What the example does:** |
| 129 | + |
| 130 | +1. Initializes two I/O interfaces (UART + USB Serial JTAG, or UART + TCP socket, depending on available hardware). |
| 131 | +2. Creates two `esp_linenoise` instances, each bound to a different set of file descriptors. |
| 132 | +3. One instance uses default read/write; the other demonstrates **custom `read_bytes_cb` and `write_bytes_cb`** (e.g. wrapping a socket or adding logging). |
| 133 | +4. Creates two separate `esp_cli` instances, each with a different command set: |
| 134 | + - Instance 1 ("user"): only basic commands (`help`, `status`, `quit`). |
| 135 | + - Instance 2 ("admin"): all commands including privileged ones (`reboot`, `config`). |
| 136 | +5. Runs each `esp_cli()` in its own FreeRTOS task. |
| 137 | +6. Demonstrates that a command in instance 1 can trigger `esp_cli_stop` on instance 2 (cross-task stop), showing the thread-safe stop mechanism. |
| 138 | +7. The `on_stop` callback for the custom-read instance demonstrates how to unblock the custom reader (e.g. by closing a socket or signaling a semaphore). |
| 139 | + |
| 140 | +**Key teaching points:** Users learn how to run multiple independent CLIs (e.g. one for local debug via UART, one for remote access), how custom I/O works end-to-end, and how `esp_cli_stop` + `on_stop` cooperate for clean shutdown with custom readers. |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +## Example 5: `advanced_arguments` — Rich Argument Parsing and Configuration |
| 145 | + |
| 146 | +**Goal:** Demonstrate commands with complex argument parsing (both manual and via `argtable3`), and global configuration tuning. |
| 147 | + |
| 148 | +**API covered:** |
| 149 | + |
| 150 | +| Component | API / Feature | |
| 151 | +|---|---| |
| 152 | +| `esp_cli_commands` | `esp_cli_commands_update_config` (customize `hint_color`, `hint_bold`, `max_cmdline_args`, `heap_caps_used`) | |
| 153 | +| `esp_cli_commands` | Manual `argc`/`argv` parsing in a command function | |
| 154 | +| `esp_cli_commands` | `esp_cli_commands_split_argv` (utility for splitting command lines) | |
| 155 | +| `esp_cli_commands` | Integration with `argtable3` for structured argument parsing | |
| 156 | +| `esp_cli_commands` | `hint_cb` returning auto-generated syntax from `arg_print_syntax_ds` | |
| 157 | +| `esp_cli_commands` | `glossary_cb` returning auto-generated help from `arg_print_glossary_ds` | |
| 158 | +| `esp_cli_commands` | `func_ctx` — passing context to command functions | |
| 159 | +| `esp_cli` | Full REPL with completion, hints showing argument usage | |
| 160 | + |
| 161 | +**What the example does:** |
| 162 | + |
| 163 | +1. Calls `esp_cli_commands_update_config` to set hint color to cyan, enable bold hints, and increase `max_cmdline_args` to 16. |
| 164 | +2. Registers a **simple command** (`echo`) that manually processes `argc`/`argv` to echo back all arguments. |
| 165 | +3. Registers a **calculator command** (`calc`) that manually parses exactly 3 arguments: `<operand> <operator> <operand>` with validation and error messages. |
| 166 | +4. Registers an **argtable3-based command** (`wifi`) with structured options: `--ssid <name>`, `--password <pass>`, `--channel <n>` (optional), `--hidden` (flag). Uses `arg_print_syntax_ds` for auto-generated hints and `arg_print_glossary_ds` for auto-generated glossary. |
| 167 | +5. Registers another **argtable3-based command** (`log`) with `--level <debug|info|warn|error>` and `--tag <component>` (optional, repeating). |
| 168 | +6. Demonstrates `func_ctx` by passing a shared application state struct to a command, allowing it to read/modify global state. |
| 169 | +7. Shows how `help wifi` and `help -v 1 wifi` produce rich, auto-generated documentation. |
| 170 | + |
| 171 | +**Key teaching points:** Users learn two approaches to argument parsing (manual vs. argtable3), how to auto-generate hints and glossary entries, how to pass application state to commands via `func_ctx`, and how to tune global configuration. |
| 172 | + |
| 173 | +--- |
| 174 | + |
| 175 | +## Coverage Summary Matrix |
| 176 | + |
| 177 | +| Feature / API | Ex.1 Basic | Ex.2 Hooks | Ex.3 CmdMgmt | Ex.4 Multi | Ex.5 Args | |
| 178 | +|---|:---:|:---:|:---:|:---:|:---:| |
| 179 | +| `esp_cli_create/destroy/start/stop/esp_cli()` | **X** | **X** | **X** | **X** | **X** | |
| 180 | +| `ESP_CLI_COMMAND_REGISTER` (static) | **X** | **X** | **X** | **X** | **X** | |
| 181 | +| `CONFIG_ESP_CLI_HAS_QUIT_CMD` | **X** | **X** | | | | |
| 182 | +| Tab completion + hints integration | **X** | | | | **X** | |
| 183 | +| `on_enter` callback | | **X** | | | | |
| 184 | +| `pre_executor` callback | | **X** | | | | |
| 185 | +| `post_executor` callback | | **X** | | | | |
| 186 | +| `on_stop` callback | | **X** | | **X** | | |
| 187 | +| `on_exit` callback | | **X** | | | | |
| 188 | +| `history_save_path` + SPIFFS | | **X** | | | | |
| 189 | +| `esp_linenoise_history_load` | | **X** | | | | |
| 190 | +| `register_cmd` / `unregister_cmd` (dynamic) | | | **X** | | | |
| 191 | +| Command sets (by name, by group, concat) | | | **X** | **X** | | |
| 192 | +| `find_command` | | | **X** | | | |
| 193 | +| Multiple instances | | | | **X** | | |
| 194 | +| Custom `read_bytes_cb` / `write_bytes_cb` | | | | **X** | | |
| 195 | +| Cross-task `esp_cli_stop` | | | | **X** | | |
| 196 | +| `esp_cli_commands_update_config` | | | | | **X** | |
| 197 | +| Manual `argc`/`argv` parsing | | | | | **X** | |
| 198 | +| `argtable3` integration | | | | | **X** | |
| 199 | +| `hint_cb` / `glossary_cb` on commands | | | **X** | | **X** | |
| 200 | +| `func_ctx` (command context) | | | | | **X** | |
| 201 | +| `esp_cli_commands_split_argv` | | | | | **X** | |
| 202 | +| `cmd_args->write_func/out_fd` | **X** | | | **X** | **X** | |
0 commit comments