(SOLUCION)Recibir datos Serial(NewSoftSerial),parsearlos separando por puntocoma

Buenas, a ver si me podeis ayudar en terminar este programita que funciona casi todo a excepción de meter cada dato en una variable para luego poder hacer cosas con ellos.

tengo un arduino que lee un snesor de ultrasonidos y me envia una cadena de caracteres por newsoftserial que son millis;numero

consigo recibir los datos, consigo que me imprima por separado millis y numero, pero necesito guardar "numero" en una variable para luego multiplicar etc.

//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1280743388/all
//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1226107230/3


char inData[80];
byte index = 0;
char flightdata[80];
int counter = 0; //initialise the counter

#include <NewSoftSerial.h>
  NewSoftSerial mySerial(2, 3);


 char *str;
 
 
void setup()
{
 Serial.begin(9600);
 // set the data rate for the NewSoftSerial port
  mySerial.begin(4800);
  mySerial.println("Hello, world! Soy Arduino COM 5 Receptor.");
}


void loop()
{
   while(mySerial.available() > 0)
   {
	char aChar = mySerial.read();    //  Serial.print("Char recibido: "); Serial.println(aChar);
          
	if(aChar == '\n')
	{
	   // End of record detected. Time to parse

                    // esto imprime el array letra a letra que contine todo lo que se recibio
                    Serial.println("Separando e imprimiendo cada grupo de caracteres separados por semicolon");
                    char *p = inData;
                    while ((str = strtok_r(p, ";", &p)) != NULL) // delimiter is the semicolon
                    { 
                      Serial.println(str);
                      flightdata[counter] = *str; //use the counter as an index to add each value to the array
		      counter++; //increment the counter
                    }
           index = 0;
	   inData[index] = NULL;
	}
	else
	{
	   inData[index] = aChar;
	   index++;
	   inData[index] = '\0'; // Keep the string NULL terminated
	}
   }




Serial.print(inData[0]);
Serial.println(inData[1]);

        // esto imprime el array letra a letra que contine todo lo que se recibio
        Serial.println("Imprimiendo array caracter a caracter almacenado en variable inData");
        int i;
        for (i = 0; i < 80; i = i + 1) {
          Serial.print(inData[i]);
        }
        //-------------------------------------------------------------------------
        
   
   
   Serial.print("El dato primero es: "); Serial.println(flightdata[0]);
   Serial.print("El dato segundo es: "); Serial.println(flightdata[1]);
   
   
   
   
   Serial.println();
   delay(1000);
} // fin del loop

muchas gracias !!!

Sergio, que no te siente mal, pero ésto ha salido muchas veces...Arduino Playground - FAQ (Calcular Int a partir de String o cadena de caracteres y otro ejemplo usando una máquina de estados )

Otros:
http://arduino.cc/forum/index.php/topic,32359.0.html

:wink:

muchas gracias Igor, no me sienta mal en absoluto, la verdad es que me he precipitado al abrir el post a lo mejor porque he buscando bastante por internet pero todo en ingles y por eso no he llegado a los post que me pones.

voy a echarles un vistazo a ver si entre todos consigo hacer varias cosas seguidas

  1. recibir una cadena separada por ;
  2. romperla por ; y meter cada valor en una variable
  3. convertir las cadenas de cada variable en variables correctas (tipo long, int etc)

necesito hacer esto tanto para lo que recibe un arduino por newsoftwareserial, como por ethernet al llamar a un php.

si consigo hacerlo todo, pondre aquí todo el codigo.

muchas gracias !!!!

Mira si esto te sirve para entender más o menos que hay que ir haciendo. Ya es cosa que lo adaptes. Cada uno tiene sus métodos.....
Es parte de un Interprete que estoy haciendo poco a poco cuando estoy aburrido, pero Input es una cadena e index es la posición actual dentro de la cadena (todo está dentro de una estructura llamada Interpreter). nSizeInput es el tamaño del array Input.

Es una función que recoge un número (float con separador decimal con el caracter ".") dentro de un string (array de carácteres).

//-------------------------------------------------------------
// Get Number (decimal '.')
//-------------------------------------------------------------
float getNumber()
{
  float myNumber;
  
  myNumber=Interpreter.Input[Interpreter.index]-'0';
  ++Interpreter.index;
  while( (Interpreter.Input[Interpreter.index]>='0') && (Interpreter.Input[Interpreter.index]<='9') && (Interpreter.index<nSizeInput) )
  {
      myNumber=myNumber*10+(Interpreter.Input[Interpreter.index]-'0');
      ++Interpreter.index;
  }
  if ( (Interpreter.Input[Interpreter.index]<='.') && (Interpreter.index<nSizeInput) )
  {
    int mytimes=1;
    float myDecimal;
    ++Interpreter.index;
    while( (Interpreter.Input[Interpreter.index]>='0') && (Interpreter.Input[Interpreter.index]<='9') && (Interpreter.index<nSizeInput) )
    {
      myDecimal=myDecimal*10+(Interpreter.Input[Interpreter.index]-'0');
      ++Interpreter.index;
      mytimes*=10.0;
    }
    myNumber+=myDecimal/mytimes;
  }
  
  return myNumber;
  
}

gracias Igor !

Me esta costando mas de lo que pensaba, tengo:
. la cadena de texto recibida
. separada por ;

pero no consigo almacenarla en otro Char para asi luego hacer un atoi o atol

//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1280743388/all
//http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1226107230/3


char inData[80];
byte index = 0;
char flightdata[80];
int counter = 0; //initialise the counter

#include <NewSoftSerial.h>
  NewSoftSerial mySerial(2, 3);


 char *str;
 char dato[100];
 
void setup()
{
 Serial.begin(9600);
 // set the data rate for the NewSoftSerial port
  mySerial.begin(4800);
  mySerial.println("Hello, world! Soy Arduino COM 5 Receptor.");
}


void loop()
{
  counter = 0;
   while(mySerial.available() > 0)
   {
	char aChar = mySerial.read();    //  Serial.print("Char recibido: "); Serial.println(aChar);
          
	if(aChar == '\n')
	{
	   // End of record detected. Time to parse
                    
                    // esto imprime el array letra a letra que contine todo lo que se recibio
                    Serial.println("Separando e imprimiendo cada grupo de caracteres separados por semicolon");
                    char *p = inData;
                    while ((str = strtok_r(p, ";", &p)) != NULL) // delimiter is the semicolon
                    { 
                      //Serial.println(str);
                        Serial.print("grupo "); Serial.print(counter); Serial.print("\t Valor: ");
                      if (counter == 1) Serial.print("****");
                      Serial.println(str);
                      flightdata = *str; //use the counter as an index to add each value to the array
		      counter++; //increment the counter
                    }
                    
                    
           //  for (int i = 0; i < 80; i = i + 1) {          Serial.print(inData[i]);        }
           
           index = 0;
	   inData[index] = NULL;
	}
	else
	{
	   inData[index] = aChar;
	   index++;
	   inData[index] = '\0'; // Keep the string NULL terminated
	}
   }



        // esto imprime el array letra a letra que contine todo lo que se recibio
        /*
        Serial.println("Imprimiendo array caracter a caracter almacenado en variable inData");
        for (int i = 0; i < 80; i = i + 1) {          Serial.print(inData[i]);        }        
        */
        //-------------------------------------------------------------------------
   
   Serial.println();
   delay(2000);
} // fin del loop

me dice

Serial_read_put_to_array.cpp: In function 'void loop()':
Serial_read_put_to_array:45: error: incompatible types in assignment of 'char' to 'char [80]'

alguien me puede explicar de forma sencillita la diferencia entre estos dos? es decir que hace el asterisco?
es que he leido cosas acerca de pointers y demas pero no pillo lo que hace

 char *str;
 char dato[100];

en mi codigo se utiliza aqui

 char *p = inData;
                    while ((str = strtok_r(p, ";", &p)) != NULL) // delimiter is the semicolon
                    { 
                      Serial.println(str);

y no entiendo lo que hace porque pensaba que estaba rompiendo usando el semicolon y metiendolo en str pero si imprimo str caracter por caracter contiene mas datos.
habia pensado en usar el contador para decir, si es el segundo parametro metelo en otra variable, y esa ya convertirla a numero.

please ayuda chicos que el resto del codigo lo entiendo y creo que esta listo para lo que quiero hacer, pero me falta separar por ";"

con este codigo he conseguido leer 1 solo valor que me llegue por serial sin punto-coma ni nada y convertirlo en un float

ahora falta lo dificil que es lo de crear un array tipo float y meter en cada posición uno de los parametros que venian separados por ";"
si alguien puede ayudarme. mil gracias

char inData[80];
byte index = 0;
float nivel_diesel = 0.0;

#include <NewSoftSerial.h>
  NewSoftSerial mySerial(2, 3);

void setup()
{
 Serial.begin(9600);
 // set the data rate for the NewSoftSerial port
  mySerial.begin(4800);
  mySerial.println("Hello, world! Soy Arduino COM 5 Receptor.");
}


void loop()
{

   while(mySerial.available() > 0)
   {
	char aChar = mySerial.read();    //  Serial.print("Char recibido: "); Serial.println(aChar);
          
	if(aChar == '\n')
	{
	   // End of record detected. Time to parse
                    {
                    float decade = pow(10,((index-1)-1));
                    nivel_diesel = 0.0;
                    Serial.println("Start:");
                    for (int i = 0; i < int(index-1); i = i + 1) {   
                        float digito = inData[i] - '0';
                        nivel_diesel = digito*decade + nivel_diesel;
                        Serial.print("decade: ");Serial.print(decade);Serial.print("\tdigito: ");Serial.println(digito);
                        decade = decade/10;
                    }
                    
                     Serial.println(nivel_diesel);
                    
                    /// pruebas matematicas con el numero
                        //Serial.println((4*final));
                    }
           index = 0;
	   inData[index] = NULL;
	}
	else
	{
	   inData[index] = aChar;
	   index++;
	   inData[index] = '\0'; // Keep the string NULL terminated
	}
   }



        // esto imprime el array letra a letra que contine todo lo que se recibio
        /*
        Serial.println("Imprimiendo array caracter a caracter almacenado en variable inData");
        for (int i = 0; i < 80; i = i + 1) {          Serial.print(inData[i]);        }        
        */
        //-------------------------------------------------------------------------
   
   Serial.println();
   delay(2000);
} // fin del loop

esto me imprime

decade: 10.00	digito: 1.00
decade: 1.00	digito: 5.00
15.00

siendo el 15 el valor que recibe de otro arduino por newsoftwareserial

poco a poco esto va saliendo !!!

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <NewSoftSerial.h>
    NewSoftSerial mySerial(2, 3);
    
char inData[80];
byte index = 0;

// Variables donde guardar los datos recibidos separados
float nivel_diesel = 0.0;
unsigned long arduino_online = 0;
int parametro3;

/*
int input []= {11,10,9} ;
int values []= {0,0,0} ;    // aqui guarda los valores tras recibirlos y separarlos
*/
long values []= {0,0,0} ;    // aqui guarda los valores tras recibirlos y separarlos


void setup()
{

 Serial.begin(9600);
  mySerial.begin(4800);
  mySerial.println("Hello, world! Soy Arduino COM 5 Receptor.");
}

void loop(){

   read_parse_serial();

}  



void read_parse_serial()
{
  while(mySerial.available() > 0)
   {
	char aChar = mySerial.read();    //  Serial.print("Char recibido: "); Serial.println(aChar);
          
	if(aChar == '\n')
	{
	   // End of record detected. Time to parse
                    parse();
           index = 0;
	   inData[index] = NULL;
	}
	else
	{
	   inData[index] = aChar;
	   index++;
	   inData[index] = '\0'; // Keep the string NULL terminated
	}
   }

}


void parse()
{
     //char signal[] = "125;0;255";    // esto es lo que tengo que cambiar por lo que venga del puerto serie
     char separator[] = ";";	   // el caracter que uso para separar los valores

     char *result = NULL;
     int index = 0;

     result = strtok( inData, separator );   // aqui se separan los caracteres
     while( (result != NULL) && (index < 3) ) {
       Serial.print(  result );
       Serial.println( );
     values[index++] = atol(result);    // aqui se convierten en valores

     result = strtok( NULL, separator );  // aqui vacia result
     }

      // Guardamos cada valor en una variable que tenga un nombre de variable significativo 
      arduino_online = values[0];
      nivel_diesel   = values[1];
      parametro3     = values[2];
      Serial.print("Millis ardui (parametro 1): "); Serial.println(arduino_online);
      Serial.print("Nivel Diesel (parametro 2): "); Serial.println(nivel_diesel);
      Serial.print("Rand number  (parametro 3): "); Serial.println(parametro3);
      // Demostracion de que el valor ya es esta guardado como un float
      Serial.println(nivel_diesel*4);
}

la salida es

1399929
15
123

Millis ardui (parametro 1): 1399929
Nivel Diesel (parametro 2): 15.00
Rand number  (parametro 3): 123
60.00

ahora a intentar optimizarla y hacerlo lo mas generico posible para que sirva para todos los usos. por ejemplo esta definido que solo le llegan 3 parametros, intentare hacerlo dinamico.

En mi forma de verlo, una máquina de estados es lo más genérico y fácil de ampliar/depurar. Pero ésto ya es cosa de cada uno.

¿Por qué no lo haces manualmente? Me refiero, así entiendes el proceso. Luego si quieres, usas las funciones de C que hay para ésto. Pero si no te quieres liar con punteros, puedes recorrer el vector y haciendo lo que toque.

Me explico, tienes tu vector dato[100] y creas una variable para ir recorriendo,digamos "i". Puedes ir recorriendolo e ir tomando decisiones. Es decir, leo la posición actual, si es un número, pues mynum=mynum10+ (dato_-'0'). Aumento el contador para leer la sigiente posición..... Cuando lo que leas es un ";", pues sabras que será tu siguiente número. Así que sigues leyendo, y en caso que sea número, pues haces mynum2=mynum210+ (dato*-'0'). Es tan fácil como un for ó while...*_

eso es muy buena idea Igor, y si te fijas es lo que he hecho en el código un poco mas arriba

con esto me he puesto a leer carácter a carácter y he hecho lo que comentas dentro de un for.
creo que con ese ejemplo que he puesto se entiende el método manual.

luego en mi ultimo ejemplo ya recurro a la función para hacer la separación y así de paso aprendo el tema de los punteros.
y creo que el codigo ha quedado bastante apañado, es decir,

  • funcion donde leo lo que me llega por serial,
  • cuando termina de leer llama a la funcion de parsear
  • la funcion de parsear se encarga de romper la cadena por el separador y meterlo en un array.
  • luego cada posición del array lo guardo en la variable que me interesa para mi programa

gracias por las ideas !

Yo te lo decía, porque así no tienes que tener un buffer. Es decir, no tienes que ir metiendo en un buffer para luego usar strtrok ó el método que quieras. Puedes ir haciendolo a "tiempo real" según te van llegando los caracteres de serie.
Me refiero, podrías hacerlo sin el for o while.... según te va llegando la trama, vas haciendo lo que toque.

También yo en su día no sabía cómo coger el tema, http://arduino.cc/forum/index.php/topic,76223.0.html

Al final en mi caso el tiempo que tenía para el envío de los datos por el puerto serie era muy reducido y no me servía este método. Lo que hice fue extraer los 2 bytes de cada número (que eran unsigned int) con highByte() y lowByte(), enviarlos, y en la recepción recomponer el número original con word(highByte, lowByte). El ahorro total de tiempo fue muy alto dado el tiempo de que disponía.

Con esto tengo una duda. Si lo que tenemos es un valor long o float y queremos enviar los 4 bytes que lo componen, ¿qué instrucciones hay para hacerlo? ¿Cómo se extraen los 4 bytes y cómo se recomponen?

Ahora existen funciones como parseInt()

chiva:
Ahora existen funciones como parseInt()

cierto y cuando sacaron 1.0 me alegre de verlo pero todavia no he hecho el cambio a 1.0 debido a problemas con la libreria ethernet y que yo tengo q poner ip fija y gateway.

ademas si recibes millis() por serial no cabe dentro de un int.
y con mi ejemplo no dependes de usar unicamente la version 1.0 de arduino sino que valdria para todas.
de todas formas, es un buen aporte chiva.

acabo de ver que existe Parselong, lo cual evita el problema que mencionaba antes del tamaño.

pues si tengo un ratito hare un código de ejemplo probando esto también ya que es interesante la idea de no necesitar un buffer.
gracias por la sugerencia Igor, si lo hago lo pondré aquí también.

Mira, al final, todo es lo mismo... Por curiosidad he mirado a ver cómo hacen parseInt

long Stream::parseInt(char skipChar)
{
  boolean isNegative = false;
  long value = 0;
  int c;

  c = peekNextDigit();
  // ignore non numeric leading characters
  if(c < 0)
    return 0; // zero returned if timeout

  do{
    if(c == skipChar)
      ; // ignore this charactor
    else if(c == '-')
      isNegative = true;
    else if(c >= '0' && c <= '9')        // is c a digit?
      value = value * 10 + c - '0';
    read();  // consume the character we got with peek
    c = timedPeek();
  }
  while( (c >= '0' && c <= '9') || c == skipChar );

  if(isNegative)
    value = -value;
  return value;
}

:wink:

Hola, te hago una consulta, ¿pudiste solucionar tu inconveniente??
Slds!