使用Arduino将serial.read()转换为可用的字符串?


71

我正在使用两个Arduino,通过newsoftserial和RF收发器相互发送纯文本字符串。

每个字符串的长度可能为20-30个字符。如何将其转换Serial.read()为字符串,以便可以执行操作if x == "testing statements"等等?


请在下面检查我的答案,它比您选择的答案更直接/更简单
Ihab 2014年

Answers:


62

带有Seri​​al.Read()的帮助中获取字符串

char inData[20]; // Allocate some space for the string
char inChar=-1; // Where to store the character read
byte index = 0; // Index into array; where to store the character

void setup() {
    Serial.begin(9600);
    Serial.write("Power On");
}

char Comp(char* This) {
    while (Serial.available() > 0) // Don't read unless
                                   // there you know there is data
    {
        if(index < 19) // One less than the size of the array
        {
            inChar = Serial.read(); // Read a character
            inData[index] = inChar; // Store it
            index++; // Increment where to write next
            inData[index] = '\0'; // Null terminate the string
        }
    }

    if (strcmp(inData,This)  == 0) {
        for (int i=0;i<19;i++) {
            inData[i]=0;
        }
        index=0;
        return(0);
    }
    else {
        return(1);
    }
}

void loop()
{
    if (Comp("m1 on")==0) {
        Serial.write("Motor 1 -> Online\n");
    }
    if (Comp("m1 off")==0) {
        Serial.write("Motor 1 -> Offline\n");
    }
}

如果这不起作用,请尝试添加inData.trim(),以防我们使用arduino控制台,会有换行符。这个stackoverflow.com/questions/24961402/…对我
有用

这不行。循环内的第二个“ if”语句将永远不会触发,因为您已经从第一个“ if”比较中读取了串行数据。
李维·罗伯茨

113

读取无限字符串

  String content = "";
  char character;

  while(Serial.available()) {
      character = Serial.read();
      content.concat(character);
  }

  if (content != "") {
    Serial.println(content);
  }

在Arduino Leonardo上比其他任何阅读方法都更可靠。由于concat可能会在RAM使用情况上出现问题,但是如果草图可以使用它,则这似乎是执行此操作的最佳方法。
Daniel F

23
非常有用和简单。但是,我发现我在串行读取每个字符之间必须稍加延迟-否则它将每个字符打印在单独的行上,而不是串联在一起。 void setup() { Serial.begin(9600); // Initialize serial port } void loop() { String content = ""; char character; while(Serial.available()) { character = Serial.read(); content.concat(character); delay (10); } if (content != "") { Serial.println(content); } }
所以它

4
如果要将这段代码移到一个名为from的单独函数中loop(),则非常重要,return(content);而不是对其进行Serial.println()赋值。否则,您将陷入无限循环,因为它将捕获函数打印到Serial的所有内容,并尝试对其进行重新处理。
depwl9992

如果仍然无法使用,请尝试使用该content.trim()方法
Yakob Ubaidi 2014年

2
您必须在打印后清除内容,否则,即使尚未到达新的串行数据,也会再次执行if块。
米哈祖尔·哈德(Md。Minhazul Haque)

54

您可以在Arduino上使用Serial.readString()Serial.readStringUntil()解析来自Serial的字符串。

您还可以使用Serial.parseInt()从串行读取整数值。

int x;
String str;

void loop() 
{
    if(Serial.available() > 0)
    {
        str = Serial.readStringUntil('\n');
        x = Serial.parseInt();
    }
}

通过串行发送的值将为my string\n5,结果将为str = "my string"x = 5


2
尝试手动将串行输入读入字符缓冲区时,我遇到了奇怪的电压波动,但是使用Serial.readStringUntil()它来解决所有问题,并使代码更具可读性!谢谢!
Eddie Fletcher

1
我进行了测试,是的,它更容易。但是,与字符缓冲区相比,此操作明显需要更多的时间和延迟。
Toni Tegar Sahidi

有趣的是,您能否分享有关您的发现的更多详细信息以及我们在这里谈论的时间有多大差异?如果您有详细的电话号码,请与我们分享。谢谢 !
Ihab

12

我自己问了同样的问题,经过一番研究,我发现了类似的问题。

对我来说,它就像是一种魅力。我用它来遥控我的Arduino。

// Buffer to store incoming commands from serial port
String inData;

void setup() {
    Serial.begin(9600);
    Serial.println("Serial conection started, waiting for instructions...");
}

void loop() {
    while (Serial.available() > 0)
    {
        char recieved = Serial.read();
        inData += recieved; 

        // Process message when new line character is recieved
        if (recieved == '\n')
        {
            Serial.print("Arduino Received: ");
            Serial.print(inData);

            // You can put some if and else here to process the message juste like that:

            if(inData == "+++\n"){ // DON'T forget to add "\n" at the end of the string.
              Serial.println("OK. Press h for help.");
            }   


            inData = ""; // Clear recieved buffer
        }
    }
}

1
比最好的更好:-)
耶万南瑟姆(Jeevanantham)2014年

4

这样会更容易:

 char data [21];
 int number_of_bytes_received;

 if(Serial.available() > 0)
 {
   number_of_bytes_received = Serial.readBytesUntil (13,data,20); // read bytes (max. 20) from buffer, untill <CR> (13). store bytes in data. count the bytes recieved.
   data[number_of_bytes_received] = 0; // add a 0 terminator to the char array
 } 

 bool result = strcmp (data, "whatever");
 // strcmp returns 0; if inputs match.
 // http://en.cppreference.com/w/c/string/byte/strcmp


 if (result == 0)
 {
   Serial.println("data matches whatever");
 } 
 else 
 {
   Serial.println("data does not match whatever");
 }

2

最好和最直观的方法是使用Arduino定义的serialEvent()回调以及loop()和setup()。

我不久前建立了一个小的库来处理消息接收,但是从来没有时间开源它。该库接收\ n代表命令的终止行和任意有效载荷,以空格分隔。您可以对其进行调整以轻松使用自己的协议。

首先,一个库SerialReciever.h:

#ifndef __SERIAL_RECEIVER_H__
#define __SERIAL_RECEIVER_H__

class IncomingCommand {
  private:
    static boolean hasPayload;
  public:
    static String command;
    static String payload;
    static boolean isReady;
    static void reset() {
      isReady = false;
      hasPayload = false;
      command = "";
      payload = "";
    }
    static boolean append(char c) {
      if (c == '\n') {
        isReady = true;
        return true;
      }
      if (c == ' ' && !hasPayload) {
        hasPayload = true;
        return false;
      }
      if (hasPayload)
        payload += c;
      else
        command += c;
      return false;
    }
};

boolean IncomingCommand::isReady = false;
boolean IncomingCommand::hasPayload = false;
String IncomingCommand::command = false;
String IncomingCommand::payload = false;

#endif // #ifndef __SERIAL_RECEIVER_H__

要使用它,请在您的项目中执行以下操作:

#include <SerialReceiver.h>

void setup() {
  Serial.begin(115200);
  IncomingCommand::reset();
}

void serialEvent() {
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    if (IncomingCommand::append(inChar))
      return;
  }
}

要使用收到的命令:

void loop() {
  if (!IncomingCommand::isReady) {
    delay(10);
    return;
  }

  executeCommand(IncomingCommand::command, IncomingCommand::payload); // I use registry pattern to handle commands, but you are free to do whatever suits your project better.

  IncomingCommand::reset();
}

2

如果要从串行端口读取消息,并且需要分别处理每条消息,则建议使用如下分隔符将消息分成多个部分:

String getMessage()
{
  String msg=""; //the message starts empty
  byte ch; // the character that you use to construct the Message 
  byte d='#';// the separating symbol 

  if(Serial.available())// checks if there is a new message;
  {
    while(Serial.available() && Serial.peek()!=d)// while the message did not finish
    {
      ch=Serial.read();// get the character
      msg+=(char)ch;//add the character to the message
      delay(1);//wait for the next character
    }
  ch=Serial.read();// pop the '#' from the buffer
  if(ch==d) // id finished
  return msg;
  else
  return "NA";
  }
else
return "NA"; // return "NA" if no message;
}

这样,您每次使用该功能时都会收到一条消息。


2

这是处理异常输入和竞争条件的更强大的实现。

  • 它检测到异常长的输入值并安全地丢弃它们。例如,如果源有错误并且生成的输入没有预期的终止符;还是恶意的。
  • 它确保字符串值始终以null终止(即使缓冲区大小已完全填满)。
  • 等待直到捕获到完整的值。例如,传输延迟可能导致Serial.available()在其余值完成到达之前返回零。
  • 当多个值到达的速度比处理速度快时,不会跳过这些值(受串行输入缓冲区的限制)。
  • 可以处理作为另一个值的前缀的值(例如,“ abc”和“ abcd”都可以读入)。

它故意使用字符数组而不是String类型,以提高效率并避免出现内存问题。它还避免使用该readStringUntil()功能,以便在输入到达之前不超时。

最初的问题没有说可变长度字符串是如何定义的,但是我假设它们以单个换行符终止-这将使它变成行读取问题。

这是一个示例,用于从串行监视器读取命令:


2
String content = "";
char character;

if(Serial.available() >0){
//reset this variable!
  content = "";
 //make string from chars
 while(Serial.available()>0) {
   character = Serial.read();
   content.concat(character);
 }
 //send back   
 Serial.print("#");
 Serial.print(content);
 Serial.print("#");
 Serial.flush();
}

警告:有时候,您的字符串会以两段或更多段的形式发送。放置一些“消息末尾”字符以检查是否将所有字符从按摩中串联起来。
flamaniac

2

值得赞扬的是岩浆。很好的答案,但是这里使用的是c ++样式字符串而不是c样式字符串。一些用户可能会发现这更容易。

String string = "";
char ch; // Where to store the character read

void setup() {
    Serial.begin(9600);
    Serial.write("Power On");
}

boolean Comp(String par) {
    while (Serial.available() > 0) // Don't read unless
                                   // there you know there is data
    {
        ch = Serial.read(); // Read a character
        string += ch; // Add it
    }

    if (par == string) {
        string = "";
        return(true);
    }
    else {
        //dont reset string
        return(false);
    }
}

void loop()
{
    if (Comp("m1 on")) {
        Serial.write("Motor 1 -> Online\n");
    }
    if (Comp("m1 off")) {
        Serial.write("Motor 1 -> Offline\n");
    }
}

1

如果您使用的是串联方法,那么在使用if else方法时,请不要忘记修剪字符串。


1

在serial.read()上使用字符串追加运算符。它比string.concat()更好

char r;
string mystring = "";

while(serial.available()) 
   {
    r = serial.read();
    mystring = mystring + r; 
   }

将流保存为字符串(在本例中为mystring)后,请使用SubString函数提取要查找的内容。


0

我可以摆脱这个:


0

许多好答案,这是我的2美分,具有问题中要求的确切功能。

另外,它应该更易于阅读和调试。

代码经过测试,最多可输入128个字符。

在Arduino uno r3(Arduino IDE 1.6.8)上测试

功能:

  • 使用串行命令输入打开或关闭Arduino板载LED(引脚13)。

命令:

  • 带领
  • 灯灭

注意:切记根据板速更改波特率。


0

这总是对我有用:)

String _SerialRead = "";

void setup() {
  Serial.begin(9600);
}

void loop() {
  while (Serial.available() > 0)        //Only run when there is data available
  {
    _SerialRead += char(Serial.read()); //Here every received char will be
                                        //added to _SerialRead
    if (_SerialRead.indexOf("S") > 0)   //Checks for the letter S
    {
      _SerialRead = "";                 //Do something then clear the string
    }
  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.