C++ 读取串行命令花费太多时间

C++ 读取串行命令花费太多时间,c++,arduino,serial-port,microcontroller,stm32,C++,Arduino,Serial Port,Microcontroller,Stm32,所以下面的问题是:我目前正在为我的NucleoF207ZG编写一个小程序,它通过串行端口为其他服务提供接口。我的程序的目标是使用?variable等命令公开数据,并使用设置值!变量(例如?threshold返回1400,并且!threshold 1234将threshold设置为1234)。此外,我的程序中的变量与EEPROM同步,以保存数据 在整个过程中,我得到了一个包含库的代码库 问题 主要问题是,如果在设置后立即执行其他命令,则设置值会占用太多时间,并破坏串行缓冲区(或类似的东西)。我已经

所以下面的问题是:我目前正在为我的NucleoF207ZG编写一个小程序,它通过串行端口为其他服务提供接口。我的程序的目标是使用
?variable
等命令公开数据,并使用
设置值!变量
(例如
?threshold
返回1400,并且
!threshold 1234
将threshold设置为1234)。此外,我的程序中的变量与EEPROM同步,以保存数据

在整个过程中,我得到了一个包含库的代码库

问题 主要问题是,如果在设置后立即执行其他命令,则设置值会占用太多时间,并破坏串行缓冲区(或类似的东西)。我已经用python编写了一些单元测试(使用
pyserial
),它们尽可能频繁地执行get请求(例如
?threshold
)。但是,如果执行set命令(如
!threshold 1400
),则在发出任何其他请求之前,我需要至少等待四秒钟,否则串行接口/缓冲区似乎会丢失一些数据。如果我试图在Arduino串行监视器上发出任何set->get请求,也会发生同样的情况。下面是一个简短的例子:

  • ?阈值
    =>返回“1400”
  • !阈值1234
  • ?阈值
    =>什么都没有发生
  • ?阈值
    =>什么都没有发生
  • ?threshold
    =>返回“无法识别的命令
    ???threshold
    ”(有关此功能,请参阅我的代码)
  • 编辑:我忘了提一些重要的事情。程序恢复后(步骤5后),可以正确查询值

  • ?阈值
    =>返回“1234”
  • 我还有一个闪烁状态指示灯(以500毫秒为单位),如果我设置了一些东西,闪烁会明显停止大约一秒钟

    代码 下面是我的代码库的(非工作)简化示例:

    #include <SimpleTimer.h>    // https://github.com/marcelloromani/Arduino-SimpleTimer
    #include <SerialCommands.h> // https://github.com/ppedro74/Arduino-SerialCommands/
    #include <EEPROM.h>
    #include "EEPROMAnything.h"
    char serial_command_buffer_[64];
    
    SerialCommands serial_commands_(&Serial, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " ");
    
    SimpleTimer timer;
    int filter_threshold;
    
    void cmd_unrecognized(SerialCommands* sender, const char* cmd) {
      sender->GetSerial()->print("Unrecognized command [");
      sender->GetSerial()->print(cmd);
      sender->GetSerial()->println("]");
    }
    
    void set_arg(SerialCommands* sender, int& injectedVar) {
      char* temp = sender->Next();
      injectedVar = atoi(temp);
    }
    
    void set_arg(SerialCommands* sender, int& injectedVar, int address) {
      set_arg(sender, injectedVar);
      EEPROM_writeAnything(address, injectedVar);
    }
    
    void echo(SerialCommands* sender, int var) { sender->GetSerial()->println(var); }
    void echo(SerialCommands* sender, String var) { sender->GetSerial()->println(var); }
    
    void get_pressure_threshold(SerialCommands* sender) { echo(sender, filter_threshold); } //?threshold
    void set_pressure_threshold(SerialCommands* sender) { set_arg(sender, filter_threshold, ADDR_THRESHOLD); } //!threshold <int>
    
    void main_timer() {
      mock_changes();
    }
    
    SerialCommand cmd_getpressthreshold("?threshold", get_pressure_threshold);
    SerialCommand cmd_setpressurethreshold("!threshold", set_pressure_threshold);
    
    void add_serial_commands() {
      serial_commands_.SetDefaultHandler(cmd_unrecognized);
      serial_commands_.AddCommand(&cmd_getpressthreshold);
      serial_commands_.AddCommand(&cmd_setpressurethreshold);
    }
    
    void setup() {
      pinMode(PB0, OUTPUT);
      pinMode(PB7, OUTPUT);
      pinMode(PB14, OUTPUT);
    
      Serial.begin(9600);
    
      timer.setInterval(500, main_timer);
      add_serial_commands();
    }
    
    void loop() {
      serial_commands_.ReadSerial();
      timer.run();
    }
    

    这看起来像是完整的
    ?pidp
    不适合缓冲区,但是,有一条
    缓冲区已满的
    消息没有显示出来。

    听起来可能是Python程序造成的。您是否记得在
    1234
    整数之后添加空终止符(
    \0
    )?串行命令通常需要字符串,必须以空终止符结尾。如果不是这样,您的Arduino可能会继续等待终结者,并且只有在超时或看门狗导致重置(可能是4秒)时才会停止。这就解释了阻塞。

    最后,我还没有真正发现/理解串行缓冲区的问题,但我已经设法实现了一个基本的解决方法。我没有总是写入EEPROM(因此需要75毫秒),而是将串行输入设置为各个变量(最多需要3毫秒)。为了保持更改,我添加了一个
    !提交保存所有值的命令。有了这个改变,我集中处理了如果我的程序花费太长时间就会弄乱串行接口的问题,因此,我必须在调用串行接口的实例中处理这个问题


    总结一下我所知道的问题:如果我的代码在向串行接口发送任何命令时阻塞/工作,串行缓冲区将以一种奇怪的、不可预测的方式填充(而不是按预期将缓冲区输入排队)。最后我不得不避免这个案子。这绝对不是完美的,但它是有效的。谢谢大家的帮助。

    正如我所提到的,我不仅在python中测试过它,而且在Arduino IDE串行监视器中也测试过它,所以这绝对不是python的问题。(我还测试了添加
    \0
    ,我没有像预期的那样更改任何内容)。此外,即使响应/请求被破坏,也会设置这些值(我忘了提到这一点,请参见我的编辑)。我的意思是,我认为问题可能出在您与Arduino交谈的方式上。您似乎复制了Github SerialCommand示例,因此它应该可以工作。为了进一步调试,您可以在代码中定义
    SERIAL\u命令\u DEBUG==1
    。在from
    第31行中,是每次调用
    serial_命令时运行的代码可以打印调试消息,这可以让您很好地了解出错的地方。在调试模式下运行几次后,我发现了两件事:1。一旦程序接收到命令,它就必须将其与所有其他命令进行比较(>30)。由于我在get命令之后设置了set命令,因此它必须更长时间地查找set命令。然而,我不完全确定仅仅30次迭代对我的arduino董事会来说是否真的有那么多工作。。。也许
    Serial.print(…)
    命令真的很慢。2.我想我已经得到了关于失败尝试的混乱输出的线索。我认为缓冲区的清除速度不够快。我会进一步调查!好的,我已经在文章中添加了调试输出(见EDIT2),但这并没有让我更接近于完全理解手头的问题。你确定这就是Arduino的全部输出吗?您所说的快速输入
    是什么意思?多快?它显示缓冲区有63字节长,所以我认为这不是问题所在。您应该记住,SerialCommand是基于字符串处理的,这在小型处理器上非常慢。
    Read: bufLen=63 bufPos=0 termPos=0 ch=[!]
    Read: bufLen=63 bufPos=1 termPos=0 ch=[p]
    Read: bufLen=63 bufPos=2 termPos=0 ch=[i]
    Read: bufLen=63 bufPos=3 termPos=0 ch=[d]
    Read: bufLen=63 bufPos=4 termPos=0 ch=[p]
    Read: bufLen=63 bufPos=5 termPos=0 ch=[ ]
    Read: bufLen=63 bufPos=6 termPos=0 ch=[1]
    Read: bufLen=63 bufPos=7 termPos=0 ch=[0]
    Read: bufLen=63 bufPos=8 termPos=0 ch=#13
    Read: bufLen=63 bufPos=9 termPos=1 ch=#10
    Received: [!pidp 10]
    Matched #30
    Read: bufLen=63 bufPos=0 termPos=0 ch=[?]