-
-
Save ridencww/4e5d10097fee0b0f7f6b to your computer and use it in GitHub Desktop.
/* | |
* Simple printf for writing to an Arduino serial port. Allows specifying Serial..Serial3. | |
* | |
* const HardwareSerial&, the serial port to use (Serial..Serial3) | |
* const char* fmt, the formatting string followed by the data to be formatted | |
* | |
* int d = 65; | |
* float f = 123.4567; | |
* char* str = "Hello"; | |
* serial_printf(Serial, "<fmt>", d); | |
* | |
* Example: | |
* serial_printf(Serial, "Sensor %d is %o and reads %1f\n", d, d, f) will | |
* output "Sensor 65 is on and reads 123.5" to the serial port. | |
* | |
* Formatting strings <fmt> | |
* %B - binary (d = 0b1000001) | |
* %b - binary (d = 1000001) | |
* %c - character (s = H) | |
* %d/%i - integer (d = 65)\ | |
* %f - float (f = 123.45) | |
* %3f - float (f = 123.346) three decimal places specified by %3. | |
* %o - boolean on/off (d = On) | |
* %s - char* string (s = Hello) | |
* %X - hexidecimal (d = 0x41) | |
* %x - hexidecimal (d = 41) | |
* %% - escaped percent ("%") | |
* Thanks goes to @alw1746 for his %.4f precision enhancement | |
*/ | |
void serial_printf(HardwareSerial& serial, const char* fmt, ...) { | |
va_list argv; | |
va_start(argv, fmt); | |
for (int i = 0; fmt[i] != '\0'; i++) { | |
if (fmt[i] == '%') { | |
// Look for specification of number of decimal places | |
int places = 2; | |
if (fmt[i+1] == '.') i++; // alw1746: Allows %.4f precision like in stdio printf (%4f will still work). | |
if (fmt[i+1] >= '0' && fmt[i+1] <= '9') { | |
places = fmt[i+1] - '0'; | |
i++; | |
} | |
switch (fmt[++i]) { | |
case 'B': | |
serial.print("0b"); // Fall through intended | |
case 'b': | |
serial.print(va_arg(argv, int), BIN); | |
break; | |
case 'c': | |
serial.print((char) va_arg(argv, int)); | |
break; | |
case 'd': | |
case 'i': | |
serial.print(va_arg(argv, int), DEC); | |
break; | |
case 'f': | |
serial.print(va_arg(argv, double), places); | |
break; | |
case 'l': | |
serial.print(va_arg(argv, long), DEC); | |
break; | |
case 'o': | |
serial.print(va_arg(argv, int) == 0 ? "off" : "on"); | |
break; | |
case 's': | |
serial.print(va_arg(argv, const char*)); | |
break; | |
case 'X': | |
serial.print("0x"); // Fall through intended | |
case 'x': | |
serial.print(va_arg(argv, int), HEX); | |
break; | |
case '%': | |
serial.print(fmt[i]); | |
break; | |
default: | |
serial.print("?"); | |
break; | |
} | |
} else { | |
serial.print(fmt[i]); | |
} | |
} | |
va_end(argv); | |
} |
Great job, thank you so much for sharing your code!
Best, Rudi
Thanks for sharing .. really fantastic your solution. But I would like to know if there is a possibility to modify to use the flash, just like Serial.print (F ("---"));
Thanks again.
Alegazzi
Standing on the shoulders of various giants, I've come up with a simple C++ template. Not sure if it's the best solution but it works with flash or normal string. A char buffer is used but deallocated upon return from helper function.
If you have a lot of fairly sizable format strings, then using F() could save some memory. However when I've used the routine in my projects, the savings wouldn't have been worth the overhead. It could be more useful to support F() as a surrogate to char* in a varargs parameter. It all depends upon the number of string literals your program is using in these statements,
There is a good discussion of F() from 2012 in the Arduino forum that may be worth looking at: https://forum.arduino.cc/index.php?topic=91314.0
In any event, Alex did provide a path forward for you to explore and all the parts are there. Good coding.
Thanks for this, works well!
For this to work on PlatformIO with an Arduino MKR1300, I had to make a few changes. Testing them in the Arduino IDE made it fail to compile, so I think these should not be added to the script, but I placed them here anyway for the few people using this in PlatformIO with the MKR1300.
- First I had to include cstdarg.h for va_start to be defined:
#include <cstdarg>
. - For passing the serial interface to the function, I had to change
HardwareSerial
in the signature toSerial_
.void serial_printf(Serial_& serial, const char* fmt, ...)
. (Probably because it uses SerialUSB)
Thanks for making this gist, I will be using it in my project and updating this comment if I find anything new.
I use templates to support various serial interfaces in my forked snippet below. Tested in Arduino IDE for Nano, ESP, STM32 mcus.
@ridencww : Thanks so much for the helpful tip. - Best - Jim