The Noiasca Buffer Print Arduino Library
The Noiasca Buffer Print is a Library very similar to Print.h but will buffer outputs until flush() is called. Despite other libraries, this class calls flush() at the end of each print() call. Why this is necessary and which possibilities are offered should be explained on this page.
I split this page in following chapters
- Idea: Why a buffered print
- Example and Proof of Concept
- Important Member Functions
- Comparision / differences to other Streaming Libraries
Idea: Why do we need a Buffer Print Library?
The Arduino Print.h serializes outputs made by print/println into single characters and calls write for each character printed to the output designation. This is fine for serial transmission, but might cause protocol overhead for slow hardware interfaces if they add a time consuming transmission for each separate communication.
Hence Noiasca BufferPrint will collect data in an internal buffer and transmit data at the end of print() only.
Lets see an I2C LCD transmission for a PCF8574 expander which uses the common 4bit mode to print 4 characters.
The first figure shows the I2C communication based on Print sending 4 characters. We need 4 I2C transmission. Each single I2C transmission sends the I2C address, the high/low nibbles of the character, 5 acknowledgements, a final stop condition and will pause the sketch to give the display time to process the command:
This is an example with BufferPrint.h: we store the data in a buffer and do one single I2C transmission for all 4 characters. We send the I2C Address once, we have one start condition, one stop condition, 17 acknowledgements and one delay for the display:
Buffered print will process all steps without the need of a manual / explicit call of flush(). Each print/println() should be able to finalize the output to the designation flush().
Examples and Proof of Concepts
Example Basic Hello World
The library comes with a basic "Hello World" examples. It shows how to define a class inherited from BufferPrint.h and a simple implementation which does a buffered print to the hardware serial interface.
Proof of Concept: Modbus RTU Sender
Modbus RTU is a communication protocol on top of RS485.
To enable a buffered print, we can use the internal buffer of NoiascaBufferPrint to collect data before starting a Modbus RTU transmission.
This is used in several of my Modbus Master examples.
Proof of Concept: Faster I2C LCD Communication
Due to the default print/write concept, communication with I2C LCD gets quite slow. If you take a common PCF8574 port expander for a LCD and print the line "Hello World" to the display each single character will be sent separately to the display. For each character the two 4bit nibbles will be sent, the enable line pulsed and the pause time applied. 11 characters - 11 times. The LCD supports a multi-character write also, but this is not used by most of the libraries. Do overcome this flaw we must inherit the LCD class not from print.h but from NoiascaBufferPrint.h. The write method can be reused as you are used to from other Print libraries. The flush() method must be implemented in the derived class and should stream the internal buffer into two 4bit nibbles, set the RS/RW lines, respect the backlight pin and finally send the to the I2C interface:
void flush() // only pseudo code { Wire.beginTransmission(lcdAddr); // initialize I2C communication // set RS/RW/backlight line for (size_t i = 0; i < getActualBufferSize(); i++) { // byte value = internal[i]; // one value from the internal buffer // Wire.write add high nibble to I2C buffer with respect of RS/RW/backlight state // Wire.write add low nibble to I2C buffer with respect of RS/RW/backlight state // Wire.write set enable pin with respect of RS/RW/Backlight // Wire.write unset enable pin with respect of RS/RW/Backlight } // set backlight pin according backlight state Wire.endTransmission(); // transmit the I2C buffer delayMicroseconds(waitshort); // standard delay after send setActualBufferSize(0); // reset buffer to 0 }
The write method still can be implemented to print one character in a single I2C transmission.
If you are interested in a faster I2C LCD implementation, have a look on my NoiascaLiquidCrystal and its lcd_PCF8574_fast.h which will come with a full working example using Noiasca Buffer Print.
Deep dive into NoiascaBufferPrint
NoiascaBufferPrint is quite similar to the original Print.h implementation.
print() and its variants calls add (instead of write) for each character handed over and will fill the buffer. All print variants have a new optional boolean parameter to enable/disable the flush after the end of the print. This is also used internally for the println() variants as they just call their print() counterpart with disabled flush add \r\n to the buffer a final call of flush at the end of println().
lcd.print("current temp:", false) // print with next line lcd.print(temperature); // now print "current temp: 12.23" in one go
add() takes a single character and adds it to the buffer. If the internal buffer is full, flush() will be called.
flush() will empty the buffer to it's designation. This virtual member function has to be implemented in your derived class and should do an optimized output of data to the designation. flush() will be either called at the end of the print statement or if the buffer has reached its buffer size.
write() - a virtual member function - must be implemented in the derived class for a single character transmission to the designation.
getBufferLength() will return the number of bytes in the buffer.
resetBuffer() will set the actual buffer to 0 and should be called at the end of your flush method.
Sidenote
The Printable() is currently not implemented.
Comparision with other Libraries
Finally let's compare the NoiascaBufferPrint.h with several other "Print" and "Helper" classes
Print.h by Arduino
Print.h serializes prints into single "characters", will print several number formats, you can specify decimals and you have an easy access to the F-Makro / values in PROGMEM. It is used in a lot of other Libraries, mainly outgoing communication and displays.
Streaming.h by Mikal Hart
Makes use of the printable() function in Print.h so you can stream to a class which inherits from Print.h.
As example to stream to Serial:
Serial << F("Voltage Panel:") << adcVoltage << 'V' << endl;
You can write your own "poor mans cout"
#include <Streaming.h> HardwareSerial &cout = Serial; void setup() { Serial.begin(115200); int inst1 = 1; int inst2 = 2; int inst3 = 3; cout << inst1 << ' ' << inst2 << ' ' << inst3 << endl; //cout << (F("poor mans cout\r\n")); //cout << (F("poor mans cout")) << endl; } void loop() { }
PString.h by Mikal Hart
PString.h inherits from Print.h and uses its own internal buffer. It is a very lightweight library to collect data into a buffer. So basically it enables you to ".print" into the buffer.
StreamLib.h by Juraj Andrassy
StreamLib.h inherits from Print.h and uses its own internal buffer. It collects data until it reaches the maximum size of the buffer. When the buffer is full it streams the content to the target. You can do a final "flush" when you want the output transmitted to your destination. I'm still using this library, for example I recommend it for Arduino Ethernet clients.
NoiascaBufferPrint.h from this page
As written above, NoiascaBufferPrint.h doesn't inherit from Print.h - it uses copies of Print.h print/println methods to give the user the same API experience like the default Print.h. Each print/println was adopted to do a final flush. It's main purpose is to automize the flush after a (buffered) print.
Summary
NoiascaBufferPrint enables you to write your own hardware drivers with the common print interface without the need of a "character-by-character" transmission of each single write. It can improve performance of data transmissions.