FinSH Console
In the early days of computer development, before the emergence of graphics systems, there was no mouse, or even keyboard. How did people interact with computers at that time? The earliest computers used punched paper strips to input commands to the computer and write programs. Later, as computers continued to develop, monitors and keyboards became standard configurations of computers, but the operating system at that time did not support graphical interfaces. Computer pioneers developed a software that accepted commands entered by users, interpreted them, passed them to the operating system, and returned the results of the operating system's execution to the user. This program was like a shell wrapped around the outside of the operating system, so it was called a shell.
Embedded devices usually need to connect the development board to the PC for communication. Common connection methods include: serial port, USB, Ethernet, Wi-Fi, etc. A flexible shell should also support working on multiple connection methods. With a shell, it is like building a bridge of communication between the developer and the computer. The developer can easily obtain the system operation status and control the system operation through commands. Especially in the debugging stage, with a shell, the developer can not only locate the problem faster, but also use the shell to call the test function, change the parameters of the test function, reduce the number of code burning times, and shorten the project development time.
FinSH is the command line component (shell) of RT-Thread, which was born based on the above considerations. FinSH is pronounced [ˈfɪnʃ]. After reading this chapter, we will have a deeper understanding of how FinSH works and how to export our own commands to FinSH.
FinSH is the command line component of RT-Thread, which provides a set of operation interfaces for users to call in the command line, mainly used for debugging or viewing system information. It can communicate with the PC using serial port/Ethernet/USB, etc. The hardware topology is shown in the figure below:
The user inputs commands on the control terminal, and the control terminal transmits the commands to FinSH in the device through serial port, USB, network, etc. FinSH will read the device input command, parse and automatically scan the internal function table, find the corresponding function name, execute the function and output a response. The response is returned through the original path and the result is displayed on the control terminal.
When the serial port is used to connect the device to the control terminal, the execution process of the FinSH command is as shown in the figure below:
FinSH supports permission verification function. The system will perform permission verification after startup. Only when the permission verification is passed will the FinSH function be enabled to improve the security of system input.
FinSH supports functions such as auto-completion and viewing historical commands. These functions can be easily used through the keys on the keyboard. The keys supported by FinSH are shown in the following table:
button | Functional Description |
Tab key | If you press the Tab key without entering any characters, all commands supported by the current system will be printed. If you press the Tab key after entering some characters, it will search for matching commands and complete the command according to the file name in the current directory of the file system. You can continue to enter and complete the command multiple times. |
↑↓ keys | Scroll up and down through the history of recently entered commands |
Backspace key | Delete |
←→ key | Move the cursor left or right |
FinSH supports command line mode, which is also called msh (module shell). In msh mode, FinSH is executed in the same way as traditional shells (dos/bash). For example, you can cd /
switch the directory to the root directory through the command.
msh parses the input characters and decomposes them into commands and parameters separated by spaces. The command execution format is as follows:
command [arg1] [arg2] [...]
The command can be either a built-in command of RT-Thread or an executable file.
Some FinSH commands are built-in by default in RT-Thread. Enter help in FinSH and press Enter or press the Tab key to print all the commands supported by the current system.
In msh mode, press the Tab key to list all currently supported commands. The number of default commands is not fixed, and each component of RT-Thread will output some commands to FinSH. For example, when the DFS component is turned on, commands such as ls, cp, and cd will be added to FinSH to facilitate debugging by developers.
The following are all currently supported commands for displaying RT-Thread kernel status information, which are printed out after pressing the Tab key. The command name is on the left and the description of the command is on the right:
Here is a list of field information returned after entering common commands, which helps developers understand the returned information content.
Use the ps or list thread command to list all thread information in the system, including thread priority, status, maximum stack usage, etc.
list thread Returns the following fields:
Fields | describe |
thread | The name of the thread |
pri | Thread priority |
status | The current state of the thread |
sp | The thread's current stack position |
stack size | Thread stack size |
max used | Maximum stack position used in thread history |
left tick | The number of ticks remaining for the thread to run |
error | Thread error code |
Use the list sem command to display all semaphore information in the system, including the semaphore name, semaphore value, and the number of threads waiting for the semaphore.
list sem returns a description of the fields:
Fields | describe |
semaphore | The name of the semaphore |
v | The current value of the semaphore |
suspend thread | The number of threads waiting for this semaphore |
Use the list event command to display all event information in the system, including the event name, event value, and the number of threads waiting for this event.
list event returns a description of the fields:
Fields | describe |
event | The name of the event set |
set | The current event in the event set |
suspend thread | The number of threads waiting for events in this event set |
Use the list mutex command to display all mutex information in the system, including the mutex name, the owner of the mutex, and the number of nestings the owner holds on the mutex.
list mutex returns a description of the fields:
Fields | describe |
mutxe | The name of the mutex |
owner | The thread currently holding the mutex |
hold | The number of times the holder has nested holds on this mutex |
suspend thread | The number of threads waiting for this mutex |
priority | Holds the thread priority |
Use the list mailbox command to display all mailbox information in the system, including the mailbox name, the number of emails in the mailbox, and the maximum number of emails that the mailbox can hold.
list mailbox Returns a description of the fields:
Fields | describe |
mailbox | Name of the mailbox |
entry | The number of messages contained in the mailbox |
size | The maximum number of messages that a mailbox can hold |
suspend thread | The number of threads waiting for this mailbox |
Use the list msgqueue command to display all message queue information in the system, including the name of the message queue, the number of messages contained in it, and the number of threads waiting for this message queue.
list msgqueue returns a description of the fields:
Fields | describe |
msgqueue | The name of the message queue |
entry | The number of messages currently contained in the message queue |
suspend thread | The number of threads waiting for this message queue |
Use the list mempool command to display all memory pool information in the system, including the name of the memory pool, the size of the memory pool, and the maximum used memory size.
List mempool returns a description of the fields:
Fields | describe |
mempool | Memory pool name |
block | Memory block size |
total | Total memory blocks |
free | Free memory blocks |
suspend thread | The number of threads waiting for this memory pool |
Use the list timer command to display information about all timers in the system, including the name of the timer, whether it is a periodic timer, and the number of beats for the timer to time out.
list timer returns a description of the fields:
Fields | describe |
timer | Timer name |
periodic | Is the timer periodic? |
timeout | The number of ticks when the timer times out |
activated | The state of the timer, activated means active, deactivated means inactive |
mode | Timer type, one shot means one-shot timing, periodic means periodic timing |
Current tick indicates the current system tick number.
Use the list device command to display all device information in the system, including the device name, device type, and number of times the device has been opened.
list device Returns the following fields:
Fields | describe |
device | Name of the device |
type | Type of device |
ref count | Number of times the device has been opened |
Use the free command to display all memory information in the system.
Description of the fields returned by free:
Fields | describe |
total memory | Total memory size |
used memory | The amount of memory used |
maximum allocated memory | Maximum allocated memory |
available | Available memory size |
In addition to the commands that come with FinSH, FinSH also provides multiple macro interfaces to export custom commands. The exported commands can be executed directly in FinSH.
Customized msh commands can be run in msh mode. To export a command to msh mode, you can use the following macro interface:
MSH_CMD_EXPORT(name, desc);
parameter | describe |
name | Commands to export |
desc | Description of the export command |
This command can export commands with parameters or without parameters. When exporting commands without parameters, the function input parameter is void, as shown in the following example:
When exporting a command with parameters, the function input parameters are int argc
and char**argv
. argc indicates the number of parameters, and argv indicates the pointer to the array of command line parameter string pointers. The following is an example of exporting a command with parameters:
There is a certain limit on the length of FinSH function names, which is controlled by the macro definition FINSH_NAME_MAX in finsh.h. The default is 16 bytes, which means that the length of FinSH commands will not exceed 16 bytes. There is a potential problem here: when a function name is longer than FINSH_NAME_MAX, after using FINSH_FUNCTION_EXPORT to export the function to the command table, the complete function name can be seen in the FinSH symbol table, but a null node error will occur when the complete input is executed. This is because although the complete function name is displayed, the first 16 bytes are actually saved as commands in FinSH. Too much input will result in the command not being found correctly. At this time, FINSH_FUNCTION_EXPORT_ALIAS can be used to rename the exported command.
FINSH_FUNCTION_EXPORT_ALIAS(name, alias, desc);
parameter | describe |
name | Commands to export |
alias | The name displayed when exporting to FinSH |
desc | Description of the export command |
You can export the command to msh mode by adding before the renamed command name __cmd_
, otherwise, the command will be exported to C-Style mode. The following example defines a hello function, renames it to ho, and then exports it as a command in C-Style mode.
FinSH functions can be tailored. The macro configuration options are defined in the rtconfig.h file. The specific configuration items are shown in the following table.
Macro Definition | Value Type | describe | default value |
#define RT_USING_FINSH | none | Enable FinSH | Open |
#define FINSH_THREAD_NAME | String | The name of the FinSH thread | "tshell" |
#define FINSH_USING_HISTORY | none | Enable the history backtracking function | Open |
#define FINSH_HISTORY_LINES | Integer | The number of historical command lines that can be traced back | 5 |
#define FINSH_USING_SYMTAB | none | Symbol tables can be used in FinSH | Open |
#define FINSH_USING_DESCRIPTION | none | Add a description to each FinSH symbol | Open |
#define FINSH_USING_MSH | none | Enable msh mode | Open |
#define FINSH_ARG_MAX | Integer | Maximum number of input parameters | 10 |
#define FINSH_USING_AUTH | none | Enable permission verification | closure |
#define FINSH_DEFAULT_PASSWORD | String | Permission verification password | closure |
The reference configuration example in rtconfig.h is shown below, which can be configured according to actual functional requirements.
This section will demonstrate how to export a custom command to msh. The sample code is shown below. The hello function is created in the code, and then the hello function can be exported to the FinSH command list through the MSH_CMD_EXPORT command.
After the system is running, press the tab key in the FinSH console to see the exported commands:
Run the hello command, and the results are as follows:
This section will demonstrate how to export a custom command with parameters to FinSH. The sample code is shown below. atcmd()
A function is created in the code, and then the function can be atcmd()
exported to the msh command list through the MSH_CMD_EXPORT command.
After the system is running, press the tab key in the FinSH console to see the exported commands:
Run the atcmd command, and the results are as follows:
Run the atcmd server command. The results are as follows:
Run the atcmd client command. The results are as follows:
FinSH is written entirely in ANSI C and has excellent portability. It takes up little memory. If you do not use the function method introduced in the previous chapter to dynamically add symbols to FinSH, FinSH will not dynamically request memory. The FinSH source code is located in components/finsh
the directory. When porting FinSH, you need to pay attention to the following aspects:
FinSH Thread:
Each command execution is completed in the context of the FinSH thread (i.e., tshell thread). When the RT_USING_FINSH macro is defined, finsh_system_init() can be called in the initialization thread to initialize the FinSH thread. In versions after RT-Thread 1.2.0, you do not need to use finsh_set_device(const char* device_name)
the function to explicitly specify the device to be used, but will automatically call rt_console_get_device()
the function to use the console device (RT-Thread 1.1.x and below must use to finsh_set_device(const char* device_name)
specify the device used by FinSH). The FinSH thread finsh_system_init()
is created in the function function, and it will always wait for the rx_sem semaphore.
Output of FinSH:
The output of FinSH depends on the output of the system, and in RT-Thread it depends on rt_kprintf()
the output. In the startup function rt_hw_board_init()
, rt_console_set_device(const char* name)
the function sets the print output device of FinSH.
FinSH input:
After the FinSH thread obtains the rx_sem semaphore, it calls rt_device_read()
the function to obtain a character from the device (select the serial port device) and then processes it. Therefore, the porting of FinSH requires rt_device_read()
the implementation of the function. The release of the rx_sem semaphore rx_indicate()
completes the input notification to the FinSH thread by calling the function. The usual process is that when the serial port receive interrupt occurs (that is, the serial port has input), the receiving interrupt service routine calls rx_indicate()
the function to notify the FinSH thread that there is input, and then the FinSH thread obtains the serial port input and finally performs the corresponding command processing.
Last updated