¿Cómo divido una cadena entrante?

Estoy enviando una lista de posiciones de servo a través de la conexión serial al arduino en el siguiente formato

1:90&2:80&3:180 

Que se analizaría como:

servoId : Position & servoId : Position & servoId : Position

¿Cómo dividiría estos valores y los convertiría en un entero?

Comentarios

  • Tengo esclavo (arduino uno) enviar una cadena a través de serial 30; 12.4; 1 y 1 maestro (esp8266) recive string quiero en el maestro tener datos separados como 30 12.4 1 y guardarlos en la tarjeta micro sd

Responder

Contrariamente a otras respuestas, prefiero mantenerme alejado de String por las siguientes razones:

  • uso de memoria dinámica (que puede llevar rápidamente a fragmentación del montón y agotamiento de la memoria )
  • bastante lento debido a la construcción operadores de acción / destrucción / asignación

En un entorno embebido como Arduino (incluso para un Mega que tiene más SRAM), prefiero usar funciones C estándar :

  • strchr() : busque un carácter en una cadena C (es decir, char *)
  • strtok() : divisiones una cadena C en subcadenas, en función de un carácter separador
  • atoi() : convierte una C cadena a un int

Eso conduciría al siguiente ejemplo de código:

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

La ventaja aquí es que no tiene lugar una asignación de memoria dinámica; incluso puede declarar input como una variable local dentro de una función que leería los comandos y los ejecutaría; una vez que se devuelve la función, se recupera el tamaño ocupado por input (en la pila).

Comentarios

  • No ‘ no había pensado en el problema de la memoria. esto es genial.
  • Excelente. Mi respuesta fue muy » arduino » y usando las funciones típicas del SDK de arduino a las que un usuario nuevo podría estar más acostumbrado, pero esta respuesta qué se debe hacer para los sistemas » de » de producción. En general, intente escapar de la asignación de memoria dinámica en sistemas integrados.

Respuesta

Esta función se puede utilizar para separar una cadena en pedazos según el carácter de separación.

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

Convertir cadena a int

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

Este fragmento de código toma una cadena y lo separa según un carácter dado y devuelve el elemento entre el carácter de separación

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

Comentarios

  • ¡Esa es una hermosa respuesta perfecta! ¡Muchas gracias!
  • Deberías elegir este. (Pulgar hacia arriba)
  • ¡elegante! funciona perfectamente para enviar pares clave-valor

Respuesta

Puede hacer algo como lo siguiente, pero tenga en cuenta cuenta varias cosas:

Si usa readStringUntil(), esperará hasta que reciba el carácter o los tiempos de espera. Por lo tanto, con su cadena actual, la última posición durará un poco más, ya que tiene que esperar. Puede agregar un & final para evitar este tiempo de espera. Puede verificar fácilmente este comportamiento en su monitor, intente enviar la cadena con y sin & adicional y verá ese retraso en el tiempo de espera.

De hecho, lo hace no necesita el índice de servo, puede enviar su cadena de posiciones y obtener el índice de servo por la posición del valor en la cadena, algo así como: 90&80&180&. Si usa el índice de servo, tal vez desee verificarlo (convertirlo a int, y luego hacer coincidir el índice de bucle i) para asegurarse de que nada salió mal con su mensaje.

Debe verificar que la cadena de retorno de readStringUntil no esté vacía. Si la función se agota, no recibió suficientes datos y, por lo tanto, cualquier intento de extraer sus valores de int producirá resultados extraños.

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

Comentarios

  • Esto parece una muy buena solución gracias. El ejemplo lo aclara perfectamente
  • ¿Qué pasaría si tuviéramos ¿Un número indefinido de entradas de servo? En mi ejemplo había 3. Pero, ¿y si a veces fuera más o menos? ¿Puede ofrecer alguna sugerencia para manejar tal escenario?
  • Seguro: hay dos posibilidades. 1 .Envía primero la cantidad de servos: 3: val1 & val2 & val3 &, lee dicho número antes de iniciar el ciclo. 2. Use un terminador diferente para indicar que no tiene más servos, repita hasta encontrarlo: val1 & val2 & val3 & #, por ejemplo.
  • Me alegro de que esta solución te haya ayudado, @ValrikRobot, ¿podrías validar la respuesta si fue útil?
  • o tú simplemente puede eliminar el for, por lo que el código funcionará cada vez que envíe un comando.

Responder

Puede usar Stream.readStringUntil(terminator) pasando un terminador diferente para cada parte.

En cada parte luego llama a String.toInt

Responder

La solución más sencilla es utilizar 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); 

Esto da el siguiente resultado:

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

¡Salud!

Comentarios

  • No funciona para serial.read () … cualquier i dea por qué? Recibo el siguiente error: invalid conversion from 'int' to 'char*' [-fpermissive]

Respuesta

Vea el ejemplo en: 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; } 

Responder

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

Respuesta

jfpoilpret proporcionó una excelente respuesta para analizar el comando serial en Arduino. Sin embargo, Attiny85 no tiene una serie bidireccional; se debe usar SoftwareSerial. Así es como se transfiere el mismo código para 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, "&"); } } }  

Esquemas de Attiny85 para números de pin ingrese la descripción de la imagen aquí

Sketch se compila 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. 

Así que hay mucho espacio y memoria para el resto del código

Comentarios

  • Cómo leer desde una serie en un ATtiny85 no es ‘ realmente parte de la pregunta.
  • Lo siento por divergiendo de la pregunta, pero la comunidad y los recursos disponibles para Attiny son mucho más pequeños que para Arduino. Las personas como yo que buscan respuestas usan la palabra clave Arduino y, a veces, se encuentran en situaciones muy complicadas al implementar el código Arduino en Attiny no siempre es trivial. Tuve que convertir el código original para que funcionara en Attiny, lo probé y decidí re it
  • Este sitio está en formato Q & A. Las respuestas deben responder a la pregunta. El suyo solo agrega algo que ‘ no está relacionado con él.

Respuesta

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 } 

Respuesta

Aquí está Arduino método para dividir una cadena como respuesta a la pregunta «¿Cómo dividir una cadena en una subcadena?» declarada como un duplicado de la presente pregunta.

El objetivo de la solución es analizar una serie de GPS posiciones registradas en un archivo tarjeta SD . En lugar de recibir un String de Serial, el String se lee desde el archivo.

La función es StringSplit() parse una cadena sLine = "1.12345,4.56789,hello" a 3 cadenas sParams[0]="1.12345", sParams[1]="4.56789" & sParams[2]="hello".

  1. String sInput: las líneas de entrada que se analizarán,
  2. char cDelim: el carácter delimitador entre parámetros,
  3. String sParams[]: la matriz de parámetros de salida,
  4. int iMaxParams: el número máximo de parámetros,
  5. Salida int: el número de parámetros analizados parámetros,

La función se basa en String::indexOf() y 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); } 

Y el uso es realmente 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(""); } 

Responder

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

Responder

No es una respuesta a su pregunta, pero puede ser útil para alguien. Si su cadena tiene un prefijo específico, puede usar simplemente startsWith y substring. Por ejemplo,

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

Y luego enviar height 10 lo que extraerá 10 .

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *