简单的命令行界面
组件和用品
| × | 1 |
关于这个项目
命令行
有时您需要找到传感器的值,或者您想告诉您的机器人执行诸如“向左移动”之类的操作。也许你需要向你的程序询问一个变量的运行时值,或者你需要设置一个数字电位器的值。
你需要的是一个命令行。是的,让您的 Arduino 响应文本命令很容易。
这是您可以建立的一个。这是一个简单、快速且非常节省内存的命令行界面 (CLI),您可以在几分钟内将其剪切并粘贴到您自己的代码中并启动并运行。命令由一个名称后跟多个参数组成。当您输入命令时,它甚至支持退格。
快速概览
每个草图都有一个 loop()
功能。您可以像下面一样简单。它在名为 CommandLine.h
的单独选项卡中调用两个例程 .我将引导您完成这两个文件,然后您就可以启动并运行了。
循环
它的作用: 每次loop()
运行它检查我们是否有来自串行端口的命令,调用 getCommandLineFromSerialPort()
.变量 CommandLine
在 CommandLine.h
中声明 但从风格上讲,您可能希望将其移至主循环选项卡。当一个完整的命令到达串口并被复制到命令缓冲区时:
char commandLine[COMMAND_BUFFER_LENGTH + 1];
然后loop()
调用 DoMyCommand()
执行正确的命令。
现在让我们来看看 CommandLine.h
中的内容 .我应该指出,我把所有的代码都放到了 CommandLine.h
,因为那么您所要做的就是将此代码剪切并粘贴到 Arduino IDE 中的一个新选项卡中(确保为该选项卡命名以“.h
”结尾 ”。然后将此文件包含在您的主文件中,即
#include "CommandLine.h"
这样做是允许您将所有命令行代码放在一个选项卡中,而无需任何额外代码即可在程序中的其他地方引用其例程。
查看 CommandLine.h
文件 CommandLine.h
包含在这篇文章的末尾。在 CommandLine.h
内 , 每一行需要修改的地方都标有注释,//Modify here
.该文件包含两个示例命令 add
和 sub
并显示如何从 DoMyCommand
中调用它们 .
对于你们中的许多人来说,这就是你所需要的。只需通过 CommandLine.h
.对于那些想要更详细外观的人,请继续阅读。
看得更深
在 CommandLine.h
内 我们首先包含
. String.h
是一个 C 标准库 .如果您之前没有接触过 C 库,请在 Internet 上快速搜索“C 编程语言”。 C 圣经由布赖恩·克尼根 (Brian Kernigan) 和丹尼斯·里奇 (Dennis Ritchie) 多年前编写,并且一直在更新。大多数人都拥有一份副本,但您可以在网上免费找到它。
我们将只使用 strtok()
中的例程(字符串到标记) .此例程读取一个标记,即由某些字符分隔的单词(strtok
的第二个参数 )。它是这样工作的。
- 当你第一次调用它时,你传递了一个字符串
ptr
它将返回第一个令牌
- 在随后的调用中,(这里是辉煌的黑客部分)将它传递给 NULL 而不是字符串
ptr
并且它将从初始字符串的中断处继续,从而一次获得一个标记(大约一个单词)。
我们还包括
从中我们只使用 atoi()
用于 ascii 到整数的转换。别担心,编译器只会包含这个例程,而不是整个库,但您可能想查看这些库中的其他例程,因为它们很有用。
接下来是我写的一个可选的小宏,叫做 print2:
#define print2(x,y) (Serial.print(x), Serial.println(y)
我一直想打印一个标签和一个字符串。你像这样使用它:
print2("myVar =", myVar);
如果 myVar 是 25,那么这将打印到串行窗口:
myVar =25
CommandLine.h
包含 getCommandLineFromSerialPort()
它从串行端口组装命令行。每次调用它从串行端口读取并将输入存储到全局输入缓冲区中,CommandLine[]
.当它到达 return
表示命令结束的字符,它返回 true
.
完全组装的缓冲区现在传递给 DoMyCommand()
它找出用户请求的功能并调用它。使用大量命令,您可以获得相当笨拙的 if-the-else 语句。
如果你有大量的命令,有很多方法可以加快速度,比如采用所谓的散列函数。或者,您可以将命令设置为只有一个字符,然后使用该字符作为 switch-case 语句的标签。我发现这些都不是经常需要的。单词比单个字符更容易记住,考虑到这是一个 Arduino,在芯片空间不足之前,您真的可以拥有多少个命令?
添加和删除
每个命令函数负责解析它自己的参数。这是一种简单的方法,并且很容易修改。另一种方法是立即读取 DoMyCommand
中的所有参数 .您可以将单个参数放入一个全局字符串数组 argv[]
.
出于本示例的目的,我定义了两个命令:add
和 sub
.这两个都使用数字参数,但我已经包含了 readWord
(可以称为 readStringToken
) 返回一个单词。你也可以修改这个 readStringToken
允许诸如“this is a string
”之类的字符串 ". 把它当作留给读者的练习。
最快的跑步方式
如果您有如上所示的主循环文件,则使用 Arduino IDE 窗口右侧的向下三角形方式创建一个新选项卡,并复制 CommandLine.h
(下面)文件放入其中,您应该可以输入 add
和 sub
命令。
现在就看你了!
代码
- 简单的 Arduino 命令行解释器
简单的 Arduino 命令行解释器C/C++
请参阅本文或代码中的评论,向您的 Arduino 草图添加一个简单的命令行。您可以添加如下命令:
添加 5, 10
减去 10, 5
或者任何你需要的东西
/********************************************* ************************************ 如何使用命令行:创建草图。查看下面的示例设置和主循环代码,并将其复制并粘贴到新草图中。创建一个新选项卡。 (使用 Arduino 编辑器最右侧的下拉菜单(小三角形)。命名选项卡 CommandLine.h 将此文件粘贴到其中。测试:像往常一样将您刚刚创建的草图下载到您的 Arduino 并打开串行窗口。输入这些命令然后回车:add 5, 10 minus 10, 5 查看包含的加减命令,然后自己写!********************* ****************************************************** ****** 这是幕后发生的事情***************************************** **************************************** 简单明了的命令行解释器这个文件将允许您在串行窗口中键入命令,例如,将 23,599 闪烁 5 playSong Yesterday 添加到您在 Arduino 上运行的草图中并执行它们。实施说明:这将使用 C 字符串而不是字符串对象,这是基于假设您需要命令行解释器,您可能也缺乏空间,并且 String 对象往往空间效率低下。 1) 简单协议命令是用空格或逗号分隔的单词和数字 第一个单词是命令,每个附加的单词是一个参数 "\n" 终止每个命令 2) 使用 C 库例程 strtok:命令是由以下字符分隔的单词空格或逗号。由某些字符(如空格或逗号)分隔的单词称为标记。为了一一获取令牌,我使用了 C lib 路由 strtok(C stdlib.h 的一部分,请参见下文如何包含它)。它是 C 语言库的一部分,您可以在线查找。基本上你:1)向它传递一个字符串(以及你使用的分隔符,即空格和逗号),它将在后续调用中返回字符串 2)中的第一个标记,将它传递给 NULL(而不是字符串 ptr),它将使用初始字符串从停止的地方继续。我已经编写了几个基本的帮助程序: readNumber:使用 strtok 和 atoi(atoi:ascii 到 int,也是 C stdlib.h 的一部分)返回一个整数。请注意,atoi 返回一个整数,如果您使用像 uint8_t 这样的 1 字节整数,则必须获得 lowByte()。 readWord:返回一个文本词的ptr 4) DoMyCommand:每个命令的if-then-elses 列表。如果所有命令都是单个字符,则可以将其设为 case 语句。使用一个词更具可读性。出于本示例的目的,我们有: Add Subtract nullCommand*//******************sample main loop code ************* ************************ #include "CommandLine.h" void setup() { Serial.begin(115200); } void loop() { bool received =getCommandLineFromSerialPort(CommandLine); //全局命令行定义在CommandLine.h中 if (received) DoMyCommand(CommandLine); }**************************************************** *********************************////命名此选项卡:CommandLine.h#include #include //下面这个宏有利于调试,例如print2("myVar=", myVar);#define print2(x,y) (Serial.print(x), Serial.println(y))#define CR '\r'#define LF '\n'#define BS '\b'#define NULLCHAR '\0'#define SPACE ' '#define COMMAND_BUFFER_LENGTH 25 //传入命令的串行缓冲区长度char CommandLine[COMMAND_BUFFER_LENGTH + 1]; //从串行读取命令到这个缓冲区。 +1 终止符的长度 charconst char *delimiters =", \n"; //命令可以用回车、空格或逗号分隔/***************************************** ****************************************************** ********************** 你的命令名称在这里*/const char *addCommandToken ="add"; //修改这里const char *subtractCommandToken ="sub"; //这里修改/************************************************* ****************************************************** ************** getCommandLineFromSerialPort() 返回下一个命令的字符串。命令由回车分隔“处理退格字符使所有字符小写**************************************** ****************************************************** **********************/boolgetCommandLineFromSerialPort(char * commandLine){ static uint8_t charsRead =0; //注意:COMAND_BUFFER_LENGTH 必须小于 255 个字符长 //异步读取直到完整的命令输入 while (Serial.available()) { char c =Serial.read(); switch (c) { case CR://现在缓冲区中可能有完整的命令,命令由 CR 和/或LS case LF:commandLine[charsRead] =NULLCHAR; //null 终止我们的命令字符数组 if (charsRead> 0) { charsRead =0; //charsRead 是静态的,所以必须重置 Serial.println(commandLine); return true; } break; case BS:// 在输入中处理退格:在最后一个字符中放置一个空格 if (charsRead> 0) { // 并调整 commandLine 和 charsRead commandLine[--charsRead] =NULLCHAR; Serial < 总和为 =", 结果); } else { if (strcmp(ptrToCommandName,subtractCommandToken) ==0) { //这里修改 result =subtractCommand(); //K&R string.h pg.第251话} else { nullCommand(ptrToCommandName); } }}
制造工艺