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
的代码了。
参考链接:
- 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