Chris--A / PrintEx

An extension to the Arduino Print library, and much, much more...
GNU General Public License v3.0
61 stars 16 forks source link

PrintEx Library for Arduino Version 1.1.10

Written by: Christopher Andrews.
Copyright: 2013(GString)-2016(PrintEx), Christopher Andrews, Released under GNU GPLv3 licence (Feel free to contact me via the issues if you would like to discuss a different licence).

Supported Platforms:
Basic Arduino Arduino Zero Arduino Due ESP8266
Teensy 2 upto 3.5 including LC ChipKit UNO32

About

This library is the descendant of a library I wrote called GString. This library allows extending any Stream or Print derived library with feature rich printing and formatting capabilities. You can directly use streaming (in & out), printf, chainable interfaces and repitition. You can even create a library which provides the enhanced capabilities by default. Or alongside Stream for a bidirectional implementation.

The printf implementation found in this library is unique. It has been built directly on top of the Arduino Print library rather than as a separate code base simply called from within. All the features found in printf use a feature already implemented in Print. This means floating point support is actually using Print::print( float );. It also contains additional functionality like support for EEPROM/PROGMEM data, repetition and chaining calls.

This tool set also replaces the globally available version of sprintf with a version providing all the features of this library. Included in this package is a selection of helper objects I have created over the years. These include tools for validation, security, stability, and diagnostics.

A sample of Arduino libraries which can be extended are as follows:

Contents:

A limited number of features require C++11. The basic Arduino's now have this support in IDE versions 1.6.6 and above. The Arduino Zero & ESP8266 already has this enabled. If you are using an old IDE there is some info here explaining how to enable C++11. However if you are attempting to use an IDE below 1.5.8 you will have to upgrade to make use of the additional features.

To extend an already existing object like Serial, or EthernetClient you'll need to make use of either StreamEx or PrintEx.

Note: This section does not apply to streaming functionality, it is available by default (see the next section for more info).

If you need to use any functions specific to the object you are using (not found in Print or Stream) you will have to use the real object. For example, Serial implements begin() which is not part of Stream:

#include <PrintEx.h>

StreamEx mySerial = Serial;

void setup(){
  Serial.begin(9600);
  mySerial.printf("The compile time & date is: %s, %s", __TIME__, __DATE__);
}

void loop() {}

2. Streaming (in/out).

NOTE: Streaming is a C++11 only feature, you can enable it manually or wait for the IDE version 1.6.6 where is has been enabled by default.

Streaming functionality is built into the PrintEx library, however you do not need to extend your object to use it (this will be done automatically). You can use any Print or Stream based object directly. Of course, a Print library does not have read capabilities and can only stream out data.

The benefit of streaming is, the code produced is smaller than the equivalent written using multiple print()/println() statements.

#include <PrintEx.h>

using namespace ios;

void setup(){

  Serial.begin( 9600 );

  //Stream out formatted data.
  Serial << "A hexidecimal number: " << hex << 43981 << endl;

  //Read two objects from Serial.
  int a;
  float f;
  Serial >> a >> f;
}

void loop(){}

Built in manipulators are listed below. If you do not include the namespace ios you will need to prefix these manipulators accordingly: ios::hex.

Manipulator Description
bin Sets an output stream to print integers in binary.
oct Sets an output stream to print integers in octal format.
dec Sets an output stream to print integers in base 10.
hex Sets an output stream to print integers in hexidecimal base.
precision Takes a single parameter: Sets the output stream to print floating point data with a certain precision (decimal places).
endl Prints a new line.
repeat Accepts two parameters: the item to print, and how many times to repeat. The item can be a character or string.

Stream buffers can be used to read data directly, for instance, you can stream a string directly into EEPROM:

EString e( 0, 10 );  //Create a buffer starting at EEPROM cell 0, with 10 bytes length.
Serial >> e;

An alternative method for streaming data out uses the function printx (only available from extended interfaces C++11 Only!). It uses the same manipulators above:

PrintEx printer = Serial;
printer.printx( "A hexidecimal number: ", hex, 4398, endl );

3. Using chainable functions.

Every interface using this library incorporates an additional set of functions specifically designed for creating a chain like syntax. For example a series of print()/println() calls can be replaced with a chain.

The functions provided are as follows:

Member Description
concat(...) This functions exactly the same as print() however it does not return a count of bytes printed, but a reference to the calling object. This allows a chain of calls to be written.
concatln(...) This is the println() equivalent of concat().
repeat(char,count) Writes a character count times to the output.
repeatln(char,count) This is almost the same as repeat() however it also appends a new line at the end of the repeated character (\r\n).
repeat(str,repeatCount) Writes a string (str)repeatCount` times to the output. The string must be null terminated.
repeatln(str,repeatCount) Similar to repeat(str, repeatCount), just appends a new line at the end.
repeat(str,size,repeatCount) Writes a string repeatCount times to the output. The length of the string is specified by size. This allows writing part of a string or one that is not null terminated.
repeatln(str,size,repeatCount) Similar to repeat(str, size, repeatCount), just appends a new line at the end.

Here is the basic idea behind chaining. Typically you'd be limited to writing code like this:

Serial.print( "ADC0 value: " );
Serial.println( analogRead(A0), DEC );

Now, when using a PrintEx based object you can replace the code with:

mySerial.concat( "ADC0 value: " ).concatln( analogRead(A0), DEC );

The usefulness of these functions is determined by your coding preference. Only repeat() and repeatln() are providing new functionality in terms of actual printing.

#include <PrintEx.h>

StreamEx serial = Serial;

void setup(){
  Serial.begin(9600);

  serial.repeatln( '=', 20 )
        .concat("Chainable functions test: ")
        .concatln(3.1415f, 3)
        .repeat( '=', 20 );
}

void loop() {}

4. printf formatting.

This library has a custom printf method for use with all interfaces found in this library. It is not as complete as a standard implementation, however it does support some custom features specific to Arduino. Basic support for the precision parameter is available, however it only affects floating point data. To request further implementation, please open an issue here.

Formatting options use the following syntax:
%[flags][width][.precision][length]specifier

Each element and their set of options is described in the tables below.

#include <PrintEx.h>

StreamEx mySerial = Serial;

void setup() {
  Serial.begin(9600);
  mySerial.printf( "%20n\nFirst printf use!\n%20n", '=', '=' );
}

void loop() {}

Helpers & Tools

All of these objects have the PrintEx functionality built in, there is no need to use PrintEx or StreamEx with these.

The helpers and tools documentation is still under construction. This is just an overview of what is available.

Object Description
BufferedPrinter Useful for buffering output to send all at once, or to prevent sending 'packets' or transfers that are too long for the interface.
DualPrinter Allows calling multiple Print interfaces through a single object.
Base64Encoder Any input this object receives is converted using Base64 encoding and written to its assigned Print interface.
URIEncoder Anything printed to this object will be URI encoded based on strictness. It supports URL encoding, HTML forms, and encoding the entire stream.
CRCStream This object calculates a running CRC for the input and output data streams of a Stream object associated with it.
RxTxCoutner This object keeps track of how much data passes through its interfaces.
NullStream A data stream with origins unknown.
PrintAdapter Converts a Print object into a Stream object.

Core Interfaces

The objects listed in this section provide the core functionality for all interfaces inherting PrintExtension.

Type Description
PrintEx This object provides an easy method of enhancing other Print based objects with the capabilities provided by PrintExtension.
StreamEx This object provides an easy method of enhancing other Stream based objects with the capabilities provided by PrintExtension while maintaining the bidirectional interface.
GString This object provides printing and formatting capabilities for blocks of memory (SRAM). The object can be passed to other Print functions.
EString This is the EEPROM equivalent of GString. This essentially allows formatted printing of strings to the EEPROM. It also allows other Print based classes to print EEPROM data easily.
PString This is a PROGMEM read-only version of GString. It allows printing of flash based strings.
PrintExtension This is a core interface for PrintEx. It provides the formatting features such as concat() and printf(). It also includes OStream which provides streaming capabilites (output only).
StreamExtension This core library encapsulates the functionality found in the PrintExtension interface, however it also provides an IStream interface allowing streaming into arbitrary elements.
NonStreamingIO This interface provides an extension to the Print class. It allows IO capabilities for derived objects that may not be streams. As in, the data printed to the object is still available for use.

Custom Configuration.

For normal use this section is not necessary. However the library can be tweaked if needed.

The available customizations can be used by defining one, or a combination of the options listed below. They can be defined either in the PrintExtension.h file, or passed as a compiler option using -D. Unfortunately defining these in your sketch will not have any effect.

Global library flags.

These settings affect all library functions that use the features described.

Define Action if defined
PRINTEX_NO_SPRINTF Do not override the global sprintf with a version supporting all the features present in PrintEx.
PRINTEX_LARGE_COUNTER Use a 16-bit value for print counters, instead of 8 bits (how many characters printed). If you do not care about the return count of print functions, leave the setting as is. This will allow more efficient code, however writes larger than 255 characters will have incorrect counts returned..

printf specific flags.

These settings only affect the printf implementation. To avoid inclusion outside of the printf/sprintf implementation, simply do not use the PrintEx function providing the feature (The compiler will remove unused code).

Define Action if defined
PRINTEX_NO_WIDTH_PARAM The width parameter must be specified inline and cannot be set to * specifying an additional input for the width.
PRINTEX_NO_PRECISION_PARAM Do not use precision handling (currently only affects floating point resolution).
PRINTEX_NO_PROGMEM Do not include PROGMEM functionality (%p).
PRINTEX_NO_EEPROM Do not include EEPROM functionality (%r).
PRINTEX_NO_FLOATING_POINT Do not include support for floating point data (%f).
PRINTEX_NO_REPEAT Do not include character repeat functionality (%n).
PRINTEX_NO_ERROR_CONDITION Do not include error handling (Error is printed on bad inputs/failed operation).

Special Thanks

Some people who have helped develop ideas & proposals.