직렬 연결을 통해 다음 형식으로 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이 더 많은 Mega의 경우에도)에서는 표준 C 함수를 사용하고 싶습니다 .
그러면 다음 코드 샘플이 생성됩니다.
// 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);
문자열을 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는 큰 제공 대답을 아두 이노에 시리얼 명령을 구문 분석. 그러나 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 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
키워드를 사용 하고 Attiny에 Arduino 코드를 구현하는 것이 항상 쉬운 것은 아니기 때문에 때로는 까다로운 상황에 처하게됩니다. Attiny에서 작동하도록 원본 코드를 변환하고 작동을 테스트하고 공유하기로 결정했습니다.
다음은 "문자열을 문자열로 나누는 방법" 질문에 대한 답변으로 문자열을 나누는 Arduino 방법입니다. 현재 질문의 복제본으로 선언되었습니다.
이 솔루션의 목적은 SD 카드 파일에 기록 된 일련의 GPS 위치 를 구문 분석하는 것 입니다. 에서 문자열을받는 대신 파일에서 문자열을 읽습니다.Serial
이 함수는 StringSplit()
문자열을 구문 분석 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("");
}