读取串行命令需要太多时间

Reading serial commands takes too much time

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

对于整个过程,我得到了一个包含 SerialCommands.h 库的代码库。

问题

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

  1. ?threshold => returns '1400'
  2. !threshold 1234
  3. ?threshold => 没有任何反应
  4. ?threshold => 没有任何反应
  5. ?threshold => returns 'unrecognized command ???threshold'(有关此功能,请参阅我的代码)

编辑:我忘记提及的重要事项。程序恢复后(第5步后)可以正确查询值。

  1. ?threshold => returns '1234'

我还有一个闪烁的状态 LED(以 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();
}

我几乎省略了涵盖 EEPROM 功能的代码,因为我已经确认它不是 slow/unpredictable 行为的来源。

调试尝试

所以主要问题似乎与设置过程的时间有关,因为如果我做的足够慢,它就会起作用。我已经尝试对代码库的所有部分进行计时,但我无法解释设置任何内容时的延迟以及未来请求所需的延迟。设置 EEPROM 值最多需要 75 毫秒,ReadSerial() 太快了,我几乎无法在 10^-3 秒内测量它。

在这一点上,我很确定我在 reading/writing 序列号 buffer/interface 上做错了什么。我在每次 set 调用后尝试 flush()ing 它什么都不做。

我还认为可能是我的 PC 串行接口(USB 端口)以某种方式导致了这个问题,因为这可以解释为什么代码库的任何部分都不会占用太多时间。但是,这与 loop() 延迟且 LED 停止闪烁的事实并不相符。

关于整个问题,对我来说最奇怪的事情之一是串行接口在发送垃圾邮件几次后如何接收命令。 cmd_unrecognized(...) 函数不接收 ?threshold?threshold?threshold???threshold,这对我来说毫无意义。

我知道这个 post 已经很长时间了,但我希望我已经提供了一个相对清晰的问题描述,我希望你们中的任何人都知道如何解决这个耗时的混乱一个问题。

EDIT2:在 SerialCommands.h 调试模式下尝试后,快速输入 !pidp 10 然后 ?pidp:

时得到以下输出
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=[?]

这在某种程度上看起来像完整的 ?pidp 不适合缓冲区,但是,有一个 buffer full 消息并没有显示。

听起来可能是由您的 Python 程序引起的。您是否记得在 1234 整数之后添加一个空终止符 ([=10=])?串行命令通常需要字符串,必须以空终止符结尾。如果不是,您的 Arduino 可能会继续等待终结器,并且仅在发生超时或看门狗导致重置(可能是 4 秒)时停止。这可以解释这种阻碍。

最终我还没有真正发现 out/understood 串行缓冲区的问题,但我已经设法实现了一个基本的解决方法。我不是总是写入 EEPROM(因此最多需要 75 毫秒),而是只将串行输入设置为相应的变量(最多需要 3 毫秒)。为了持久化更改,我添加了一个 !commit 命令来保存所有值。通过此更改,我已经 集中解决了 如果我的程序运行时间太长会干扰串行接口的问题,因此,我将不得不在调用串行接口的实例中处理这个问题。

据我所知总结问题:如果我的代码是 blocking/working,而任何命令被发送到串行接口,串行缓冲区以一种奇怪的、不可预测的方式填充(而不是排队按预期缓冲输入)。归根结底,我只需要避免这种情况。这绝对不是完美的,但它确实有效。感谢任何人的帮助。