使用 RT-Thread 的 FinSH 对硬件进行编程
硬件 RT-Thread

> 
> 由于物联网（IoT）的兴起，对硬件进行编程变得越来越普遍。RT-Thread 可以让你可以用 FinSH 从 Linux 命令行与设备进行沟通、
> 
> 
> 


![](/data/attachment/album/202009/29/233059w523g55qzvo53h6i.jpg)


RT-Thread 是一个开源的[实时操作系统](https://opensource.com/article/20/6/open-source-rtos)，用于对物联网（IoT）设备进行编程。FinSH 是 [RT-Thread](https://github.com/RT-Thread/rt-thread) 的命令行组件，它提供了一套操作界面，使用户可以从命令行与设备进行沟通。它主要用于调试或查看系统信息。


通常情况下，开发调试使用硬件调试器和 `printf` 日志来显示。但在某些情况下，这两种方法并不是很有用，因为它是从运行的内容中抽象出来的，而且它们可能很难解析。不过 RT-Thread 是一个多线程系统，当你想知道一个正在运行的线程的状态，或者手动控制系统的当前状态时，这很有帮助。因为它是多线程的，所以你能够拥有一个交互式的 shell，你可以直接在设备上输入命令、调用函数来获取你需要的信息，或者控制程序的行为。如果你只习惯于 Linux 或 BSD 等现代操作系统，这在你看来可能很普通，但对于硬件黑客来说，这是极其奢侈的，远超将串行电缆直接连线到电路板上以获取一丝错误的做法。


FinSH 有两种模式。


* C 语言解释器模式，称为 c-style。
* 传统的命令行模式，称为 msh（模块 shell）。


在 C 语言解释器模式下，FinSH 可以解析执行大部分 C 语言的表达式，并使用函数调用访问系统上的函数和全局变量。它还可以从命令行创建变量。


在 msh 模式下，FinSH 的操作与 Bash 等传统 shell 类似。


### GNU 命令标准


当我们在开发 FinSH 时，我们了解到，在编写命令行应用程序之前，你需要熟悉 GNU 命令行标准。这个标准实践的框架有助于给界面带入熟悉感，这有助于开发人员在使用时感到舒适和高效。


一个完整的 GNU 命令主要由四个部分组成。


1. 命令名（可执行文件）：命令行程序的名称；
2. 子命令：命令程序的子函数名称。
3. 选项：子命令函数的配置选项。
4. 参数：子命令函数配置选项的相应参数。


你可以在任何命令中看到这一点。以 Git 为例：



```
git reset --hard HEAD~1

```

这一点可以分解为：


![GNU command line standards](/data/attachment/album/202009/29/233147h3c5qxp3x58qccaz.png "GNU command line standards")


可执行的命令是 `git`，子命令是 `reset`，使用的选项是 `--head`，参数是 `HEAD~1`。


再举个例子：



```
systemctl enable --now firewalld

```

可执行的命令是 `systemctl`，子命令是 `enable`，选项是 `--now`，参数是 `firewalld`。


想象一下，你想用 RT-Thread 编写一个符合 GNU 标准的命令行程序。FinSH 拥有你所需要的一切，并且会按照预期运行你的代码。更棒的是，你可以依靠这种合规性，让你可以自信地移植你最喜欢的 Linux 程序。


### 编写一个优雅的命令行程序


下面是一个 RT-Thread 运行命令的例子，RT-Thread 开发人员每天都在使用这个命令：



```
usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]
                      [--upgrade] [--printenv]

optional arguments:
  -h, --help      show this help message and exit
  --force-update  force update and clean packages, install or remove the
                  packages by your settings in menuconfig
  --update        update packages, install or remove the packages by your
                  settings in menuconfig
  --list          list target packages
  --wizard        create a new package with wizard
  --upgrade       upgrade local packages list and ENV scripts from git repo
  --printenv      print environmental variables to check

```

正如你所看到的那样，它看起来很熟悉，行为就像你可能已经在 Linux 或 BSD 上运行的大多数 POSIX 应用程序一样。当使用不正确或不充分的语法时，它会提供帮助，它支持长选项和短选项。这种通用的用户界面对于任何使用过 Unix 终端的人来说都是熟悉的。


### 选项种类


选项的种类很多，按长短可分为两大类。


1. 短选项：由一个连字符加一个字母组成，如 `pkgs -h` 中的 `-h` 选项。
2. 长选项：由两个连字符加上单词或字母组成，例如，`scons- --target-mdk5` 中的 `--target` 选项。


你可以把这些选项分为三类，由它们是否有参数来决定。


1. 没有参数：该选项后面不能有参数。
2. 参数必选：选项后面必须有参数。
3. 参数可选：选项后可以有参数，但不是必需的。


正如你对大多数 Linux 命令的期望，FinSH 的选项解析非常灵活。它可以根据空格或等号作为定界符来区分一个选项和一个参数，或者仅仅通过提取选项本身并假设后面的内容是参数（换句话说，完全没有定界符）。


* `wavplay -v 50`
* `wavplay -v50`
* `wavplay --vol=50`


### 使用 optparse


如果你曾经写过命令行程序，你可能会知道，一般来说，你所选择的语言有一个叫做 optparse 的库或模块。它是提供给程序员的，所以作为命令的一部分输入的选项（比如 `-v` 或 `--verbose`）可以与命令的其他部分进行*解析*。这可以帮助你的代码从一个子命令或参数中获取一个选项。


当为 FinSH 编写一个命令时，`optparse` 包希望使用这种格式：



```
MSH_CMD_EXPORT_ALIAS(pkgs, pkgs, this is test cmd.);

```

你可以使用长形式或短形式，或者同时使用两种形式来实现选项。例如：



```
static struct optparse_long long_opts[] =
{
    {"help"        , 'h', OPTPARSE_NONE}, // Long command: help, corresponding to short command h, without arguments.
    {"force-update",  0 , OPTPARSE_NONE}, // Long comman: force-update, without arguments
    {"update"      ,  0 , OPTPARSE_NONE},
    {"list"        ,  0 , OPTPARSE_NONE},
    {"wizard"      ,  0 , OPTPARSE_NONE},
    {"upgrade"     ,  0 , OPTPARSE_NONE},
    {"printenv"    ,  0 , OPTPARSE_NONE},
    { NULL         ,  0 , OPTPARSE_NONE}
};

```

创建完选项后，写出每个选项及其参数的命令和说明：



```
static void usage(void)
{
    rt_kprintf("usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]\n");
    rt_kprintf("                      [--upgrade] [--printenv]\n\n");
    rt_kprintf("optional arguments:\n");
    rt_kprintf("  -h, --help      show this help message and exit\n");
    rt_kprintf("  --force-update  force update and clean packages, install or remove the\n");
    rt_kprintf("                  packages by your settings in menuconfig\n");
    rt_kprintf("  --update        update packages, install or remove the packages by your\n");
    rt_kprintf("                  settings in menuconfig\n");
    rt_kprintf("  --list          list target packages\n");
    rt_kprintf("  --wizard        create a new package with wizard\n");
    rt_kprintf("  --upgrade       upgrade local packages list and ENV scripts from git repo\n");
    rt_kprintf("  --printenv      print environmental variables to check\n");
}

```

下一步是解析。虽然你还没有实现它的功能，但解析后的代码框架是一样的：



```
int pkgs(int argc, char **argv)
{
    int ch;
    int option_index;
    struct optparse options;

    if(argc == 1)
    {
        usage();
        return RT_EOK;
    }

    optparse_init(&options, argv);
    while((ch = optparse_long(&options, long_opts, &option_index)) != -1)
    {
        ch = ch;

        rt_kprintf("\n");
        rt_kprintf("optopt = %c\n", options.optopt);
        rt_kprintf("optarg = %s\n", options.optarg);
        rt_kprintf("optind = %d\n", options.optind);
        rt_kprintf("option_index = %d\n", option_index);
    }
    rt_kprintf("\n");

    return RT_EOK;
}

```

这里是函数头文件：



```
#include "optparse.h"
#include "finsh.h"

```

然后，编译并下载到设备上。


![Output](/data/attachment/album/202009/29/233154xqmusussoo9vq90s.png "Output")


### 硬件黑客


对硬件进行编程似乎很吓人，但随着物联网的发展，它变得越来越普遍。并不是所有的东西都可以或者应该在树莓派上运行，但在 RT-Thread，FinSH 可以让你保持熟悉的 Linux 感觉。


如果你对在裸机上编码感到好奇，不妨试试 RT-Thread。




---


via: <https://opensource.com/article/20/9/hardware-command-line>


作者：[Alan Smithee](https://opensource.com/users/alansmithee) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


OK
RT-Thread is an open source [real-time operating system](https://opensource.com/article/20/6/open-source-rtos) used for programming Internet of Things (IoT) devices. FinSH is [RT-Thread](https://github.com/RT-Thread/rt-thread)'s command-line component, and it provides a set of operation interfaces enabling users to contact a device from the command line. It's mainly used to debug or view system information.

Usually, development debugging is displayed using hardware debuggers and `printf`

logs. In some cases, however, these two methods are not very useful because it's abstracted from what's running, and they can be difficult to parse. RT-Thread is a multi-thread system, though, which is helpful when you want to know the state of a running thread, or the current state of a manual control system. Because it's multi-threaded, you're able to have an interactive shell, so you can enter commands, call a function directly on the device to get the information you need, or control the program's behavior. This may seem ordinary to you if you're only used to modern operating systems such as Linux or BSD, but for hardware hackers this is a profound luxury, and a far cry from wiring serial cables directly onto boards to get glimpses of errors.

FinSH has two modes:

- A C-language interpreter mode, known as c-style
- A traditional command-line mode, known as
`msh`

(module shell)

In the C-language interpretation mode, FinSH can parse expressions that execute most of the C language and access functions and global variables on the system using function calls. It can also create variables from the command line.

In `msh`

mode, FinSH operates similarly to traditional shells such as Bash.

## The GNU command standard


When we were developing FinSH, we learned that before you can write a command-line application, you need to become familiar with GNU command-line standards. This framework of standard practices helps bring familiarity to an interface, which helps developers feel comfortable and productive when using it.

A complete GNU command consists of four main parts:

**Command name (executable):**The name of the command line program**Sub-command:**The sub-function name of the command program**Options:**Configuration options for the sub-command function**Arguments:**The corresponding arguments for the configuration options of the sub-command function

You can see this in action with any command. Taking Git as an example:

`git reset --hard HEAD~1`


Which breaks down as:

![GNU command line standards GNU command line standards](https://opensource.com/sites/default/files/uploads/command-line-apps_2.png)

(Cathy, CC BY-SA 4.0)

The executable command is **git**, the sub-command is **reset**, the option used is **--head**, and the argument is **HEAD~1**.

Another example:

`systemctl enable --now firewalld`


The executable command is **systemctl**, the sub-command is **enable**, the option is **--now**, and the argument is **firewalld**.

Imagine you want to write a command-line program that complies with the GNU standards using RT-Thread. FinSH has everything you need, and will run your code as expected. Better still, you can rely on this compliance so you can confidently port your favorite Linux programs.

## Write an elegant command-line program

Here's an example of RT-Thread running a command that RT-Thread developers use every day.

```
usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]
[--upgrade] [--printenv]
optional arguments:
-h, --help show this help message and exit
--force-update force update and clean packages, install or remove the
packages by your settings in menuconfig
--update update packages, install or remove the packages by your
settings in menuconfig
--list list target packages
--wizard create a new package with wizard
--upgrade upgrade local packages list and ENV scripts from git repo
--printenv print environmental variables to check
```


As you can tell, it looks familiar and acts like most POSIX applications that you might already run on Linux or BSD. Help is provided when incorrect or insufficient syntax is used, both long and short options are supported, and the general user interface is familiar to anyone who's used a Unix terminal.

## Kinds of options

There are many different kinds of options, and they can be divided into two main categories by length:

**Short options:**Consist of one hyphen plus a single letter, e.g., the`-h`

option in`pkgs -h`

**Long options:**Consist of two hyphens plus words or letters, e.g., the`--target`

option in`scons- --target-mdk5`


You can divide these options into three categories, determined by whether they have arguments:

**No arguments:**The option cannot be followed by arguments**Arguments must be included:**The option must be followed by arguments**Arguments optional:**Arguments after the option are allowed but not required

As you'd expect from most Linux commands, FinSH option parsing is pretty flexible. It can distinguish an option from an argument based on a space or equal sign as delimiter, or just by extracting the option itself and assuming that whatever follows is the argument (in other words, no delimiter at all):

`wavplay -v 50`

`wavplay -v50`

`wavplay --vol=50`


## Using optparse

If you've ever written a command-line application, you may know there's generally a library or module for your language of choice called optparse. It's provided to programmers so that options (such as **-v **or **--verbose**) entered as part of a command can be *parsed* in relation to the rest of the command. It's what helps your code know an option from a sub-command or argument.

When writing a command for FinSH, the `optparse`

package expects this format:

`MSH_CMD_EXPORT_ALIAS(pkgs, pkgs, this is test cmd.);`


You can implement options using the long or short form, or both. For example:

```
static struct optparse_long long_opts[] =
{
{"help" , 'h', OPTPARSE_NONE}, // Long command: help, corresponding to short command h, without arguments.
{"force-update", 0 , OPTPARSE_NONE}, // Long comman: force-update, without arguments
{"update" , 0 , OPTPARSE_NONE},
{"list" , 0 , OPTPARSE_NONE},
{"wizard" , 0 , OPTPARSE_NONE},
{"upgrade" , 0 , OPTPARSE_NONE},
{"printenv" , 0 , OPTPARSE_NONE},
{ NULL , 0 , OPTPARSE_NONE}
};
```


After the options are created, write the command and instructions for each option and its arguments:

```
static void usage(void)
{
rt_kprintf("usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]\n");
rt_kprintf(" [--upgrade] [--printenv]\n\n");
rt_kprintf("optional arguments:\n");
rt_kprintf(" -h, --help show this help message and exit\n");
rt_kprintf(" --force-update force update and clean packages, install or remove the\n");
rt_kprintf(" packages by your settings in menuconfig\n");
rt_kprintf(" --update update packages, install or remove the packages by your\n");
rt_kprintf(" settings in menuconfig\n");
rt_kprintf(" --list list target packages\n");
rt_kprintf(" --wizard create a new package with wizard\n");
rt_kprintf(" --upgrade upgrade local packages list and ENV scripts from git repo\n");
rt_kprintf(" --printenv print environmental variables to check\n");
}
```


The next step is parsing. While you can't implement its functions yet, the framework of the parsed code is the same:

```
int pkgs(int argc, char **argv)
{
int ch;
int option_index;
struct optparse options;
if(argc == 1)
{
usage();
return RT_EOK;
}
optparse_init(&options, argv);
while((ch = optparse_long(&options, long_opts, &option_index)) != -1)
{
ch = ch;
rt_kprintf("\n");
rt_kprintf("optopt = %c\n", options.optopt);
rt_kprintf("optarg = %s\n", options.optarg);
rt_kprintf("optind = %d\n", options.optind);
rt_kprintf("option_index = %d\n", option_index);
}
rt_kprintf("\n");
return RT_EOK;
}
```


Here is the function head file:

```
#include "optparse.h"
#include "finsh.h"
```


Then, compile and download onto a device.

![Output Output](https://opensource.com/sites/default/files/uploads/command-line-apps_3.png)

Cathy, CC BY-SA 4.0)

## Hardware hacking

Programming hardware can seem intimidating, but with IoT it's becoming more and more common. Not everything can or should be run on a Raspberry Pi, but with RT-Thread you can maintain a familiar Linux feel, thanks to FinSH.

If you're curious about coding on bare metal, give RT-Thread a try.

## 1 Comment