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 ?
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.
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.
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
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