sprintf() doesn't work properly on Arduino?

Thread Starter

Irving

Joined Jan 30, 2016
3,843
Been having some issue with weird results on an Arduino-based datalogger and finally tracked it down to the sprintf() statement used to build and format the result string. The screenshot shows the simplest example - where the order of two variables makes a difference to the output - but in others whole sections of the result buffer (buf) get overwritten or simply cease to exist. Short of rewriting sprintf() myself any ideas?

1618681962620.png
 
Last edited:

geekoftheweek

Joined Oct 6, 2013
1,201
I can't explain why, but after a couple checks it seems it is shifting the output 16 bits.
Code:
          12 =  0x0c    786432 = 0xc0000
10764 =  0x2a0c    705429504 = 0x2a0c0000
.....
Just out of curiosity does Serial.sprintf() give the same?
 

michael8

Joined Jan 11, 2015
410
in the format for the printf %10lu -> one zero <letter Ll> <letter U>
what is that supposed to do? Perhaps an int is 16 bits and the letter L makes it take a 32 bit number?
 

nsaspook

Joined Aug 27, 2009
13,079
Maybe it's due to Asynchronicity, Serial.sprintf might return before the supplied 'buf' pointer buffer is completely transmitted. I haven't reviewed the details of Arduino buffering to know if it uses the passed Serial.sprintf buffer in a background process that actually talks to a UART. As a test put a small delay between the A and B formatting and transmit code sections.

https://coursework.vschool.io/asynchronicity/
 
Last edited:

click_here

Joined Sep 22, 2020
548
> ... in the format for the printf %10lu -> one zero <letter Ll> <letter U>
what is that supposed to do?

%lu is "unsigned long", which is what uint32_t is for the arduino.

Shouldn't the printing specifier for "interval" also be of type %lu?
 

Thread Starter

Irving

Joined Jan 30, 2016
3,843
in the format for the printf %10lu -> one zero <letter Ll> <letter U>
what is that supposed to do? Perhaps an int is 16 bits and the letter L makes it take a 32 bit number?
Exactly so. L = long, U = unsigned and 10 = number of characters, left filled with spaces.

@geekoftheweek Yes, I'd noticed that. If you try:

sprintf(buf,"%UL", 1) you get " 65536"

BTW there is no Serial.sprintf() function. Serial.print() gives the right answer, but it's unformatted.
 

click_here

Joined Sep 22, 2020
548
I tried your program on a standard C compiler

This works as expected on my c compiler - Note the %5lu instead of %5d
Code:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    unsigned long start, then, interval;
   
    char buf[128];
   
    start = 100UL;
    then = 200UL;
   
    interval = then - start;
   
    sprintf(buf,"%c %10lu %5lu", 'A', then, interval);
    puts(buf);
   
    sprintf(buf,"%c %5lu %10lu", 'A', interval, then);
    puts(buf);
   
   
    putchar('\n');
    return EXIT_SUCCESS;
}
Code:
Output:
A        200   100
A   100        200
 

Thread Starter

Irving

Joined Jan 30, 2016
3,843
Maybe it's due to Asynchronicity, Serial.sprintf might return before the supplied 'buf' pointer buffer is completely transmitted. I haven't reviewed the details of Arduino buffering to know if it uses the passed Serial.sprintf buffer in a background process that actually talks to a UART. As a test put a small delay between the A and B formatting and transmit code sections.

https://coursework.vschool.io/asynchronicity/
Nice try but no. Serial.print() is fully buffered and is blocking. In my real-world code the buffer is fully assembled before Serial.print() is called and it doesn't return from the call until fully transmitted - a scope shows that, as well as there is at least a 10mS delay between packets. If you take "buf" and extract Strings from it you'll see the contents are wrong.
 

Thread Starter

Irving

Joined Jan 30, 2016
3,843
I tried your program on a standard C compiler

This works as expected on my c compiler - Note the %5lu instead of %5d
Code:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    unsigned long start, then, interval;
   
    char buf[128];
   
    start = 100UL;
    then = 200UL;
   
    interval = then - start;
   
    sprintf(buf,"%c %10lu %5lu", 'A', then, interval);
    puts(buf);
   
    sprintf(buf,"%c %5lu %10lu", 'A', interval, then);
    puts(buf);
   
   
    putchar('\n');
    return EXIT_SUCCESS;
}
Output:
A 200 100
A 100 200
Yes, it's only the Arduino implementation as far as I can tell. I need to find the source code and take a look...
 

nsaspook

Joined Aug 27, 2009
13,079
Nice try but no. Serial.print() is fully buffered and is blocking. In my real-world code the buffer is fully assembled before Serial.print() is called and it doesn't return from the call until fully transmitted - a scope shows that, as well as there is at least a 10mS delay between packets. If you take "buf" and extract Strings from it you'll see the contents are wrong.

Have you tried two separate string buffers for A and B or moving buf inside the function for stack allocation of space instead of heap? Moving things around might help to pin point the cause of memory corruption.

Like, Is it the transmit side or receiver side causing the problem?
 
Last edited:

geekoftheweek

Joined Oct 6, 2013
1,201
BTW there is no Serial.sprintf() function. Serial.print() gives the right answer, but it's unformatted.
Actually probably meant Serial.printf()... either way I have Serial.printf() a couple hundred times (it seems like) in some ESP8266 programming I've done with the Arduino IDE. I don't know where I first saw it, but it seems to work for that at least.
 

Thread Starter

Irving

Joined Jan 30, 2016
3,843
Actually probably meant Serial.printf()... either way I have Serial.printf() a couple hundred times (it seems like) in some ESP8266 programming I've done with the Arduino IDE. I don't know where I first saw it, but it seems to work for that at least.
Serial.printf() is natively supported in ESP8266 and ESP32. i've used it extensively, but it's not on Arduino Uno which has a much smaller code space.

I was planning to move this to an ESP32 eventually anyway, just all my boards are tied up doing other stuff right now; I need to order a few more in.
 
Last edited:

Thread Starter

Irving

Joined Jan 30, 2016
3,843
Have you tried two separate string buffers for A and B or moving buf inside the function for stack allocation of space instead of heap? Moving things around might help to pin point the cause of memory corruption.

Like, Is it the transmit side or receiver side causing the problem?
I'm fairly convinced it's in the handling of a long quantity; things go awry on %ld and %li too. I'm guessing it's something to do with pointer sizes, though why the order makes a difference I can't fathom right now.

@click_here Thanks for the link, I'll go have a look in the suggested locations in that article later today (after Imola GP!)
 

geekoftheweek

Joined Oct 6, 2013
1,201
@Irving now I understand. I have not worked with the Arduino line itself. In all reality printf and sprintf should call the same routines I would think in the end anyways... Just had a curious moment there.
 

mckenney

Joined Nov 10, 2018
125
Yes, it's only the Arduino implementation as far as I can tell. I need to find the source code and take a look...
No, it's C (K&R even mentions it). The compiler doesn't know that you've put a 16-bit format (%5d), it only knows that interval is a 32-bit number, so that's how many argument bits it puts on the stack (really varargs array). sprintf (varargs) doesn't know it's a 32-bit number, only that it's a 16-bit format, so it only extracts that many. The result is that in case B everything shifts down, so sprintf reads the wrong 4th argument. Case A works by accident, since the shift is invisible.

Change your format to %5ld (though it really should be %5lu), as click_here suggested.
 
Top