다음 형식으로 직렬 연결을 통해 arduino에 서보 위치 목록을 보냅니다.
1:90&2:80&3:180
다음과 같이 구문 분석됩니다.
servoId : Position & servoId : Position & servoId : Position
이 값을 어떻게 분할하고 다음으로 변환합니까? 정수?
코멘트
- 나는 슬레이브 (arduino uno)가 시리얼 30; 12.4; 1 및 1 마스터 (esp8266) 수신 문자열을 통해 문자열을 보냅니다. 마스터에서 30 12.4 1과 같은 데이터를 분리하고 마이크로 SD 카드에 저장하고 싶습니다.
답변
반대로 다른 답변에 대해서는 다음과 같은 이유로 String
에서 벗어나고 싶습니다.
- 동적 메모리 사용 (즉, 힙 조각화 및 메모리 고갈 )
- constr로 인해 상당히 느림 경매 / 파괴 / 할당 연산자
Arduino와 같은 임베디드 환경 (SRAM이 더 많은 Mega에서도)에서는 표준 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
(스택에서)가 차지하는 크기가 복구됩니다.
댓글
- 메모리 문제를 ‘ 생각하지 않았습니다. 훌륭합니다.
- 훌륭합니다. 내 대답은 매우 ” arduino ” 기반이며 새로운 사용자가 더 익숙 할 수있는 일반적인 arduino SDK 기능을 사용했지만이 대답은 ” 생산 ” 시스템에 대해 수행해야하는 작업 일반적으로 임베디드 시스템의 동적 메모리 할당에서 벗어나십시오.
Answer
이 기능을 사용할 수 있습니다. 구분 문자가 무엇인지에 따라 문자열을 조각으로 분리합니다.
String xval = getValue(myString, ":", 0); String yval = getValue(myString, ":", 1); Serial.println("Y:" + yval); Serial.print("X:" + xval);
문자열을 정수로 변환
int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(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); } } }
댓글
- 아주 좋은 해결책 인 것 같습니다. 감사합니다. 예제를 통해 완벽하게 설명합니다.
- 정의되지 않은 수의 서보 입력이 있습니까? 제 예에는 3 개가있었습니다.하지만 때때로 더 많거나 적다면 어떻게 될까요 이러한 시나리오를 처리하기위한 제안을 제공 할 수 있습니까
- 물론 : 두 가지 가능성이 있습니다. 1 .먼저 서보 수 : 3 : val1 & val2 & val3 &, 읽기 루프를 시작하기 전에 이러한 번호. 2. 다른 터미네이터를 사용하여 더 이상 서보가 없음을 표시하고 찾을 때까지 반복합니다. val1 & val2 & val3 & #입니다.
- 이 솔루션이 도움이 되었으니 다행입니다. @ValrikRobot, 답변이 유용했는지 확인해 주시겠습니까?
- 또는 귀하 for를 제거 할 수 있으므로 명령을 보낼 때마다 코드가 작동합니다.
Answer
Stream.readStringUntil(terminator)
를 사용하여 각 부분에 대해 다른 종결자를 전달할 수 있습니다.
각 부분에서 그런 다음 String.toInt
Answer
가장 간단한 해결책은 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
건배!
댓글
- serial.read () …에서 작동하지 않습니다. 왜? 다음 오류가 발생합니다.
invalid conversion from 'int' to 'char*' [-fpermissive]
Answer
예제 참조 : 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 는 훌륭한 답변을 제공했습니다. / a> 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, "&"); } } }
Sketch는 다음으로 컴파일됩니다.
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.
따라서 나머지 코드를위한 충분한 공간과 메모리가 있습니다.
댓글 h3>
- ATtiny85에서 시리얼을 읽는 방법은 ‘ 실제로 질문의 일부가 아닙니다.
- 죄송합니다. 질문과는 다르지만 Attiny에 사용할 수있는 커뮤니티 및 리소스는 Arduino보다 훨씬 작습니다. 답을 찾는 저와 같은 사람들은
Arduino
키워드를 사용하고 때때로 Arduino 코드를 구현할 때 매우 까다로운 상황에 처하게됩니다. Attiny는 항상 사소한 것은 아닙니다. 원본 코드를 Attiny에서 작동하도록 변환해야하고 작동하는지 테스트 한 다음 sha로 결정했습니다. re it
- 이 사이트는 Q & A 형식입니다. 대답은 질문에 답해야합니다. 귀하의 이메일은 ‘ 관련이없는 항목을 추가합니다.
Arduino
키워드를 사용하고 때때로 Arduino 코드를 구현할 때 매우 까다로운 상황에 처하게됩니다. Attiny는 항상 사소한 것은 아닙니다. 원본 코드를 Attiny에서 작동하도록 변환해야하고 작동하는지 테스트 한 다음 sha로 결정했습니다. re it 답변
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 }
답변
다음은 Arduino입니다. 메서드는 다음과 같이 선언 된 “문자열을 부분 문자열로 분할하는 방법?” 질문에 대한 답으로 문자열을 분할합니다. 현재 질문의 중복입니다.
솔루션의 목적은 일련의 GPS 를 구문 분석하는 것입니다. SD 카드 파일에 로그인 된 위치. Serial
에서 문자열을받는 대신 파일에서 문자열을 읽습니다.
함수는 StringSplit()
구문 분석입니다. a 문자열 sLine = "1.12345,4.56789,hello"
-3 개 문자열 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(""); }
답변
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(){}
답변
귀하의 질문에 대한 답변은 아니지만 누군가에게 유용 할 수 있습니다. 문자열에 특정 접두사가있는 경우 startsWith
및 substring
를 사용할 수 있습니다. 예 :
void loop () if(Serial.available()){ command = Serial.readStringUntil("\n"); Serial.println("Received command: " + command); if(command.startsWith("height")) { Serial.println(command.substring(7)); } } }
그런 다음 height 10
를 전송하여 10
를 추출합니다. .