11

I have the following code for an Arduino sketch:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
static FILE lcdout = {0} ;

static int lcd_putchar(char ch, FILE* stream)
{
    lcd.write(ch) ;
    return (0) ;
}

void setup() {
  lcd.begin(16, 2);
  fdev_setup_stream (&lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE);
}

void loop() 
{
  stdout = &lcdout;
  printf("%.2f Volts", 2.0);
}

The problem comes at the last line of the code. This should print out "2.00 Volts" but instead, it prints "? Volts" (a question mark instead of the actual float value). If I try to format an integer, this works great.

So basically, if I replace the printf line with the following, it will work properly:

printf("%d Volts", 2); //prints correctly "2 Volts"

Any idea what's the problem ?

Nicu Surdu
  • 8,172
  • 9
  • 68
  • 108
  • 1
    A dumbed-down stdlib that can't handle floating point conversions? – Daniel Fischer Jan 03 '13 at 20:39
  • 1
    What's funny is that if you just do an lcd.print(2.0) it will print exactly what you want. No need for sprintf or any of that. And you can specify the number of digits right of decimal with an optional second parameter. E.g. lcd.print(2,3) will give you "2.000". – James Newton Mar 19 '16 at 01:11

4 Answers4

14

The GNU toolchain for AVRs (which is included with the Arduino IDE) uses a "minified" version of the C standard library by default, in which, for example, the floating-point support is reduced/taken away from formatted I/O functions (just in order printf() to fit in the few kBytes long storage of the chip.)

If you want this to work, you have to link agains another library containing the normal version of printf(), by using the -Wl,-u,vfprintf -lprintf_flt linker flags.

  • So I'll have to provide this linker arguments when I link my code, or I'll have to compile the library again and provide this linker arguments ? – Nicu Surdu Jan 03 '13 at 21:43
  • 1
    @NicolaeSurdu You don't have to recompile the library, you have to provide these flags when you're linking your own code. –  Jan 03 '13 at 21:47
  • I wouldn't say minified. minified means it has all the functionality but the code has been obfuscated to save space. what you mean is that the code has been slimmed down by removing some of its functionality. – Octopus Apr 17 '15 at 07:49
  • user529758 where exactly do we need to put these flags?, in "compiler.ldflags=" in the platform.txt file? – Akay Jan 07 '23 at 17:51
5

From avr-libc documentation:

If the full functionality including the floating point conversions is required, the following options should be used:

-Wl,-u,vfprintf -lprintf_flt -l

Note that if your MCU doesn't have any floating point support, you should try to avoid floating point operations completely. The floating point operations will be done in software which is very inefficient and needs a lot a flash memory.

Community
  • 1
  • 1
ouah
  • 142,963
  • 15
  • 272
  • 331
  • You don't even need `-lprintf_min` IIRC (it has been a while I played around with AVRs), `libc.a` contains the minified library. –  Jan 03 '13 at 20:44
  • @H2CO3 I removed the line. I also think that if you don't use the full `printf_flt`, you don't have any floating point support with `printf`. – ouah Jan 03 '13 at 20:46
  • Yes, that's what I'm saying essentially. You'll always get `?` instead. –  Jan 03 '13 at 20:48
  • where exactly do we need to put these flags?, in "compiler.ldflags=" in the platform.txt file? – Akay Jan 07 '23 at 18:03
0

I did this one:

unsigned char buffer[32];

void setup() {
  serial.begin();
}

void loop() {
  if(serial.available()) {
    int size = serial.read(buffer);
    if (size!=0) {
      //serial.write((const uint8_t*)buffer, size);
      int bright = atoi((char *) buffer);

      //int final = ((unsigned int)buffer[0]);

      //int final = bright -'0';
      serial.write(bright);
      serial.write('\n');
    }
  }
  serial.poll();
}

and now i get an ascii char when i send a value from 0-255 through the usb. I should find a way to convert the ascii char to int.

e.g i type 65 and it prints A

evans
  • 23
  • 4
0

I have some old code that might help if you want to avoid the printf entirely and simply need to print with a given number of digits before and after the decimal. This code compiles in C and works fine in the Arduino IDE as well. It could almost certainly be done in fewer lines of C++. The pow10 could be done programmatically, but powers weren't supported in the version of C I was working with:

#include <stdio.h>

/*
Because lcd and serial don't support printf, and its very costly, and all we need
is simple formating with a certain number of digits and precision, this ftoa is enough.
If digits is negative, it will pad left.
*/
#define  BUF_LEN 20
char buf[BUF_LEN]; //need a buffer to hold formatted strings to send to LCD

int ftoa(char * str, float f, char digits, char precision) {
char i=0,k,l=0;
long a,c;
long pow10[10] = {1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
unsigned char b;
char decimal='.';

  if(digits>=10) {return 0;};
  // check for negative float
  if(f<0.0) {
    str[i++]='-';
    f*=-1;
    (0<digits?digits--:digits++);
    }
  a=(int)f; // extracting whole number
  f-=a; // extracting decimal part
  k = digits;
  // number of digits in whole number
  while(k>=0)   {
    c = pow10[k];
    c = a/c;
    if(c>0) { break; }
    k--;
    } // number of digits in whole number are k+1
  if (0<k && digits==k && c>10) { //overflow
    decimal = 'e';
    }
/*
extracting most significant digit i.e. right most digit , and concatenating    to string
obtained as quotient by dividing number by 10^k where k = (number of digit -1)
*/
  for(l=abs(k);l>=0;l--){
    c = pow10[l];
    b = a/c;
    str[i++]=(l&&!b?' ':b+48); //digit or pad
    a%=c;
    }
  if (precision) {str[i++] = decimal;};
/* extracting decimal digits till precision */
  if (0>precision) {k=0; precision=abs(precision);}
  for(l=0;l<precision;l++) {
    f*=10.0;
    b = (int)f; //math floor
    str[i++]=b+48; //48 is ASCII 0
    f-=(float)b;
    if (!k && 0==f) { break; } //nothing left, save chars.
    //won't work if there are any floating point errors.
    }
  str[i]='\0';
  return i;
  }

You can play with it and see it run here: http://ideone.com/AtYxPQ

James Newton
  • 698
  • 9
  • 19