It became clear that compiling with newlib nano + Float Printf did not automatically extend the Stream or Print class with printf methods. It is not clear what needs to be done to enable printf though I suspect the form SerialX.printf() will never work. I'm not sure where printf "prints" to but how can it be redirected to a Serial device?
It is possible, have used for this small patch, can paste it late evening.
As disadvantage It use static buffer, but better than nothing.
It would also be nice for STM to (also) provide an "official" solution here.
we can try to ask @fpistm to integrate it into repo
It would be good since there is an expectation that selecting the menu options will enable this functionality.
@pkourany this is also kind of idea: https://gist.github.com/ridencww/4e5d10097fee0b0f7f6b
I have working "lite" printf implementation that was developed for the STM32 Maple Core but I would rather not deviate from the STM32 Core. Worst case, we do a PR for print.x in the Core code.
can't find this "lite" version ..., ok found it in Rogers core
@uzi18, it is not in Roger's code. I added it based on a contribution from a member on STMDuino:
... added to Print.cpp
#ifdef LEAN_PRINTF
//------------------------------------------------
#ifdef toupper
#undef toupper
#endif
#ifdef tolower
#undef tolower
#endif
#ifdef islower
#undef islower
#endif
#ifdef isdigit
#undef isdigit
#endif
#define toupper(c) ((c)&=0xDF)
#define tolower(c) ((c)|=0x20)
#define islower(c) ((unsigned char)c >= (unsigned char)'a' && (unsigned char)c <= (unsigned char)'z')
#define isdigit(c) ((unsigned char)c >= (unsigned char)'0' && (unsigned char)c <= (unsigned char)'9')
typedef union {
unsigned char byte[5];
long l;
unsigned long ul;
float f;
const char *ptr;
} value_t;
size_t Print::printDigit(unsigned char n, bool lower_case)
{
register unsigned char c = n + (unsigned char)'0';
if (c > (unsigned char)'9') {
c += (unsigned char)('A' - '0' - 10);
if (lower_case)
c += (unsigned char)('a' - 'A');
}
return write(c);
}
static void calculateDigit (value_t* value, unsigned char radix)
{
unsigned long ul = value->ul;
unsigned char* pb4 = &value->byte[4];
unsigned char i = 32;
do {
*pb4 = (*pb4 << 1) | ((ul >> 31) & 0x01);
ul <<= 1;
if (radix <= *pb4 ) {
*pb4 -= radix;
ul |= 1;
}
} while (--i);
value->ul = ul;
}
size_t Print::printf(const char *format, ...)
{
va_list ap;
bool left_justify;
bool zero_padding;
bool prefix_sign;
bool prefix_space;
bool signed_argument;
bool char_argument;
bool long_argument;
bool lower_case;
value_t value;
int charsOutputted;
bool lsd;
unsigned char radix;
unsigned char width;
signed char decimals;
unsigned char length;
char c;
// reset output chars
charsOutputted = 0;
va_start(ap, format);
while( c=*format++ ) {
if ( c=='%' ) {
left_justify = 0;
zero_padding = 0;
prefix_sign = 0;
prefix_space = 0;
signed_argument = 0;
char_argument = 0;
long_argument = 0;
radix = 0;
width = 0;
decimals = -1;
get_conversion_spec:
c = *format++;
if (c=='%') {
charsOutputted+=write(c);
continue;
}
if (isdigit(c)) {
if (decimals==-1) {
width = 10*width + c - '0';
if (width == 0) {
zero_padding = 1;
}
} else {
decimals = 10*decimals + c - '0';
}
goto get_conversion_spec;
}
if (c=='.') {
if (decimals==-1)
decimals=0;
else
; // duplicate, ignore
goto get_conversion_spec;
}
if (islower(c)) {
c = toupper(c);
lower_case = 1;
} else
lower_case = 0;
switch( c ) {
case '-':
left_justify = 1;
goto get_conversion_spec;
case '+':
prefix_sign = 1;
goto get_conversion_spec;
case ' ':
prefix_space = 1;
goto get_conversion_spec;
case 'B': /* byte */
char_argument = 1;
goto get_conversion_spec;
// case '#': /* not supported */
case 'H': /* short */
case 'J': /* intmax_t */
case 'T': /* ptrdiff_t */
case 'Z': /* size_t */
goto get_conversion_spec;
case 'L': /* long */
long_argument = 1;
goto get_conversion_spec;
case 'C':
if( char_argument )
c = va_arg(ap,char);
else
c = va_arg(ap,int);
charsOutputted+=write(c);
break;
case 'S':
value.ptr = va_arg(ap,const char *);
length = strlen(value.ptr);
if ( decimals == -1 ) {
decimals = length;
}
if ( ( !left_justify ) && (length < width) ) {
width -= length;
while( width-- != 0 ) {
charsOutputted+=write(' ');
}
}
while ( (c = *value.ptr) && (decimals-- > 0)) {
charsOutputted+=write(c);
value.ptr++;
}
if ( left_justify && (length < width)) {
width -= length;
while( width-- != 0 ) {
charsOutputted+=write(' ');
}
}
break;
case 'D':
case 'I':
signed_argument = 1;
radix = 10;
break;
case 'O':
radix = 8;
break;
case 'U':
radix = 10;
break;
case 'X':
radix = 16;
break;
default:
// nothing special, just output the character
charsOutputted+=write(c);
break;
}
if (radix != 0) {
unsigned char store[6];
unsigned char *pstore = &store[5];
if (char_argument) {
value.l = va_arg(ap, char);
if (!signed_argument) {
value.l &= 0xFF;
}
} else if (long_argument) {
value.l = va_arg(ap, long);
} else { // must be int
value.l = va_arg(ap, int);
if (!signed_argument) {
value.l &= 0xFFFF;
}
}
if ( signed_argument ) {
if (value.l < 0)
value.l = -value.l;
else
signed_argument = 0;
}
length=0;
lsd = 1;
do {
value.byte[4] = 0;
calculateDigit(&value, radix);
if (!lsd) {
*pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
pstore--;
} else {
*pstore = value.byte[4];
}
length++;
lsd = !lsd;
} while( value.ul );
if (width == 0) {
// default width. We set it to 1 to output
// at least one character in case the value itself
// is zero (i.e. length==0)
width = 1;
}
/* prepend spaces if needed */
if (!zero_padding && !left_justify) {
while ( width > (unsigned char) (length+1) ) {
charsOutputted+=write(' ');
width--;
}
}
if (signed_argument) { // this now means the original value was negative
charsOutputted+=write('-');
// adjust width to compensate for this character
width--;
} else if (length != 0) {
// value > 0
if (prefix_sign) {
charsOutputted+=write('+');
// adjust width to compensate for this character
width--;
} else if (prefix_space) {
charsOutputted+=write(' ');
// adjust width to compensate for this character
width--;
}
}
/* prepend zeroes/spaces if needed */
if (!left_justify) {
while ( width-- > length ) {
charsOutputted+=write( zero_padding ? '0' : ' ');
}
} else {
/* spaces are appended after the digits */
if (width > length)
width -= length;
else
width = 0;
}
/* output the digits */
while( length-- ) {
lsd = !lsd;
if (!lsd) {
pstore++;
value.byte[4] = *pstore >> 4;
} else {
value.byte[4] = *pstore & 0x0F;
}
charsOutputted+=printDigit(value.byte[4], lower_case);
}
}
} else {
charsOutputted+=write(c);
}
}
va_end(ap);
return (size_t)charsOutputted;
}
#endif
Hi @pkourany
your issue is to extend class with ::printf() method ? Right?
About standard C printf, it is supported thanks the newlib nano implementation.
By default there is no float support in this printf, to enable it you have to enable it explicitely using the option: newlib nano + Float Printf
Newlib-nano is different from newlib in addition to the libraries' name.
Formatted input/output of floating-point number are implemented as weak symbol.
If you want to use %f, you have to pull in the symbol by explicitly specifying"-u" command option.
-u _scanf_float
-u _printf_floate.g. to output a float, the command line is like:
$ arm-none-eabi-gcc --specs=nano.specs -u _printf_float $(OTHER_LINK_OPTIONS)
By default C printf will output on the DEBUG_UART:
https://github.com/stm32duino/Arduino_Core_STM32/blob/a8350866daf5abfd97b7cd76ec514a1f22cace39/cores/arduino/stm32/uart.c#L587
this is done thanks the _write() in syscall which can be redefined as weak:
https://github.com/stm32duino/Arduino_Core_STM32/blob/a8350866daf5abfd97b7cd76ec514a1f22cace39/cores/arduino/syscalls.c#L84-L85
It is also used by the core_debug feature to print some debug message (disabled per default).
@fpistm, I defined DEBUG_UART in my variant.h file and used printf() instead of Serial.printf() and all compiled. I will be testing and reporting back. Thanks!
my patch:
diff --git a/cores/arduino/Print.h b/cores/arduino/Print.h
index 05b585ea..68058b1f 100644
--- a/cores/arduino/Print.h
+++ b/cores/arduino/Print.h
@@ -103,6 +103,44 @@ class Print {
void println(uint64_t, uint8_t = DEC);
void print(uint64_t, uint8_t = DEC);
#endif
+
+#include <stdarg.h>
+#define PRINTF_BUF 80 // define the tmp buffer size (change if desired)
+ void printf(const char *format, ...)
+ {
+ char buf[PRINTF_BUF];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ for(char *p = &buf[0]; *p; p++) // emulate cooked mode for newlines
+ {
+ if(*p == '\n')
+ write('\r');
+ write(*p);
+ }
+ va_end(ap);
+ }
+#ifdef F // check to see if F() macro is available
+ void printf(const __FlashStringHelper *format, ...)
+ {
+ char buf[PRINTF_BUF];
+ va_list ap;
+ va_start(ap, format);
+#ifdef __AVR__
+ vsnprintf_P(buf, sizeof(buf), (const char *)format, ap); // progmem for AVR
+#else
+ vsnprintf(buf, sizeof(buf), (const char *)format, ap); // for the rest of the world
+#endif
+ for(char *p = &buf[0]; *p; p++) // emulate cooked mode for newlines
+ {
+ if(*p == '\n')
+ write('\r');
+ write(*p);
+ }
+ va_end(ap);
+ }
+#endif
+
};
#endif
@fpistm, I got everything working with and without float support. Thanks!