​由于物联网 (IoT) 的兴起,编程硬件变得更加普遍。RT-Thread 允许您使用 FinSH 从 Linux 命令行联系设备。RT-Thread 是一个开源实时操作系统, 用于对物联网 (IoT) 设备进行编程。

FinSH 是RT-Thread的命令行组件,它提供了一组操作接口,使用户可以通过命令行联系设备。主要用于调试或查看系统信息。

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

FinSH 有两种模式:

  • 一种C语言解释器模式,称为c-style

  • 传统的命令行模式,称为msh(模块外壳)

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

msh模式下,FinSH 的运行方式与 Bash 等传统 shell 类似。

GNU 命令标准

当我们开发 FinSH 时,我们了解到在编写命令行应用程序之前,您需要熟悉 GNU 命令行标准。这个标准实践框架有助于提高界面的熟悉度,从而帮助开发人员在使用它时感到舒适和高效。

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

  1. 命令名称(可执行):命令行程序的名称

  2. 子命令:命令程序的子功能名称

  3. Options:子命令功能的配置选项

  4. Arguments:子命令功能的配置选项对应的参数

您可以使用任何命令来查看此操作。以Git为例:

git reset --hard HEAD~1

其分解为:

GNU 命令标准

可执行命令是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. 短选项:由一个连字符加一个字母组成,例如,-h选项pkgs -h

  2. 长选项:由两个连字符加单词或字母组成,例如,--target选项scons- --target-mdk5

您可以将这些选项分为三类,具体取决于它们是否有参数:

  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"

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

物联网技术:从 Linux 命令行对硬件进行编程

硬件黑客

对硬件进行编程似乎令人生畏,但随着物联网的发展,它变得越来越普遍。并非所有内容都可以或应该在 Raspberry Pi 上运行,但借助 FinSH,借助 RT-Thread,您可以保持熟悉的 Linux 感觉。

如果您对裸机编码感兴趣,请尝试一下 RT-Thread。