By dave | June 25, 2017

When writing Arduino libraries that need quite a few IO pins, it can be difficult to decide if the library should use an IO expansion device such as the 8574 chip, or just use Arduino pins directly. This library is designed to easily solve both cases at once, for the vast majority of cases.

The ubiquitous LiquidCrystal library; which almost all Arduino developers will have come across at some point, has two versions LiquidCrystal and LiquidCrystalI2C. If a similar abstraction method had been used, there would be no need for two versions. I’m not saying anything bad about the library; more just pointing out an observation as I use it a lot.

How it works

This library abstracts away the access to read and write to pins, such that there is an implementation that works with Arduino pins direct and another that uses an 8574 IO expander. Other chips could easily be added in the future and I’d happily accept pull requests or patches!

When using Arduino pins mode, there is very little overhead, it just calls directly out to Arduino underlying functions. However, for i2c you need to understand what the library is doing. As there is an overhead to sending things over the wire, rather than sending each bit change one at a time, the library waits for a runLoop() call, and then sends a write command if needed followed by a read.

Although I’ve packaged this as a library, I assume that some people may want to copy the header and source into their own library directly, and that’s absolutely fine, but please do rename the classes in that case to avoid conflicts and consider an attribution to me. Many libraries could make use of this code, so I’ve put a very open and permissive license on it: Apache 2.0; which allows commercial use.

Getting the source

This library is hosted on our github account, it comes complete with a working example showing both modes of operation.

Git hub library page [].

To use the library, get the latest source as an zip and expand it into your Arduino libraries directory.

Trying out the IO abstraction library

In all cases include the library

#include <BasicIoAbstraction.h>

To create an instance for direct Arduino pins, simply:

BasicIoAbstraction* pins = ioUsingArduino();

To create an instance for an 8574 IO expander provide the i2c address and include the Wire library:

#include <Wire.h>

BasicIoAbstraction* pins = ioFrom8754(0x20);

To configure input and output pins use the library instead of the usual pinMode call. Use standard Arduino constants for INPUT and OUTPUT

pins->pinDirection(0, INPUT);
pins->pinDirection(6, OUTPUT);

In the main loop read and write values using the library. For the Arduino version, the pin number is used, for i2c 8574 it is the bit 0-7 of the port.

uint8_t switchValue = pins->readValue(0);
pins->writeValue(6, switchValue);

// this is absolutely needed on the IO exapnder, it makes it send the i2c command


Reducing overhead

As the old saying goes, it’s better to work smarter than harder (although it’s often said at inappropriate times). Rather than polling for a switch transition, why not use the interrupt support available on the 8574 chip, and only read from it when needed.

If you are bit blasting at a very high frequency or using PWM, you’re better off using Arduino pins directly.

Overhead of using I2C / virtual tables

There are few main sources of overhead in this library. For most cases, these should not pose a problem:

  1. it uses an abstract class with virtual method calls, AVR chips have no loop unravelling so cannot optimise out the jump.
  2. If you are using i2c, there is the obvious overhead of sending commands to the chip.
  3. There is an extra object, albeit small.

As far as I can tell, it uses no RAM beyond the object creation, the virtual method table usage is all in flash memory, so that should not be a problem for most devices. If the library is constantly setting and testing bits thousands of times a second, then this library is probably not appropriate; but I’d argue that in that case neither is using an IO expander.

comments powered by Disqus
We use cookies to analyse traffic and to personalise content and adverts. Our social buttons may also use cookies.