Arduino 读取字符串();代码运行缓慢

Arduino readString(); code runs slow

我有以下需要快速执行的代码,但它需要花费大量时间来更改值,无论如何如何使此任务更快?

我正在使用 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);

}

来自 Arduino docs on readString:

Serial.readString() reads characters from the serial buffer into a string. The function terminates if it times out (see setTimeout()).

docs on setTimeout:

Serial.setTimeout() sets the maximum milliseconds to wait for serial data when using Serial.readBytesUntil(), Serial.readBytes(), Serial.parseInt() or Serial.parseFloat(). It defaults to 1000 milliseconds.

这意味着 readString 总是等待 1 秒以确保字符串发送完成并具有完整的字符串。
不幸的是,这意味着它的响应速度很慢。您可以使用 setTimeout 降低超时,但您仍然会有一些延迟,或者如果您将其设置得太低,您可能会得到不完整的 stings。

最好的解决方案是使用 readStringUntil,这样当你得到一个终止符(如换行符)时,你就知道你有一个完整的字符串。

替换

Command = Serial.readString();

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

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

编辑:重要更新见文末。

这可以明显加快,但首先让我们看一下在当前代码的每个循环迭代中必须完成的工作:

  • 作为 ,您可能会在 readString() 中浪费一些时间。
  • 对于每个值,必须发送、读取、解析 15 到 20 个字节并将其转换为 int
  • 对于每个接收到的值(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)。


Update:显然我们不能只使用'x'或一个零字节作为停止字节,因为每个RGB值也可以是一个'x' 或零字节(这里已经晚了:)。虽然可以使用 ReadBytes() 代替,但最好有一个停止字节来保持缓冲区对齐。所以我建议使用 0xff (255) 作为停止字节,并确保 RGB 值的 none 可以是 0xff.

为了以防将来可能有其他消息类型,每条消息也可以在前面加上消息代码(1 或 2 个字节)。

我每次用串口通讯的时候都是readBytesUntil()。 它完成了工作,它总是获得整个字符串,但与 readString() 相同的问题至少需要 1000 毫秒才能完成。

通过减少延迟,同时使用 Serial.setTimeout()Serial.readBytesUntil() 对我来说效果很好。
类似于:

Serial.setTimeout(250);
inData = Serial.readStringUntil('\n');