Skip to content

Commit 2f78d6c

Browse files
committed
feat(esp_cli): Add examples to the component
1 parent a6b5793 commit 2f78d6c

32 files changed

Lines changed: 1705 additions & 1 deletion

esp_cli/examples/EXAMPLES_PLAN.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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** |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
cmake_minimum_required(VERSION 3.22)
2+
3+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
4+
set(COMPONENTS main)
5+
project(advanced_arguments)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
set(priv_requires esp_cli esp_cli_commands esp_linenoise argtable3 esp_stdio)
2+
3+
idf_component_register(
4+
SRCS "advanced_arguments_main.c"
5+
INCLUDE_DIRS "." "../../utils"
6+
PRIV_REQUIRES ${priv_requires}
7+
)

0 commit comments

Comments
 (0)