sprintf with floats for M0

hello,
I just received my new M0 Board (a Adafruit ItsyBitsy M0), and could not make sprintf and/or dtostrf work (SAMD Boards installed correctly).

Then I found this piece of code which I c+p'ed into my Sketch - and now surprisingly both sprintf and dtostrf work fine. But I am curious about 2 things:

  1. how does that snippet make both functions work, especially sprintf for fp?
  2. do I always have to c+p it or is there also a way by a #include <....> ?

Snippet:

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

complete code:

// 


#pragma once

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif


int i=12345;
int32_t l=1234567890;
float  p=3.14159265358979323846;  // 20 Nachkommastellen
double e=2.71828182845904523536;  // 20 Nachkommastellen
char buf[30];

void setup() {
  // Serial output in setup() unvisible
  Serial.begin(115200);

  delay(1);
  Serial.println("sprintf Test\n");
  sprintf(buf, "i=%d", i); Serial.println(buf);
  sprintf(buf, "l=%ld", l); Serial.println(buf);
  sprintf(buf, "f=%25.20f", p); Serial.println(buf);
  sprintf(buf, "e=%25.20f", e); Serial.println(buf);
  delay(1);
}

void loop() {
  // Serial output works
  sprintf(buf, "i=%d", i); Serial.println(buf);
  
  sprintf(buf, "l=%dl", l); Serial.println(buf);
  
  sprintf(buf, "f=%25.20f", p); Serial.println(buf);
  dtostrf(p,25,20,buf);  Serial.print("  ");  Serial.println(buf);
  
  sprintf(buf, "e=%25.20f", e); Serial.println(buf);
  dtostrf(e,25,20,buf);  Serial.print("  ");  Serial.println(buf);

  
  delay(10000);

}

linking dtostrf.c enables float support in printf with asm(".global _printf_float"). linking sprintf doesn't enable it. using String with float uses dtostrf so enables it too.

the lines you copy-pasted are in avr/dtostrf.h. #include <avr/dtostrf.h> would do

thanks, unfortunately I totally missed your replay... :-/

but that's strange now -
again I used sprintf but it doesn't work any longer in my new code surprisingly -

how can I make sprintf work for floats with my M0 ?

my M0 core is 1.6.19, IDE 1.8.5

sprintf(buf, "d=%+6.1f r=%+4d p=%+4d", anglef, roll, pitch);

shows just
d= r= +4 p= +2

instead, this works though:
dtostrf(anglef, 6, 1, buf1);
sprintf(buf, "d=%s r=%+4d p=%+4d", buf1, roll, pitch);

#include <avr/dtostrf.h>

Juraj:
#include <avr/dtostrf.h>

I did that, but neverthless sprintf don't work for floats
(1st line above)

shows just
d= r= +4 p= +2

so how is it possible to have sprintf for floats for the M0/Zero/Itsybitsy M0/Feather M0 ?
So far it does not work on those platforms.

OTOH, sprintf works fine with floats for the Due and the ESP8266 though !!

any ideas anyone?

The magic words have already been uttered here:

Juraj:
linking dtostrf.c enables float support in printf with asm(".global _printf_float")

Put that in some suitable function like setup() or whichever function happens to use floats with sprintf(). Just make sure that the function is actually used.

Arduinos traditionally support C printf() but the cut-down version without f-p.
They expect you to use the C++ print() from Print.h

The overloaded print(float, prec=2) has a default precision of 2. So you can just say print(float)
If you want a different number of decimal places, use the full signature.
If you want to control width and precision, use dtostrf().

Some ARM cores use the full-fat version of printf(). After all, you have got plenty of Flash.

Most Arduinos can find dtostrf() by themselves. The M0 and Zero require you to specifically include the header. But the M0 and Zero come with full-fat printf() anyway.

STM32 ST Core has a cut-down printf() for compatibility.
STM32 stm32duino Core has full-fat printf()
ESP8266 and ESP32 have full-fat printf()

I did not try Due.

#if defined(ARDUINO_SAM_ZERO)
#include "avr/dtostrf.h"
#endif

void setup()
{
    Serial.begin(9600);
    Serial.println("print Floating Point on Arduino");
    Serial.print("regular Serial.print(1.234) ");
    Serial.println(1.234);
    char buf[40];
    Serial.print("sprintf(2.345) ");
    sprintf(buf, "%5.2f", 2.345);
    Serial.println(buf);
    Serial.print("dtostrf(3.456) ");
    dtostrf(3.456, 5, 2, buf);
    Serial.println(buf);
}

void loop()
{
}

Incidentally, dtostrf() is handy for formatting integers i.e. with prec=0.
Unfortunately, some cores don't use a proper dtostrf().

David.

Thank you very much, David, now I see - and now it works for my Itsybitsy M0 (i.e., of course outcommented #ifdef for that core)!
sprintf actually is far better for floats than dtostrf, e.g. for left/right text justify, filling chars, adding + signum for positive values, or adding text and multiple variants and formattings.

I'm very glad it's working now, again, thanks a lot!

No. dtostrf() can format just as well as printf() i.e. width and precision.
However printf() makes life a lot easier than Serial.print() when you have multiple items.

The whole operation is done in one statement. Ok, two if you count sprintf(buf, ...) + Serial.print(buf)

It is wise to write code that will work on ALL Arduino targets. Most do not have full-fat printf(). All Arduinos have dtostrf().
You can either use dtostrf() in a Serial.print() or as an %s argument in printf().

In an ideal world, you would not need the specific include. Especially an include path that will fail on some cores. Most Arduinos include all relevant headers via "Arduino.h" (that is prepended to every INO file)

David.

the goal in that case was

sprintf(buf, "d=%+6.1f r=%+4d p=%+4d", anglef, roll, pitch); // right justified, with explicit extra "+" for positive values

or, e.g., for better structered readability on TFTs:

sprintf(buf, "d=%-6.1f r=%-4d p=%-4d", anglef, roll, pitch); // left justified

but that was only 1 example of lots, mostly I need multiples of floats in either line.

dostrf is far less powerful IMO for such combined chart tables having to show different formatted floats, and extremely cumbersome to daisy chain them up then with extra string constants.

I'm glad sprintf works for ARMs and ESPs, and for some AVR - in case - I'll switch to dtostrf then, unavoidably.

Yes. Both your examples would be painful to do in Print.h methods.
Fairly painful to do in dtostrf() too. You would need separate char buffers for each argument. And conditionals for the + sign.

God did an excellent job when she invented printf().

David.

hi,
now a new problem:
I have to call dtostrf in the sketch using sprintf

sprintf(buf, "%6.4f", 2.345);

just shows a blank line, but with

sprintf(buf, "%6.4f", 2.345);
Serial.println(buf);
dtostrf(3.456, 6, 4, buf);
Serial.println(buf);

it suddenly shows both lines correctly!

Actually I don't need dtostrf at all - how can I do without and how can that be fixed?

Did I use too many words in reply #7?

Put this in setup()

asm(".global _printf_float");

oqibidipo:
Did I use too many words in reply #7?

Put this in setup()

asm(".global _printf_float");

yes, perhaps too many words, my apologies! 8)

and with that symbols
asm(".global _printf_float");
now it works finally, thanks a lot! :slight_smile:

For others that may find this thread:

The previous post by oqibidipo is very straighforward and simple, however, for those using an AVR board that want to know more about customizing the Arduino IDE and allowing the extra overhead of the full printf related tools, read on:

A solution can be found by reading the AVR documentation for the printf family of functions that will lead you to vfprintf... at the bottom there it describes compiler flags needed for properly linking in the full functionality (see following link):
https://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1

The problem is that knowing where to put the compiler flags can be difficult. Compiler options are found in a file called "platform.txt" and in this case we want to add to "compiler.c.elf.extra_flags". If you want to add your own flags without messing up the default Arduino files then use "platform.local.txt" (you will have to create this file). Assuming Windows, for standard Arduino boards, the path to these files is similar to:
*C:\Program Files (x86)\Arduino\hardware\arduino\avr*

If you have a non-standard board, you will need to modify folder options to show hidden folders, and the path is similar to:
_C:\Users*\AppData\Local\Arduino15\packages*\hardware\avr**_

I would recommend simply adding the file "platform.local.txt" and in that file adding the following line:

compiler.c.elf.extra_flags=-Wl,-u,vfprintf -lprintf_flt -lm

That should do it. Floats will now be printed instead of "?". :slight_smile:

NOTE: When enabling the full vfprintf library, my code memory footprint (flash) increased by 1472 bytes.

@DatuPuti, this thread is about SAMD, not AVR

@Juraj

My bad!

Although Google didn't care... I just figured I'd try to share since now and then I get help by reading forum threads.

After googling a bit I found a few better threads to post to. Is this at all helpful here or should I delete?