0

I use sprintf() to fill a buffer and then send this buffer over UART.

I have follow many exemple and use case of sprintf but my code doesn't seems to work

Here is my code example :

#include "stdio.h"
#include "usart.h"
#define Uart husart1

int main(void)
{
    uint32_t m = 3;
    char c = 'A';
    uint32_t o = 1;
    char buffer[256];
    sprintf(buffer, "AT+,%lu,%c,%lu\r\n", m, c, o);
    HAL_UART_Transmit(&Uart, (uint8_t*) buffer, strlen(buffer), 1000);
}

With this piece of code my output is AT+,lu,,lu

Do you have any idea of what is going wrong ?

Edit 1 : When I replace %lu by %d my compilator says this error :

format '%d' expects argument of type 'int', but argument 5 has type 'uint32_t {aka long unsigned int}' [-Wformat=]

So I have had %ld and the buffer send is like AT+,ld,,ld

Edit 2 : When I replace %lu by %u my compilator says this error :

format '%u' expects argument of type 'unsigned int', but argument 3 has type 'uint32_t {aka long unsigned int}' [-Wformat=]

Edit 3: with #include <stdio.h> I have the same errors

Edit 4: regarding the answers I want to keep the uint32_t as my AT parameters can be 32bit long

Edit 5: I tried to cast the type using %d and (int16_t) as follow :

sprintf(buffer, "AT+,%d,%c,%d\r\n", (int16_t) m, c, (int16_t) o);

and this time my resut is "AT+, ,A, "

Why does my code is upset with signed or unsigned integer ?

Edit 6:

With the use of inttypes.h, it looks like this :

sprintf(buffer, "AT+,%"PRIu32",%c,%"PRIu32"\r\n", m, c, o);

and my output still look like AT+,lu,,lu

Clément
  • 37
  • 5
  • What compiler and compiler options are you using? What system are you running on? Specifically, what implementation of `sprintf` are you using? – KamilCuk Sep 22 '21 at 09:55
  • 1
    depending on the architecture `%lu` (specifically the `l` for "long") may be wrong for a 32 bit int. That can indeed cause all kinds of trouble (when the sprintf implementation reads the next 4 bytes from the stack as well, like bytes of the format string). – Peter - Reinstate Monica Sep 22 '21 at 09:56
  • Simple test of what @Peter-ReinstateMonica mentioned: Make those `uint32_t`s into `int`s and `sprintf(buffer, "AT+,%d,%c,%d\r\n", m, c, o);` instead. I bet all `AT` commands can be created using `int`s. – Ted Lyngmo Sep 22 '21 at 09:58
  • 1
    The proper (i.e., portable) conversion is to use the somewhat unwieldy defines from inttypes.h, see https://stackoverflow.com/a/3168298/3150802. – Peter - Reinstate Monica Sep 22 '21 at 09:59
  • Regarding `#include "stdio.h"` - Do you have a local `stdio.h` that you've modified? Why not ``? – Ted Lyngmo Sep 22 '21 at 10:00
  • @Peter-ReinstateMonica good luck on nano glibc implementations :). Did you ever try it? If not, why do you advice it? – 0___________ Sep 22 '21 at 10:02
  • @TedLyngmo Using the generic C types the way to go in any case as long as we don't run into value range issues (we sure don't here). The values are not at all used in binary form which would require a specific bit size anyway. – Peter - Reinstate Monica Sep 22 '21 at 10:03
  • @Peter-ReinstateMonica It's creating old `AT` commands so my bet is that `int`s will do fine. – Ted Lyngmo Sep 22 '21 at 10:04
  • Regarding the edit: If you change from `%lu` to `%d` you must change the types from `uint32_t` to `int` too. – Ted Lyngmo Sep 22 '21 at 10:05
  • @0___________ I wasn't aware this is on a nano; I also have zero experience with the standard libraries available for those. I was simply recommending the portable way to code such printf conversions which may not be common knowledge but tend to occur more often because of the 32/64 bit mix of architectures out there. – Peter - Reinstate Monica Sep 22 '21 at 10:05
  • @Peter-ReinstateMonica `HAL_UART_Transmit` is a part of STM32 HAL library. BTW use of standard `printf` or `scanf` family functions makes no sense at all. – 0___________ Sep 22 '21 at 10:10
  • @0___________ "use of standard printf or scanf family functions makes no sense at al" -- and that is because? – Peter - Reinstate Monica Sep 22 '21 at 10:11
  • They are too heavy, they are not reentrant, Cortex-M scanf uses malloc which is unacceptable for most embedded standards and simple prevents it from using in any RTOS project. I use my own simplified versions of print and scanf In my projects (sometimes I add some additional formats like printing IP or date) – 0___________ Sep 22 '21 at 10:15
  • @0___________ Oh, ok. "on resource starved machines" sounds different than "not at all" ;-) – Peter - Reinstate Monica Sep 22 '21 at 10:16
  • @Peter-ReinstateMonica *`"on resource starved machines"`* I did not write this, there are more reasons – 0___________ Sep 22 '21 at 10:18

2 Answers2

1

Embedded targets usually use nano implementation of the standard library and it does not support many printf formats.

Simply use %u for uint32_t as uint32_t is the native integer size for the STM32 uCs.

If you want very very "conforming" add the cast to unsigned int.

0___________
  • 60,014
  • 4
  • 34
  • 74
  • [The format specifiers for `uint32_t` is `PRIu32`](https://stackoverflow.com/q/3168275/995714) – phuclv Sep 22 '21 at 10:04
  • @0___________ One could, obviously, have one's own string defines for those standard definitions on devices that don't provide them and continue to write portable (and correct) code. You don't need to touch the glibc. – Peter - Reinstate Monica Sep 22 '21 at 10:09
  • it has nothing to do with the board. It's in the C99+ standard. If you're not compiling in C99 mode or not using a C99+ compliant compiler then obviously it's not available. Get a better compiler – phuclv Sep 22 '21 at 10:09
  • The recommendation to stay away from heavy stdlib functions may be good -- maybe you can add what you recommend for use cases like this. `itoa()` (if available)? Roll your own? – Peter - Reinstate Monica Sep 22 '21 at 10:19
  • @Peter-ReinstateMonica that depends on the project. Usually, I use my own simplified implementations of printf. Bare in mind that you do not have a file system as well + no dynamic memory allocation (except the memory pools - but it does require your own implementation) – 0___________ Sep 22 '21 at 10:21
0

Correct format specifier for uint32_t is in define macro PRIu32, which expands to correct format specfier string. It's found in inttypes.h. So your code becomes like this (also fixed the include brackets, assuming none of these headers are part of your project, but part of the toolchain):

#include <stdio.h>
#include <inttypes.h>
#include <usart.h>

#define Uart husart1

int main(void)
{
    uint32_t m = 3;
    char c = 'A';
    uint32_t o = 1;
    char buffer[256];
    sprintf(buffer, "AT+,%"PRIu32",%c,%"PRIu32"\r\n", m, c, o);
    HAL_UART_Transmit(&Uart, (uint8_t*) buffer, strlen(buffer), 1000);
}

The macros may look a bit confusing, so an explanation. In C, you can concatenate string literals at compile time by just by putting them one after the other. In other words, "count is %" "lu" " items" produces exactly same string literal as "count is %lu items". And inttypes.h contains a define like #define PRIu32 "u" or whatever is correct format on the platform.


Reference link: https://en.cppreference.com/w/c/types/integer


If this does not work, you have non-conforming standard library. This can happen on embedded platforms. You need to construct the string in some other way than using printf for types which it does not support. You can consult your standard library documentation, or you could write your own functions, for example using strncat as a model you could create functions like

char *strncat_u32(char *dest, uint32_t value, size_t n);
hyde
  • 60,639
  • 21
  • 115
  • 176
  • I have answer to this proposition in my Edit n°6 – Clément Sep 22 '21 at 13:22
  • @Clément Added a paragraph about this – hyde Sep 22 '21 at 16:08
  • Thanks ! By the way in other part of my project there is the use of %lu and It works so I don't know why this file doesn't want to use the same format as others – Clément Sep 23 '21 at 07:15
  • @Clément If it is the same compiler, with the same command line options, and you run the compiled program on same target platform, and still one does "normal" `printf`, while other has some apparently very limited version... That's quite funny indeed. But impossible to know what's the problem without all the details. – hyde Sep 23 '21 at 12:53