Implement prinf function to stm32 arduino ide

This is a very common issue on embedded systems like the STM32. The standard printf function is often not fully implemented in the Arduino core to save space, especially the parts that format floating-point numbers (like %f).

Here are the best ways to solve this, from easiest to most robust.

Method 1: Check for a "printf float" Build Flag (The Best Fix)

Many STM32 Arduino cores (like the popular STM32duino) let you enable full printf support. This is the cleanest solution, as it will make your original Serial1.printf(...) code work perfectly.

  1. In the Arduino IDE, go to the Tools menu.
  2. Look for options related to your STM32 board.
  3. Find a setting like "C runtime library" or "printf/scanf float support".
  4. If you see it, change the value to one that includes float support (e.g., "Newlib-nano with float printf" or "Enabled").

If you find and enable this option, recompile and upload. Your Serial1.printf line should now work.


Method 2: Use sprintf (The Standard C Way)

If you can't find the build flag, the most standard C/C++ approach is to use sprintf (string print format) to build your string in a character buffer first, and then print that buffer to the serial port.

  1. Include stdio.h (which you already have).
  2. Define a buffer: Create a character array large enough to hold your final string.
  3. Use sprintf: Call the function to format your variables into the buffer.
  4. Print the buffer: Use Serial1.print() to send the result.

Here is how you would adapt your code:

// ... inside your loop()
if (millis() - timer > 1000) {
    timer = millis(); // reset the timer

    // ... (all your variable assignments for LatD, LatM, etc. go here) ...
    int H=(GPS.hour);
    int Mn=(GPS.minute);
    int S = (GPS.seconds);
    int nb =((int)GPS.satellites);
    String T= (String(H)+":"+String(Mn)+":"+String(S));
    int Sp=(GPS.speed);
    int Cog=(GPS.angle);
    float Long =(GPS.longitude);
    // Be careful: Long60 is not defined in your sketch. 
    // I'll assume you meant (LongD * 100) based on your other code.
    // float LongS =( Long-(LongD*100))/-1000; // Example fix
    int LongD =(GPS.longitude)/100;
    int LongM= ((GPS.longitude)-(LongD*100));
    float LongS = (Long - (LongD * 100 + LongM)) * 60; // Just guessing at your GPS math
    char Long1=(GPS.lon);
    int Lat2=(GPS.latitude);
    float Lat =(GPS.latitude);
    // float LatS =( (Lat2- Lat)-60); // This logic seems circular
    int LatD =(GPS.latitude)/100;
    int LatM =((GPS.latitude)-(LatD*100));
    float LatS = (Lat - (LatD * 100 + LatM)) * 60; // Just guessing at your GPS math
    char Lat1=(GPS.lat);


    // 1. Define a buffer
    // Make it large enough for your whole string. 150-200 chars should be safe.
    char buffer[200];

    // 2. Use sprintf to format the string into the buffer
    // This is the C-standard printf function.
    sprintf(buffer, "%.2i-%i'%2.3f%c-%.2i-%i'%2.3f%c-%.2i-%.2i-%i-%.2i-%.2i-%.2i\n",
            LatD, LatM, LatS, Lat1, 
            LongD, LongM, LongS, Long1, 
            Sp, nb, Cog, H, Mn, S);

    // 3. Print the buffer to Serial1
    Serial1.print(buffer);

    // You can now delete your String concatenation method
    // String message = ...
    // Serial1.println(message);
}

What If Method 2 Still Fails? (The Float Problem)

If you use sprintf and your output looks strange (e.g., the floats just show up as ? or f), it means sprintf also doesn't have float support enabled.

In this case, you must convert the floats to strings manually using the classic Arduino dtostrf() function (double to string float).

Here's the combined sprintf + dtostrf solution:

// ... inside your loop()
if (millis() - timer > 1000) {
    timer = millis(); // reset the timer

    // ... (all your variable assignments go here) ...
    // ... LatD, LatM, LatS, Lat1, etc...
    float LatS = ...; // Your calculation
    float LongS = ...; // Your calculation
    
    // 1. Create buffers for your floats
    char lat_buffer[10]; // Buffer for LatS
    char lon_buffer[10]; // Buffer for LongS

    // 2. Convert floats to strings
    // dtostrf(float_variable, min_width, num_decimal_places, destination_buffer);
    dtostrf(LatS, 4, 3, lat_buffer);  // (e.g., "12.345" -> min width 4, 3 decimals)
    dtostrf(LongS, 4, 3, lon_buffer);

    // 3. Define your main buffer
    char buffer[200];

    // 4. Use sprintf, but use %s (string) for the floats
    sprintf(buffer, "%.2i-%i'%s%c-%.2i-%i'%s%c-%.2i-%.2i-%i-%.2i-%.2i-%.2i\n",
            LatD, LatM, lat_buffer, Lat1,  // <-- Use lat_buffer
            LongD, LongM, lon_buffer, Long1, // <-- Use lon_buffer
            Sp, nb, Cog, H, Mn, S);

    // 5. Print the final buffer
    Serial1.print(buffer);
}

A Note on Your String Method

Your current solution using String concatenation works, but it's generally discouraged on microcontrollers like the STM32:
String message = String(LatD)+"-"+String(LatM)+...

  • Memory Usage: It uses dynamic memory allocation, which can fragment the small amount of RAM you have.
  • Speed: It's much slower than sprintf.

For small, simple strings, it's fine. But for complex, repetitive formatting like this, sprintf (Method 2) or dtostrf (Method 3) are much more stable and efficient.

Thanks for all guys i did not expected so much reply on this topic but really I am ok with bperrybap in my case of large data i really prefer printf function because i can shape the output data from the gps that woult be received by my esp32 custom lcd . More globaly Arduino is a so good plateform for beginners however they have to progress on their development to keep on the track even more with the advent of the IA

The snprintf() function is only safer than sprintf() if you use it properly otherwise it does not

To add to that, even sprintf() warns you that you are writing more bytes into the buffer than you should be

Obviously. But if you pass in the proper buffer size, which is easy if you use something like sizeof() on something like a local character array, which is what was being used in the example code above, it will protect from overflowing the buffer and corrupting the stack.

i.e.
instead of this:

// 1. Define a buffer
// Make it large enough for your whole string. 150-200 chars should be safe.
char buffer[200];

// 2. Use sprintf to format the string into the buffer
// This is the C-standard printf function.
sprintf(buffer, "%.2i-%i'%2.3f%c-%.2i-%i'%2.3f%c-%.2i-%.2i-%i-%.2i-%.2i-%.2i\n",
        LatD, LatM, LatS, Lat1, 
        LongD, LongM, LongS, Long1, 
        Sp, nb, Cog, H, Mn, S);

do this:

 // 1. Define a buffer
    // Make it large enough for your whole string. 150-200 chars should be safe.
    char buffer[200];

    // 2. Use snprintf to format the string into the buffer
    // This is the C-standard printf function.
    snprintf(buffer, sizeof(buffer), "%.2i-%i'%2.3f%c-%.2i-%i'%2.3f%c-%.2i-%.2i-%i-%.2i-%.2i-%.2i\n",
            LatD, LatM, LatS, Lat1, 
            LongD, LongM, LongS, Long1, 
            Sp, nb, Cog, H, Mn, S);

So easy to do, and avoids any potential buffer overflow that corrupts the stack.
i.e. avoid the mind set of creating a buffer a size of “should be safe” and use snprintf() to ensure its safety.

I don’t see how it could, given sprintf() doesn’t have the buffer size.

While compilers are now checking the number an types of arguments against printf type formatting strings at compile time, sprintf() itself doesn’t have enough information to be able to check for buffer overflow at runtime hence the need for for snprintf()

sprintf() handles output formatting runtime. It has no idea how big the buffer is.
All it has is a pointer to some memory where to store the formatting output.
It also has no idea how large the formatted output will be before it is done, since it doesn’t know the size of any strings passed in as pointers, or the character count for any formatted numbers before it starts the formatting.

sprintf() simply does not have enough information to be able to always protect against runtime buffer overflow. At a minimum it needs to know the buffer size.
snprintf() passes in this size information to ensure that the formatting does not overflow the buffer.

From you code using snprintf()

// 1. Define a buffer
    // Make it large enough for your whole string. 150-200 chars should be safe.
    char buffer[200];

As to

neither do I, but try this and read the warnings


void setup()
{
    Serial.begin(115200);
    char buffer[2];
    sprintf(buffer, "A1");
}

void loop()
{
}

warning: '__builtin_strcpy' writing 3 bytes into a region of size 2 overflows the destination [-Wstringop-overflow=]

and what do you make of this ?

void setup()
{
    Serial.begin(115200);
    char buffer[2];
    snprintf(buffer, 10, "A1");
    Serial.println(buffer);
}

void loop()
{
}
warning: '__builtin_strcpy' writing 3 bytes into a region of size 2 overflows the destination [-Wstringop-overflow=]
 }

At least the fact that it is a warning makes sense

I think perhaps you have missed my point.
The code I copied was from the @epeaogih code posted above.
I tried to modify as little of that code as possible so
I intentionally only modified the code to use snprintf() instead of sprintf()
in the preferred example code to demonstrate how simple using snprintf() vs sprintf() is.
I guess I could have added a comment saying something like
// using snprintf() instead of sprintf() to ensure that buffer is never overrun

My comment is still correct.
Don’t rely on buffers that “should be safe” to protect your code from a buffer over run.
Should is not an assurance.
i.e. make an estimate on the buffer size needed that “should be safe” like @epeaogih did but then use snprintf() so you can tell the formatter what size the buffer is to ensure that the buffer is never overrun due to an oversight of something unexpected happening causing a larger than expected output.
snprintf() will truncate the output to ensure that no buffer run occurs.

You have given a very simple example using a string literal that allows some compile time checking but does not tend to reflect real life.
Sure for string literals and constants, and a local buffer where the compiler can see the sizes at compile time, the compiler can do some sanity checks, but that isn’t what I was talking about and that isn’t how people are accidentally causing buffer overflows.

Which is why I said:

While compilers are now checking the number an types of arguments against printf type formatting strings at compile time, sprintf() itself doesn’t have enough information to be able to check for buffer overflow at runtime hence the need for for snprintf()

Notice the explicit reference and differentiation of compile time vs run time checking.

In real world applications the sizes of buffers, and the sizes of the strings are often not known at compile time because the buffer and arguments are often pointers to memory whose sizes and contents cannot be known at compile time.
i.e. the buffer memory pointer and string pointers can get passed in from outside the scope of the function or compilation unit.
There is no way to check for buffer overflow at compile time time when the sizes are not known at compile time.
There is also no way to check for buffer overflow at run time when the buffer size is not known.
This is why the ‘n’ formatting functions: snprintf(), vsnprintf() were created.
This allows run time checking for buffer overflow since the buffer size has been specified.

Think about my Pprintf() function.
I explicitly used vsnprintf() instead of vsprintf() because the Pprintf() function and the functions it calls have no idea the size of the formatted output.
The only way to ensure that there is no buffer overflow is is use vsnprintf() instead of vsprintf()

Again, I would highly recommend that sprintf() be avoided since it is easy to accidentally create a buffer overflow due to some parameter generating more formatted output than expected that could cause a buffer overflow.
Using snprintf() ensures that the buffer won’t overflow even if the parameters and formatting specification would generate more output than the buffer could hold.
snprintf() will truncate the output to the specified buffer size to prevent a buffer overflow.

IMO, sprintf() should never be used. It is easy to use snprintf() instead and sprintf() is the cause of so many accidental buffer overflow issues.

– bill