Arduino命令解析库(Commander)的使用和实现如何高效优化?

摘要:Arduino命令解析库(Commander)的使用和实现 在嵌入式系统中,特别是在Arduino平台上,命令解析是一种常见的通信方式。 本文将介绍一个简单的Arduino命令解析库(Commander),并提供使用示例、头文件和源文件。
Arduino命令解析库(Commander)的使用和实现 在嵌入式系统中,特别是在Arduino平台上,命令解析是一种常见的通信方式。 本文将介绍一个简单的Arduino命令解析库(Commander),并提供使用示例、头文件和源文件。 内容摘自simpleFOC的Commander部分。 使用示例 以下是一个简单的使用示例,演示如何在Arduino中使用Commander库: #include <Commander.h> Commander command = Commander('\n', false); Commander command2 = Commander('\n', false); // 一级命令 void doL(char *cmd) { // L-0.02 // 取出cmd中的带符号浮点数 float value_1 = atof(cmd); Serial.printf("L: %f\n", value_1); // 将后续目录进行解析 command2.run(cmd); }; // 二级命令 void doLA(char *cmd) { // LA-0.02 // 取出cmd中的带符号浮点数 float value_1 = atof(cmd); Serial.printf("LA: %f\n", value_1); }; void setup() { Serial.begin(115200); // 添加新命令 (命令关键字,回调函数,标签文本) command.add('L', doL, "getL"); command2.add('A', doLA, "getLA"); delay(100); } void loop() { delay(2); command.run(Serial); } 在这个示例中,我们创建了两个Commander实例:command和command2,分别用于处理一级命令和二级命令。通过定义回调函数 doL 和 doLA,我们可以在收到相应命令时执行自定义的操作。 Commander.h #ifndef COMMANDS_H #define COMMANDS_H #include "Arduino.h" // commander configuration #define CMD_SCAN '?' //!< command scaning the network - only for commander #define CMD_VERBOSE '@' //!< command setting output mode - only for commander #define CMD_DECIMAL '#' //!< command setting decimal places - only for commander #define MAX_COMMAND_LENGTH 20 // Commander用于显示用户类型的详细信息 enum VerboseMode : uint8_t { nothing = 0x00, // 无显示 - 适合监控 on_request = 0x01, // 仅在用户请求时显示 user_friendly = 0x02 // 向用户显示文本消息 }; // 回调函数指针定义 typedef void (*CommandCallback)(char *); //!< 命令回调函数指针 /** * 这是一个Commander类,它实现了基于IDvalue(例如"AB5.321" - 命令ID A,子命令ID B,值 5.321)的字符串通信协议。 * - 该类可以与HardwareSerial实例结合使用,它将读写,或者可以用来解析从用户外部获得的字符串。 * - Commander还提供了一个非常简单的命令>回调接口,使用户可以将回调函数附加到某个命令ID,详见函数add()。 */ class Commander { public: /** * 默认构造函数接收一个串行接口,它用于输出值。 * 如果使用run()函数,它将使用此串行实例读取串行用户命令。 * * @param serial - 串行通信端口实例 * @param eol - 换行符号字符 * @param echo - 回显最后一个输入字符(用于命令行反馈) */ Commander(Stream &serial, char eol = '\n', bool echo = false); Commander(char eol = '\n', bool echo = false); /** * run()函数,它读取串行端口并触发已添加到Commander的回调函数,当用户请求它们时,即当他发送命令时。 * * 它有默认命令(字母可以在commands.h文件中更改) * '@' - 详细模式 * '#' - 小数位数 * '?' - 扫描命令 - 显示所有附加节点的标签。 */ void run(); /** * run()函数,它读取用户输入的字符串并触发已添加到Commander的回调函数, * 当用户请求它们时,即当他发送命令时。 * * 它有默认命令(字母可以在commands.h文件中更改) * '@' - 详细模式 * '#' - 小数位数 * '?' - 扫描命令 - 显示所有附加节点的标签。 * @param reader - 读取用户输入的临时流 * @param eol - 临时的换行符。 */ void run(Stream &reader, char eol = '\n'); /** * run()函数,它读取用户输入的字符串并触发已添加到Commander的回调函数, * 当用户请求它们时,即当他发送命令时。 * * 它有默认命令(字母可以在commands.h文件中更改) * '@' - 详细模式 * '#' - 小数位数 * '?' - 扫描命令 - 显示所有附加节点的标签。 * @param user_input - 用户输入的字符串。 */ void run(char *user_input); /** * add()函数,用于在Commander上添加具有命令ID的回调函数。 * * @param id - char命令字母 * @param onCommand - void函数指针(char *) * @param label - 发送扫描命令时显示的字符串标签(可选) */ void add(char id, CommandCallback onCommand, char *label = nullptr); // 输出变量 VerboseMode verbose = VerboseMode::user_friendly; //!< 标志该命令应输出用户易于理解的文本的标志 uint8_t decimal_places = 3; //!< 用于显示数字时使用的小数位数 // 监视函数 Stream *com_port = nullptr; //!< 如果提供了,则是串行端口终端变量 char eol = '\n'; //!< 结束符字符 bool echo = false; //!< 回显最后一个输入的字符(用于命令行反馈) private: // 订阅的命令回调变量 CommandCallback call_list[20]; //!< 存储命令回调函数指针的数组,20是任意数字 char call_ids[20]; //!< 已添加的回调命令 char *call_label[20]; //!< 已添加的回调标签 int call_count = 0; //!< 订阅的回调函数数量 // 帮助串行通信读取的变量 char received_chars[MAX_COMMAND_LENGTH] = {0}; //!< 目前接收到的用户信息,等待换行 int rec_cnt = 0; //!< 接收到的字符数量 // 串行打印功能 /** * 如果verbose模式打开,则仅打印字符串消息 * @param message - 要打印的消息 */ void printVerbose(const char *message); /** * 仅在verbose模式打开时打印字符串消息 * - 处理由F宏定义的字符串的函数 * @param message - 要打印的消息 */ void printVerbose(const __FlashStringHelper *message); /** * 使用所需的小数点数将数字打印到串行 * @param message - 要打印的数字 */ void print(const float number); void print(const int number); void print(const char *message); void print(const __FlashStringHelper *message); void print(const char message); void println(const float number); void println(const int number); void println(const char *message); void println(const __FlashStringHelper *message); void println(const char message); void printError(); bool isSentinel(char ch); }; #endif Commander.cpp #include "Commander.h" Commander::Commander(Stream &serial, char eol, bool echo) { com_port = &serial; this->eol = eol; this->echo = echo; } Commander::Commander(char eol, bool echo) { this->eol = eol; this->echo = echo; } void Commander::add(char id, CommandCallback onCommand, char *label) { call_list[call_count] = onCommand; call_ids[call_count] = id; call_label[call_count] = label; call_count++; } void Commander::run() { if (!com_port) return; run(*com_port, eol); } void Commander::run(Stream &serial, char eol) { Stream *tmp = com_port; // save the serial instance char eol_tmp = this->eol; this->eol = eol; com_port = &serial; // a string to hold incoming data while (serial.available()) { // get the new byte: int ch = serial.read(); received_chars[rec_cnt++] = (char)ch; // end of user input if (echo) print((char)ch); if (isSentinel(ch)) { // execute the user command run(received_chars); // reset the command buffer received_chars[0] = 0; rec_cnt = 0; } if (rec_cnt >= MAX_COMMAND_LENGTH) { // prevent buffer overrun if message is too long received_chars[0] = 0; rec_cnt = 0; } } com_port = tmp; // reset the instance to the internal value this->eol = eol_tmp; } void Commander::run(char *user_input) { // execute the user command char id = user_input[0]; switch (id) { case CMD_SCAN: for (int i = 0; i < call_count; i++) { print(call_ids[i]); print(":"); if (call_label[i]) println(call_label[i]); else println(""); } break; case CMD_VERBOSE: if (!isSentinel(user_input[1])) verbose = (VerboseMode)atoi(&user_input[1]); printVerbose(F("Verb:")); switch (verbose) { case VerboseMode::nothing: println(F("off!")); break; case VerboseMode::on_request: case VerboseMode::user_friendly: println(F("on!")); break; } break; case CMD_DECIMAL: if (!isSentinel(user_input[1])) decimal_places = atoi(&user_input[1]); printVerbose(F("Decimal:")); println(decimal_places); break; default: for (int i = 0; i < call_count; i++) { if (id == call_ids[i]) { call_list[i](&user_input[1]); break; } } break; } } bool Commander::isSentinel(char ch) { if (ch == eol) return true; else if (ch == '\r') { printVerbose(F("Warn: \\r detected! \n")); } return false; } void Commander::print(const int number) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->print(number); } void Commander::print(const float number) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->print((float)number, (int)decimal_places); } void Commander::print(const char *message) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->print(message); } void Commander::print(const __FlashStringHelper *message) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->print(message); } void Commander::print(const char message) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->print(message); } void Commander::println(const int number) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->println(number); } void Commander::println(const float number) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->println((float)number, (int)decimal_places); } void Commander::println(const char *message) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->println(message); } void Commander::println(const __FlashStringHelper *message) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->println(message); } void Commander::println(const char message) { if (!com_port || verbose == VerboseMode::nothing) return; com_port->println(message); } void Commander::printVerbose(const char *message) { if (verbose == VerboseMode::user_friendly) print(message); } void Commander::printVerbose(const __FlashStringHelper *message) { if (verbose == VerboseMode::user_friendly) print(message); } void Commander::printError() { println(F("err")); } 在源文件中,我们实现了Commander类的构造函数和一些成员函数,其中包括添加命令、运行解析器等功能。这个简单的库通过串口与用户进行交互,解析用户输入的命令,并根据命令执行相应的回调函数。 在使用Commander库时,用户只需要定义自己的回调函数,并在setup函数中添加命令即可。这样,Arduino就可以通过串口接收命令并执行相应操作,实现了简单而灵活的命令解析功能。