Jak podzielić przychodzący ciąg?

Wysyłam listę położeń serwa przez łącze szeregowe do arduino w następującym formacie

1:90&2:80&3:180 

Który zostałby przeanalizowany jako:

servoId : Position & servoId : Position & servoId : Position

Jak podzielić te wartości i przekonwertować je na liczba całkowita?

Komentarze

  • Mam slave (arduino uno) wysłać ciąg przez serial 30; 12.4; 1 i 1 master (esp8266) recive string chcę, aby w master rozdzieliłem dane, takie jak 30 12.4 1 i zapisz je na karcie micro sd

Odpowiedź

Wręcz przeciwnie jeśli chodzi o inne odpowiedzi, wolałbym trzymać się z daleka od String z następujących powodów:

  • dynamiczne użycie pamięci (które może szybko doprowadzić do fragmentacja sterty i wyczerpanie pamięci )
  • dość wolno z powodu zwężenia Operatory przypisania / zniszczenia / przypisania

W środowisku osadzonym, takim jak Arduino (nawet w przypadku Mega, które ma więcej pamięci SRAM), wolałbym raczej używać standardowe funkcje C :

  • strchr() : wyszukaj znak w ciągu C (np char *)
  • strtok() : podziały łańcuch C na podciągi, na podstawie znaku separatora
  • atoi() : konwertuje C ciąg do int

Prowadziłoby to do następującego przykładowego kodu:

 // 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, "&"); }  

Zaletą jest brak dynamicznej alokacji pamięci; możesz nawet zadeklarować input jako zmienną lokalną wewnątrz funkcji, która odczytuje polecenia i wykonuje je; po zwróceniu funkcji rozmiar zajmowany przez input (w stosie) jest odzyskiwany.

Komentarze

  • Hadn ' nie pomyślał o problemie z pamięcią. to świetnie.
  • Świetnie. Moja odpowiedź była bardzo ” arduino ” oparta na typowych funkcjach arduino SDK, do których nowy użytkownik mógłby być bardziej przyzwyczajony, ale ta odpowiedź brzmi co należy zrobić w przypadku ” systemów produkcyjnych „. Ogólnie rzecz biorąc, spróbuj uciec od dynamicznej alokacji pamięci w systemach osadzonych.

Odpowiedź

Tej funkcji można użyć aby rozdzielić ciąg na części w oparciu o znak rozdzielający.

String xval = getValue(myString, ":", 0); String yval = getValue(myString, ":", 1); Serial.println("Y:" + yval); Serial.print("X:" + xval); 

Konwertuj ciąg znaków na int

int xvalue = xvalue.toInt(xval); int yvalue = yvalue.toInt(yval); 

Ten fragment kodu pobiera ciąg i oddziela go na podstawie podanego znaku i zwraca Element między znakiem rozdzielającym

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]) : ""; } 

Komentarze

  • to piękna, doskonała odpowiedź! wielkie dzięki!
  • Powinieneś wybrać ten. (Kciuki w górę)
  • eleganckie! działa idealnie do wysyłania par klucz-wartość

Odpowiedź

Możesz zrobić coś podobnego do poniższego, ale weź pod uwagę uwzględnij kilka rzeczy:

Jeśli używasz readStringUntil(), będzie czekać, aż otrzyma znak lub limit czasu. Tak więc, z twoim obecnym ciągiem, ostatnia pozycja będzie trwała trochę dłużej, ponieważ musi czekać. Możesz dodać końcowy &, aby uniknąć tego limitu czasu. Możesz łatwo sprawdzić to zachowanie na swoim monitorze, spróbować wysłać ciąg z dodatkowym & i bez niego, a zobaczysz takie opóźnienie.

Właściwie to robisz nie potrzebujesz indeksu serwomechanizmu, możesz po prostu wysłać ciąg pozycji i uzyskać indeks serwomechanizmu na podstawie pozycji wartości w ciągu, na przykład: 90&80&180&. Jeśli używasz indeksu serwomechanizmu, być może chcesz go sprawdzić (przekonwertuj na int, a następnie dopasuj indeks pętli i), aby upewnić się, że nic nie poszło nie tak z Twoją wiadomością.

Musisz sprawdzić, czy zwracany ciąg z readStringUntil nie jest pusty. Jeśli przekroczono limit czasu funkcji, nie otrzymałeś wystarczającej ilości danych, a zatem każda próba wyodrębnienia wartości int przyniesie dziwne wyniki.

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); } } } 

Komentarze

  • Dziękuję, to wygląda na bardzo dobre rozwiązanie. Przykład wyjaśnia to doskonale.
  • A gdyby tak niezdefiniowana liczba wejść serw? w moim przykładzie było 3. Ale co by było, gdyby czasami było ich więcej lub mniej. Czy możesz zaproponować jakąś sugestię dotyczącą takiego scenariusza
  • Jasne: są dwie możliwości. 1 .Najpierw wyślij liczbę serw: 3: val1 & val2 & val3 &, przeczytaj taką liczbę przed uruchomieniem pętli. 2. Użyj innego terminatora, aby wskazać, że nie ma więcej serw, zapętlaj, dopóki ich nie znajdziesz: val1 & val2 & val3 & #, na przykład.
  • Cieszę się, że to rozwiązanie pomogło Ci, @ValrikRobot, czy mógłbyś zweryfikować odpowiedź, czy była przydatna?
  • lub ty może po prostu usunąć for, więc kod będzie działał za każdym razem, gdy wyślesz polecenie.

Odpowiedź

Możesz użyć Stream.readStringUntil(terminator) , przekazując inny terminator dla każdej części.

Na każdej części następnie dzwonisz pod numer String.toInt

Odpowiedz

Najprostszym rozwiązaniem jest użycie 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); 

Daje to następujący wynik:

n=6 id1=1, pos1=90 id2=2, pos2=80 id3=3, pos3=180 

Pozdrawiam!

Komentarze

  • Nie działa dla serial.read () … any i dea dlaczego? Pojawia się następujący błąd: invalid conversion from 'int' to 'char*' [-fpermissive]

Odpowiedź

Zobacz przykład pod adresem: 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; } 

Odpowiedź

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; } } } 

Odpowiedź

jfpoilpret pod warunkiem świetnej odpowiedź do analizowania poleceń szeregowych na Arduino. Jednak Attiny85 nie ma dwukierunkowego portu szeregowego – należy użyć oprogramowania SoftwareSerial. W ten sposób można przenieść ten sam kod dla 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, "&"); } } }  

Schematy Attiny85 dla numerów styków tutaj wprowadź opis obrazu

Sketch kompiluje się do:

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. 

Jest więc dużo miejsca i pamięci na resztę kodu

Komentarze

  • Jak czytać z numeru seryjnego na ATtiny85 to nie ' to naprawdę część pytania.
  • Przepraszamy za różni się od pytania, ale społeczność i zasoby dostępne dla Attiny są znacznie mniejsze niż dla Arduino. Osoby takie jak ja, szukające odpowiedzi, używają słowa kluczowego Arduino i czasami wpadają w bardzo trudne sytuacje, gdy implementują kod Arduino w Attiny nie zawsze jest trywialne, musiałem przekonwertować oryginalny kod, aby działał na Attiny, przetestowałem jego działanie i zdecydowałem się sha re it
  • Ta witryna jest w formacie Q &. Odpowiedzi powinny odpowiadać na pytanie. Twój po prostu dodaje coś, co ' nie jest z nim związane.

Odpowiedź

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 } 

Odpowiedź

Oto Arduino metoda dzielenia ciągu znaków jako odpowiedź na pytanie „Jak podzielić ciąg na podciąg?” zadeklarowana jako duplikat obecnego pytania.

Celem rozwiązania jest przeanalizowanie serii GPS pozycje zarejestrowane w karcie SD . Zamiast otrzymywania ciągu znaków z Serial, ciąg jest odczytywany z pliku.

Funkcja to StringSplit() parse Ciąg sLine = "1.12345,4.56789,hello" do 3 ciągów sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: linie wejściowe do przeanalizowania,
  2. char cDelim: znak separatora między parametrami,
  3. String sParams[]: wyjściowa tablica parametrów,
  4. int iMaxParams: maksymalna liczba parametrów,
  5. Dane wyjściowe int: liczba przeanalizowanych parametry,

Funkcja jest oparta na String::indexOf() i 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); } 

A użycie jest naprawdę proste:

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(""); } 

Odpowiedź

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(){} 

Odpowiedź

To nie jest odpowiedź na Twoje pytanie, ale może się komuś przydać. Jeśli ciąg ma określony prefiks, możesz użyć po prostu startsWith i substring. Np.

void loop () if(Serial.available()){ command = Serial.readStringUntil("\n"); Serial.println("Received command: " + command); if(command.startsWith("height")) { Serial.println(command.substring(7)); } } } 

A następnie wyślij height 10 co wyodrębni 10 .

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *