is it possible to add a new usefull function to the Serial object?

127 views
Skip to first unread message

Roberto Boffelli

unread,
Feb 10, 2019, 9:46:18 AM2/10/19
to Developers
hi guys,

sorry for this post if i will make some error or if i'm disturbing you and in the last for my bad english

the reason that has 'port' me to inquire this question ... is a little think that is less in the Serial[x] object

it don't has the opportunity to use the soft and usefull mode to implement the power of the printf function

positive charateristics ......... 
 
i have implemented a workaround to implement the printf to use with Serial object ......

unfortunattly i have try to derive the HardwareSerial directly but the code is compiled correctly but in 

the run the solution don't work in 'totally' ..... 

the second version (the workaround) work correctly ... but it's needed to include and create the istance 

BSerial .....

can you implement only the 'printf' function part in the possible new version of the HardwareSerial 

object class?  .... i will include follow the source code ..... 


Thank you

have a nice day

Roberto74 ($B0FFE)


// = = = = = = = = = = = = = = = = = = = = = = = =
// THIS WORK AROUND WORK CORRECTLY
// = = = = = = = = = = = = = = = = = = = = = = = =
// if necessary include <stdarg.h>
class BSerial
{
public:
static const size_t N_CHAR_OUTPUT_BUFFER_SIZE = 256;

// - - - - - - - - - -
public:
BSerial(HardwareSerial* P1_HardwareSerial_pac)
{
this->P1_HardwareSerial= P1_HardwareSerial_pac;
}
// - - - - - - - - - -
public:
int printf(String P1_string, ...)
{
va_list arg_ptr;

va_start(arg_ptr, P1_string);

int N_write_Char = vsnprintf(A_EndBuffer, BSerial::N_CHAR_OUTPUT_BUFFER_SIZE, &P1_string[0], arg_ptr);

va_end(arg_ptr);

if (N_write_Char < (int) BSerial::N_CHAR_OUTPUT_BUFFER_SIZE)
{
P1_string[N_write_Char+1] = '\0';
}
else
{
return -(N_write_Char + 1 - (int) BSerial::N_CHAR_OUTPUT_BUFFER_SIZE);
}

this->P1_HardwareSerial->print(A_EndBuffer);

return N_write_Char;
}
// - - - - - - - - - -
private:
char A_EndBuffer[BSerial::N_CHAR_OUTPUT_BUFFER_SIZE];
// - - - - - - - - - -
public:
HardwareSerial* P1_HardwareSerial;
};
// = = = = = = = = = = = = = = = = = = = = = = = =





// = = = = = = = = = = = = = = = = = = = = = = = =
// THIS PROBABLY MORE RIGHT IMPLEMENTATION IS COMPILE CORRECTLY BUT PRINT ONLY TWO CHARS
// = = = = = = = = = = = = = = = = = = = = = = = =
/*
// se necessario includere <stdarg.h>
class BSerialT: public HardwareSerial
{
public:
static const size_t N_CHAR_OUTPUT_BUFFER_SIZE = 256;

// - - - - - - - - - -
public:
BSerialT(HardwareSerial& Ps1_HardwareSerial_pac): HardwareSerial(Ps1_HardwareSerial_pac)

{
}
// - - - - - - - - - -
public:
~BSerialT()
{
}
// - - - - - - - - - -
public:
int printf(String P1_string, ...)
{
va_list arg_ptr;

va_start(arg_ptr, P1_string);

int N_write_Char = vsnprintf(A_EndBuffer, BSerialT::N_CHAR_OUTPUT_BUFFER_SIZE, &P1_string[0], arg_ptr);

va_end(arg_ptr);

if (N_write_Char < (int) BSerialT::N_CHAR_OUTPUT_BUFFER_SIZE)
{
P1_string[N_write_Char+1] = '\0';
}
else
{
return -(N_write_Char + 1 - (int) BSerialT::N_CHAR_OUTPUT_BUFFER_SIZE);
}

this->println(A_EndBuffer);

return N_write_Char;
}
// - - - - - - - - - -
private:
char A_EndBuffer[BSerialT::N_CHAR_OUTPUT_BUFFER_SIZE];
// - - - - - - - - - -
};
*/
// = = = = = = = = = = = = = = = = = = = = = = = =




Roger Irwin

unread,
Feb 10, 2019, 12:51:38 PM2/10/19
to devel...@arduino.cc
This would require linking in a lot of code for a little microprocessor. If you had a bigger processor and wanted printf functionality you can sprintf to your buffer and send that on the serial.



--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.

William Westfield

unread,
Feb 10, 2019, 9:39:26 PM2/10/19
to Arduino Developers, r.bof...@gmail.com

usefull mode to implement the power of the printf function

There were already instruction for adding “Serial.printf()” to the “Print” class here:
Does your code improve on that?


This would require linking in a lot of code for a little microprocessor.

The code is not linked in if it is not used.  (or perhaps, it is eliminated as “unused" during linking.)
It’s about 1.7k worth on an AVR - the big disadvantage of printf() is that if you use it at all, you will end up with ALL of the formatting capability included, since it doesn’t know until runtime which formats will get used.

(Alas, the Arduino team does not seem very willing to add this feature.)

BillW/WestfW

Mike

unread,
Feb 10, 2019, 10:13:28 PM2/10/19
to devel...@arduino.cc
With a large move to SAMD21, there is more space. The '328 is being left behind both in capability and expense.

Jim Leonard

unread,
Feb 10, 2019, 11:07:59 PM2/10/19
to devel...@arduino.cc
On Sun, Feb 10, 2019 at 10:13:13PM -0500, Mike wrote:
> With a large move to SAMD21, there is more space. The '328 is being left
> behind both in capability and expense.

Except that there are lots of people still using the 328 because it is both cheap and bullet proof (resistant anyways). I buy arduino nano and mini clones for less than $2 a piece for temporary LED displays and literally just leave them exposed to the elements. I have some that have run for years like this. They literally only stop working if they are fully immersed in rain water (but resume immediately once I pull them out).

Roger Irwin

unread,
Feb 11, 2019, 1:53:41 AM2/11/19
to devel...@arduino.cc
Is there any real advantage in using a 'Serial.printf' as opposed to using a sprintf to a buffer and sending that to the serial (which is what I would do anyway on any platform, easier to debug when on 2 lines ;.) )


Adrian Godwin

unread,
Feb 11, 2019, 5:06:40 AM2/11/19
to devel...@arduino.cc
You have to know in advance the worst-case length of the buffer, which might be a significant fraction of the 2K ram.

Roger Irwin

unread,
Feb 11, 2019, 9:28:29 AM2/11/19
to devel...@arduino.cc
In the OP's example he had declared a static buffer... which frankly is something I would do in any embedded setup that doesn't have an os handling memory management.


Adrian Godwin

unread,
Feb 11, 2019, 10:50:21 AM2/11/19
to devel...@arduino.cc
Yes, but that's my point. A static buffer big enough for the largest possible use (maybe at least 128 chars)  is a substantial chunk of memory.

William Westfield

unread,
Feb 11, 2019, 4:33:17 PM2/11/19
to Developers

 A static buffer big enough for the largest possible use (maybe at least 128 chars)  is a substantial chunk of memory.

It still doesn’t get allocated (static, or automatic) unless you use the function…

BillW

Adrian Godwin

unread,
Feb 11, 2019, 4:44:04 PM2/11/19
to devel...@arduino.cc
If you use the buffer to sprintf (snprintf ?)your output and then serial.Write that buffer, then it's in your own code and certainly is allocated. If it's inside printf then no, only used if printf is called. But that's not what the suggestion was for. It was to  buffer the output inline.

Printf internally wouldn't buffer into a static buffer anyway - at worst it would use stack or heap (which I agree, is not good on an embedded system), but more likely on a resource-constrained build would use putchar or similar : the only buffer would be the serial output buffer which is there anyway.



Adrian Godwin

unread,
Feb 11, 2019, 5:04:16 PM2/11/19
to devel...@arduino.cc
 Note that most of the above mentioned approaches DO actually do the sprintf/write thing under a wrapper, with a buffer between 80 and 256 bytes in size. So Roger's suggestion has something of an advantage in making it easier to allow a buffer of the correct size to be used rather than one that's substantially larger than necessary or too small to compose the output.

I _would_ like to see printf in the standard library. As Bill says, the codespace will only be used if it's called. But I _wouldn't_ like to see it using a local buffer. I'd like it to use no extra buffering at all.

This used to work :

#include "printf.h"

where printf.h is
/*
 Copyright (C) 2011 J. Coliz <mani...@ymail.com>
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 */
 
/**
 * @file printf.h
 *
 * Setup necessary to direct stdout to the Arduino Serial library, which
 * enables 'printf'
 */

#ifndef __PRINTF_H__
#define __PRINTF_H__

#ifdef ARDUINO

int serial_putc( char c, FILE * )
{
  Serial.write( c );

  return c;
}

void printf_begin(void)
{
  fdevopen( &serial_putc, 0 );
}

#else
#error This example is only for use on Arduino.
#endif // ARDUINO

#endif // __PRINTF_H__


(This comes from maniacbugs nrf24 libraries).

I think it works by just using gcc's printf / stdio and linking it's character output into serial.write. It has the disadvantage that it only works for a stream called 'serial', not for any other stream, but presumably could be used to extend the class instead.

However, although it compiles and links (with about 1.5k of code space and 6 bytes of data) it doesn't seem to actually work any more. I haven't looked into why that is.




 

Adrian Godwin

unread,
Feb 11, 2019, 5:13:02 PM2/11/19
to devel...@arduino.cc
Back again !

The reason it didn't work is that it needs initialising. So the main code should be

#include "printf.h"

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  printf_begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  printf("Hello World\n");
}

This takes (on an at328) 2398 bytes of flash and 212 bytes of data. The same thing with serial.write alone (but all the huge stack of serial.print() calls for every significant output instead of printf's convenient formatting) is  1400 bytes of code and 200 bytes of ram. So practically no data overhead and only 5% of codespace on a 328. I think that's a decent tradeoff.

Roger Irwin

unread,
Feb 13, 2019, 2:10:41 AM2/13/19
to devel...@arduino.cc
This again is why using sprintf is always the better option on a system with no os or memory management at a microkernel level; it makes controlling memory use easier.

For example: the same buffer I use for printing to the serial port can be unioned with a structure used for save/restore of data to flash. Obviously I need to suspend serial operations when transferring to the flash, but such are the compromises of using a microcontroller with 2K of RAM.

The overhead at a binary level is approximately zero. At a source level it means a few extra characters:

Serial.printf("N1 %i %f\n",anInt,aFloat);

Becomes:

sprintf(u.buffer, "N1 %i %f\n",anInt,aFloat);
Serial.print(u.buffer);

However I also note that whilst I use printf a lot when developing on Real Time kernels....i.e. larger systems, I've bever really wanted to use it on little controllers as sending stuff out the serial port and I'm far more likely to convert on the fly as the data goes out. Essentially, if you imagine your serial port stuff is in your main loop and your real time control is on an RTI, then a sequence such as:

Serial.print("Your numbers are: ");
Serial.print(anInt);
Serial.print("   ");
Serial.println(aFloat);

Whilst apperaring much 'greater' at a source code level in reality has a lowwer overhead; data goes down the wire as soon as it is created.








William Westfield

unread,
Feb 13, 2019, 4:20:00 AM2/13/19
to Developers

no os or memory management

The current implementations of Serial.printf() use an intermediate buffer and sprintf() to it first.

But It should be possible to write it in a way that utilizes the underlying ave-libc printf() function, which does NOT buffer the data at all, although it might involve some messy C/C++ interactions…
(Basically, you’d dynamically create the FILE (14 bytes, currently) set it up to use this.write(), and call printf().)

BillW

Massimo Banzi

unread,
Feb 13, 2019, 4:26:42 AM2/13/19
to Arduino Developers
Honestly I would avoid adding printf to Serial., the syntax is not very friendly.

People who know how to use it can use sprintf and pass the buffer to print.

The only thing I would add is multiple arguments support for print() println() if somebody needs something more sophisticated they can use sprintf. 

M

--

Massimo Banzi
Co-Founder



Rob Tillaart

unread,
Feb 13, 2019, 4:44:29 AM2/13/19
to Arduino Developers
FYI,
The streaming.h from http://arduiniana.org/libraries/streaming/ does a good job for printing multiple arguments and imho is easy to understand.
e.g.  lcd << "GPS #" << gpsno << " date: " << day << "-" << month << "-" << year << endl;


Massimo Banzi

unread,
Feb 13, 2019, 5:26:58 AM2/13/19
to Arduino Developers
IMHO I find that syntax confusing :)

(It’s different from everything else we use in arduino so it’s a different metaphor to introduce to users etc etc)

It’s nice it’s a library so advanced users can easily add the to their code

M



--

Massimo Banzi
Co-Founder


Adrian Godwin

unread,
Feb 13, 2019, 6:53:10 AM2/13/19
to devel...@arduino.cc
The streaming syntax is clear enough for simple statements but quickly becomes a mess if any width formatting is required.
 

newc...@flounder.com

unread,
Feb 13, 2019, 1:55:40 PM2/13/19
to devel...@arduino.cc
I would love to have a feature like
Serial.print(value, “03d”);
Or
Serial.print(value, “4u”);

Getting things to line up in columns is more an exercise in pain
management than an exercise in programming. Since I don’t think there are
any overloads that have a string as a second parameter, this should not
cause any backward-compatibility issues, and I am more likely to use
Serial.print(value, “04x”);
Than
Serial.print(value, HEX);

I don’t find streaming syntax valuable at all, and I’ve been programming
in C+ for 24 years. When I write 150,000 lines of code, and 45 of them
are prints, streaming output holds little charm.
Joe

Andrew Kroll

unread,
Feb 14, 2019, 6:27:07 AM2/14/19
to Arduino Developers
My USB host library has a printf helper for various platforms, and since it also fixes HEAP to be ISR safe, it even works in an ISR without trouble.
Note that printf on the DUE (IIRC zero too) "just works" to print out the debug port, which is what I always use for console on the DUE anyway.
printf syntax is difficult? No, I don't think so, but regex IS difficult. Worse sprintf can overflow. Better to use snprintf instead, since it won't overflow your buffer.

The thing that hoses most people is they tend to try and do too much with a smaller than needed MCU, and then wonder why it breaks. This is what I see the most often.

I don't understand why people are so against using malloc/new etc either. At most you end up saving 2 bytes, which is next to nothing, and you end up polluting the namespace with globals. You can actually do more if you plan well and free/delete as soon as you no longer require the memory, and seem to operate on more data than you would think you can.


Visit my github for awesome Arduino code @ https://github.com/xxxajk

Thomas Roell

unread,
Feb 14, 2019, 7:17:49 AM2/14/19
to Arduino Developers
I am curious there. For the "printf()" are you using snprintf() to a temp buffer on a stack, or some other way ?

malloc() / free() ... Low new() / delete() would be any different. The real grief is that there is no good mechanism to tell the programmer that they screwed up somewhere. Like a OOM assert or something. Event worse, on ARM your stack grows towards the heap. Yes, one could half way detect that, but at the end of the day, if you cannot tell the user ...

- Thomas

Ian Katz

unread,
Feb 14, 2019, 8:08:08 AM2/14/19
to devel...@arduino.cc
There's no conflict with releasing this code as 3rd-party Arduino
library, right? That would seem to provide either the space-saving or
the user-friendliness, at the user's own option, and leave the
remainder of the discussion to be "how best to write a cross-platform
implementation (in terms of responding to preprocessor defines for
serial ports)".

Andrew Kroll

unread,
Feb 14, 2019, 8:29:31 AM2/14/19
to Arduino Developers
On Thu, Feb 14, 2019 at 7:17 AM Thomas Roell <grumpyo...@gmail.com> wrote:
I am curious there. For the "printf()" are you using snprintf() to a temp buffer on a stack, or some other way ?

malloc() / free() ... Low new() / delete() would be any different. The real grief is that there is no good mechanism to tell the programmer that they screwed up somewhere. Like a OOM assert or something.


Yes, you can sense OOM. new and malloc returns NULL if OOM. You can also use sbrk() to find out how much RAM you have, so, still, a non-issue. On avr, you can use the heap variables and subtract the SP and get it too. Still, I'm not sure where you think there is a problem.
 
Event worse, on ARM your stack grows towards the heap. Yes, one could half way detect that, but at the end of the day, if you cannot tell the user ...



Not a problem, free/delete as soon as you are done with the memory. printf does exactly this before it returns.
Normally the heap arena fragments are coalesced, so unless you do something totally stupid that causes unusable fragments, this is not an issue either. Heap will not simply grow forever, it uses a first-fit strategy on MCUs.

 

Massimo Banzi

unread,
Feb 14, 2019, 8:34:51 AM2/14/19
to Arduino Developers
We’re going off topic people. :)

If you want to discuss this new topic please open another thread 

Thanks!


--

Massimo Banzi
Co-Founder


Dennis Lee Bieber

unread,
Feb 14, 2019, 12:41:53 PM2/14/19
to devel...@arduino.cc
On Thu, 14 Feb 2019 06:26:52 -0500, Andrew Kroll
<xxx...@gmail.com> declaimed the following:

>
>I don't understand why people are so against using malloc/new etc either.
>At most you end up saving 2 bytes, which is next to nothing, and you end up
>polluting the namespace with globals. You can actually do more if you plan
>well and free/delete as soon as you no longer require the memory, and seem
>to operate on more data than you would think you can.
>

If one has come from an avionics real-time environment, dynamic memory
allocation is only done during initialization of the application -- not
during application run (so, in common Arduino-ese, you would do dynamic
memory allocation in setup(), and never in loop() ). Dynamic allocation
leads to uncertain timings and potential for failure -- insufficient memory
during setup() means the application never starts, and signals a condition
that must be resolved before the application goes "live" (eg: the plane
will never leave the hanger). But a failure during loop() can result in
hazardous behavior (eg: the plane dies after reaching cruise altitude)


--
Wulfraed Dennis Lee Bieber AF6VN
wlf...@ix.netcom.com HTTP://wlfraed.home.netcom.com/

William Westfield

unread,
Feb 14, 2019, 10:31:00 PM2/14/19
to Developers
The only thing I would add is multiple arguments support for print() println()
I would love to have a feature like Serial.print(value, “03d”);

Therein lies the problem.  “lots of people” would like to have better output formatting, and/or the ability to print multiple things in a single statement.  libc’s printf() is an existing and well-tested standard mechanism for doing that, but it is relatively large, not “friendly”, and the simple approach to using it requires an unknown amount of memory…

(I might argue that there is NO existing “friendly” way of doing formatted output.  At least, not without getting significantly worse than printf() in terms of runtime resource usage…)

BillW

Bill Perry

unread,
Mar 9, 2019, 3:08:42 AM3/9/19
to Developers
Roberto,
What would be better and solves what you are wanting is to add a printf() method to the Print class.
That way it works for any hardware i/o class, not just the Serial object,  that inherits Print which means it works on many devices including LCDs and LED matrix devices.
Many Arduino users keep asking for this and it has been discussed over and over again through the years but the ones that control Arduino simply do not want it to exist in the Arduino.cc bundled cores.
The playground printf page was created nearly 10 years ago to try to help Arduino users that wanted this functionality.
It is also the reason I updated the playground printf wiki (over 5 years ago) to better explain various printf() options including how to add a printf() method to the Print class.
There is also some recent discussion of this and other variadic printing mechanisms over in one of the other threads that has been going on for 2 years now: https://groups.google.com/a/arduino.cc/forum/#!topic/developers/sTacUMEXBTw

I've all but given up on ever expecting a printf() method to ever be added to the Print class of the Arduino.cc cores.

On the positive side, many 3rd party vendors have seen the value of having a printf() method in their Print class and have included it in their cores.
Teensy/Teensy3, pic32, espxx cores all include a printf() method in their Print class.

--- bill


Juraj Andrássy

unread,
Mar 31, 2019, 6:56:31 AM3/31/19
to Developers
My Arduino library StreamLib contains Print class enhancement with pritnf implementation for all platforms. It doesn't use buffer to format the output, because it uses vfprintf to directly output the result.
The class FormattedPrint is base class for other Print implementations in the library.
BufferedPrint wraps any other Print implementation and buffers the output. flush() is then mandatory. The buffer is allocated by the user and provided over constructor.
ChunkedPrint extends BufferedPrint and implements chunked transfer encoding for HTTP.
CStringBuilder prints with Print methods into provided char buffer.


J.A
Reply all
Reply to author
Forward
0 new messages