With nRF5 SDK v14.0.0, we have significantly refactored the Command Line Interface (CLI) module to make it more useful and user friendly.
The new CLI has the following features:
- Support for multiple instances.
- Advanced cooperation with the nrf_log.
- Support for static and dynamic commands.
- Smart command completion with the Tab key.
- Built-in commands: clear, colors, history, and resize.
- Viewing recently executed commands using the Up/Down keys.
- Text edition using Left/Right/End/Home/Insert keys.
- Build-in handler to display commands help.
- Support for multiline commands.
- Support for ANSI escape codes for cursor control and color printing (VT100).
The module can be connected to any transport. At this point, the following transport layers are implemented:
- USB CDC ACM
Why have we reworked the CLI?
We noticed that the CLI that was provided in nRF5 SDK v13.0.0 had great potential to simplify work with our nRF5 chips. Unfortunately, in its previous shape, it only allowed for adding some simple commands, the handling of which required a lot of manual and repetitive work.
The main drawbacks that we noticed were:
- A need to implement a lot of
if - else ifstatements to handle command parameters.
- Missing capability to complete command parameters with the Tab key.
- Static size of the
nrf_cli_fprintffunction buffer could cause an issue where full string was not printed.
- Missing integration with the NRF_LOG module.
Taking these assumptions into consideration, we started our work to deliver a new command line interface.
New commands concept
Commands in the new CLI are organized in a tree structure. Each module can register root commands (level 0) and corresponding dynamic or static subcommands (level > 0). Thanks to this approach, you can now use the Tab key to auto-complete or view all commands and their subcommands.
What is more, each and every command or subcommand can have or not have a handler. CLI will analyze the entered command line and it will execute the command or subcommand that has a handler and highest level. Higher-level subcommands without a handler will be passed as arguments.
An example command may look like that:
log enable info app
- log - Root command with a simple handler to print help.
- enable - Subcommand with a handler.
- info - Subcommand without a handler.
- app - Subcommand without a handler.
In such case, CLI will execute the handler for subcommand
enable and it will pass
app as arguments to it. Despite the fact that
app are subcommands without a handler, they can still be completed or prompted with the Tab key.
Another innovation we have introduced is the concept of dynamic subcommands. The advantage of using them is that their number and syntax might not be known during compile time.
Below, we present a practical example how you can use the new CLI with dynamic commands. Let us assume that we would like to scan for the available Bluetooth devices and then connect to one of them. To realize that, we can implement the following command trees:
- connect - Root command for connecting to a Bluetooth device.
- disconnect - Root command for disconnecting from a Bluetooth device.
- scan - Root command for printing help.
- start - Subcommand for searching for the advertising Bluetooth devices. These devices are then used as dynamic commands.
- stop - Subcommand for stopping the search for advertising Bluetooth devices. It then removes the dynamic subcommands.
- NULL - Empty list of dynamic subcommands.
After typing connect and hitting the Tab button, nothing happens because the list of dynamic commands is empty.
We need to scan for advertising devices first:
Now, dynamic subcommands are available and can be prompted with the Tab button.
Next, we can also connect to the selected device:
To be continued...