Printable: The class you didn’t know existed in Arduino and that you won’t be able to stop using

Printable: The class you didn’t know existed in Arduino and that you won’t be able to stop using

Imagine you have a class Clock (that represents … well, clocks) that works perfectly, but sometimes it needs to print to the serial port (Serial), other times to an LCD display (lcd), and other times to custom hardware, as usual. How would you do it?

  • 3 or more versions of the same class?
  • 3 different classes?
  • A huge switch that covers all uses?
  • Indiscriminately using getter methods to repeat the same task over and over again?

If you have already faced this problem, or are working on a similar project, then I am sure the class Printable will be of interest to you.

The class Printable provides an easy, fast, and safe way to add printing functionality to your classes in different scenarios, like the ones I set out above, without duplicating your code.

Assuming the class Clock is already working, you could do this in a project:

Clock serial_clock( 12, 0, 0 );
serial_clock.printTo( Serial ); // Output (in a console): 12:00:00

While in another project you would do the following without modifying a single instruction in the class Clock and without passing weird arguments that don’t scale well:

Clock lcd_clock( 12, 0, 0 );
lcd_clock.printTo( lcd ); // Output (in a LCD): 12:00:00

In certain cases, where you have both Serial and enabled lcd enabled you might be able to print both at the same time:

Clock a_clock( 12, 0, 0 );
a_clock.printTo( Serial ); // Output (in a console): 12:00:00
a_clock.printTo( lcd );    // Output (in a LCD): 12:00:00

Finally (a bit more advanced), if you wrote your own class MyAmazingDisplay (which inherits from the class Print), you could do the following:

MyAmazingDisplay mad();
Clock my_clock( 12, 0, 0 );
my_clock.printTo( mad ); // Output (in your amazing display): 12:00:00

As you can see, the magic happens in the .printTo() class method Printable. Later, when I explain how to use the class, you’ll notice that you’ll also be using the old printing methods: print()println(), etc.

For reasons that I don’t understand, this class, despite its usefulness, is poorly documented and for that reason I decided to write a little about it.

So keep reading to learn about it and start using it in your projects.

Tabla de contenidos

Programming principles involved

(If you are in a hurry, or already know the concepts related to this class, you can skip to the Examples section.)

In the Printable class, a couple of the most popular principles in programming have been used: single responsibility and dependency injection principles.

Single Responsibility Principle

The first, Single Responsibility Principle, states that a class should do one thing, and do it well. In our example, the Clock class performs only the logic of any clock, without mixing in other responsibilities, such as printing.

Visit this link to learn more about this valuable programming principle.

Aren’t we supposed to see the time on a clock? Yes, but not always. However, as I pointed out above, one will want to print the time on different devices at different times and projects, and it is impossible for a class to know in advance all types of displays.

Dependency Injection Principle

The second principle involved, Dependency injection , also known as the Hollywood principle , applies the following methodology:

Don’t call us, we call you.

Hollywood principle

In the instruction:

serial_clock.printTo( Serial );

we are injecting a dependency (the “actor” Serial) that will eventually be called by the method printTo() (who acts as the “movie producer”). In this example, the movie producer can call different actors: Seriallcd, etc, depending on the movie they are producing.

Visit this link to learn more about this valuable programming principle.

You can visit this article of mine in which I go into more depth about this principle (a bit advanced, but worth reading).

Code reuse

All programming principles, including the two above, promote code reuse. In this case, although it may not be obvious at this point, you will be reusing the Arduino infrastructure to print to different devices. For example, you will not have to write yourself functions to convert integers to text strings, but you will simply use the methods that you already know and that are part of both objects Serial and lcd. When I develop the idea later, you will see what I am talking about.

This is relevant because I’ve seen many examples on the Internet where Arduino programmers (including myself) write conversion functions over and over again for their devices, such as displays. Why do we need to do this when the platform already provides all these tools?

If we knew the platform a little more in depth we would know that we can use the methods print()print(), etc, to our advantage, and in exchange we only have to write a function that writes one character at a time to our devices.

A few days ago I started to investigate how to use the HT1622 chip (to control liquid crystal displays) since the Chinese documentation is in Chinese and leaves a lot to be desired. I found an Arduino code on Github where the chip driver uses GPIO pins instead of the SPI serial interface; that is not important, since it did work for me as a proof of concept. The interesting thing is that the upper layer, the one that uses the GPIO-based driver, had functions to convert integers to character strings, since the driver writes letter by letter. By that time I already knew the function Printable and the hierarchy of the class Print (which I will write an article about later) and I started to think about why that programmer was reinventing the wheel, and why I had done it before knowing about the existence of both classes. And it was this reflection on the lack of knowledge of the platform that motivated me to write this article, which I hope will be useful to you.

To finish this section, I’ll show you a small fragment of the code I found so you can get an idea of ​​the problems we can get into by reinventing the wheel in every project we write. Can you imagine all the time the programmer wasted looking for and correcting bugs?

void HT1621::print(long num, const char* flags, int precision){
    if(num > 999999) // basic checks
        num = 999999; // clip into 999999
    if(num < -99999) // basic checks
        num = -99999; // clip into -99999

    char localbuffer[7]; //buffer to work within the function
    snprintf(localbuffer, 7, flags, num); // convert the decimal into string
    #ifdef _HTDEBUG
        Serial.begin(9600);
        Serial.print(localbuffer);
        Serial.print("\t");
    #endif

    // horrible handling but should get us working. needs refactor in next major
    if (precision > 0 && (num) < pow(10, precision)) {
        // we remove extra leading zeros
        for (int i = 0; i < (5 - precision); i++) {
            #ifdef _HTDEBUG
                Serial.print(localbuffer[1]);
            #endif // _HTDEBUG
            if(localbuffer[i+1] == '0' && localbuffer[i] != '-'){ // we remove only if there is another zero ahead AND if it's not a minus sign
                localbuffer[i] = ' ';

// etc ...

Clase Printable

The class Printable is a utility class, that is, it is not a class from which you can instantiate objects, but it will add the functionality to print, either to the serial port (Serial) or to a 16×2 LCD (lcd), or as I mentioned, to objects of classes that have inherited from the base class Print.

By its nature you cannot do the following with the class Printable:

Printable p; // Error
p.printTo();

Since it includes the pure virtual printTo() public method, this class becomes an abstract class (and that’s why you can’t instantiate objects of it):

virtual size_t printTo( Print& p ) const = 0;

As a client of the class Printable you will need to provide the code printTo() (I will show you later how to achieve this ). In terms of Object Oriented Programming , OOP, you will implement the method  printTo().

The argument p, which is a reference to the class Print, represents any object capable of printing, such as the Serial and objects lcd.

Namely, the class Print is the base class for the different classes that print in Arduino. In another article I will show you how to take advantage of this class for your own devices. For example, a class  myDisplay, which represents a display that you’ve built and which have the same operations as Serial and lcdprint()println(), etc, but without you having to reinvent the wheel.

The modifier const is a mandatory line of defense that indicates that the code implementing the method will printTo() will not modify the object p ; that is, within the method printTo()the object p is read-only. If your code somehow tries to modify p, then the compiler will let you know through a compilation error.

Finally, printTo() it returns the number of characters that have been written to the device. It is up to the client to use or not use this value (we never use it, although we should).

The class Printableis a mixin class .

Mixin classes

Mixin classes represent a software design pattern that promotes code reuse.

That is, mixin classes are base classes that add functionality to other classes through the mechanism of inheritance, but are in no way part of them in a traditional generalization hierarchy, nor can you instantiate objects of them.

In this case, the class Printable is a mixin class that is already programmed, but you could write your own mixin classes to reuse them in your projects or just for fun.

Using the Printable class

To use the class Printable (and in general any mixin class) your classes must inherit from it and implement the method printTo() (or, in general, all the pure virtual methods that the mixin class exposes).

The following code shows you what you need to do to embed the class Printable in the class Clock:

class Clock : public Printable // (1)
{
   private:
      // same as before...

   public:
      // same as before...

      virtual size_t printTo( Print& p ) const override; // (2)
};

size_t Clock::printTo( Print& p ) const // (3)
{
   if( this->hrs < 10 ) p.print( '0' );
   p.print( this->hrs );
   p.print( ":" );

   if( this->min < 10 ) p.print( '0' );
   p.print( this->min );
   p.print( ":" );

   if( this->sec < 10 ) p.print( '0' );
   p.println( this->sec );
}

From the above code you can observe 3 things:

  1. The class Clock inherits from the class Printable.
  2. You indicate ( override) that you are going to provide the code for the method printTo().
  3. You provide the code for the method printTo()you implement the method printTo(), in OOP terms).

Inside the method printTo() the object p is a reference to any instance (object) of some class that has inherited from Print, such as objects Serial and lcd. In other words, p represents a device capable of printing .

As a happy consequence of the above, the object p contains all the printing functions we are used to: print()println(), etc, which you can use to print whatever you want without any extra effort. How cool is that?

Code reuse

It is important for you to notice that by using the class Printable you will be able to print on known devices without writing a single line of code that has to do with printing, for example, you will not have to convert from string to integer, or from integer to string, or from real number to string, etc. The class Printable, inheriting from the class Print does it for you.

The ending a ble and curious names

The ending ble (ble in English, and able and ible in Spanish) means “to grant the ability to”:

  • “Printable” – having the ability to print.
  • “Serializable”, which has the ability to take binary data and convert it to a text stream to be transmitted over some serial channel.
  • “Switchable”, which has the ability to be in one of two states (on or off).
  • “Movable”, that can be moved.
  • Etc.

Officially, according to the Royal Spanish Academy ( RAE ), the ending “ -ble ” means:

suf. Forma adjetivos casi siempre deverbales. Indica posibilidad pasiva, es decir, capacidad o aptitud para recibir la acción del verbo. Si el verbo es de la primera conjugación, el sufijo toma la forma -able. Prorrogable. Si es de la segunda o tercera, toma la forma -ible. Reconocible, distinguible. Los derivados de verbos intransitivos o de sustantivos suelen tener valor activo. Agradable, servible.

RAE

In our example, we are adding the ability to print to the client class Clock. This relationship can result in very curious names, such as ClockPrintable, since it is customary to add the particle able to the names of our client classes:

  • Print -> Printable
  • Switch -> Switchable
  • Time -> Timeable (I have a simple Timeable mixin class that gives clients the ability to run for a certain ammount of time with minimal intervention from you: for example, a relay will deactivate after a some interval of time automatically. If you’re interested, let me know it in the comments.)

Now that we’ve grasped the most important concepts around the Printable class it is time to see a complete example.

Example

The following is the (almost) complete code for the class Clock. Also note that if you’re using an Arduino sketch (which you’re probably doing), then there’s no need to add the header file <Printable.h>, but I’m including it for sake of completeness.

#include "Printable.h"

class Clock : public Printable
{
   private:
      uint8_t hrs;
      uint8_t min;
      uint8_t sec;

   public:
      Clock( uint8_t _hrs, uint8_t _min, uint8_t _sec )
         : hrs{_hrs}, min{_min}, sec{_sec}
      { /* nothing*/ }

      // clock logic here...

      // magic happens here:
      virtual size_t printTo( Print& p ) const override
      {
         if( this->hrs < 10 ) p.print( '0' );
         p.print( this->hrs );
         p.print( ":" );

         if( this->min < 10 ) p.print( '0' );
         p.print( this->min );
         p.print( ":" );

         if( this->sec < 10 ) p.print( '0' );
         p.println( this->sec );
      }
};

Clock my_clock( 9, 30, 5 );

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

   // initialize the lcd here (if it's ever used)
}

void loop()
{
   // each second:
   my_clock.printTo( Serial );

   // if lcd has been properly initialized:
   // my_clock.printTo( lcd );
}

To keep the example simple and manageable, I’ve left out some parts that you can easily fill in. Despite those omissions, the code contains what we’re talking about today and it compiles.

On the other hand, you can certainly remove the code that refers to an LCD, since the purpose of having included it was so that you could see the flexibility and usefulness of this class.

NOTE: If you have been following my articles you will know that I don’t like sketches and when it’s time to program into the Arduino platform I’d rather prefer to program from the function main(), as the gods of programming expect from us. If you do the same, then you must include the header <Printable.h> in the declaration file of the client class (that is, in the “.h” file).

What do we take with us and what’s next?

Today we have seen a way to take advantage of the Arduino infrastructure and avoid duplicating code by using a very useful class.

We also saw how some universal programming principles are used in the Arduino platform and how we can also incorporate them into our own codes. I recommend that you read about these principles and start putting them into practice, your life as a programmer will take a 180-degree turn.

You are given the task of incorporating the class Printable into your projects and I am also given the task of writing an article about the classes Print and Stream, which as I mentioned, will allow our hardware to use the tools that the Arduino platform provides so that we avoid having to unravel the wheel in each project you start.

Addendum

I couldn’t leave without showing you the simplicity of the class Printable. You can find it in your /arduino/hardware/arduino/avr/cores/arduino/Printable.h installation:

#include <stdlib.h>

class Print;

/** The Printable class provides a way for new classes to allow themselves to be printed.
    By deriving from Printable and implementing the printTo method, it will then be possible
    for users to print out instances of this class by passing them into the usual
    Print::print and Print::println methods.
*/

class Printable
{
  public:
    virtual size_t printTo(Print& p) const = 0;
};

The class Print (mentioned in the third line) does the magic of handling integers, floats, strings, etc. I’m preparing an article about it. Keep tuned!


Tengo un pequeño curso gratuito sobre el sistema operativo en tiempo real FreeRTOS donde utilizo a Arduino como conejillo de indias. Puedes echarle una mirada en el siguiente enlace:

Índice del curso

Espero que esta entrada haya sido de tu interés. Si fue así podrías suscribirte a mi blog o comparte esta entrada con alguien que consideres que puede serle de ayuda.


Fco. Javier Rodríguez
Escrito por:

Fco. Javier Rodríguez

Soy Ingeniero Electrónico con 20+ años de experiencia en el diseño y desarrollo de productos electrónicos de consumo y a medida, y 12+ años como profesor. Egresado de la UNAM, también tengo el grado de Maestro en Ingeniería por la misma universidad. Mi perfil completo lo puede encontrar en: https://www.linkedin.com/in/fjrg76-dot-com/

Ver todas las entradas