OS
v1.7.5
Documentation
|
A command-line interface (CLI) is a way to interact directly with the software of an embedded system in the form of text commands and responses. It can be seen as a typed set of commands to produce a result, but here, the commands are typed in real-time by a user through a specific interface, for example, UART, USB, LAN, etc.
A CLI is often developed to aid initial driver development and debugging. This CLI might become the interface (or one of the interfaces) used by a sophisticated end-user to interact with the product. Think of typing commands to control a machine, or perhaps for low-level access to the control system as a development tool, tweaking time-constants and monitoring low-level system performance during testing.
The provided development API parses and handles input commands, following a simplified form of the extended AT-commands syntax.
As seen in the figure, the CLI has a few components described below:
The syntax is straightforward and the rules are provided below:
EOL
character. By default, the CLI uses the carriage return character. (We will use CR
to represent a carriage return character in this document).cli::commandType::ACT
) : This is the simplest type of commands that can be subscribed. It's normally used to execute the action that the command should do. This type does not take arguments or modifiers, for example : AT+CMD
cli::commandType::READ
) : This type of command allows you to read or test a value already configured for the specified parameter. Only one argument is allowed. for example "AT+CMD?"
or "AT+CMD?PARAM1"
cli::commandType::TEST
) : These types of commands allow you to get the values that can be set for its parameters. No parameters are allowed here. Example "AT+CMD=?"
cli::commandType::PARA
) : These types of commands allow n arguments to be passed for setting parameters, for example: AT+CMD=x
,y If none of the types is given at the input, the command response will be ERROR
OK:
Indicates the successful execution of the command.ERROR:
A generalized message to indicate failure in executing the command.UNKNOWN
: The input command is not subscribed.NOT ALLOWED
: The command syntax is not one of the allowed types.NONE
: No response.All responses are followed by a CR
LF
Errors generated during the execution of these AT commands could be due to the following reasons:
In case of an error, the string ERROR
or "ERROR:<error_no>"
are displayed.
Before starting the CLI development, the corresponding instance must be defined; a variable of class qOS::commandLineInterface. The instance should be initialized using the qOS::commandLineInterface::setup() method.
The AT CLI is able to subscribe to any number of custom AT commands. For this, the qOS::commandLineInterface::add() method should be used.
This function subscribes the CLI instance to a specific command with an associated callback function, so that the next time the required command is sent to the CLI input, the callback function will be executed. The CLI parser only analyzes commands that follow the simplified AT-Commands syntax already described.
The command callback should be coded by the application writer. Here, the following prototype should be used:
The callback takes one argument of type qOS::cli::handler_t and returns a single value. The input argument it's just a pointer to public data of the CLI instance where the command it subscribed to. From the callback context, can be used to print out extra information as a command response, parse the command parameters, and query properties with crucial information about the detected command, like the type, the number of arguments, and the subsequent string after the command text. To see more details please check the qOS::cli::handler_t class reference.
The return value (an enum of type qOS::cli::response ) determines the response shown by the Output printer component. The possible allowed values are:
cli::response::OK
: as expected, print out the OK string.cli::response::ERROR
: as expected, print out the ERROR string.cli::ERROR_CODE(no)
: Used to indicate an error code. This code is defined by the application writer and should be a value between 1
and 32766
. For example, a return value of cli::ERROR_CODE(15)
, will print out the string ERROR:15
.cli::response::NO_RESPONSE
: No response will be printed out.A simple example of how the command callback should be coded is shown below:
Input handling is simplified using the provided methods of this class. The qOS::commandLineInterface::isrHandler() function are intended to be used from the interrupt context. This avoids any kind of polling implementation and allows the CLI application to be designed using an event-driven pattern.
The two overloads for this method feed the parser input, the first one with a single character and the second with a string. The application writer should call one of these functions from the desired hardware interface, for example, from a UART receive ISR.
If there is no intention to feed the input from the ISR context, the methods qOS::commandLineInterface::raise() or qOS::commandLineInterface::exec() can be called at demand from the base context. As expected, both functions send the string to the specified CLI. The difference between both methods is that qOS::commandLineInterface::raise() sends the command through the input, marking it as ready for parsing and acting as the Input handler component.
The qOS::commandLineInterface::exec(), on the other hand, executes the components of Pre-parsing and Postparsing bypassing the other components, including the Output printer, so that it must be handled by the application writer.
The parser can be invoked directly using the qOS::commandLineInterface::run() method. Almost all the components that make up the CLI are performed by this method, except for the Input Handler, which should be managed by the application writer itself.
In this way, the writer of the application must implement the logic that leads this function to be called when the input-ready condition is given.
The simple approach for this is to check the return value of any of the input feeder APIs and set a notification variable when they report a ready input. Later in the base context, a polling job should be performed over this notification variable, running the parser when their value is true, then clearing the value after to avoid unnecessary overhead.
The recommended implementation is to leave this job handled by a task instead of coding the logic to know when the CLI should run. For this, an overload of qOS::core::add() is provided accepting an instance of the CLI. This overload adds a task to the scheduling scheme running an AT Command Line Interface and is treated as an event-triggered task. The address of the parser instance will be stored in the TaskData
storage-Pointer.
After invoked, both CLI and task are linked together in such a way that when an input-ready condition is given, a notification event is sent to the task launching the CLI components. As the task is event-triggered, there is no additional overhead and the writer of the application can assign a priority value to balance the application against other tasks in the scheduling scheme.
The following example demonstrates the usage of a simple command-line interface using the UART peripheral with two subscribed commands :
"at+gpio"
.A command to retrieve the compilation timestamp "at+info"
. First, let's get started defining the required objects to set up the CLI instance:
Then the CLI instance is configured by subscribing commands and adding the task to the OS. A wrapper function is required here to make the UART output-function compatible with the CLI API.
The CLI input is feeded from the interrupt context by using the UART receive ISR:
Finally, the command callbacks are later defined to perform the requested operations.