Enviar datos a Arduino desde monitor serial

Hola, buenas a todos.

Leí hace poco que se pueden enviar datos al arduino por puerto serie siempre que se encuentre conectado por USB al PC. Por lo visto se pueden enviar usando el botón "enviar" que aparece arriba a la derecha en el monitor serial del software "arduino". Sabía que existía ese botón pero jamás pensé que tuviera utilidad :LOL:

Mi pregunta es... ¿en qué formato se envía lo que escribo? Por lo que he leído se envían los bytes en bruto pero me parece un poco chocante. ¿No se pueden enviar floats sin necesidad de rebanarse la cabeza intentando averiguar a que serie de bytes corresponden? ¿Cómo recibe por ejemplo (en binario) el arduino el texto "5648"? (sin las comillas)

Gracias por adelantado.
Un saludo, electronicos!
 
Los tipos de datos son algo inherente al lenguaje. Las comunicaciones son en bruto, tu defines un protocolo y tratas los datos como sea, por ejemplo mandas 5648f o fl5648 y al tener ese formato, en tu programa arduino lo haces un cast a float. Por esto se definen protocolos de comunicación entre aplicaciones.
 
Sí, pero si inicio comunicación serial con "Serial.begin()" y voy leyendo enteros con "Serial.parseInt()" por ejemplo, leerá el primer byte del buffer y lo tratará como entero. Pero a la hora de meterlo desde el monitor el propio pc tendrá que hacer una conversión de lo que tu escribas para pasarlo a binario. Y esa codificación a binario es la que desconozco. No sé cual usa.

Saludos!

Envía el contenido del cuadro de texto que tienes al lado.
Como suele ser ASCII, pues ASCII

y no puedo enviar floats de alguna forma?
gracias
 
Escribes 1653.564 le das a enviar y recibirás esa cadena ASCII, lo puedes filtrar con parsefloat o parseint a conveniencia.
Escribir un float en el teclado podría ser una paranoia total...
 
Entonces el monitor convierte esa información a binario de punto flotante al enviarlo, ¿no? O sea, genera 32 bits en el buffer con ese número codificado en ellos.
 
Para que el cliente y el servidor se entiendan deben ponerse de acuerdo en las reglas (protocolo) de intercambio de información.

Si envías información ASCII por la terminal, el Arduino recibirá los bytes que forman esos caracteres ASCII. Si esos caracteres forman un número en punto flotante, necesitas que el Arduino tengo una subrutina que haga la transformación.

En cambio, si le envías los bytes que forman un número en punto flotante, puedes recogerlos y guardarlos directamente. Eso sí: deben formar un número en punto flotante con el formato en que trabaja Arduino (que no tiene por qué ser el mismo formato de punto flotante del emisor). Si no son iguales, volvemos a necesitar una subrutina de transformación.

Aquí, además, tienes un problema añadido: los bytes que forman los números en punto flotante van en crudo, por lo que debes crear un protocolo para que Arduino sepa cuándo comienza (y termina) la transmisión de cada valor. Poner un carácter de fin de línea a veces es suficiente, pero no siempre.

Por ejemplo, 3.4, como punto flotante en simple precisión, se almacena en los sistemas Linux x86_64 como los 4 bytes siguientes:
Código:
$ perl -E 'print pack "f", 3.4' |hexdump -C
00000000  9a 99 59 40                                       |..Y@|
00000004
Son esos 4 bytes los que tenemos que enviar. Si el receptor sabe que son bytes que forman un número en punto flotante, lo puede reconstruir:
Código:
$ perl -E 'say unpack "f", join "", map { chr hex } qw(9a 99 59 40);'                                                                                                      
3.40000009536743
 
Última edición por un moderador:
No no no no noooo
El monitor serial es monitor serial única y exclusivamente. Por eso se llama así y no se llama conversor de punto flotante.
Evidentemente en el cable solo hay binario, no puede haber otra cosa. Si ese binario al recibirse en el PC cuadra con cosas ASCII verás esas cosas ASCII sean las que sean.
Para enviar lo mismo pero al revés. Como en el cuadro de texto solo cabe texto; 5671 es el texto "5671" no es el cinco mil seiscientos setenta y uno binario. Se envía el carácter ASCII de cinco, el del seis etc
Cuando llega al arduino esa cadena la puedes convertir a float o int o lo que sea.

Edito, no había visto el comentario de Joaquín Ferrero.
Es mucho mas eficiente mandar el numero binario que en cadena ascii, pero es mucho menos legible. Si lo haces ya no podrás usar sin mas un monitor serie o un terminal. Necesitarás una aplicación especifica y la depuración es mas liosa.
 
Última edición:
Se codifican en 4 bytes (los float), pero lo más seguro es que estén en formato numérico de los chips de AVR.

Edito: encontré un hilo (en inglés) sobre este tema de transmitir flotantes.
 
Última edición por un moderador:
Usa parsefloat, va perfectamente.
Vale que la trasnmisión igual son 10bytes en lugar de 6 pero si tu aplicación no es muy intensiva lo mismo te da, y a cambio la comunicación es "legible para los humano"
 
Ahora mismo estaba probando este, con conversión ASCII - float y funciona. Hay que meter los números en formato X.XX

Es sólo un código de prueba. Realmente mi pregunta era únicamente para saber cómo funcionaban los float a través de la consola para así poder simular comunicación TTL sin necesidad de hacerla físicamente.

Código:
byte parteEntera, primerDecimal, segundoDecimal;
float salida;

void setup(){
  Serial.begin(9600);
}

void loop(){
  if(Serial.available() > 3){
    
      parteEntera = Serial.read();
      Serial.read();
      primerDecimal = Serial.read();
      segundoDecimal = Serial.read();
      
      Serial.print(parteEntera);
      Serial.print(".");
      Serial.print(primerDecimal);
      Serial.println(segundoDecimal);
    
    salida = ((float)(parteEntera)-48.0)+0.1*((float)(primerDecimal)-48.0)+0.01*((float)(segundoDecimal)-48.0);
    
    Serial.println(salida);
    Serial.println("------------------------");
  }
}
 
Casi todas las líneas que has escrito las puedes sustituir por un sencillo
Código:
    salida = Serial.parseFloat();
que es lo que recomendaba Scooter, que se encargará de leer la entrada y de traducirla a un número en punto flotante.

Documentación de Serial.parseFloat.

También puedes usar la función básica atof(). Ejemplo.

Pero recordemos que esto es comunicación usando los caracteres ASCII. Si se trata de conectar varios dispositivos por TTL, pues es más eficiente transmitir los flotantes tal cual son, y así te ahorras la conversión en ambos extremos.
 
Última edición por un moderador:
Esa es la ventaja de los TTL.

Pero la función parseFloat la estaba usando y es la que me daba lecturas extrañas por ser ASCII lo que se introduce por el monitor serial.

Cuando al buffer llegan datos binarios con estructura de punto flotante, al usar parseFloat se lee tal cual. El problema viene cuando los datos binarios representan caracteres ASCII y al intentar interpretarlos como float da un resultado completamente distinto. Hay que hacer una conversión como la que yo estaba probando. Pero podéis ver fácilmente que para precisión de dos decimales ya es un buen pedacito de código que, insertado en un código que haga otras funciones, va a generar lo que se llama un señor tocho.
 
Pero la función parseFloat la estaba usando y es la que me daba lecturas extrañas por ser ASCII lo que se introduce por el monitor serial.
Es que es eso justamente lo que hace: interpreta los caracteres ASCII presentes en el canal Serial para devolver el float que encuentre. La función para la interpretación en el primer carácter que no pertenezca a un flotante (un carácter que no sea '+', '-', un dígito, o el '.').

Entonces, ¿a qué lecturas extrañas te refieres? ¿Quieres decir que el Arduino no te respondía lo que le enviabas?

Cuando al buffer llegan datos binarios con estructura de punto flotante, al usar parseFloat se lee tal cual. El problema viene cuando los datos binarios representan caracteres ASCII y al intentar interpretarlos como float da un resultado completamente distinto.
No, no es así: parseFloat necesita como entrada caracteres ASCII para luego interpretarlos (parse) a un número en punto flotante.

Aquí hay un ejemplo:

PHP:
/* Hipotenusa - Interactivo vía Terminal serie
   M. Ray Burnette 20130125
   Arduino Nano 328P target processor
   Binary sketch size: 5,184 bytes (of a 30,720 byte maximum)
*/

#include "math.h"               // incluir la biblioteca matemática

float a;                        // lados del triángulo
float b;
float h;                        // hipotenusa a calcular
char basura = ' ';              // variable para eliminar la basura que no nos interesa


void setup() {                  // ejecutar una vez, al principio

    Serial.begin(9600);         // velocidad inicial a 9600 bps

    Serial.println("Vamos a calcular la hipotenusa, h");
    Serial.println("");

    Serial.flush();             // limpiar el canal serie
}

void loop() {                   // bucle principal

    Serial.println("Entre un valor para el cateto 'a', pulse ENTRAR");

    while (Serial.available() == 0) ;    // esperar aquí hasta que haya un carácter disponible
 
    {
        // Cateto 'a'
        a = Serial.parseFloat();        // esto es nuevo en Arduino 1.0

        Serial.print("a = ");           // lo devolvemos
        Serial.println(a, DEC);

        while (Serial.available() > 0)  // .parseFloat() puede dejar algún carácter no numérico
        {
            junk = Serial.read();       // limpiamos el búfer
        }
    }

    Serial.println("Entre un valor para el cateto 'b', pulse ENTRAR");

    while (Serial.available() == 0) ;

    {
        // Cateto 'b'
        b = Serial.parseFloat();

        Serial.print("b = ");
        Serial.println(b, DEC);

        while (Serial.available() > 0)
        {
            junk = Serial.read();
        }
    }

    h = sqrt (float( a*a + b*b ));      // cálculo de la hipotenusa

    Serial.print("hipotenusa = ");
    Serial.println(h, DEC);
    Serial.println();
}
Como ves, parseFloat() ahorra muchas líneas, pero hay que asegurarse de que está leyendo lo que queremos. Los bucles while() están ahí para eliminar del búfer lo que pueda seguir al número flotante, para que no quede nada suelto. Por ejemplo, ahí se desecha el/los carácter/es de fin de línea que sigue al número.
 
Yo he insertado "3.2" (por ejemplo) en el monitor serial con un parse float y me ha devuelto basura. ¿Hará falta un + o un - delante como dices?

Pero el código que pones es php. ¿Estás seguro de que ambos parse funcionan de la misma forma?
 
No es código PHP. He usado el etiquetado PHP de esta web para que salga el código coloreado, nada más.

Prueba este sketch, y cuando te pida el valor de los catetos, metes valores float (sin las comillas, claro). Solo el valor float seguido de Entrar (o Enter, según tu teclado).
 
Testeado y funciona! Muchas gracias!!

EDITO: ¿Por qué es necesario vaciar el buffer justo después de leer el flotante?
 
Última edición:
Atrás
Arriba