SILICON LABS Command Line Interface Software
![]()
Overview
The CLI component provides an input handler and command interpreter that receive keyboard input, parse command lines into commands and arguments, validate/decode arguments, and call the appropriate C command handler. It can merge commands from multiple components and supports features such as grouped commands, help, TAB auto-complete, cursor movement, and command history.
Specifications
| CLI implementation modules | Input handler; Command interpreter |
| Command handler signature | typedef void (*sl_cli_command_func_t)(sl_cli_command_arg_t *arguments); |
| Command argument struct | typedef struct { struct sl_cli *handle; int argc; void **argv; int arg_ofs; } sl_cli_command_arg_t; |
| Argument count/offset behavior | argc includes group names (if any) + command + corresponding arguments; arg_ofs marks where command arguments begin |
| Argument access macros (examples) | sl_cli_get_argument_int8/16/32, sl_cli_get_argument_uint8/16/32, sl_cli_get_argument_hex(a,n,l), sl_cli_get_argument_string(a,n) |
| Command group type | typedef struct { sl_slist_node_t node; bool in_use; const sl_cli_command_entry_t *command_table; } sl_cli_command_group_t; |
| Command entry type | typedef struct { const char *name; const sl_cli_command_info_t *command; const bool is_shortcut; } sl_cli_command_entry_t; |
| Command information type | typedef struct { sl_cli_command_func_t function; char *help; char *arg_help; sl_cli_argument_type_t arg_type_list[]; } sl_cli_command_info_t; (help/arg_help fields included when SL_CLI_HELP_DESCRIPTION_ENABLED) |
| Command execution I/O | Uses iostreams (input/output). Each CLI instance uses a dedicated iostream instance. |
| Argument validation types | Signed/unsigned numeric values: 8, 16, 32 bit; hexadecimal; string arguments |
| Special last-argument behavior | Last argument can be configured as optional, mandatory, or allowing multiple values |
| Built-in CLI features (selection) | Help, TAB auto-complete, cursor movement (left/right/home buffer navigation), command history, central decoding/validation |
| Bare metal configuration | Input handler provides a tick/process-action function that polls iostream; invoked via sl_system_process_action |
| Kernel configuration | Creates one task per CLI instance; tasks started by the application calling sl_system_kernel_start |
| CLI command groups typing rule | Group names must be typed before the command name; nested groups require all group names before the command |
| Storage NVM3 range size | 256 keys per storage range |
| Storage RAM instance isolation | Each CLI instance has its own RAM buffer; commands aren’t shared across instances |
Quick Guide
- Include the CLI and iostream components in your project; assign instance names to both (CLI instance name can be arbitrary; iostream instances use predefined names for the example).
- Create a top-level command group (
sl_cli_command_group_t) and an array of command entries (sl_cli_command_entry_t), pointing each entry to command info (sl_cli_command_info_t) and its command handler. - Define the command info for each command, including the command function and the argument type list (and help text when enabled).
- Implement the command handler(s) with signature
void handler(sl_cli_command_arg_t *arguments), and read validated argument values using the providedsl_cli_get_argument_*macros, using the argument offset indicated by the CLI (via the argument list indexing described in the macros section). - Install the command group into the CLI instance using
sl_cli_command_add_command_group(cli_handle, &a_group_0). - At runtime, use the built-in commands/features:
help,help group,help command, TAB auto-complete, and command history via up-arrow.
Troubleshooting
- Command handler not called: The CLI will not call the command handler unless the entered command arguments are correct for the configured argument types; ensure your command line matches the expected argument formats and counts.
- Help output missing or incomplete: The text displayed by
help/help commandmust be supplied by the implementer (place details in the command tables / command info structures). - Unable to navigate/edit the input line: Use the supported keys: up/down arrows for history, left/right arrows to move the cursor, BACKSPACE to delete left of cursor, and DEL to delete under the cursor. Typed characters insert at the cursor position.
- Argument offsets/groups confusion in handlers: Because command groups may be present,
argcincludes group names (if any) plus command and arguments, whilearg_ofsmarks where the command arguments begin; usearg_ofsas the basis for interpreting argument positions via the macros’ offset convention. - Long-running command handlers in bare metal: The manual notes that long-running command handlers can stall other process-action functions; return from the process-action function before the work completes, and avoid assuming completion based on
sl_system_process_actionreturn timing. - Multiple CLI instances don’t share I/O or stored state as expected: Each CLI instance uses a dedicated iostream instance. For RAM storage, each CLI instance has its own RAM buffer; commands defined in one instance are not available to other instances.
Command Line Interface (CLI)
Introduction
The Command Line Interface (CLI) software component simplifies creating command line user interfaces for programs. It handles tasks such as receiving keyboard input, parsing the input strings for commands and arguments, and calling the proper C function with decoded and validated arguments. Functions can use standard output functions such as printf to write information to the console.Multiple components (parts of an application) may define CLI commands, where the running program will typically merge all commands into a superset of commands with common input and output, I/O. The CLI uses I/O functions provided by the I/O Streams framework, iostream.The CLI component supports multiple instances, where separate CLI instances will use separate iostream instances. Groups of commands can be assigned to specific CLI instances making it possible for one application to use multiple CLI instances, where each is customized for its particular use.
Features
- The CLI has several built in features that will be inherited by all applications using the CLI component. The following sections explain some of the CLI features.
- Note: Some features can be simplified or removed to reduce code size by changing the application configuration.
Command Groups
- The CLI supports both stand alone commands and commands that belong to groups. A stand alone command is executed by typing the command name and arguments. If a command belongs to a group, the group name must be typed before the command name. Groups can be nested, and in that case, all group names must be typed before the command name.
- If an application has implemented the commands on and off that are part of the groups green and red in group led , the user can type: led green on or led red off to control the LEDs.
Help
- The CLI has a built in help command, which can be used as follows: By typing help , all commands are listed.
- By typing help group , all commands in the group are listed.
- By typing help command , help for the command is shown.
- The information that is displayed when using the help command must be supplied by the implementer. Details for each command must be placed in command tables. See the Command tables section for more details
Auto-Complete
- The CLI can complete a partially typed command by pressing the TAB key.
Cursor Movement
- The up-arrow and down-arrow keys can be used to navigate up and down in the history buffer.
- The left-arrow and right-arrow keys are used to move the cursor in the current command line. The BACKSPACE key is used to delete the character to the left of the cursor. The DEL key is used to delete the character under the cursor. Typed characters will be inserted at the cursor position.
Command History
- Commands are stored in the history buffer when executed. Previous commands can be executed again by pressing the up-arrow key one or more times.
- Note: The history buffer is a simple character array, which does not define how many command lines can be stored. The history buffer stores command lines sequentially, making room for more short commands than long.
Dynamic Registration of New Commands
- An application using the CLI component can define commands that must be installed either at compile time or at run time. Defining commands at compile time may be sufficient for simple applications, while more advanced applications with several CLI instances, may utilize that the CLI can add command groups at run time.
Central Decoding and Validation of Command Arguments
- The CLI has built in functions to decode and validate command arguments. The command handler will not be called unless the command is entered with correct arguments.
- The CLI argument validation can handle signed and unsigned numeric values with 8, 16 and 32 bit sizes, hexadecimal and string arguments. The command handler will be called with decoded arguments using an argc, argv pattern similar to how C/C++ applications pass command line arguments to the main function.
- If none of the built in argument types can be used by the command, it can use string arguments and decode the strings in the command handler.
- Note: For the last argument, it is possible to specify that it is optional, mandatory or can have multiple values.
Input and Output:
- The CLI is using iostreams for input and output. Each CLI instance must have a dedicated iostream instance.iostream instance to the CLI instance.
Requirements not Defined by the CLI
- Command naming rules or output formats are not defined by the CLI.
Design
- To fully understand how to use the CLI, it is crucial to understand the design and some implementation details. The following sections will try to explain the basics and how to create user commands..
Command
Modules
The CLI has been implemented as two independent software modules; input handler and command interpreter .The input handler handles the user input and allows the user to enter command lines. When the user types the ENTER key, the input string is passed to the command interpreter where the string will be interpreted and the appropriate command handler will be called with the command arguments. Normally both modules are used together, but an application can use the command interpreter directly. In that case, the command interpreter must be called with a command line as input parameter
Command Signature
All command handlers must have the same signature, defined in sl_cli_types.h
- typedef void (*sl_cli_command_func_t)(sl_cli_command_arg_t *arguments);
Command Arguments
Command arguments are available via the arguments pointer passed to the command handler function. The arguments pointer will point to a struct, which holds the argument values. The arguments struct has the following definition:M![]()
The handle identifies the CLI instance where the command was issued. By including the instance handle, it is possible to share the same command handler between multiple CLI instances. By using the argc , argv way of passing argument values, it is possible to have a common command handler signature but support variable number of arguments. When the command handler is called, the argc , argv arguments will contain all commands and arguments. Because the CLI supports command groups, the command may consist of multiple command groups along with the command itself. The arg_ofs tells the command handler the offset where the command arguments begin. The cli.h has defined the following macros that can be used to get argument values. The macros use a for the arguments pointer and n for the argument offset, starting on 0 for the first argument.![]()
Command Tables
Adding commands to the CLI requires using three different data structures. The data structures have a root node referred to as a “command group”, where sub-structures defining command details are nested. The “command group” may contain details for one or more commands and it is possible to install multiple “command groups” in each CLI instance. The “command group” must be of the type
Note: Each time a command group is installed in a CLI instance, the application must support one unique command group data structure. Command groups cannot be re-used or shared between CLI instances.The next level is an array where each of the elements must be a “command entry”. Typically one array element exists for each command. If a command has both a long and a short (shortcut) name, it will take two array elements. Each of the “command entries” must be of the type:
A “command entries” array can be re-used and shared between multiple CLI instances. Finally, “command information” for commands is defined in one data structure for each command. The “command information” structure is defined with the following type:
A “command information” data structure can be re-used and shared between multiple CLI commands andinstances.
Command Handler
In addition to the tables described above, each command must have a command handler. The command handler is typically written in C-code, where all command handlers have the same signature. The command handler function prototype has the following definition:
- typedef void (*sl_cli_command_func_t)(sl_cli_command_arg_t *arguments);
Differences between Bare Metal and Kernel Configurations
The CLI component can be used in both bare metal and kernel configurations. The differences between the two configurations are explained in the following sections.
Bare Metal
In a bare metal configuraton, the input handler has a process-action (tick) function that will poll the iostream for input. When generating a project with uc, the CLI process-action function will be installed into the system framework. Calling the sl_system_process_action function calls the CLI process-action function.
Note: CLI command handlers that take a long time to execute can (should) return from the process-action function before they are complete to not stall other component process-action functions. Execution will in that case be split into several calls to the process-action function. The application should not make any assumptions about when any of the command handlers have completed execution based on when the sl_system_process_action returns
Kernel
In a kernel configuration, the CLI will create a task where the input handler polls the iostream for input data. One task is created for each CLI instance. The task(s) must be started by the application by calling the sl_system_kernel_start function.
Configuration
The CLI has two sets of configurations. One is common for all CLI instances and is placed in the sl_cli_config.h file. The other is a configuration file for each CLI instance with the file name sl_cli_config_<instance-name>.h .
Examples
The following steps demonstrate how to create and install a CLI command.Note: To avoid forward definitions in the C-code, the following steps will typically be implemented in reverse order. Include the cli and iostream components to the project. Both cli and iostream_usart instances must specify instance names. The iostream_usart component has some pre-defined instance names while the cli instance name can be anything.
Define and implement CLI commands![]()
The output from executing help will in this case be:![]()
Extensions
The CLI comes with some pre-made components that can be useful for some applications. To take advantage of these components, the user must: 1. Define the command(s) in the command tables. Use the command handlers supplied with the component(s). 2. Include the component(s) in the project. The components come with the command handlers and integrate with the CLI. Most of the components however do not define the command name. Instead, it must be defined by the application implementer
- Note: The CLI extensions described here use the method of splitting some of the command handler operations into multiple process-action calls for bare-metal configuration.
Common properties for the storage components: The storage components share some common properties although data is stored in different locations. The most important are: New commands can be added, existing commands can be deleted and stored commands can be executed. One benefit of running commands from a storage compared to typing the commands at user input is that the next command in the list will be executed immediately after the previous command has completed. Commands will be executed in the sequence they are added and stored, and there will be zero or little delay between commands.
Simple Password Protection
The cli_simple_password component has been designed to provide functionalities to limit access to the CLI interface. It asks to configure a password at first startup and requires the configured password at the next access. It also includes security mechanisms to limit the number of attempts with invalid passwords. In other words, access is locked when the maximum number of retries is reached. If the retry limit is reached, a Security flag is raised and will be displayed on the login page until cleared. Depending on the device used, the key and the encrypted password are stored in the Secure Element or with NVM3. The cli_simple_password component defines the following user commands and their associated handlers![]()
![]()
Storage NVM3
The cli_storage_nvm3 component has been designed to store command lines in NVM3 storage. In addition to letting the user control when the commands are executed, the stored commands will automatically be executed at program start. The cli_storage_nvm3 component does not define the user commands. However, it implements a few functions where the following four are designed to be used as CLI command handlers directly.
In addition, get the number of command lines stored in NVM3 storage by calling the following function
The cli_storage_nvm3 depends on the cli_default component that defines the NVM3 storage area. This area is typically shared between different parts of the application, where each has its own range of NVM3 object keys to avoid collisions. The cli_storage_nvm3 component has a range with 256 keys. You can configure each CLI instance to use a sub-set of the available 256 keys to separate commands between different CLI instances.
Storage RAM
The cli_storage_ram component has been designed to store command lines in a ram buffer. The cli_storage_ram component does not define the user commands. However, it implements a few functions where the following four are designed to be used as CLI command handlers directly
In addition, get the number of command lines stored in RAM by calling:
Each instance of the CLI will have its own RAM buffer. Commands defined in one CLI instance will not be available for other instances.
Delay
The cli_delay component may be useful to add delays between commands when running command sequences from storage. The function will delay for the specified number of milliseconds. During the delay, it will allo ot er components to run in the background. B. The command handler has the following function prototype:
![]()
Documents / Resources
![]() | Command Line Interface Software |
References
- User Manualmanual.tools
