Come divido una stringa in arrivo?

Sto inviando un elenco di posizioni dei servo tramite la connessione seriale ad arduino nel seguente formato

1:90&2:80&3:180 

Che verrebbe analizzato come:

servoId : Position & servoId : Position & servoId : Position

Come dovrei suddividere questi valori e convertirli in un numero intero?

Commenti

  • Ho slave (arduino uno) invia una stringa tramite seriale 30; 12.4; 1 e 1 master (esp8266) riceve una stringa voglio nel master avere dati separati come 30 12.4 1 e salvarli nella scheda micro sd

risposta

Al contrario ad altre risposte, preferisco stare lontano da String per i seguenti motivi:

  • utilizzo dinamico della memoria (che può portare rapidamente a frammentazione heap e esaurimento memoria )
  • abbastanza lento a causa di costr Operatori di asportazione / distruzione / assegnazione

In un ambiente embedded come Arduino (anche per un Mega che ha più SRAM), preferisco usare funzioni C standard :

  • strchr() : cerca un carattere in una stringa C (es char *)
  • strtok() : suddivisioni una stringa C in sottostringhe, in base a un carattere separatore
  • atoi() : converte una C stringa a un int

Ciò porterebbe al seguente esempio di codice:

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

Il vantaggio qui è che non ha luogo alcuna allocazione dinamica della memoria; puoi persino dichiarare input come variabile locale allinterno di una funzione che leggerebbe i comandi e li eseguirà; una volta restituita la funzione, viene ripristinata la dimensione occupata da input (nello stack).

Commenti

  • ‘ non aveva pensato al problema della memoria. è fantastico.
  • Eccellente. La mia risposta era molto ” arduino ” e utilizzava le tipiche funzioni SDK di arduino a cui un nuovo utente potrebbe essere più abituato, ma questa risposta cosa si dovrebbe fare per i sistemi ” di produzione “. In generale, cerca di uscire dallallocazione dinamica della memoria nei sistemi integrati.

Risposta

Questa funzione può essere utilizzata per separare una stringa in pezzi in base a quale sia il carattere di separazione.

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

Converti stringa in int

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

Questo pezzo di codice accetta una stringa e lo separa in base a un dato carattere e restituisce lelemento tra i caratteri separatori

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

Commenti

  • questa è una bellissima risposta perfetta! grazie mille!
  • Dovresti scegliere questo. (Pollice in alto)
  • elegante! funziona perfettamente per inviare coppie chiave-valore

Risposta

Potresti fare qualcosa di simile, ma ti preghiamo di tener conto account diverse cose:

Se usi readStringUntil(), aspetterà fino a quando non riceverà il carattere o i timeout. Pertanto, con la stringa corrente, lultima posizione durerà un po più a lungo, poiché deve aspettare. Puoi aggiungere un & finale per evitare questo timeout. Puoi facilmente controllare questo comportamento nel tuo monitor, provare a inviare la stringa con e senza lextra & e vedrai questo ritardo di timeout.

In realtà lo fai non è necessario lindice del servo, puoi semplicemente inviare la tua stringa di posizioni e ottenere lindice del servo dalla posizione del valore nella stringa, qualcosa come: 90&80&180&. Se usi lindice servo, forse vuoi controllarlo (convertirlo in int, quindi abbinare lindice del ciclo i) per assicurarti che nulla sia andato storto con il tuo messaggio.

Devi controllare che la stringa restituita da readStringUntil non sia vuota. Se la funzione va in timeout, non hai ricevuto dati sufficienti e quindi qualsiasi tentativo di estrarre i tuoi valori int produrrà risultati strani.

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

Commenti

  • Questa sembra unottima soluzione grazie. Lesempio lo chiarisce perfettamente
  • E se avessimo un numero indefinito di ingressi servo? nel mio esempio ce nera 3. Ma cosa succederebbe se a volte fosse più o meno. Puoi offrire qualche suggerimento per gestire uno scenario del genere
  • Certo: ci sono due possibilità. 1 .Invia prima il numero di servi: 3: val1 & val2 & val3 &, leggi tale numero prima di avviare il ciclo. 2. Utilizza un terminatore diverso per indicare che non hai più servi, continua finché non lo trovi: val1 & val2 & val3 & #, ad esempio.
  • Sono contento che questa soluzione ti abbia aiutato, @ValrikRobot, potresti per favore convalidare la risposta se fosse utile?
  • o tu può semplicemente rimuovere il for, quindi il codice funzionerà ogni volta che invii un comando.

Answer

Puoi utilizzare Stream.readStringUntil(terminator) passando un terminatore diverso per ogni parte.

Su ogni parte quindi chiami String.toInt

Answer

La soluzione più semplice è utilizzare 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); 

Questo fornisce il seguente output:

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

Cheers!

Commenti

  • Non funziona per serial.read () … any i dea perché? Ottengo il seguente errore: invalid conversion from 'int' to 'char*' [-fpermissive]

Risposta

Vedi esempio su: 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; } 

Risposta

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

Risposta

jfpoilpret ha fornito unottima risposta per analizzare il comando seriale su Arduino. Tuttavia Attiny85 non ha una seriale bidirezionale: è necessario utilizzare SoftwareSerial. In questo modo si trasferisce lo stesso codice per 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, "&"); } } }  

Schemi Attiny85 per numeri pin inserisci qui la descrizione dellimmagine

Sketch viene compilato in:

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. 

Quindi cè molto spazio e memoria per il resto del codice

Commenti

  • Come leggere da seriale su un ATtiny85 non ‘ non fa parte della domanda.
  • Ci scusiamo per divergendo dalla domanda, ma la comunità e le risorse disponibili per Attiny sono molto più piccole di quelle per Arduino. Le persone come me che cercano risposte usano la parola chiave Arduino e talvolta si trovano in situazioni molto difficili come implementare il codice Arduino su Attiny non è sempre banale: ha dovuto convertire il codice originale per farlo funzionare su Attiny, lo ha testato e ha deciso di farlo re it
  • Questo sito è in formato Q & A. Le risposte dovrebbero rispondere alla domanda. Il tuo aggiunge semplicemente qualcosa che ‘ non è correlato ad esso.

Risposta

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 } 

Risposta

Ecco Arduino per dividere una stringa come risposta alla domanda “Come dividere una stringa in sottostringa?” dichiarata come un duplicato della presente domanda.

Lobiettivo della soluzione è analizzare una serie di GPS posizioni registrate in un file scheda SD . Invece di ricevere una stringa da Serial, la stringa viene letta dal file.

La funzione è StringSplit() parse una stringa sLine = "1.12345,4.56789,hello" a 3 stringhe sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: le righe di input da analizzare,
  2. char cDelim: il carattere delimitatore tra i parametri,
  3. String sParams[]: larray di parametri di output,
  4. int iMaxParams: il numero massimo di parametri,
  5. Output int: il numero di parametri analizzati parametri,

La funzione è basata su String::indexOf() e 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); } 

E lutilizzo è davvero semplice:

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

Risposta

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

Risposta

Non è una risposta alla tua domanda ma può essere utile per qualcuno. Se la tua stringa ha un prefisso specifico, puoi utilizzare semplicemente startsWith e substring. Ad esempio

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

Quindi invia height 10 cosa estrarrà 10 .

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *