Skip to content

Commit 193724f

Browse files
committed
feat(esp_cli): Add examples to the component
1 parent 59698ff commit 193724f

31 files changed

Lines changed: 1566 additions & 1 deletion
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+
)
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*
8+
* esp_cli Advanced Arguments Example
9+
*
10+
* This example demonstrates rich argument parsing approaches and global
11+
* configuration tuning:
12+
*
13+
* Global configuration:
14+
* - esp_cli_commands_update_config() to set hint color, bold, max args
15+
*
16+
* Manual argc/argv parsing:
17+
* - "echo" command: echoes all arguments back
18+
* - "calc" command: <operand> <operator> <operand> with validation
19+
*
20+
* argtable3 integration:
21+
* - "wifi" command: --ssid <name> --password <pass> [--channel <n>] [--hidden]
22+
* Uses arg_print_syntax_ds() for auto-generated hints
23+
* Uses arg_print_glossary_ds() for auto-generated glossary
24+
* - "log" command: --level <debug|info|warn|error> [--tag <component>]
25+
*
26+
* func_ctx (command context):
27+
* - A shared application state struct is passed via func_ctx to the
28+
* "wifi" command, allowing it to read/modify global state
29+
*
30+
* Utility:
31+
* - esp_cli_commands_split_argv() demonstrated for parsing sub-strings
32+
*
33+
* Tab-completion and hints:
34+
* - Completion and hints callbacks wired to show argument usage
35+
* - "help wifi" and "help -v 1 wifi" produce auto-generated docs
36+
*/
37+
38+
#include <stdio.h>
39+
#include <stdlib.h>
40+
#include <string.h>
41+
#include <unistd.h>
42+
#include "freertos/FreeRTOS.h"
43+
#include "freertos/task.h"
44+
#include "esp_log.h"
45+
#include "esp_err.h"
46+
#include "esp_stdio.h"
47+
#include "esp_cli.h"
48+
#include "esp_cli_commands.h"
49+
#include "esp_linenoise.h"
50+
#include "argtable3/argtable3.h"
51+
#include "command_utils.h"
52+
53+
static const char *TAG = "advanced_arguments_example";
54+
55+
#define EXAMPLE_MAX_CMD_LINE_LENGTH 128
56+
#define EXAMPLE_MAX_ARGS 16
57+
58+
typedef struct {
59+
struct arg_str *arg1;
60+
struct arg_str *arg2;
61+
struct arg_end *end;
62+
} echo_args_t;
63+
64+
typedef struct {
65+
struct arg_str *operator;
66+
struct arg_str *operand1;
67+
struct arg_str *operand2;
68+
struct arg_end *end;
69+
} calc_args_t;
70+
71+
typedef enum cmd_type {
72+
CMD_ECHO = 0,
73+
CMD_CALC,
74+
CMD_UNKNOWN
75+
} cmd_type_e;
76+
77+
static const char cmd_echo_help[] = "Echo all arguments back to the console";
78+
static const char cmd_calc_help[] = "Simple integer calculator";
79+
80+
static echo_args_t s_echo_args;
81+
static calc_args_t s_calc_args;
82+
83+
static int cmd_echo_func(void *context, esp_cli_commands_exec_arg_t *cmd_args, int argc, char **argv)
84+
{
85+
(void)context;
86+
if (!argv || argc < 2) {
87+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "Usage: echo [args...]\n");
88+
return -1;
89+
}
90+
for (int i = 1; i < argc; i++) {
91+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "%s", argv[i]);
92+
if (i < argc - 1) {
93+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, " ");
94+
}
95+
}
96+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "\n");
97+
return 0;
98+
}
99+
100+
static int cmd_calc_func(void *context, esp_cli_commands_exec_arg_t *cmd_args, int argc, char **argv)
101+
{
102+
(void)context;
103+
if (argc != 4) {
104+
int color = 0;
105+
bool bold = false;
106+
const char *hint = esp_cli_commands_get_hint(NULL, "math_op", &color, &bold);
107+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "Usage: math_op %s\n", hint ? hint : "<add|sub|mul|div> <a> <b>");
108+
return -1;
109+
}
110+
const char *op = argv[1];
111+
int a = atoi(argv[2]);
112+
int b = atoi(argv[3]);
113+
int result = 0;
114+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "Performing operation: %s %d %d\n", op, a, b);
115+
if (strcmp(op, "add") == 0) {
116+
result = a + b;
117+
} else if (strcmp(op, "sub") == 0) {
118+
result = a - b;
119+
} else if (strcmp(op, "mul") == 0) {
120+
result = a * b;
121+
} else if (strcmp(op, "div") == 0) {
122+
if (b == 0) {
123+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "Error: Division by zero\n");
124+
return -2;
125+
}
126+
result = a / b;
127+
} else {
128+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "Unknown operation: %s\n", op);
129+
return -3;
130+
}
131+
WRITE_FN(cmd_args->write_func, cmd_args->out_fd, "Result: %d\n", result);
132+
return 0;
133+
}
134+
135+
static void init_command_args(void)
136+
{
137+
s_echo_args.arg1 = arg_str0(NULL, NULL, "<arg1>", "First argument");
138+
s_echo_args.arg2 = arg_str0(NULL, NULL, "<arg2>", "Second argument");
139+
s_echo_args.end = arg_end(2);
140+
141+
s_calc_args.operator = arg_str1(NULL, NULL, "<operator>", "Operator (+, -, *, /)");
142+
s_calc_args.operand1 = arg_str1(NULL, NULL, "<operand1>", "First operand");
143+
s_calc_args.operand2 = arg_str1(NULL, NULL, "<operand2>", "Second operand");
144+
s_calc_args.end = arg_end(3);
145+
}
146+
147+
static void *get_args_from_cmd_type(cmd_type_e cmd_type)
148+
{
149+
switch (cmd_type) {
150+
case CMD_ECHO:
151+
return &s_echo_args;
152+
case CMD_CALC:
153+
return &s_calc_args;
154+
default:
155+
return NULL;
156+
}
157+
}
158+
159+
static const char *cmd_generic_hint_cb(void *context)
160+
{
161+
cmd_type_e cmd = (cmd_type_e)context;
162+
void *args = get_args_from_cmd_type(cmd);
163+
if (!args) {
164+
return NULL;
165+
}
166+
167+
arg_dstr_t ds = arg_dstr_create();
168+
arg_print_syntax_ds(ds, args, NULL);
169+
const char *hint_str = strdup(arg_dstr_cstr(ds));
170+
arg_dstr_destroy(ds);
171+
172+
return hint_str;
173+
}
174+
175+
static const char *cmd_generic_glossary_cb(void *context)
176+
{
177+
cmd_type_e cmd = (cmd_type_e)context;
178+
void *args = get_args_from_cmd_type(cmd);
179+
if (!args) {
180+
return NULL;
181+
}
182+
183+
arg_dstr_t ds = arg_dstr_create();
184+
arg_print_glossary_ds(ds, args, NULL);
185+
const char *glossary_str = strdup(arg_dstr_cstr(ds));
186+
arg_dstr_destroy(ds);
187+
188+
return glossary_str;
189+
}
190+
191+
ESP_CLI_COMMAND_REGISTER(echo, advanced_args, cmd_echo_help, cmd_echo_func,
192+
(void *)CMD_ECHO, cmd_generic_hint_cb, cmd_generic_glossary_cb);
193+
194+
ESP_CLI_COMMAND_REGISTER(calc, advanced_args, cmd_calc_help, cmd_calc_func,
195+
(void *)CMD_CALC, cmd_generic_hint_cb, cmd_generic_glossary_cb);
196+
197+
static void example_completion_cb(const char *str, void *cb_ctx, esp_linenoise_completion_cb_t cb)
198+
{
199+
esp_cli_commands_get_completion(NULL, str, cb_ctx, cb);
200+
}
201+
202+
static char *example_hints_cb(const char *str, int *color, int *bold)
203+
{
204+
return (char *)esp_cli_commands_get_hint(NULL, str, color, (bool *)bold);
205+
}
206+
207+
void cli_task(void *args)
208+
{
209+
esp_cli_handle_t esp_cli_hdl = (esp_cli_handle_t)args;
210+
if (esp_cli_hdl) {
211+
esp_cli(esp_cli_hdl);
212+
}
213+
214+
ESP_LOGI(TAG, "Returned from esp_cli repl\n");
215+
vTaskDelete(NULL);
216+
}
217+
218+
void app_main(void)
219+
{
220+
/* configure the IO used by the esp_cli instance.
221+
* In the scope of this example, we will just use the
222+
* default UART (CONFIG_ESP_CONSOLE_UART_DEFAULT)
223+
* and let esp_stdio configure it accordingly */
224+
ESP_ERROR_CHECK(esp_stdio_install_io_driver());
225+
226+
/* init the argtable structures of the registered commands */
227+
init_command_args();
228+
229+
/* update the esp_cli_commands configuration if the default config
230+
* is not suitable */
231+
esp_cli_commands_config_t cmd_config = {
232+
.hint_color = 36,
233+
.hint_bold = true,
234+
.max_cmdline_args = EXAMPLE_MAX_ARGS,
235+
.max_cmdline_length = EXAMPLE_MAX_CMD_LINE_LENGTH,
236+
.heap_caps_used = MALLOC_CAP_DEFAULT,
237+
};
238+
ESP_ERROR_CHECK(esp_cli_commands_update_config(&cmd_config));
239+
240+
/* create the esp_linenoise instance that will be used by the esp_cli
241+
* instance. Since the IO driver used is the default UART, we don't have
242+
* to specify in_fd and out_fd. They will be set to fileno(stdin) and fileno(stdout)
243+
* which will redirect the default read and write call with those FDs to the
244+
* UART driver read and write functions */
245+
esp_linenoise_handle_t esp_linenoise_hdl = NULL;
246+
esp_linenoise_config_t esp_linenoise_config;
247+
esp_linenoise_get_instance_config_default(&esp_linenoise_config);
248+
esp_linenoise_config.completion_cb = example_completion_cb;
249+
esp_linenoise_config.hints_cb = example_hints_cb;
250+
ESP_ERROR_CHECK(esp_linenoise_create_instance(&esp_linenoise_config, &esp_linenoise_hdl));
251+
if (!esp_linenoise_hdl) {
252+
ESP_LOGE(TAG, "Failed to create esp_linenoise instance\n");
253+
return;
254+
}
255+
256+
/* create an esp_cli instance */
257+
esp_cli_handle_t esp_cli_hdl = NULL;
258+
esp_cli_config_t esp_cli_config = {
259+
esp_linenoise_hdl,
260+
NULL, /* this example does not require a command set handle to be specified */
261+
EXAMPLE_MAX_CMD_LINE_LENGTH,
262+
NULL, /* this example does not require a file path to store the history */
263+
{ NULL, NULL }, /* this example does not require this callback to be implemented */
264+
{ NULL, NULL }, /* this example does not require this callback to be implemented */
265+
{ NULL, NULL }, /* this example does not require this callback to be implemented */
266+
{ NULL, NULL }, /* this example does not require this callback to be implemented */
267+
{ NULL, NULL }, /* this example does not require this callback to be implemented */
268+
};
269+
ESP_ERROR_CHECK(esp_cli_create(&esp_cli_config, &esp_cli_hdl));
270+
271+
xTaskCreate(cli_task, "cli_task", 4096, esp_cli_hdl, 5, NULL);
272+
273+
/* start the CLI repl loop */
274+
ESP_ERROR_CHECK(esp_cli_start(esp_cli_hdl));
275+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies:
2+
espressif/esp_cli: "*"
3+
espressif/argtable3: "*"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CONFIG_ESP_CLI_HAS_QUIT_CMD=y
2+
CONFIG_ESP_TASK_WDT_EN=n
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(basic)
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 esp_stdio)
2+
3+
idf_component_register(
4+
SRCS "basic_main.c"
5+
INCLUDE_DIRS "." "../../utils"
6+
PRIV_REQUIRES ${priv_requires}
7+
)

0 commit comments

Comments
 (0)