Poesía Binaria

Utilizar float con sprintf() y derivados en Arduino


El objetivo de la plataforma Arduino es que los programas sean pequeños, ya que tienen que caber en pocos Kbs. Un pequeño problema que tenemos con eso es que muchas bibliotecas no están implementadas completamente, sino que las encontramos en su versión light, en las que implementan sólo las funcionalidades más normales.
Un ejemplo de ello son los comandos printf() y scanf() cuya funcionalidad no cubre los valores de punto flotante (float, double), y que si hacemos el siguiente programa:

1
2
3
4
5
6
7
8
9
10
11
12
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  char buffer[30];
  sprintf (buffer, "Valor: %f\n", 25.4);
  Serial.println(buffer);
  delay(1000);
}

Veremos constantemente:


Valor: ?
Valor: ?
Valor: ?

Y no muestra el valor deseado. Eso sí, ocupa 4.29Kb

La clave para la solución

El tema está en que el compilador no enlaza con las bibliotecas buenas, sino que enlaza con las ligeras. Por lo que tenemos que hacer que éste enlace con las bibliotecas correctas añadiendo el siguiente parámetro al compilador avr-gcc:

-Wl,-u,vfprintf -lprintf_flt

Haciendo el cambio para SConstruct

Si para compilar tus proyectos de Arduino utilizas este método, tienes que modificar una línea en el archivo SConstruct… pongo parte del contexto, cerca de la línea 170, donde pone:

1
2
3
4
5
6
envArduino.Append(BUILDERS = {'Processing':Builder(action = fnProcessing,
        suffix = '.cpp', src_suffix = '.pde')})
envArduino.Append(BUILDERS={'Elf':Builder(action=AVR_BIN_PREFIX+'gcc '+
        '-mmcu=%s -Os -Wl,--gc-sections -o $TARGET $SOURCES -lm'%MCU)})
envArduino.Append(BUILDERS={'Hex':Builder(action=AVR_BIN_PREFIX+'objcopy '+
        '-O ihex -R .eeprom $SOURCES $TARGET')})

Debemos escribir:

1
2
3
4
5
6
envArduino.Append(BUILDERS = {'Processing':Builder(action = fnProcessing,
        suffix = '.cpp', src_suffix = '.pde')})
envArduino.Append(BUILDERS={'Elf':Builder(action=AVR_BIN_PREFIX+'gcc '+
        '-mmcu=%s -Os -Wl,--gc-sections -o $TARGET $SOURCES-Wl, -u,vfprintf -lprintf_flt -lm'%MCU)})
envArduino.Append(BUILDERS={'Hex':Builder(action=AVR_BIN_PREFIX+'objcopy '+
        '-O ihex -R .eeprom $SOURCES $TARGET')})

Con esta modificación, el programa en Arduino (el binario ocupa 5.8Kb) debe dar:

Valor: 25.400000
Valor: 25.400000
Valor: 25.400000

Más información

La IDE de Arduino hace las llamadas al compilador desde su código fuente en lugar de utilizar un Makefile, por ejemplo, por lo que para compilar incluyendo las instrucciones del linkador en el código fuente del IDE. El problema de esto es que no hay una forma fácil de conmutar el linkado con printf_flt y printf_min (que es como se llama la versión light).

Con scanf() también se puede

scanf() también permite la inclusión de la biblioteca que es capaz de leer floats. La instrucción que hay que escribir es:

-Wl,-u,vfscanf -lscanf_flt

Foto: Eric Lim Photography (Flickr) compartido con CC-by a 30/11/2011

También podría interesarte....