Wie teile ich eine eingehende Zeichenfolge?

Ich sende eine Liste von Servopositionen über die serielle Verbindung an das Arduino im folgenden Format

1:90&2:80&3:180 

Dies würde analysiert werden als:

servoId : Position & servoId : Position & servoId : Position

Wie würde ich diese Werte aufteilen und in konvertieren? eine ganze Zahl?

Kommentare

  • Ich habe Slave (Arduino Uno) senden Zeichenfolge über serielle 30; 12.4; 1 und 1 Master (esp8266) Empfangszeichenfolge Ich möchte im Master Daten wie 30 12.4 1 getrennt haben und sie in einer Micro-SD-Karte speichern.

Antwort

Im Gegenteil Bei anderen Antworten würde ich mich aus folgenden Gründen lieber von String fernhalten:

  • dynamische Speichernutzung (dies kann schnell zu Heap-Fragmentierung und Speicherauslastung )
  • ziemlich langsam aufgrund von constr Funktions- / Zerstörungs- / Zuweisungsoperatoren

In einer eingebetteten Umgebung wie Arduino (selbst für ein Mega mit mehr SRAM) würde ich lieber Standard-C-Funktionen :

  • strchr() : Suche nach einem Zeichen in einer C-Zeichenfolge (dh char *)
  • strtok() : Teilungen Ein C-String in Teilzeichenfolgen, basierend auf einem Trennzeichen
  • atoi() : konvertiert ein C. Zeichenfolge zu einem int

Dies würde zu folgendem Codebeispiel führen:

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

Der Vorteil hierbei ist, dass keine dynamische Speicherzuweisung stattfindet. Sie können sogar input als lokale Variable innerhalb einer Funktion deklarieren, die die Befehle lesen und ausführen würde. Sobald die Funktion zurückgegeben wird, wird die von input (im Stapel) belegte Größe wiederhergestellt.

Kommentare

  • Hatte ‚ nicht an das Speicherproblem gedacht. Das ist großartig.
  • Ausgezeichnet. Meine Antwort war sehr “ arduino “ und verwendete typische Arduino SDK-Funktionen, an die ein neuartiger Benutzer besser gewöhnt sein könnte, aber diese Antwort ist Was ist für “ Produktionssysteme “ zu tun? Versuchen Sie im Allgemeinen, der dynamischen Speicherzuweisung in eingebetteten Systemen zu entgehen.

Antwort

Diese Funktion kann verwendet werden eine Zeichenfolge basierend auf dem Trennzeichen in Teile zu trennen.

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

String in int konvertieren

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

Dieser Codeabschnitt benötigt einen String und trennt es basierend auf einem bestimmten Zeichen und gibt das Element zwischen dem Trennzeichen zurück.

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

Kommentare

  • das ist eine schöne perfekte Antwort! Vielen Dank!
  • Sie sollten diesen wählen. (Daumen hoch)
  • elegant! funktioniert perfekt zum Senden von Schlüsselwertpaaren

Antwort

Sie könnten Folgendes tun, aber bitte berücksichtigen Berücksichtigen Sie verschiedene Dinge:

Wenn Sie readStringUntil() verwenden, wird gewartet, bis das Zeichen oder die Zeitüberschreitung empfangen wird. Daher hält die letzte Position mit Ihrer aktuellen Zeichenfolge etwas länger, da sie warten muss. Sie können ein abschließendes & hinzufügen, um dieses Zeitlimit zu vermeiden. Sie können dieses Verhalten auf Ihrem Monitor leicht überprüfen, versuchen, die Zeichenfolge mit und ohne das zusätzliche & zu senden, und Sie werden eine solche Zeitüberschreitungsverzögerung feststellen.

Sie tun dies tatsächlich Wenn Sie den Servoindex nicht benötigen, können Sie einfach Ihre Positionszeichenfolge senden und den Servoindex anhand der Wertposition in der Zeichenfolge abrufen, z. B.: 90&80&180&. Wenn Sie den Servoindex verwenden, möchten Sie ihn möglicherweise überprüfen (in int konvertieren und dann mit dem Schleifenindex i abgleichen), um sicherzustellen, dass mit Ihrer Nachricht nichts schief gelaufen ist. P. >

Sie müssen überprüfen, ob die von readStringUntil zurückgegebene Zeichenfolge nicht leer ist. Wenn die Funktion eine Zeitüberschreitung aufweist, haben Sie nicht genügend Daten erhalten, und daher führt jeder Versuch, Ihre int -Werte zu extrahieren, zu seltsamen Ergebnissen.

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

Kommentare

  • Dies scheint eine sehr gute Lösung zu sein, danke. Das Beispiel klärt es perfekt
  • Was wäre, wenn wir es hätten In meinem Beispiel gab es eine undefinierte Anzahl von Servo-Eingängen. Aber was ist, wenn es manchmal mehr oder weniger war? Können Sie einen Vorschlag für die Behandlung eines solchen Szenarios machen?
  • Sicher: Es gibt zwei Möglichkeiten. 1 .Senden Sie zuerst die Anzahl der Servos: 3: val1 & val2 & val3 &, lesen solche Nummer vor dem Starten der Schleife. 2. Verwenden Sie einen anderen Terminator, um anzuzeigen, dass Sie keine Servos mehr haben. Führen Sie eine Schleife durch, bis Sie diese finden: val1 & val2 & val3 & # zum Beispiel.
  • Ich bin froh, dass diese Lösung Ihnen geholfen hat, @ValrikRobot. Könnten Sie bitte die Antwort validieren, wenn sie nützlich war?
  • oder Sie Sie können das for einfach entfernen, sodass der Code jedes Mal funktioniert, wenn Sie einen Befehl senden.

Antwort

Sie können Stream.readStringUntil(terminator) verwenden, indem Sie für jedes Teil einen anderen Terminator übergeben.

Für jedes Teil Sie rufen dann String.toInt

Antwort

Die einfachste Lösung besteht darin, sscanf () zu verwenden.

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

Dies ergibt die folgende Ausgabe:

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

Prost!

Kommentare

  • Es funktioniert nicht für serial.read () … any i Dea warum? Ich erhalte die folgende Fehlermeldung: invalid conversion from 'int' to 'char*' [-fpermissive]

Antwort

Siehe Beispiel unter: 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; } 

Antwort

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

Antwort

jfpoilpret lieferte eine großartige Antwort zum Parsen des seriellen Befehls auf Arduino. Attiny85 hat jedoch keine bidirektionale Seriennummer – SoftwareSerial muss verwendet werden. Auf diese Weise portieren Sie denselben Code für 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, "&"); } } }  

Attiny85-Schaltpläne für Pin-Nummern Geben Sie hier die Bildbeschreibung ein

Skizze kompiliert 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. 

Für den Rest des Codes ist also viel Platz und Speicher vorhanden.

Kommentare

  • Wie man auf einem ATtiny85 aus der Serie liest, ist nicht ‚ nicht wirklich Teil der Frage.
  • Entschuldigung Abweichend von der Frage, aber die für Attiny verfügbaren Communitys und Ressourcen sind viel kleiner als für Arduino. Leute wie ich, die nach Antworten suchen, verwenden das Schlüsselwort Arduino und geraten manchmal in sehr schwierige Situationen, wenn sie Arduino-Code implementieren Attiny ist nicht immer trivial. Musste den Originalcode konvertieren, um mit Attiny zu arbeiten, testete ihn und entschied sich für sha re it
  • Diese Site hat das Format Q & A. Antworten sollten die Frage beantworten. Mit freundlichen Grüßen fügt nur etwas hinzu, das ‚ nicht damit zusammenhängt.

Antwort

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 } 

Antwort

Hier ist Arduino Methode zum Teilen eines Strings als Antwort auf die Frage „Wie teile ich einen String in Teilzeichenfolgen?“ deklariert als ein Duplikat der vorliegenden Frage.

Ziel der Lösung ist es, eine Reihe von GPS zu analysieren Positionen, die in einer SD-Karte -Datei angemeldet sind. Anstatt einen String von Serial empfangen zu lassen, wird der String aus der Datei gelesen.

Die Funktion lautet StringSplit() parse a String sLine = "1.12345,4.56789,hello" bis 3 Strings sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: die zu analysierenden Eingabezeilen,
  2. char cDelim: das Trennzeichen zwischen Parametern,
  3. String sParams[]: das Ausgabearray von Parametern,
  4. int iMaxParams: die maximale Anzahl von Parametern,
  5. Ausgabe int: die Anzahl der analysierten Parameter,

Die Funktion basiert auf String::indexOf() und 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); } 

Und die Verwendung ist wirklich einfach:

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

Antwort

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

Antwort

Es ist keine Antwort auf Ihre Frage, aber es kann für jemanden nützlich sein. Wenn Ihre Zeichenfolge ein bestimmtes Präfix hat, können Sie einfach startsWith und substring verwenden. Beispiel:

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

Und senden Sie dann height 10, was 10 extrahiert .

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.