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:

msh />
RT-Thread shell commands:
pin              - pin [option]
reboot           - Reboot System
help             - RT-Thread shell help.
ps               - List threads in the system.
free             - Show the memory usage in the system
clear            - clear the terminal screen
version          - show RT-Thread version information
list             - list objects


msh />list
Usage: list [options]
[options]:
    thread     - list threads
    timer      - list timers
    sem        - list semaphores
    mutex      - list mutexs
    event      - list events
    mailbox    - list mailboxs
    msgqueue   - list message queues
    memheap    - list memory heaps
    mempool    - list memory pools
    device     - list devices
    fd         - list file descriptors
msh />
copymistakeCopy Success

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.

msh />list thread
thread   pri  status      sp     stack size max used left tick  error
-------- ---  ------- ---------- ----------  ------  ---------- ---
tshell    20  running 0x00000160 0x00001000    15%   0x00000003 OK
aio      128  suspend 0x00000084 0x00000800    06%   0x0000000a OK
sys work  23  suspend 0x00000084 0x00000800    06%   0x0000000a OK
tidle0   255  ready   0x0000005c 0x00001000    04%   0x00000016 OK
timer      4  suspend 0x00000078 0x00000400    11%   0x00000009 OKcopymistakeCopy Success

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.

msh />list sem
semaphor v   suspend thread
-------- --- --------------
shrx     000 0
sem_sd0  001 0
psem     001 0
pmq      001 0
wqueue   000 0
sd_ack   000 0
wqueue   000 0copymistakeCopy Success

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.

msh />list event
event      set    suspend thread
-----  ---------- --------------copymistakeCopy Success

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.

msh />list mutex
mutex      owner  hold suspend thread priority
-------- -------- ---- -------------- --------
fat0     (NULL)   0000 0              255
sd_bus_l (NULL)   0000 0              255
fdlock   (NULL)   0000 0              255
fslock   (NULL)   0000 0              255
dfs_mgr  (NULL)   0000 0              255
heap     (NULL)   0000 0              255copymistakeCopy Success

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.

msh />list mailbox
mailbox  entry size suspend thread
-------- ----  ---- --------------
mmcsdhot 0001  0004 0
mmcsdmb  0000  0004 1:mmcsd_decopymistakeCopy Success

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.

msh />list msgqueue
msgqueue entry suspend thread
-------- ----  --------------copymistakeCopy Success

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.

msh />list mempool
mempool block total free suspend thread
------- ----  ----  ---- --------------
signal  0012  0032  0032 0copymistakeCopy Success

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.

msh />list timer
timer     periodic   timeout    activated     mode
-------- ---------- ---------- ----------- ---------
tshell   0x00000000 0x00000000 deactivated one shot
aio      0x00000000 0x00000000 deactivated one shot
mmcsd_de 0x00000001 0x0000000d deactivated one shot
sys work 0x00000000 0x00000000 deactivated one shot
tidle0   0x00000000 0x00000000 deactivated one shot
timer    0x00000000 0x00000000 deactivated one shot
current tick:0x00017c0dcopymistakeCopy Success

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.

msh />list device
device           type         ref count
-------- -------------------- ----------
sd       Block Device         1
rtc      RTC                  0
zero     Miscellaneous Device 0
shm      Unknown              0
uart1    Character Device     0
uart0    Character Device     2copymistakeCopy Success

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.

msh />free
total    : 66606976
used     : 17792
maximum  : 20000
available: 66589184copymistakeCopy Success

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:

void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}

MSH_CMD_EXPORT(hello , say hello to RT-Thread);copymistakeCopy Success

When exporting a command with parameters, the function input parameters are int argcand 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:

static void atcmd(int argc, char**argv)
{
    ……
}

MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd <server|client>);copymistakeCopy Success

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.

void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}

FINSH_FUNCTION_EXPORT_ALIAS(hello , ho, say hello to RT-Thread);copymistakeCopy Success

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.

/* 开启 FinSH */
#define RT_USING_FINSH

/* 将线程名称定义为 tshell */
#define FINSH_THREAD_NAME "tshell"

/* 开启历史命令 */
#define FINSH_USING_HISTORY
/* 记录 5 行历史命令 */
#define FINSH_HISTORY_LINES 5

/* 开启使用 Tab 键 */
#define FINSH_USING_SYMTAB
/* 开启描述功能 */
#define FINSH_USING_DESCRIPTION

/* 定义 FinSH 线程优先级为 20 */
#define FINSH_THREAD_PRIORITY 20
/* 定义 FinSH 线程的栈大小为 4KB */
#define FINSH_THREAD_STACK_SIZE 4096
/* 定义命令字符长度为 80 字节 */
#define FINSH_CMD_SIZE 80

/* 开启 msh 功能 */
#define FINSH_USING_MSH
/* 最大输入参数数量为 10 个 */
#define FINSH_ARG_MAX 10copymistakeCopy Success

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.

#include <rtthread.h>

void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}

MSH_CMD_EXPORT(hello , say hello to RT-Thread);copymistakeCopy Success

After the system is running, press the tab key in the FinSH console to see the exported commands:

msh />
RT-Thread shell commands:
hello             - say hello to RT-Thread
version           - show RT-Thread version information
……copymistakeCopy Success

Run the hello command, and the results are as follows:

msh />hello
hello RT_Thread!
msh />copymistakeCopy Success

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.

#include <rtthread.h>

static void atcmd(int argc, char**argv)
{
    if (argc < 2)
    {
        rt_kprintf("Please input'atcmd <server|client>'\n");
        return;
    }

    if (!rt_strcmp(argv[1], "server"))
    {
        rt_kprintf("AT server!\n");
    }
    else if (!rt_strcmp(argv[1], "client"))
    {
        rt_kprintf("AT client!\n");
    }
    else
    {
        rt_kprintf("Please input'atcmd <server|client>'\n");
    }
}

MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd <server|client>);copymistakeCopy Success

After the system is running, press the tab key in the FinSH console to see the exported commands:

msh />
RT-Thread shell commands:
hello             - say hello to RT-Thread
atcmd             - atcmd sample: atcmd <server|client>
version           - show RT-Thread version information
……copymistakeCopy Success

Run the atcmd command, and the results are as follows:

msh />atcmd
Please input 'atcmd <server|client>'
msh />copymistakeCopy Success

Run the atcmd server command. The results are as follows:

msh />atcmd server
AT server!
msh />copymistakeCopy Success

Run the atcmd client command. The results are as follows:

msh />atcmd client
AT client!
msh />copymistakeCopy Success

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/finshthe 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

Assoc. Prof. Wiroon Sriborrirux, Founder of Advance Innovation Center (AIC) and Bangsaen Design House (BDH), Electrical Engineering Department, Faculty of Engineering, Burapha University