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 (npchar *
) -
strtok()
: podziały łańcuch C na podciągi, na podstawie znaku separatora -
atoi()
: konwertuje C ciąg doint
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
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"
.
-
String sInput
: linie wejściowe do przeanalizowania, -
char cDelim
: znak separatora między parametrami, -
String sParams[]
: wyjściowa tablica parametrów, -
int iMaxParams
: maksymalna liczba parametrów, - 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
.