# ESP CLI Service

[English]

## 简介

CLI Service 是 ESP Service 的子类，把 ESP-IDF 控制台的串口交互嵌入服务生命周期。它提供两层命令：内置静态系统命令，以及由服务管理器驱动的动态服务命令与工具命令。通过串口终端即可完成交互式调试与运行时控制。

备注

需要 ESP-IDF >= 5.4。

## 功能清单

- UART REPL 集成：`esp_console` REPL 直接嵌入服务生命周期，调用 `esp_service_start()` 时启动，停止/销毁时关闭

- 内置系统命令：自动注册 `sys_heap`、`sys_chip`、`sys_uptime`、`sys_reboot`

- 静态命令：通过 `esp_cli_service_register_static_command()` 在 REPL 启动前后随时注册任意 `esp_console_cmd_t`

- 动态 `svc` 命令：从终端列出、查看和控制（`start` / `stop` / `pause` / `resume`）已跟踪的服务

- 动态 `tool` 命令：枚举并调用 `esp_service_manager` 暴露的 JSON Schema 工具，支持 `key=value` 和 `--json` 两种参数传入方式

- 所有公开 API 由内部互斥锁保护，可从任意任务安全调用

## 技术拆解

### 命令体系

REPL 上挂载的命令分为三类：内置系统命令始终可用；应用通过 `esp_cli_service_register_static_command()` 注册的静态命令；以及在绑定 `esp_service_manager` 后才可用的 `svc` / `tool` 动态命令。调用 `esp_service_start()` 后 REPL 任务被创建，所有待注册的静态命令随即注册到 `esp_console`。

            flowchart TD
        UART[UART 终端] --> REPL[esp_console REPL]
        REPL --> Sys["内置 sys_* 命令"]
        REPL --> Static["静态命令"]
        REPL --> Dyn["动态命令（需绑定 manager）"]
        Dyn --> Svc["svc 命令：已跟踪服务 / manager"]
        Dyn --> Tool["tool 命令：manager 工具注册表"]

    esp_cli_service_config_t cfg = ESP_CLI_SERVICE_CONFIG_DEFAULT();
    cfg.prompt = "app>";

    esp_cli_service_t *cli = NULL;
    esp_cli_service_create(&cfg, &cli);

    esp_cli_service_track_service(cli, (esp_service_t *)my_svc, "audio");

    const esp_console_cmd_t my_cmd = {
        .command = "version",
        .help    = "Print firmware version",
        .func    = cmd_version,
    };
    esp_cli_service_register_static_command(cli, &my_cmd);

    esp_service_start((esp_service_t *)cli);

### svc 与 tool 命令

`svc` 命令（`list` / `info` / `start` / `stop` / `pause` / `resume`）优先展示通过 `esp_cli_service_track_service()` 显式跟踪的服务，其次是绑定的 `esp_service_manager` 中登记的服务。`tool` 命令（`list` / `info` / `call`）枚举管理器暴露的 JSON Schema 工具；`tool call` 会按工具的 `inputSchema` 把 `key=value` 键值对序列化成 JSON 对象，支持 `string`、`integer`、`number`、`boolean` 类型，遇到 `array` 或 `object` 等复杂类型时改用 `--json` 传入原始 JSON 参数。管理器通过配置中的 `manager` 字段或运行期调用 `esp_cli_service_bind_manager()` 绑定，两种动态命令都依赖这一步。

## 应用示例

- `components/esp_cli_service/examples/` 提供独立示例，执行 `idf.py set-target <chip>` 后 `idf.py build flash monitor` 即可上板体验。

## FAQ

Q1：串口终端上没有出现提示符怎么办？

先确认已调用 `esp_service_start()`（REPL 任务只在启动时创建），再检查终端模拟器选择的 UART 端口和波特率是否匹配 IDF menuconfig 中配置的 console UART；如果注册的命令分配了较多数据，默认 4096 字节的 `task_stack` 未必够用，需要适当增大。

Q2：为什么 ``svc`` / ``tool`` 命令提示 “manager not bound”？

这两类动态命令都需要先绑定 `esp_service_manager`：在配置中设置 `cfg.manager`，或在运行期调用 `esp_cli_service_bind_manager()` 之后再使用。

## API 参考

### Header File

- components/esp_cli_service/include/esp_cli_service.h

### Functions

esp_err_t esp_cli_service_create(const esp_cli_service_config_t *cfg, esp_cli_service_t **out)

  Create a CLI service handle (UART REPL backed by esp_console)

  备注

  Not ISR-safe. Not thread-safe for concurrent creation of the same logical instance; typical use is from one init task. May allocate heap memory.

  备注

  Internal mutex acquisition may block briefly when contended.

  参数:

    - cfg – [in] Configuration; `struct_size` must equal `sizeof(esp_cli_service_config_t)`

    - out – [out] Receives new handle on success; set to NULL on error

  返回:

    - ESP_OK On success

    - ESP_ERR_INVALID_ARG cfg or out is NULL, or struct_size is wrong

    - ESP_ERR_NO_MEM Allocation failed

esp_err_t esp_cli_service_destroy(esp_cli_service_t *svc)

  Destroy a CLI service and free its resources.

          Stops the REPL if running, frees tracked/static command state, and invalidates @a svc.

  备注

  After this returns, svc must not be used even if the return code is not ESP_OK (resources are still released).

  备注

  Not ISR-safe. Not thread-safe against concurrent use of svc or its REPL task; call when the service is quiesced.

  参数:

    svc – [in] Handle from esp_cli_service_create

  返回:

    - ESP_OK On success.

    - ESP_ERR_INVALID_ARG svc is NULL.

    - Other Error from underlying service teardown; all resources are freed regardless.

esp_err_t esp_cli_service_bind_manager(esp_cli_service_t *svc, esp_service_manager_t *mgr)

  Bind or replace the service manager used for dynamic `svc` / `tool` commands.

  备注

  Not ISR-safe. Thread-safe with other APIs on the same handle (mutex held briefly). May block briefly when acquiring an internal mutex.

  参数:

    - svc – [in] Service handle

    - mgr – [in] Manager pointer (may be NULL to clear)

  返回:

    - ESP_OK Manager bound.

    - ESP_ERR_INVALID_ARG svc is NULL.

    - ESP_ERR_TIMEOUT Mutex acquisition timed out.

esp_err_t esp_cli_service_track_service(esp_cli_service_t *svc, esp_service_t *service, const char *category)

  Track a service by name for `svc` list / lifecycle commands.

          If the name already exists, updates category and service pointer.

  备注

  Not ISR-safe. Thread-safe with other APIs on the same handle.

  参数:

    - svc – [in] Service handle

    - service – [in] Service to track (name from `esp_service_get_name`)

    - category – [in] Category string for display (may be NULL, stored as empty string)

  返回:

    - ESP_OK Tracked (created or updated).

    - ESP_ERR_INVALID_ARG svc or service is NULL; service name is empty.

    - ESP_ERR_NO_MEM Allocation failed.

    - ESP_ERR_TIMEOUT Mutex acquisition timed out.

    - Other Error from esp_service_get_name.

esp_err_t esp_cli_service_untrack_service(esp_cli_service_t *svc, const char *name)

  Remove a tracked service entry by logical service name.

  备注

  Not ISR-safe. Thread-safe with other APIs on the same handle.

  参数:

    - svc – [in] Service handle

    - name – [in] Service name previously tracked

  返回:

    - ESP_OK Service untracked.

    - ESP_ERR_INVALID_ARG svc is NULL or name is empty.

    - ESP_ERR_NOT_FOUND name not in the tracked list.

    - ESP_ERR_TIMEOUT Mutex acquisition timed out.

esp_err_t esp_cli_service_register_static_command(esp_cli_service_t *svc, const esp_console_cmd_t *cmd)

  Register an extra static esp_console command before or after REPL start.

          Command name must be a single token (no spaces). Strings are copied; the @a cmd
          descriptor need only live for the call. If the REPL is already running, the command is
          registered with esp_console immediately.

  备注

  Not ISR-safe. Thread-safe with other APIs on the same handle. Do not register from inside another command handler unless re-entrancy is safe for esp_console on your IDF version.

  参数:

    - svc – [in] Service handle

    - cmd – [in] Command descriptor (see `esp_console_cmd_t`)

  返回:

    - ESP_OK Command registered.

    - ESP_ERR_INVALID_ARG svc or cmd is NULL; invalid command descriptor.

    - ESP_ERR_INVALID_STATE Command name already registered.

    - ESP_ERR_NO_MEM Allocation or storage failed.

    - ESP_ERR_TIMEOUT Mutex acquisition timed out.

### Structures

struct esp_cli_service_config_t

  CLI service creation configuration.

          Set @c struct_size to @c sizeof(esp_cli_service_config_t) on every call. The @c reserved
          fields must be zero. Use @ref ESP_CLI_SERVICE_CONFIG_DEFAULT() unless you intentionally
          override fields.

          String ownership: @c prompt is copied internally; the pointer in @a cfg only needs to
          remain valid for the duration of @ref esp_cli_service_create. @c base_cfg fields follow
          @c esp_service_init rules.

  Public Members

  uint32_t struct_size

    Must equal `sizeof(esp_cli_service_config_t)`

  uint32_t reserved[2]

    Reserved; must be zero

  esp_service_config_t base_cfg

    Core service registration configuration

  const char *prompt

    REPL prompt string (copied on create)

  uint16_t max_cmdline_length

    Maximum command line length for esp_console

  uint32_t task_stack

    REPL task stack size in bytes

  uint32_t task_prio

    REPL task priority

  esp_service_manager_t *manager

    Optional manager for `tool` commands

### Macros

ESP_CLI_SERVICE_CONFIG_DEFAULT()

### Type Definitions

typedef struct esp_cli_service esp_cli_service_t

提供有关此文档的反馈