Autor Tema: Problemas con código entrada de datos por puerto serial  (Leído 3465 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
Problemas con código entrada de datos por puerto serial
« en: 05 de Septiembre de 2016, 02:00:00 »
Hola

El siguiente código permite capturar del puerto serial del arduino mega, diferentes tipos de datos, primero solicita un numero entero en diferentes rangos,

ejemplo de entrada:

1:> 255
2:> 32767
3:> 65535
4:> 2147483647
5:> 4294967295

luego un float, luego una cadena y finalmente un bool

6:> 1.234
7:> Hola HispanoAmérica
8:> 1

funciona bien para el caso de entrada de enteros cuando son "verdadero enteros", pero si el usuario en lugar de entrar un numero entero, escribe un numero real o decimal, lleva la parte decimal a la siguiente entrada o solicitud, se pierde la siguiente solicitud de entrada :(

1:> 1.1

Ahora si el entero pasa de 4294967296, se desborda, como hago para capturar valores mas grandes? El IDE de arduino no soporta por ejemplo unsigned long long int?

Gracias

Código: C++
  1. bool loop_flag = false;
  2.  
  3. void setup() {
  4.   Serial.begin(9600); // abre el puerto serie a 9600 bps:  
  5. }
  6.  
  7. void loop() {
  8.   if (loop_flag == false) {
  9.     Serial.println("*** Ejecucion Iniciada. ***");
  10.     entrada_salida(); // nombre del proceso principal
  11.     Serial.println("Desea reinicar");
  12.     if (readString_Serial() != "Y\n" ) {
  13.       loop_flag = true;
  14.       Serial.println("*** Ejecucion Finalizada. ***");
  15.       Serial.end();
  16.     }
  17.   }
  18. }
  19.  
  20. void entrada_salida() {
  21.   byte a;
  22.   int a0;
  23.   unsigned int a1;
  24.   long a2;
  25.   unsigned long a3;
  26.  
  27.   float b;
  28.   String c;
  29.   bool d;
  30.   // leer de a uno
  31.   Serial.println( "leer diferentes tipos \n" );
  32.  
  33.   Serial.println( "entero minimo sin signo entre 0 y 255 1:>" ); // desborde en 256 -0
  34.   a = readshortInt_0_255_Serial();
  35.   Serial.println(a);
  36.  
  37.   Serial.println( "entero corto con signo entre -32,768 y 32,767 2:>" ); // desborde en 32768 o -32769
  38.   a0 = readInteger_neg32k_32k_Serial();
  39.   Serial.println(a0);
  40.  
  41.   Serial.println( "entero sin signo desde 0 hasta 65.535 3:>" ); // desborde en 65536
  42.   a1 = readInteger_0_65k_Serial();
  43.   Serial.println(a1);
  44.  
  45.   Serial.println( "entero largo con signo entre -2.147.483,648 hasta 2.147.483.647 4:>" ); // 2147483648 -2147483649
  46.   a2 = readInteger_neg2_1e9_2_1e9_Serial();
  47.   Serial.println(a2);
  48.  
  49.   Serial.println( "entero largo sin signo dese0 hasta 4.294.967.295 5:>" ); // 4294967296
  50.   a3 = readInteger_0_4_2e9_Serial();
  51.   Serial.println(a3);
  52.  
  53.   //
  54.   Serial.println( "real 6:>" );
  55.   b = readFloat_Serial();
  56.   Serial.println(b);
  57.  
  58.   Serial.println( "cadena 7:>" );
  59.   c = readString_Serial();
  60.   Serial.println(c);
  61.  
  62.   Serial.println( "logico 8:>" );
  63.   d = readBool_Serial();
  64.   Serial.println(d);
  65.  
  66. }
  67.  
  68. // Subfuciones terminal
  69. String readString_Serial() {
  70.   while (Serial.available() == 0) {} // Espera la entrada del usuario
  71.   return Serial.readString();
  72. }
  73.  
  74. // Integer
  75.  
  76. byte readshortInt_0_255_Serial() {  // [0, 255]
  77.   while (Serial.available() == 0); // Espera la entrada del usuario
  78.   return Serial.parseInt();
  79. }
  80.  
  81. int readInteger_neg32k_32k_Serial() { // [-32,768, ..., 32,767]
  82.   while (Serial.available() == 0); // Espera la entrada del usuario
  83.   return Serial.parseInt();
  84. }
  85.  
  86. unsigned int readInteger_0_65k_Serial() { // [0, ..., 65,535 ]
  87.   while (Serial.available() == 0); // Espera la entrada del usuario
  88.   return Serial.parseInt();
  89. }
  90.  
  91. long readInteger_neg2_1e9_2_1e9_Serial() { // [-2,147,483,648, ... 2,147,483,647]
  92.   while (Serial.available() == 0); // Espera la entrada del usuario
  93.   return Serial.parseInt();
  94. }
  95.  
  96. unsigned long readInteger_0_4_2e9_Serial() { // [0, ..., 4.294.967.295]
  97.   while (Serial.available() == 0); // Espera la entrada del usuario
  98.   return Serial.parseInt();
  99. }
  100.  
  101. ///
  102.  
  103. float readFloat_Serial() {
  104.   while (Serial.available() == 0); // Espera la entrada del usuario
  105.   return Serial.parseFloat();
  106. }
  107.  
  108. bool readBool_Serial() { // número  igual a cero se toma como 0 (false), otro caso se convierte a 1 (true)
  109.   while (Serial.available() == 0); // Espera la entrada del usuario
  110.   return Serial.parseInt();
  111. }
« Última modificación: 05 de Septiembre de 2016, 11:44:26 por CompSystems »
Desde Colombia

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #1 en: 05 de Septiembre de 2016, 03:48:39 »
La solucion esta en leer el codigo y ver como funciona el parseInt, lo demas seria una simple busqueda de google.

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #2 en: 05 de Septiembre de 2016, 11:23:58 »
Tenía un problema al hacer la promoción de datos (casting) todo lo estaba llevando a INTEGER, había perdida de datos, código actualizado

Sobre parseInt()  dice en http://www.prometec.net/funcionesii/

“parseInt() devuelve el primer número entero (de tipo long) válido a partir de la posición actual. ... parseInt() termina cuando se encuentra el primer carácter que no es un dígito Esta función forma parte de la claseStream y es llamada por cualquier clase que herede de Stream (Wire, Serial, etc)”

Al entrar un numero decimal en una solicitud de número entero encuentra que el carácter punto y retorna la parte entera, pero la parte decimal de lleva a la segunda entrada, esto se puede visualizar ejecutando el código y escribiendo en la primera entrada por ejemplo 1.234
Imprime 1
Luego imprime 234, Parece ser que punto hace la veces de espacio entonces la entrada se interpreta como dos argumentos, ¿Cómo evitar esto? es decir que la linea de entrada "Serial Monitor" interprete un solo argumento. o que 1.234 lo trunque a 1

Similarmente sucede lo mismo si se ingresa 1 234

PD:
Si se desborda en 1 el valor del límite SUPERIOR del tipo de dato, retorna el límite inferior del tipo de dato

1:> 256 -> 0  [0, 255]
2:> 32768 ->  -32,768 [-32,768, ..., 32,767]
3:> 65536 -> 0 [0, ..., 65,535 ]
4:> 2147483648 -> -2147483648 [-2,147,483,648, ... 2,147,483,647]
5:> 4294967296 -> 0 [0, ..., 4.294.967.295]

Si se desborda en -1 el valor del límite INFERIOR del tipo de dato, retorna el límite superior del tipo de dato

1:> -1   -> 255  [0, 255]
2:> -32769 ->  -32,767 [-32,768, ..., 32,767]
3:> -1 -> 65535 [0, ..., 65,535 ]
4:> -2147483649 -> 2147483647 [-2,147,483,648, ... 2,147,483,647]
5:> -1 -> 4294967295 [0, ..., 4.294.967.295]

/!\ para la tarjeta de desarollo "Arduino Due" los rangos son mas amplios para algunos casos =)

La instrucción read  no verifica el desborde, si el programador no tiene en cuenta los límites del tipo de dato se puede dar un error lógico.
« Última modificación: 05 de Septiembre de 2016, 11:46:16 por CompSystems »
Desde Colombia

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #3 en: 05 de Septiembre de 2016, 11:46:15 »
No entiendo por que lo explicas si sos el que preguntas?, hubieras mirado el codigo y te hubieras dado cuenta, sin necesidad de preguntar. No se cual es tu objetivo.

Algo asi como un profesor te hace una pregunta que tal ves alguno conteste o no y luego lo explica detalladamente. Pero la diferencia es que el profesor ya conoce su materia (o al menos lo aparenta).
« Última modificación: 05 de Septiembre de 2016, 11:48:30 por KILLERJC »

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #4 en: 05 de Septiembre de 2016, 12:04:56 »
pregunto una inquietud o duda que tengo en algunas publicaciones, investigo en la internet, libros etc, pero no siempre es fácil encontrarla, muchas veces me acuesto y hasta sueño con el problema, precisamente en este caso la publique el problema anoche y me desperté con la solución de porque no imprimía bien los datos que le solicitaba, no tenia encuenta el tipo final que retornaba cada función entonces me autorespondo y trato de explicarlo, pues a alguien le puede servir, no se trata de que yo pregunte y si descubro la solución me quede callado, quiero que quede una buena documentación en mis publicaciones, esto sitio web no es un chat, esto es un libro de consultas, ayuda, publicaciones, aportes etc

mi interpretación de parseINT, es

Citar
Parece ser que punto hace la veces de espacio entonces la entrada se interpreta como dos argumentos

con una nueva inquietud
¿Cómo evitar que la linea de entrada "Serial Monitor" interprete un solo argumento? por ejemplo que 1.234 lo lea como 1
« Última modificación: 05 de Septiembre de 2016, 12:10:10 por CompSystems »
Desde Colombia

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #5 en: 05 de Septiembre de 2016, 12:18:24 »
No estoy de acuerdo con vos, ya que esto no es un blog y si tenes la capacidad de buscar en internet lo encontrabas en 2 segundos al porque de tu problema.
En fin yo no tengo ningun poder para decirte que hacer. Solo opino, ya que no puedo hacer mas nada :P. Asi que aca termino este tema para no arruinar el post y te contesto tu pregunta

Citar
¿Cómo evitar esto que la linea de entrada "Serial Monitor" interprete un solo argumento? por ejemplo que 1.234 lo lea como 1
Lo unico que se me ocurre es: Leer como string hasta el \r\n y luego parsearlo como entero/float o lo que desees.

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #6 en: 05 de Septiembre de 2016, 12:54:19 »
por lo general en los BLOGS son publicaciones tipo articulo y no para resolver preguntas o problemas, y los usuarios pueden comentar y muchos de ellos hacen preguntas y sigue siendo un blog. Un foro también se usa para hacer publicaciones y no se convierte en un blog, no hay problema que un usuario solicite que le ayuden a resolver un problema y si el encuentra la respuesta o solución el mismo se autoresponda.

Si hubiera encontrado la respuesta en 2" no hubiera publicado esto ...

todas las respuestas son bienvenidas , y sigamos discutiendo ... que para esto son los foros 8)
« Última modificación: 05 de Septiembre de 2016, 13:26:30 por CompSystems »
Desde Colombia

Desconectado tsk

  • PIC18
  • ****
  • Mensajes: 258
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #7 en: 06 de Septiembre de 2016, 03:43:44 »
Si tiene soporte a los long long int, lo único que no viene implementado son las funciones strtoll y strtoull, por lo que tendrías que implementarlas.

Prueba agregando una variable tipo usingned long long int. El tipo de dato long long int tiene un tamaño de 8 bytes o 64 bits, por lo que si no quieres escribir unsigned long long int.

Si la librería parseInt no llena tus expectativas, entonces la mejor manera de solventar las limitaciones que imponen es que hagas tu propia función, para ello debes comenzar con un tokenizer. Esa función va a separar el string de entrada en tokens.

Tu puedes decidir el separador

Un ejemplo sencillo en C (imagina que en algún lugar tienes un buffer donde vas a colocar los tokens)

Código: [Seleccionar]
uint8_t tokenizer(char *str, char sep)
{
    uint8_t i=0, token_counter=0;
   
    for(*str;*str!=0;*str++)
    {
        if(*str == sep) {
            temp_buffer[token_counter++][i] = 0;
            i = 0;       
        } else {
            temp_buffer[token_counter][i++] = *str;
        }

    }

    if(strlen(temp_buffer[token_counter])>0)
        temp_buffer[token_counter][i]=0;

    return token_counter+1;
}

ahora supón que mandas por el puerto serie el siguiente comando

Código: [Seleccionar]
AT @1 23 67

Tu separador va a ser un espacio, por lo tanto

Código: [Seleccionar]
tokens = tokenizer(entrada, ' ');

al finalizar la variable tokens tendrá un valor de 4 y en el buffer tendrás en las primeras 4 posiciones

Código: [Seleccionar]
AT\0
@1\0
23\0
67\0

Después puedes implementar tu propia función para convertir a entero o flotante o procesar los datos de acuerdo a tus necesidades. Así si quieres tener un unsigned long long int, podrás tenerlo sin complicaciones.

¿Cómo puedes convertir el string a entero?

Este, por ejemplo, sería el contenido de tu string
Código: [Seleccionar]
["3","3","3","4","5"]
A cada uno de los caracteres le restas 30
Código: [Seleccionar]
[3,3,3,4,5]

Como estamos en base 10
Código: [Seleccionar]
[3*10^4,3*10^3,3*10^2,4*10^1,5*10^0]

Y sumas el resultado quedando 33345

Aunque también puedes ver la implementación de strtoull que no es incluida en las librerías de arduino(avr)

https://opensource.apple.com/source/Libc/Libc-391.4.1/stdlib/FreeBSD/strtoull.c

En las librerías incluidas en el arduino para avr, sólo vienen las funciones strtol y strtoul, para el caso del arduino due, las cosas son distintas, ya que en el si puedes hacer uso del strtoull sin complicaciones.

PS: para hacer uso de strtoul, strtol, etc. es necesario agregar un include

Código: [Seleccionar]
#include <stdlib.h>

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
Re:Problemas con código entrada de datos por puerto serial
« Respuesta #8 en: 08 de Septiembre de 2016, 23:07:01 »
Gracias tsk, pronto analizo tu respuesta
Desde Colombia


 

anything