Issues with converting float to string with sprintf

Hi,

I’m trying to convert a float to a string so I can publish temperatures to Spark.

However, it seems that sprintf will convert the number as a null. Searching this forum seems to indicate that this should work. Casting the variable as an int also works so it appears to be an issue with floats/double.

##Sample code

void setup() {
    Serial.begin(9600); 
}

void loop() {
        delay(2000);
	char t1[20];
	
	sprintf(t1, "Value: %.02", (double) 55.21);
	Serial.println(t1);
        Spark.publish("Test", t1);
}

Another weird thing is if I modify the string in sprintf from Value: %.02 to %.02, I will see “Test” in my serial console.

This works fine if I cast to an int

void setup() {
    Serial.begin(9600); 
}

void loop() {
    delay(2000);
    char t1[20];
	
    sprintf(t1, "%d", (int) 55.21);
    Serial.println(t1);
    Spark.publish("Test", t1);
}

What am I doing wrong?
Thanks!

1 Like

Actually this could be googled, since this is not a C/C++ forum and this is definetly a C/C++ question :wink:
But you could try using %f for float (may require type casting into (float)) or %lf or %5.2 (%[width in total].[number of digits after decimal point]) should work for doubles.


The bit with Test in your serial output is odd, in deed, but it might be due to some accidental pointer/string issue in connection with a malformed format string in your sprintf().

Are you running this on the core or on the photon? For 0.4.0, sprintf(%f) has been removed since it adds many kilobytes to the firmware size.

The way to do this portably across all firmware versions is to use the String class:

float f = 1.23456;
String sf(f, 5);  // <--- number of decimals
Serial.print(sf);
1 Like

That has the potential to break a lot of user code including a lot of the publish examples.

If this is the path you really want, you need to do a better job explaining how to avoid constructing and destroying String objects since that leads to mysterious crashes due to heap fragmentation. A lot of people including me, avoid Arduino Strings if possible for that reason.

1 Like

There is also a C-string function,

void dtoa(double val, uint8_t prec, char* out);

That avoids the heap fragmentation.

Otherwise, totally agreed. I hope to re-instate %f support in sprintf() with 0.4.1 and in such a way that the firmware is increased only if the user actually uses sprintf().

1 Like

The support was removed because it causes the photon to SOS. We didn’t have time to look into the cause prior to the release, but will look into it for 0.4.1.

1 Like

Sorry - forgot to mention that I’m using the Photon that I got from Makerfaire SF. I’m assuming that would be using 0.4.0, but let me know if there is a way to check the version of the firmware.

Thanks for the heads-up about sprintf not in 0.4.0 Unfortunately, I can’t get either of your workarounds to work.

Arduino string:

Code

    float f = 1.23456;
    String sf = (f, 5); // <-- Missing function name?
    Serial.println(sf);

###Console

test_float_conv.cpp:11:22: error: invalid conversion from 'int' to 'const char*' [-fpermissive]
delay(2000);
^
In file included from ../inc/spark_utilities.h:31:0,
from ../inc/spark_wiring.h:33,
from ../inc/application.h:29,
from test_float_conv.cpp:2:
../inc/spark_wiring_string.h:67:2: error: initializing argument 1 of 'String::String(const char*)' [-fpermissive]
String(const char *cstr = "");

##With dtoa:

Code

        char testStr[20];
	double d = 1.23456;
	dtoa( d, 2, testStr);
	Serial.println(testStr);

Console

^
test_float_conv.cpp: In function 'void loop()':
test_float_conv.cpp:17:21: error: invalid conversion from 'char*' to 'int' [-fpermissive]
String sf = (f, 2);
^
test_float_conv.cpp:17:21: error: too few arguments to function 'char* dtoa(double, int, int, int*, int*, char**)'
In file included from /opt/gcc_arm/arm-none-eabi/include/stdlib.h:11:0,
from ../../core-common-lib/CC3000_Host_Driver/cc3000_common.h:43,
from ../../core-common-lib/SPARK_Firmware_Driver/inc/hw_config.h:35,
from ../inc/main.h:37,
from ../inc/spark_utilities.h:30,
from ../inc/spark_wiring.h:33,
from ../inc/application.h:29,
from test_float_conv.cpp:2:
/opt/gcc_arm/arm-none-eabi/include/stdlib.h:169:8: note: declared here
char * _EXFUN(dtoa,(double, int, int, int *, int*, char**));
^
make: *** [test_float_conv.o] Error 1

It appears that the function signature that you’ve provided is incorrect.


Whichever method is easiest is fine with me.

Thanks!

There was a typo in my example :blush: - please try

float f = 0.5;
String s(f, 6);

The second error is strange - the IDE is compiling against the Core firmware. (cc: @suda, @Dave.) We know this because of the mention of cc3000 which doesn’t exist for the Photon.

Please check that you have your photon starred under “Devices”.

Apparently, I have building against the “Spark Firmware 0.3.4 (Oct 21)” firmware with no way to change it. Under “Devices”, it only list my device id so there is no model that I can verify against. (Btw - in Chrome, trying to expand the firmware will redirect to the Applications view. Works fine in Firefox).

Device ID is [redacted - please don’t post your device ID in the public forum. PM it] in case you need it.

Should I try to upgrade my firmware to 0.4.0?

It’s not possible to upgrade yourself yet - the IDE should be using the latest version for the photon since 0.3.4 simply doesn’t work with the photon. I’m sure the crew are looking into it and will report back when a fix is available.

Okay - thanks again!

How can I get this functionality back? My libraries use sprintf and I want to retain compatibility with the photon.

Hi @loopj

As @mdma said in the quote above, they removed sprint because it caused Photon to panic generating an SOS. I am sure the sprint will be added again once it works on Photon. In mean time, there are a lot of alternatives if you are interested in a temporary work-around.

Oops, didn’t spot the comment about SOS, thought this was just a file-size optimization thing.

@loopj, I can tell you that on the current develop branch of the code, sprintf has been added back. :smile:

1 Like

@peekay123 still seems to be broken for me, on this commit (latest on develop branch, from 5 days ago).

@dan, did you “make clean all” on BOTH the system (via the firmware\modules directory) and your app?

yep, is it working well for you?

Here’s a minimal example that breaks:

clone repo at this commit, then run

<...>firmware-develop/modules > make clean all PLATFORM=photon

then given this:

//sprintftest.cpp
#include "application.h"

char thing[16];

void setup() {
}

void loop() {
	sprintf(thing, "testing%d", 123);
}

run

<...>firmware-develop/main > make clean all PLATFORM=photon APP=sprintftest

Here’s the error I get:

  <...>firmware-develop/main >make clean all PLATFORM=photon APP=sprintftest
    `sprintf' referenced in section `.text.module_user_loop' of /var/folders/c1/dt61zfs90816tqlxt_9s76m40000gn/T//cci2Dim1.ltrans0.ltrans.o: defined in discarded section `.text' of rt_dynalib.o (symbol from plugin)
    collect2: error: ld returned 1 exit status
    make[1]: *** [../../../build/target/user-part/platform-6-m-lto/applications/sprintftest/sprintftest.elf] Error 1
    make: *** [modules/photon/user-part] Error 2

@dan, crap! I just recompiled one of my apps and got the same error. However, on a previous compile I did, @mdma suggested I use COMPILE_LTO=n in the make line and that seems to work. I have not tested the resulting bin but I believe it worked last time I tried. This is only a temporary solution!

Thanks, confirmed that it’ll compile with this option included. A bit wary to develop much along this branch depending on that compiler flag, though.

1 Like