Comment diviser une chaîne entrante?

Jenvoie une liste de positions de servo via la connexion série à larduino au format suivant

1:90&2:80&3:180 

Ce qui serait analysé comme suit:

servoId : Position & servoId : Position & servoId : Position

Comment pourrais-je diviser ces valeurs et les convertir en un entier?

Commentaires

  • Jai lesclave (arduino uno) envoie une chaîne via série 30; 12.4; 1 et 1 maître (esp8266) reçoivent une chaîne Je veux dans le maître avoir des données séparées comme 30 12.4 1 et les enregistrer dans une carte micro sd

Répondre

Au contraire à dautres réponses, je « préfère rester à lécart de String pour les raisons suivantes:

  • utilisation de la mémoire dynamique (qui peut rapidement conduire à Fragmentation du tas et épuisement de la mémoire )
  • assez lent en raison de la construction Opérateurs daction / destruction / assignation

Dans un environnement embarqué comme Arduino (même pour un Mega qui a plus de SRAM), je préfère utiliser fonctions C standard :

  • strchr() : recherche un caractère dans une chaîne C (ie char *)
  • strtok() : scission une chaîne C en sous-chaînes, basée sur un caractère de séparation
  • atoi() : convertit un C chaîne à un int

Cela conduirait à lexemple de code suivant:

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

Lavantage ici est quaucune allocation dynamique de mémoire na lieu; vous pouvez même déclarer input comme une variable locale à lintérieur dune fonction qui lirait les commandes et les exécuterait; une fois la fonction retournée, la taille occupée par input (dans la pile) est récupérée.

Commentaires

  • Hadn ‘ t pensé au problème de mémoire. cest génial.
  • Excellent. Ma réponse était très basée sur  » arduino  » et utilisant des fonctions SDK arduino typiques auxquelles un nouvel utilisateur pourrait être plus habitué, mais cette réponse est que doit-on faire pour les systèmes de  » production « . En général, essayez déchapper à lallocation dynamique de mémoire dans les systèmes embarqués.

Réponse

Cette fonction peut être utilisée pour séparer une chaîne en morceaux en fonction du caractère de séparation.

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

Convertir la chaîne en entier

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

Ce bloc de code prend une chaîne et le sépare en fonction dun caractère donné et renvoie Lélément entre le caractère de séparation

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

Commentaires

  • cest une belle réponse parfaite! merci beaucoup!
  • Vous devriez choisir celui-ci. (Bravo)
  • élégant! fonctionne parfaitement pour envoyer des paires valeur / clé

Réponse

Vous pouvez faire quelque chose comme ce qui suit, mais veuillez prendre en compte compte plusieurs choses:

Si vous utilisez readStringUntil(), il attendra jusquà ce quil reçoive le caractère ou les délais. Ainsi, avec votre chaîne actuelle, la dernière position durera un peu plus longtemps, car elle doit attendre. Vous pouvez ajouter un & de fin pour éviter ce délai. Vous pouvez facilement vérifier ce comportement sur votre moniteur, essayez denvoyer la chaîne avec et sans le supplément & et vous verrez un tel délai dexpiration.

Vous faites réellement pas besoin de lindex servo, vous pouvez simplement envoyer votre chaîne de positions, et obtenir lindex servo par la position de la valeur dans la chaîne, quelque chose comme: 90&80&180&. Si vous utilisez lindex dasservissement, vous voudrez peut-être le vérifier (convertir en int, puis faire correspondre lindex de boucle i) pour vous assurer que rien ne sest mal passé avec votre message.

Vous devez vérifier que la chaîne de retour de readStringUntil nest pas vide. Si la fonction expire, vous navez pas reçu suffisamment de données, et donc toute tentative dextraire vos valeurs de int produira des résultats étranges.

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

Commentaires

  • Cela semble être une très bonne solution merci. Lexemple clarifie parfaitement les choses
  • Et si nous avions un nombre indéfini dentrées dasservissement? Dans mon exemple, il y en avait 3. Mais que se passerait-il si parfois cétait plus ou moins. Pouvez-vous proposer une suggestion pour gérer un tel scénario
  • Bien sûr: il y a deux possibilités. 1 .Envoyez dabord le nombre de servos: 3: val1 & val2 & val3 &, lire ce nombre avant de démarrer la boucle. 2. Utilisez un autre terminateur pour indiquer que vous navez plus de servos, bouclez jusquà ce que vous le trouviez: val1 & val2 & val3 & #, par exemple.
  • Heureux que cette solution vous ait aidé, @ValrikRobot, pourriez-vous valider la réponse si elle était utile?
  • ou vous peut simplement supprimer le for, et ainsi le code fonctionnera à chaque fois que vous enverrez une commande.

Réponse

Vous pouvez utiliser Stream.readStringUntil(terminator) en passant un terminateur différent pour chaque partie.

Sur chaque partie vous appelez ensuite String.toInt

Réponse

La solution la plus simple consiste à utiliser 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); 

Cela donne le résultat suivant:

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

Cheers!

Commentaires

  • Cela ne fonctionne pas pour serial.read () … tout i dea pourquoi? Jobtiens lerreur suivante: invalid conversion from 'int' to 'char*' [-fpermissive]

Réponse

Voir lexemple sur: 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; } 

Réponse

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

Réponse

jfpoilpret a fourni une excellente réponse pour analyser la commande série sur Arduino. Cependant Attiny85 na pas de série bidirectionnelle – SoftwareSerial doit être utilisé. Cest ainsi que vous portez le même code pour 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, "&"); } } }  

Schémas Attiny85 pour les numéros de broches entrez la description de limage ici

Sketch se compile en:

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. 

Il y a donc beaucoup despace et de mémoire pour le reste du code

Commentaires

  • Comment lire une série sur un ATtiny85 nest ‘ pas vraiment une partie de la question.
  • Désolé pour divergeant de la question, mais la communauté et les ressources disponibles pour Attiny sont beaucoup plus petites que pour Arduino. Les gens comme moi à la recherche de réponses utilisent le mot-clé Arduino et se retrouvent parfois dans des situations très délicates comme la Attiny nest pas toujours anodin. Jai dû convertir le code original pour quil fonctionne sur Attiny, il a testé son fonctionnement et a décidé de re it
  • Ce site est au format Q & A. Les réponses doivent répondre à la question. Le vôtre ajoute simplement quelque chose qui ‘ ny est pas lié.

Réponse

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 } 

Réponse

Voici Arduino méthode pour fractionner une chaîne en réponse à la question « Comment fractionner une chaîne en sous-chaîne? » déclaré comme une copie de la question actuelle.

Lobjectif de la solution est danalyser une série de GPS positions enregistrées dans un fichier carte SD . Au lieu davoir une chaîne reçue de Serial, la chaîne est lue à partir du fichier.

La fonction est StringSplit() parse une chaîne sLine = "1.12345,4.56789,hello" à 3 chaînes sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: les lignes dentrée à analyser,
  2. char cDelim: le caractère délimiteur entre les paramètres,
  3. String sParams[]: le tableau de sortie des paramètres,
  4. int iMaxParams: le nombre maximum de paramètres,
  5. Sortie int: le nombre danalyses paramètres,

La fonction est basée sur String::indexOf() et 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); } 

Et lutilisation est vraiment simple:

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

Réponse

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

Réponse

Ce nest pas une réponse à votre question mais cela peut être utile pour quelquun. Si votre chaîne a un préfixe spécifique, vous pouvez utiliser simplement startsWith et substring. Par exemple,

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

Et puis envoyez height 10 ce qui extraira 10 .

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *