Vamos a programar #94 - Formateando números en arduino - Camino a speedometer version final.

Hola de nuevo a todos, el día de hoy vamos a continuar con mas partes para llegar a la versión final del velocímetro.


Leyendo algunos comentarios que hace la gente sobre que más se le podría modificar al velocímetro, muchos de ellos coincidían que había que corregir algunos de ellos. Los principales "detalles" estaban en la pantalla. Si bien no eran errores, aprovechando que se va a actualizar, decidí arreglarlos de una vez.

Supongamos que en la pantalla tenemos una velocidad de 10.09 km/h pero si desaceleramos, a por ejemplo 9.1 la pantalla mostraba algo cómo 9.19, y la cosa era peor si nos deteníamos ya que se mostraba 0.09. La razón por la que pasaba esto es por cómo se maneja la pantalla. Para ponerlo de forma simple, solo cuando se escribe algo en cierta posición, la pantalla se actualiza pero solo en esa zona, cuando escribimos "v=10.09" se usan siete espacios y mientras los usemos con números que usen el mismo "ancho" no se verá ningún problema, pero cuando tenemos cualquier número inferior a 10.00 cómo 9.9 ahora solo se usan menos espacios por lo que en lugar de actualizar los mismos siete de antes, solo se actualizan cinco o seis. Para poder solucionarlo, podemos escribir número que si importar cual sea, siempre ocupen la misma cantidad de espacios.

En C existen varias formas de formatear números, pero algunas de ellas no están disponibles para usarse en arduino (y debo de admitirlo, eso no lo sabia hasta apenas) y por eso veremos cómo es posible usar el reemplazo que arduino ofrece.

Primero que nada, hay que definir cual es el "estilo" que queremos que cada número tenga. En el caso del velocímetro, hay cuatro números importantes: la velocidad, la distancia, las revoluciones (o vuletas) y el voltaje de la batería. En el caso de la velocidad, una velocidad máxima de 99.99km/h es mas que suficiente para una bicicleta por lo que el numero ocuparía cinco espacios (con el punto incluido). Para las distancia, un viaje de 999.99 km es mas que suficiente para la mayoría de los ciclistas, por lo que seis digitos parecen suficiente, para las revoluciones, 999999 parecen suficientes por lo que seis espacios parecen suficientes y finalmente la batería, ya que el voltaje máximo de una sola pila es 4.2v, con solo reservar tres espacios es mas que suficiente.

Ahora veamos un código hipotético en C que serviría para usarse en un programa de computadora (por ejemplo) pero que no produciría el resultado esperado en arduino. EL código es el siguiente:

#include <stdio.h>

float Speed = 9.1;
float Distance = 9.04;
int Revolution = 16384;
float Battery = 2.0;

char BuffSpeed[20];
char BuffDistance[20];
char BuffRevolution[20];
char BuffBattery[20];

int main()
{

    sprintf(BuffBattery , "R=%3.1f\n", Battery);
    sprintf(BuffRevolution , "R=%6d\n", Revolution);
    sprintf(BuffDistance , "d=%6.2f\n", Distance);
    sprintf(BuffSpeed, "V=%5.2f\n", Speed);

    printf(BuffSpeed);
    printf(BuffDistance);
    printf(BuffRevolution);
    printf(BuffBattery);

    return 0;
}

Al compilarlo con los valores asignados, produce el siguiente resultado:

V= 9.10

d=  9.04

R= 16384

R=2.0

Pero cómo no es muy fácil de apreciar, solo para este ejemplo vamos a poner "0" en lugar de espacios en blanco para que se mas fácil de apreciar (en la pantalla no para poder preservarla un poco mas.) por lo que probamos con el siguiente código:

#include <stdio.h>

float Speed = 9.1;
float Distance = 9.04;
int Revolution = 16384;
float Battery = 2.0;

char BuffSpeed[20];
char BuffDistance[20];
char BuffRevolution[20];
char BuffBattery[20];

int main()
{

    sprintf(BuffBattery , "R=%3.1f\n", Battery);
    sprintf(BuffRevolution , "R=%06d\n", Revolution);
    sprintf(BuffDistance , "d=%06.2f\n", Distance);
    sprintf(BuffSpeed, "V=%05.2f\n", Speed);

    printf(BuffSpeed);
    printf(BuffDistance);
    printf(BuffRevolution);
    printf(BuffBattery);

    return 0;
}

Al compilarlo con los valores asignados, produce el siguiente resultado:

V=09.10

d=009.04

R=016384

R=2.0

Ahora resulta mas sencillo observar que por ejemplo la velocidad siempre ocupara cinco espacios a pesar de ser menor a 10km/h y que por ejemplo la distancia ocupa seis espacios a pesar de ser menor a 100km.

Pero que pasa si tratamos de ejecutar el mismo código en arduino?


Cómo puede observarse, la mayor parte del código se compila pero no funciona. Esto se debe principalmente a que a la hora de formatear los números del tipo "float" requiere mas poder de procesamiento por lo que en arduino no se implementó (**o eso es lo que quiero creer**), para el caso del los enteros es posible usar la misma función "sprintf()".

Para poder hacer algo similar, podemos usar la funcion "dtostrf()". Esta funcion lo que hace es convertir un número "double" a su equivalente en "string", para eso la funcion recibe cuatro parametros.

  • char * dtostrf (double __val, signed char __width, unsigned char __prec, char *__s)
    • __val es el valor que se va a convertir
    • __width es el tamaño que debe de tener la cadena.
    • __prec Es la cantidad de decimales que usaran
    • __s Es el sitio donde se almacenara el resultado.
Ahora recordemos que queremos que la velocidad tenga cinco espacios, pero además de dos decimales, lo que podemos hacer es algo cómo lo que sigue:

dtostrf(Speed , 5 , 2 , BuffVelocity);

Con lo anterior le estamos indicando que el string resultante debe de convertir la variable "speed", pero además el resultado, debe de tener una longitud de 5 y dos decimales, todo lo anterior lo debe de guardar en "BuffVelocity". Solo para ejemplificar, portemos el programa que corre en la computadora para que corra en arduino y para eso usaremos una pantalla LCD.

//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
// Conexiones de la pantalla usando el convertidor a i2c
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

float Speed = 9.1;
float Distance = 9.04;
int Revolution = 16384;
float Battery = 2.0;

char BuffSpeed[20];
char BuffDistance[20];
char BuffRevolution[20];
char BuffBattery[20];


void setup()
{
    lcd.init();
    lcd.backlight();
}


void loop()
{
    dtostrf(Speed , 5 , 2 , BuffSpeed);
    dtostrf(Distance , 6 , 2, BuffDistance);
    sprintf(BuffRevolution , "R=%6d", Revolution);
    dtostrf(Battery , 4 , 2, BuffBattery);

    lcd.setCursor(0, 0);
    lcd.print("v=");
    lcd.setCursor(2 , 0);
    lcd.print(BuffSpeed);

    lcd.setCursor(8, 0);
    lcd.print("d=");
    lcd.setCursor(10, 0);
    lcd.print(BuffDistance);

    lcd.setCursor(0,1);
    lcd.print(BuffRevolution);

    lcd.setCursor(10,1);
    lcd.print("v=");
    lcd.print(BuffBattery);
}


Al compilarlo y ejecutarlo tenemos que ahora si se produce el resultado esperado.

Y bien, por ahora es todo, ya casi llegamos a la versión final del velocímetro que justo acaba de cumplir tres años desde la primera.

Los códigos de antes solo fueron ilustrativos, por lo que si quieres probarlos, puedes copiarlos y pegarlos para usarlos.

Los leo luego.

No hay comentarios.