Skip to content

Instantly share code, notes, and snippets.

@CAFxX
Last active July 12, 2020 06:55
Show Gist options
  • Select an option

  • Save CAFxX/14e5b5f779f2c395e6c4762674be43d1 to your computer and use it in GitHub Desktop.

Select an option

Save CAFxX/14e5b5f779f2c395e6c4762674be43d1 to your computer and use it in GitHub Desktop.
/*
serial_printf.ino
2020 CAFxX
serial_printf is a printf for Arduino Serial output that uses no dynamic allocation (malloc)
and constant stack space (in addition to that of printf).
Supports the same syntax of the standard Arduino/AVR printf-like functions.
Returns the number of characters printed.
Examples:
serial_printf("Hello %s!", "world");
serial_printf("Temperature: %d°C, Humidity: %d%%\n", (int)temp, (int)humid);
cprintf/vcprintf are printf-like functions that take an arbitrary print function.
The print function is invoked for each character to be printed, and it should
return true if the character was printed or false otherwise.
Example:
LiquidCrystal *lcd;
auto lcdprint = [](char c, void *lcd) -> bool { return ((LiquidCrystal*)lcd).write(c) == 1; };
cprintf(lcdprint, lcd, "Hello %s!", "world");
All functions have _P variants that treat the fmt argument as a pointer to PROGMEM.
*/
int serial_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
int serial_printf_P(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
int cprintf(int(*print)(char, FILE*), const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
int cprintf_P(int(*print)(char, FILE*), const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
int vcprintf(int(*print)(char, FILE*), const char *fmt, va_list args) __attribute__ ((format (printf, 3, 0)));
int vcprintf_P(int(*print)(char, FILE*), const char *fmt, va_list args) __attribute__ ((format (printf, 3, 0)));
int serial_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int r = vcprintf(serialPrint, NULL, fmt, args);
va_end(args);
return r;
}
int serial_printf_P(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int r = vcprintf_P(serialPrint, NULL, fmt, args);
va_end(args);
return r;
}
bool serialPrint(char c, void*) {
return Serial.write(c) == 1;
}
int cprintf(bool(*print)(char, void*), void *arg, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int r = vcprintf(print, arg, fmt, args);
va_end(args);
return r;
}
int cprintf_P(bool(*print)(char, void*), void *arg, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int r = vcprintf_P(print, arg, fmt, args);
va_end(args);
return r;
}
int vcprintf(bool(*print)(char, void*), void *arg, const char *fmt, va_list args) {
FILE f;
openfile(&f, print, arg);
int r = vfprintf(&f, fmt, args);
// TODO: add fdev_close()
return r;
}
int vcprintf_P(bool(*print)(char, void*), void *arg, const char *fmt, va_list args) {
FILE f;
openfile(&f, print, arg);
int r = vfprintf_(&f, fmt, args);
// TODO: add fdev_close()?
return r;
}
void openfile(FILE *f, bool(*print)(char, void*), void *arg) {
struct params { bool(*print)(char, void*); void *arg; } p = { print = print, arg = arg };
fdev_setup_stream(f, [](char c, FILE *f) -> int {
params *p = (params*)fdev_get_udata(f);
return p->print(c, p->arg) ? 0 : 1;
}, NULL, _FDEV_SETUP_WRITE);
fdev_set_udata(f, &p);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment