Last active
July 12, 2020 06:55
-
-
Save CAFxX/14e5b5f779f2c395e6c4762674be43d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| 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