Arduino 串口接受字符串操作

自定义函数操作方式

/*使用自定函数,将字符转化为字符串,再进行操作*/
 
String comdata = "";//声明字符串变量
 
void setup() 
{
  Serial.begin(9600);      //设定的波特率
}
 
void loop() 
{
   while (Serial.available() > 0)  
    {
        comdata += char(Serial.read());
        delay(2);
    }
    
   if (comdata.length() > 0)
    {
       Serial.println(comdata);
       comdata = "";
    }
}

此方法使用自定函数实现了字符到字符串的转换再进行操作,使用起来还不错,推荐大家使用。

采用库函数Serial.readString();

void setup() 
{
  Serial.begin(9600);      //设定的波特率
}
 
void loop() 
{
  String rx_buffer;
  rx_buffer=Serial.readString();
  Serial.print(rx_buffer);
}

此方法使用简单暴力,但是有个缺点,官方库函数默认的参数使得读取串口操作必须要阻塞1s的时间,实时性较差;

在这里,提供一种修改方法:

在Arduino IDE 安装路径下找到stream.h 文件修改参数

①文件路径:C:\Program Files\arduino-1.8.19\hardware\arduino\avr\cores\arduino

②找到文件 stream.h,修改参数:Stream() {_timeout=200;},红色数值单位为毫秒,根据需要修改然后保存编译即可生效;

stream.h文件内容(突出显示 要修改的地方:Stream() {_timeout=200;}):

/*
  Stream.h - base class for character-based streams.
  Copyright (c) 2010 David A. Mellis.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

  parsing functions based on TextFinder library by Michael Margolis
*/

#ifndef Stream_h
#define Stream_h

#include <inttypes.h>
#include "Print.h"

// compatability macros for testing
/*
#define   getInt()            parseInt()
#define   getInt(ignore)    parseInt(ignore)
#define   getFloat()          parseFloat()
#define   getFloat(ignore)  parseFloat(ignore)
#define   getString( pre_string, post_string, buffer, length)
readBytesBetween( pre_string, terminator, buffer, length)
*/

// This enumeration provides the lookahead options for parseInt(), parseFloat()
// The rules set out here are used until either the first valid character is found
// or a time out occurs due to lack of input.
enum LookaheadMode{
    SKIP_ALL,       // All invalid characters are ignored.
    SKIP_NONE,      // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
    SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
};

#define NO_IGNORE_CHAR  '\x01' // a char not found in a valid ASCII numeric field

class Stream : public Print
{
  protected:
    unsigned long _timeout;      // number of milliseconds to wait for the next char before aborting timed read
    unsigned long _startMillis;  // used for timeout measurement
    int timedRead();    // read stream with timeout
    int timedPeek();    // peek stream with timeout
    int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout

  public:
    virtual int available() = 0;
    virtual int read() = 0;
    virtual int peek() = 0;

    Stream() {_timeout=100;}

// parsing methods

  void setTimeout(unsigned long timeout);  // sets maximum milliseconds to wait for stream data, default is 1 second
  unsigned long getTimeout(void) { return _timeout; }
  
  bool find(char *target);   // reads data from the stream until the target string is found
  bool find(uint8_t *target) { return find ((char *)target); }
  // returns true if target string is found, false if timed out (see setTimeout)

  bool find(char *target, size_t length);   // reads data from the stream until the target string of given length is found
  bool find(uint8_t *target, size_t length) { return find ((char *)target, length); }
  // returns true if target string is found, false if timed out

  bool find(char target) { return find (&target, 1); }

  bool findUntil(char *target, char *terminator);   // as find but search ends if the terminator string is found
  bool findUntil(uint8_t *target, char *terminator) { return findUntil((char *)target, terminator); }

  bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen);   // as above but search ends if the terminate string is found
  bool findUntil(uint8_t *target, size_t targetLen, char *terminate, size_t termLen) {return findUntil((char *)target, targetLen, terminate, termLen); }

  long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
  // returns the first valid (long) integer value from the current position.
  // lookahead determines how parseInt looks ahead in the stream.
  // See LookaheadMode enumeration at the top of the file.
  // Lookahead is terminated by the first character that is not a valid part of an integer.
  // Once parsing commences, 'ignore' will be skipped in the stream.

  float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
  // float version of parseInt

  size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer
  size_t readBytes( uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); }
  // terminates if length characters have been read or timeout (see setTimeout)
  // returns the number of characters placed in the buffer (0 means no valid data found)

  size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character
  size_t readBytesUntil( char terminator, uint8_t *buffer, size_t length) { return readBytesUntil(terminator, (char *)buffer, length); }
  // terminates if length characters have been read, timeout, or if the terminator character  detected
  // returns the number of characters placed in the buffer (0 means no valid data found)

  // Arduino String functions to be added here
  String readString();
  String readStringUntil(char terminator);

  protected:
  long parseInt(char ignore) { return parseInt(SKIP_ALL, ignore); }
  float parseFloat(char ignore) { return parseFloat(SKIP_ALL, ignore); }
  // These overload exists for compatibility with any class that has derived
  // Stream and used parseFloat/Int with a custom ignore character. To keep
  // the public API simple, these overload remains protected.

  struct MultiTarget {
    const char *str;  // string you're searching for
    size_t len;       // length of string you're searching for
    size_t index;     // index used by the search routine.
  };

  // This allows you to search for an arbitrary number of strings.
  // Returns index of the target that is found first or -1 if timeout occurs.
  int findMulti(struct MultiTarget *targets, int tCount);
};

#undef NO_IGNORE_CHAR
#endif

使用ArdunioJson发送Json数据包

注意:这里使用的ArduinoJson的V6版本,关于V6和V5版本的切换,可以按照报错网站查询并修改。

#include <ArduinoJson.h>
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("begin now");
}

void loop() {
  // put your main code here, to run repeatedly:
   DynamicJsonDocument data(256);
   data["temp"]=25;
   data["hum"]=110;
   char json_string[256];
   serializeJson(data,json_string);
   Serial.println(json_string);
}
在这里插入图片描述

解析Json数据包(int,char型)

// 电源属性修改的回调函数
void powerCallback(JsonVariant p)
{
    extern int a;
    int PowerSwitch = p["PowerSwitch"];
    if (PowerSwitch == 2)
    {
       digitalWrite(LED_BUILTIN,HIGH);  // 启动设备
       a=2;
    } 
    if (PowerSwitch == 3)
    {
       digitalWrite(LED_BUILTIN,LOW); // 启动设备
       a=3;
    }
}
//字符回调函数
void Receive_Zi_Fu_Callback(JsonVariant p)
{
      const char* Zi_Fu = p["Receive_Zi_Fu"];/
      String S(Zi_Fu);//const char*转String
      S = S.substring(2,6);	//截取字符串的一部分,可以去掉,比如123456,就是3456
      extern char *c;
      const int len=S.length();
      c = new char[len+1];
      strcpy(c,S.c_str());//Strinf转char* ,是为了OLED_ShowString
      OLED_ShowString(0,0,c,16);
      Serial.println(S);
}

关于char和String

  • char表示的是字符,定义用单引号;
  • String表示字符串,定义用双引号。
  • string的内存管理是由系统处理,除非系统内存池用完,不然不会出现这种内存问题。
  • char *的内存管理由用户自己处理,很容易出现内存不足的问题。

当我们要存一个串,但是不知道其他需要多少内存时, 用string来处理就最好不过了。当你知道了存储的内存的时候,可以用char *,但是不如用string的好,用指针总会有隐患。

使用ArdunioJson解析Json数据包

#include <ArduinoJson.h>
 
void setup() {
  Serial.begin(9600);
}
 
void loop() { 

  DynamicJsonDocument  jsonBuffer(200);
   
   String json =
      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
   deserializeJson(jsonBuffer, json);
    JsonObject root = jsonBuffer.as<JsonObject>();
 
  const char* sensor = root["sensor"];
  long time = root["time"];
  double latitude = root["data"][0];
  double longitude = root["data"][1];
 
  Serial.println();
  Serial.println(sensor);
  Serial.println(time);
  Serial.println(latitude, 6);
  Serial.println(longitude, 6);
  delay(500);
}

解析Arduino串口Json包

//{"state":1,"LeftMotor":100,"RightMotor":100,"UpServo":10,"DownServo":10,"Light":0,"Pump":0}
#include <ArduinoJson.h>

String rx_buffer = "";//声明字符串变量
void setup()
{
  Serial.begin(9600);      //设定的波特率
}

void loop()
{
  while (Serial.available() > 0)
  {
    rx_buffer += char(Serial.read());
    delay(2);
  }

  if (rx_buffer.length() > 0)
  {
    DynamicJsonDocument  jsonBuffer(400);
    deserializeJson(jsonBuffer, rx_buffer);
    JsonObject root = jsonBuffer.as<JsonObject>();

    int state = root["state"];
    int LeftMotor = root["LeftMotor"];
    int RightMotor = root["RightMotor"];
    int UpServo = root["UpServo"];
    int DownServo = root["DownServo"];
    int Light = root["Light"];
    int Pump = root["Pump"];

    Serial.println(state);
    Serial.println(LeftMotor);
    Serial.println(RightMotor);
    Serial.println(UpServo);
    Serial.println(DownServo);
    Serial.println(Light);
    Serial.println(Pump);
    Serial.print(rx_buffer);
    rx_buffer = "";
  }
}

Arduino修改Serial接收缓冲区大小

向Arduino发送一个90字节的字符串,不管怎么样都只能收到63个字节的数据,感觉应该是串口缓冲区大小的限制,修改完后,串口收发数据正常。

看到网上有资料说,直接添加以下宏定义就可以了:

#define SERIAL_RX_BUFFER_SIZE 2048 //修改串口发送缓冲区大小为2048

但是由于Arduino的编译器是先导入其他头文件,再编译用户区代码的,所以会提示已经被重定义。

那么我们就只能根据提示,去修改C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\HardwareSerial.h的代码了。

image-20220430225817140

参考链接:

  • https://blog.csdn.net/huanzx/article/details/79596855
  • https://blog.csdn.net/weixin_43475628/article/details/118925979
  • https://blog.csdn.net/qq_27508477/article/details/105419505