Arduino Sprintf Float Not Formatting

Arduino sprintf float not formatting

Due to some performance reasons %f is not included in the Arduino's implementation of sprintf(). A better option would be to use dtostrf() - you convert the floating point value to a C-style string, Method signature looks like:

char *dtostrf(double val, signed char width, unsigned char prec, char *s)

Use this method to convert it to a C-Style string and then use sprintf, eg:

char str_temp[6];

/* 4 is mininum width, 2 is precision; float value is copied onto str_temp*/
dtostrf(temp, 4, 2, str_temp);
sprintf(temperature,"%s F", str_temp);

You can change the minimum width and precision to match the float you are converting.

Arduino very randomly freezes on printing float to string via String and sprintf

After a lot of debugging, it seems that the issue indeed does have to do with ability of Arduino to print floats to string, and not with pointers or non-terminated string issues in my code.

It seems to be stalling in the dtostrf() call of the String constructor:

// ~/.platformio/packages/framework-arduino-samd/cores/arduino/api/String.cpp
String::String(float value, unsigned char decimalPlaces)
{
static size_t const FLOAT_BUF_SIZE = FLT_MAX_10_EXP + FLT_MAX_DECIMAL_PLACES + 1 /* '-' */ + 1 /* '.' */ + 1 /* '\0' */;
init();
char buf[FLOAT_BUF_SIZE];
decimalPlaces = min(decimalPlaces, FLT_MAX_DECIMAL_PLACES);
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); // <-- HERE
}

and it seems to be related to the assembler call to _print_float within the dtostrf() function:

// ~/.platformio/packages/framework-arduino-samd/cores/arduino/api/deprecated-avr-comp/avr/dtostrf.c.impl
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
asm(".global _printf_float"); // If this line is uncommented, the stall wont happen
char fmt[20];
sprintf(fmt, "%%%d.%df", width, prec);
sprintf(sout, fmt, val); // If the above line is not commented out, the system will freeze here
return sout;
}

I realize this might be a pretty unsatisfactory answer for anyone coming across this thread... But for what it's worth, the solution for us will be to use PString (https://github.com/boseji/PString-Arduino-lib) as they at least seem stable so far.

I will follow up on this thread if the issue ever surfaces again despite using PStrings

Using floats with sprintf() in embedded C

Since you're on an embedded platform, it's quite possible that you don't have the full range of capabilities from the printf()-style functions.

Assuming you have floats at all (still not necessarily a given for embedded stuff), you can emulate it with something like:

char str[100];
float adc_read = 678.0123;

char *tmpSign = (adc_read < 0) ? "-" : "";
float tmpVal = (adc_read < 0) ? -adc_read : adc_read;

int tmpInt1 = tmpVal; // Get the integer (678).
float tmpFrac = tmpVal - tmpInt1; // Get fraction (0.0123).
int tmpInt2 = trunc(tmpFrac * 10000); // Turn into integer (123).

// Print as parts, note that you need 0-padding for fractional bit.

sprintf (str, "adc_read = %s%d.%04d\n", tmpSign, tmpInt1, tmpInt2);

You'll need to restrict how many characters come after the decimal based on the sizes of your integers. For example, with a 16-bit signed integer, you're limited to four digits (9,999 is the largest power-of-ten-minus-one that can be represented).

However, there are ways to handle this by further processing the fractional part, shifting it by four decimal digits each time (and using/subtracting the integer part) until you have the precision you desire.


Update:

One final point you mentioned that you were using avr-gcc in a response to one of the other answers. I found the following web page that seems to describe what you need to do to use %f in your printf() statements here.

As I originally suspected, you need to do some extra legwork to get floating point support. This is because embedded stuff rarely needs floating point (at least none of the stuff I've ever done). It involves setting extra parameters in your makefile and linking with extra libraries.

However, that's likely to increase your code size quite a bit due to the need to handle general output formats. If you can restrict your float outputs to 4 decimal places or less, I'd suggest turning my code into a function and just using that - it's likely to take up far less room.

In case that link ever disappears, what you have to do is ensure that your gcc command has "-Wl,-u,vfprintf -lprintf_flt -lm". This translates to:

  • force vfprintf to be initially undefined (so that the linker has to resolve it).
  • specify the floating point printf() library for searching.
  • specify the math library for searching.

Sprintf is not printing everything in the line (Arduino)

So I realized where my error was, I typed %t instead of %d in one of the variables, here;

TEF \t %d \t %t \n

This wasn't popping up as an error but it kinda stopped showing the rest of the line. Fixing this now actually shows everything.

Thanks to everyone else for trying to help, and I'll actually still use some of those solutions hoping to make my program a bit more efficient.

Formatting float with sprintf fails

The format %4.1f says "print fps using a width of at least four characters, with one digit after the decimal point".

You always have at least one digit before the decimal, the decimal, and one digit after the decimal, that's at least three of the requested four chars. If the value of fps is between 0 (inclusive) an 10 (exclusive), you get one space of padding, otherwise none.

If you want four characters including the padding before the decimal point, you need %6.1f, since two places are used for the point and the digit after.

Is there a less-space taking way than using sprintf to format floats to strings?

Is there a less-space taking way than using sprintf to format floats to strings?

... code takes input from sensors and outputs the data from the sensors along with other strings on an alphanumeric character

Do not use floating point at all. @user3386109

The reading from the sensor is certainly an integer. Convert that reading to deci-degrees C using integer math and then print.

TMP235 example

Temperature  Output
-40 C 100
0 C 500
150 C 2000

#define SCALE_NUM ((int32_t)(150 - -40) * 10)
#define SCALE_DEM (2000 - 100)
#define OFFSET (500)

int temperature_raw = temperature_sensor();
int temperature_decidegreesC = (temperature_raw - OFFSET)*SCALE_NUM/SCALE_DEN;
send_integer(temperature_decidegreesC/10);
send_char('.');
send_char(abs(temperature_decidegreesC/10) + '0');

Other improvements can be had, but avoiding FP variables and math and using integer math is the key.

Arduino: printf/fprintf prints question mark instead of float

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.

STM32 RTOS (H743) Crashing when using sprintf or snprintf with float formatting

Here is how I fixed the problem.
The problem is known, and ST hasn't fixed the issue since it was first brought up here a year ago, see:
https://community.st.com/s/question/0D50X0000BB1eL7SQJ/bug-cubemx-freertos-projects-corrupt-memory
and here's the recommended fix:
http://www.nadler.com/embedded/newlibAndFreeRTOS.html

A bit over my head so I chose to go the route of using a lighter version of the printf function:
https://github.com/mpaland/printf

I hope it helps someone else with the issue.

Cheers



Related Topics



Leave a reply



Submit