Arduino readString(); 代码运行缓慢

3
我有以下代码需要快速执行,但更改值却需要很长时间,有没有其他方法可以使这个任务更快?
我使用 indexOf()substring() 来完成此任务。这是为了更改灯带 LED 的颜色。
// declare LED Series A Pins R-G-B (PWM Pins)
  int const AledRedPin = 6;
  int const AledGreenPin = 5;
  int const AledBluePin = 3;

// declare LED Series B Pins R-G-B (PWM Pins)
  int const BledRedPin = 10;
  int const BledGreenPin = 11;
  int const BledBluePin = 9;

// serial input variable & string
// initialise LED Series A Pins R-G-B (PWN Value: 0 to 255)
// initial value = 255
  int AledRed = 255;
  int AledGreen = 255;
  int AledBlue = 255;

// initialise LED Series A Pins R-G-B (PWN Value: 0 to 255)
// initial value = 255
  int BledRed = 255;
  int BledGreen = 255;
  int BledBlue = 255;

//serial input
  String Command = "";

//string manipulation
  int cmdindexval = 0;
  String CommandType = "";
  int CommandValue = 0;
  String Series = "";

void setup() {
  // put your setup code here, to run once:
  // start serial
    Serial.begin(9600);
      while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB
      }

  // set LED Series A Pins as Output R-G-B
    pinMode(AledRedPin, OUTPUT);
    pinMode(AledGreenPin, OUTPUT);
    pinMode(AledBluePin, OUTPUT);

  // set LED Series B Pins as Output R-G-B
    pinMode(BledRedPin, OUTPUT);
    pinMode(BledGreenPin, OUTPUT);
    pinMode(BledBluePin, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  // read from serial if it's available
    if (Serial.available() > 0) {
      Command = Serial.readString(); //read string from serial monitor
      cmdindexval = Command.indexOf('='); //read characters until '=' then assign the value
      CommandType = Command.substring(0, cmdindexval); //assign the value from 0 to cmdindexval
      //Series = Command.substring(0, 1); //read first character
      CommandValue = Command.substring(cmdindexval + 1).toInt(); //assign the value after '=' and convert string to Int
      Serial.println(CommandType + " ,is equal to " + CommandValue + " ,Series: " + Series);    
      //if (Series == "A") {
        if (CommandType == "ACledRed"){
          AledRed = CommandValue;
        }
       else if (CommandType == "ACledGreen"){
          AledGreen = CommandValue;
        }
        else if (CommandType == "ACledRedBlue") {
          AledBlue = CommandValue;
        }
      //}
      //else if (Series == "B") {
        if (CommandType == "BCledRed") {
          BledRed = CommandValue;
        }
        else if (CommandType == "BCledGreen") {
          BledGreen = CommandValue;
        }
        else if (CommandType == "BCledBlue") {
          BledBlue = CommandValue;
        }
     //}
    } //end serial

    analogWrite(AledRedPin, AledRed);
    analogWrite(AledGreenPin, AledGreen);
    analogWrite(AledBluePin, AledBlue);

    analogWrite(BledRedPin, BledRed);
    analogWrite(BledGreenPin, BledGreen);
    analogWrite(BledBluePin, BledBlue);

}

1
你确定 indexOf 是慢的,而不是基于超时的 readString(默认超时为1秒)吗? - gre_gor
我一点也不确定。@gre_gor - The Little Cousin
3个回答

13

从Arduino readString文档:

Serial.readString()函数将字符从串行缓冲区读入字符串。如果该函数超时,则函数终止(请参阅setTimeout())。

以及setTimeout文档:

Serial.setTimeout()设置最大毫秒数,用于在使用Serial.readBytesUntil()、Serial.readBytes()、Serial.parseInt()或Serial.parseFloat()时等待串行数据。默认值为1000毫秒。

这意味着readString总是等待1秒钟,以确保字符串的发送完成并且具有完整的字符串。
不幸的是,这意味着响应速度较慢。您可以通过setTimeout降低超时时间,但仍然会有一些延迟,如果设置得太低,可能会导致不完整的字符串。

最好的解决方案是使用readStringUntil,这样当你收到终止字符(比如换行符)时,就知道你已经得到了一个完整的字符串。
替换为:
Command = Serial.readString();

使用

Command = Serial.readStringUntil('\n');

并确保设置串行监视器以发送换行符。


您好,完成此任务仍需要高达1000毫秒的时间。 :( - The Little Cousin
2
如果您正在发送换行符,则不应该这样做。 - gre_gor
@gre_gor 你救了我的命。 - Alex

6

编辑: 请查看末尾的重要更新。

这可以被显著加快,但首先让我们看看当前代码每次循环迭代都要处理的工作:

  • 正如@gre_gor 已经解释的那样,在readString()中你可能会浪费一些时间。
  • 对于每个值,需要发送、读取、解析和转换为int之间的15到20个字节。
  • 对于每个接收到的值(R、G或B),analogWrite()都会被调用6次(而且analogWrite()并不是真正快速的)。这意味着为了更改两个序列,analogWrite()会被调用36次(这可能是最花时间的地方)。如果没有可用的串行数据,analogWrite()仍然会被调用6次。
  • 而且,例子中还每次调用Serial.println() - 所以最好关闭它。

为了加速这个过程,RGB值可以在一个小缓冲区中发送(假设你也控制发送端),并使用Serial.readBytesUntil()读取。

如果A和B的值一起发送,则6个RGB值可以作为6个字节发送:

byte rcvBuffer[7];

void loop() {

    if (Serial.available() > 0) {

        // message: RGBRGBx - but see update below
        int numRead = Serial.readBytesUntil(0x78, rcvBuffer, 7); // 0x78 is 'x'

        if (numRead == 7) { // or 6, see below

            analogWrite(AledRedPin,   rcvBuffer[0]);
            analogWrite(AledGreenPin, rcvBuffer[1]);
            analogWrite(AledBluePin,  rcvBuffer[2]);

            analogWrite(BledRedPin,   rcvBuffer[3]);
            analogWrite(BledGreenPin, rcvBuffer[4]);
            analogWrite(BledBluePin,  rcvBuffer[5]);
        }
        // else ignore this read - could be a first unaligned read
    }
}

如果只发送A或B的值:

byte rcvBuffer[5];

void loop() {

    // You could probably even remove the Serial.available() check
    if (Serial.available() > 0) {

        // message: TRGBx where T is Type ('A' or 'B')
        int numRead = Serial.readBytesUntil(0x78, rcvBuffer, 5); // 0x78 is 'x'

        if (numRead == 5) {  // or 4, see below

            switch (rcvBuffer[0]) {
                case 'A':
                    analogWrite(AledRedPin,   rcvBuffer[1]);
                    analogWrite(AledGreenPin, rcvBuffer[2]);
                    analogWrite(AledBluePin,  rcvBuffer[3]);
                    break;
                case 'B':
                    analogWrite(BledRedPin,   rcvBuffer[1]);
                    analogWrite(BledGreenPin, rcvBuffer[2]);
                    analogWrite(BledBluePin,  rcvBuffer[3]);
                    break;
                default :
                    // do nothing, or send error message
            }
        }
    }
}

我使用'x'作为停止字节来使其可见,但你也可以使用零字节。

现在,我不确定readBytesUntil()是否将终止字节读入缓冲区或跳过它,并且无法立即测试。但我认为只有RGB值被读入缓冲区。在这种情况下,您将不得不更改这些值以符合我在注释中提供的值。

为了节省更多时间,您可以检查每个值,并仅在自上次调用以来该值发生更改时才调用analogWrite()(对于每个R、G和B)。


更新:显然我们不能只使用'x'或零字节作为停止字节,因为RGB值中的每一个也可能是'x'或零字节(现在已经很晚了 :)。虽然可以使用ReadBytes(),但最好有一个停止字节来保持缓冲区对齐。因此,我建议使用0xff(255)作为停止字节,并确保RGB值中没有一个是0xff

以防未来可能会出现其他消息类型,每个消息还可以以消息代码(1或2个字节)为前缀。


惊人的解释,非常有帮助。:) 但它不能正常工作 xD 试一下并让我知道你是否进行了编辑。非常感谢。 - The Little Cousin

0
每当我使用串口进行通信时,我总是使用readBytesUntil()。它可以完成任务,总是获取整个字符串,但与readString()相同的问题是需要至少1000ms才能完成。
通过同时使用Serial.setTimeout()Serial.readBytesUntil(),我成功地减少了延迟。类似于这样:
Serial.setTimeout(250);
inData = Serial.readStringUntil('\n');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接