ฉันกำลังส่งรายการตำแหน่งเซอร์โวผ่านการเชื่อมต่อแบบอนุกรมไปยัง arduino ในรูปแบบต่อไปนี้
1:90&2:80&3:180
ซึ่งจะแยกเป็น:
servoId : Position & servoId : Position & servoId : Position
ฉันจะแบ่งค่าเหล่านี้เป็นอย่างไรและแปลงเป็นจำนวนเต็มได้อย่างไร
ฉันกำลังส่งรายการตำแหน่งเซอร์โวผ่านการเชื่อมต่อแบบอนุกรมไปยัง arduino ในรูปแบบต่อไปนี้
1:90&2:80&3:180
ซึ่งจะแยกเป็น:
servoId : Position & servoId : Position & servoId : Position
ฉันจะแบ่งค่าเหล่านี้เป็นอย่างไรและแปลงเป็นจำนวนเต็มได้อย่างไร
คำตอบ:
ตรงกันข้ามกับคำตอบอื่น ๆ ฉันควรอยู่ห่างจากString
เหตุผลดังต่อไปนี้:
ในสภาพแวดล้อมแบบฝังตัวเช่น Arduino (แม้แต่เมกะที่มี SRAM มากกว่า) ฉันควรใช้ฟังก์ชั่น C มาตรฐาน :
strchr()
: ค้นหาอักขระในสตริง C (เช่นchar *
)strtok()
: แยกสตริง C ออกเป็นสตริงย่อยตามอักขระตัวคั่น atoi()
: แปลงสตริง C เป็น int
ซึ่งจะนำไปสู่ตัวอย่างโค้ดต่อไปนี้:
// Calculate based on max input size expected for one command
#define INPUT_SIZE 30
...
// Get next command from Serial (add 1 for final 0)
char input[INPUT_SIZE + 1];
byte size = Serial.readBytes(input, INPUT_SIZE);
// Add the final 0 to end the C string
input[size] = 0;
// Read each command pair
char* command = strtok(input, "&");
while (command != 0)
{
// Split the command in two values
char* separator = strchr(command, ':');
if (separator != 0)
{
// Actually split the string in 2: replace ':' with 0
*separator = 0;
int servoId = atoi(command);
++separator;
int position = atoi(separator);
// Do something with servoId and position
}
// Find the next command in input string
command = strtok(0, "&");
}
ข้อดีคือไม่มีการจัดสรรหน่วยความจำแบบไดนามิก คุณสามารถประกาศinput
เป็นตัวแปรโลคอลภายในฟังก์ชันที่จะอ่านคำสั่งและเรียกใช้งานมัน เมื่อฟังก์ชันถูกส่งคืนขนาดที่ถูกครอบครองโดยinput
(ในสแต็ก) จะถูกกู้คืน
ฟังก์ชั่นนี้สามารถใช้แยกสตริงออกเป็นชิ้น ๆ ตามลักษณะของตัวแยก
String xval = getValue(myString, ':', 0);
String yval = getValue(myString, ':', 1);
Serial.println("Y:" + yval);
Serial.print("X:" + xval);
แปลง String เป็น int
int xvalue = stringToNumber(xval);
int yvalue = stringToNumber(yval);
รหัสอันนี้ใช้สตริงและแยกมันตามตัวอักษรที่กำหนดและส่งกลับรายการระหว่างตัวละครแยก
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = { 0, -1 };
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
คุณสามารถทำสิ่งต่อไปนี้ แต่โปรดคำนึงถึงหลายสิ่ง:
หากคุณใช้readStringUntil()
มันจะรอจนกว่าจะได้รับตัวละครหรือหมดเวลา ดังนั้นด้วยสตริงปัจจุบันของคุณตำแหน่งสุดท้ายจะคงอยู่อีกต่อไปอีกเล็กน้อยเนื่องจากต้องรอ คุณสามารถเพิ่มส่วนท้าย&
เพื่อหลีกเลี่ยงการหมดเวลานี้ คุณสามารถตรวจสอบพฤติกรรมนี้ได้ง่าย ๆ ในมอนิเตอร์ลองส่งสตริงที่มีและไม่มีส่วนเสริม&
และคุณจะเห็นการหมดเวลาดังกล่าว
90&80&180&
จริงๆแล้วคุณไม่จำเป็นต้องดัชนีเซอร์โวคุณก็สามารถส่งสายของคุณของตำแหน่งและได้รับดัชนีเซอร์โวโดยตำแหน่งค่าในสตริงสิ่งที่ชอบ: หากคุณใช้ดัชนีเซอร์โวคุณอาจต้องการตรวจสอบ (แปลงเป็นint
แล้วจับคู่ดัชนีลูป i) เพื่อให้แน่ใจว่าไม่มีอะไรผิดปกติกับข้อความของคุณ
คุณต้องตรวจสอบว่าสตริงที่ส่งคืนจากreadStringUntil
ไม่ว่างเปล่า หากการหมดเวลาของฟังก์ชั่นคุณไม่ได้รับข้อมูลเพียงพอดังนั้นความพยายามใด ๆ ในการแยกint
ค่าของคุณจะให้ผลลัพธ์ที่แปลก
void setup() {
Serial.begin(9600);
}
void loop() {
for(int i=1; i<=3; i++) {
String servo = Serial.readStringUntil(':');
if(servo != ""){
//here you could check the servo number
String pos = Serial.readStringUntil('&');
int int_pos=pos.toInt();
Serial.println("Pos");
Serial.println(int_pos);
}
}
}
คุณสามารถใช้Stream.readStringUntil(terminator)
ผ่านเทอร์มิเนเตอร์ต่าง ๆ สำหรับแต่ละส่วนได้
ในแต่ละส่วนคุณจะเรียก String.toInt
ทางออกที่ง่ายที่สุดคือการใช้sscanf ()
int id1, id2, id3;
int pos1, pos2, pos3;
char* buf = "1:90&2:80&3:180";
int n = sscanf(buf, "%d:%d&%d:%d&%d:%d", &id1, &pos1, &id2, &pos2, &id3, &pos3);
Serial.print(F("n="));
Serial.println(n);
Serial.print(F("id1="));
Serial.print(id1);
Serial.print(F(", pos1="));
Serial.println(pos1);
Serial.print(F("id2="));
Serial.print(id2);
Serial.print(F(", pos2="));
Serial.println(pos2);
Serial.print(F("id3="));
Serial.print(id3);
Serial.print(F(", pos3="));
Serial.println(pos3);
สิ่งนี้ให้ผลลัพธ์ต่อไปนี้:
n=6
id1=1, pos1=90
id2=2, pos2=80
id3=3, pos3=180
ไชโย!
invalid conversion from 'int' to 'char*' [-fpermissive]
ดูตัวอย่างได้ที่: https://github.com/BenTommyE/Arduino_getStringPartByNr
// splitting a string and return the part nr index split by separator
String getStringPartByNr(String data, char separator, int index) {
int stringData = 0; //variable to count data part nr
String dataPart = ""; //variable to hole the return text
for(int i = 0; i<data.length()-1; i++) { //Walk through the text one letter at a time
if(data[i]==separator) {
//Count the number of times separator character appears in the text
stringData++;
} else if(stringData==index) {
//get the text when separator is the rignt one
dataPart.concat(data[i]);
} else if(stringData>index) {
//return text and stop if the next separator appears - to save CPU-time
return dataPart;
break;
}
}
//return text if this is the last part
return dataPart;
}
String getValue(String data, char separator, int index)
{
int maxIndex = data.length() - 1;
int j = 0;
String chunkVal = "";
for (int i = 0; i <= maxIndex && j <= index; i++)
{
chunkVal.concat(data[i]);
if (data[i] == separator)
{
j++;
if (j > index)
{
chunkVal.trim();
return chunkVal;
}
chunkVal = "";
}
else if ((i == maxIndex) && (j < index)) {
chunkVal = "";
return chunkVal;
}
}
}
jfpoilpretให้คำตอบที่ดีสำหรับการแยกวิเคราะห์คำสั่งอนุกรมบน Arduino อย่างไรก็ตาม Attiny85 ไม่มีซีเรียลสองทิศทาง - ต้องใช้ SoftwareSerial นี่คือวิธีที่คุณพอร์ตรหัสเดียวกันสำหรับ Attiny85
#include <SoftwareSerial.h>
// Calculate based on max input size expected for one command
#define INPUT_SIZE 30
// Initialize SoftwareSerial
SoftwareSerial mySerial(3, 4); // RX=PB3, TX=PB4
// Parameter for receiving Serial command (add 1 for final 0)
char input[INPUT_SIZE + 1];
void setup() {
mySerial.begin(9600);
}
void loop() {
// We need this counter to simulate Serial.readBytes which SoftwareSerial lacks
int key = 0;
// Start receiving command from Serial
while (mySerial.available()) {
delay(3); // Delay to allow buffer to fill, code gets unstable on Attiny85 without this for some reason
// Don't read more characters than defined
if (key < INPUT_SIZE && mySerial.available()) {
input[key] = mySerial.read();
key += 1;
}
}
if (key > 0) {
// Add the final 0 to end the C string
input[key] = 0;
// Read each command pair
char* command = strtok(input, "&");
while (command != 0)
{
// Split the command in two values
char* separator = strchr(command, ':');
if (separator != 0)
{
// Actually split the string in 2: replace ':' with 0
*separator = 0;
int servoId = atoi(command);
++separator;
int position = atoi(separator);
}
// Find the next command in input string
command = strtok(0, "&");
}
}
}
Attiny85 แผนงานสำหรับหมายเลขพิน
ร่างรวบรวมเป็น:
Sketch uses 2244 bytes (27%) of program storage space. Maximum is 8192 bytes.
Global variables use 161 bytes (31%) of dynamic memory, leaving 351 bytes for local variables. Maximum is 512 bytes.
ดังนั้นมีพื้นที่และหน่วยความจำมากมายสำหรับรหัสที่เหลือ
Arduino
คำหลักและบางครั้งเข้าสู่สถานการณ์ที่ยุ่งยากมากเนื่องจากการติดตั้งรหัส Arduino ลงบน Attiny นั้นไม่ได้เป็นเรื่องเล็กน้อยเสมอไป ต้องแปลงรหัสต้นฉบับให้ทำงานบน Attiny ทดสอบใช้งานได้และตัดสินใจที่จะแชร์
char str[] = "1:90&2:80&3:180"; // test sample serial input from servo
int servoId;
int position;
char* p = str;
while (sscanf(p, "%d:%d", &servoId, &position) == 2)
{
// process servoId, position here
//
while (*p && *p++ != '&'); // to next id/pos pair
}
void setup() {
Serial.begin(9600);
char str[] ="1:90&2:80";
char * pch;
pch = strtok(str,"&");
printf ("%s\n",pch);
pch = strtok(NULL,"&"); //pch=next value
printf ("%s\n",pch);
}
void loop(){}
นี่คือวิธีArduinoเพื่อแยกสตริงเป็นคำตอบสำหรับคำถาม"วิธีการแยกสตริงในสตริงย่อย" ประกาศเป็นสำเนาของคำถามปัจจุบัน
วัตถุประสงค์ของการแก้ปัญหาคือการแยกตำแหน่งGPS ที่บันทึกไว้ในไฟล์การ์ด SD แทนที่จะได้รับ String จากSerial
String จะอ่านจากไฟล์
ฟังก์ชั่นStringSplit()
แยกสตริงsLine = "1.12345,4.56789,hello"
3 Strings sParams[0]="1.12345"
, และsParams[1]="4.56789"
sParams[2]="hello"
String sInput
: บรรทัดอินพุตที่ถูกวิเคราะห์คำchar cDelim
: อักขระตัวคั่นระหว่างพารามิเตอร์String sParams[]
: อาร์เรย์เอาต์พุตของพารามิเตอร์int iMaxParams
: จำนวนพารามิเตอร์สูงสุดint
: จำนวนพารามิเตอร์แยกวิเคราะห์ฟังก์ชั่นนี้เป็นไปตามString::indexOf()
และString::substring()
:
int StringSplit(String sInput, char cDelim, String sParams[], int iMaxParams)
{
int iParamCount = 0;
int iPosDelim, iPosStart = 0;
do {
// Searching the delimiter using indexOf()
iPosDelim = sInput.indexOf(cDelim,iPosStart);
if (iPosDelim > (iPosStart+1)) {
// Adding a new parameter using substring()
sParams[iParamCount] = sInput.substring(iPosStart,iPosDelim-1);
iParamCount++;
// Checking the number of parameters
if (iParamCount >= iMaxParams) {
return (iParamCount);
}
iPosStart = iPosDelim + 1;
}
} while (iPosDelim >= 0);
if (iParamCount < iMaxParams) {
// Adding the last parameter as the end of the line
sParams[iParamCount] = sInput.substring(iPosStart);
iParamCount++;
}
return (iParamCount);
}
และการใช้งานนั้นง่ายมาก:
String sParams[3];
int iCount, i;
String sLine;
// reading the line from file
sLine = readLine();
// parse only if exists
if (sLine.length() > 0) {
// parse the line
iCount = StringSplit(sLine,',',sParams,3);
// print the extracted paramters
for(i=0;i<iCount;i++) {
Serial.print(sParams[i]);
}
Serial.println("");
}