SkillAgentSearch skills...

IoAbstraction

Rotary encoders, fully debounced switches, EEPROM support on Arduino and mbed - direct and over I2C

Install / Use

/learn @TcMenu/IoAbstraction
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

IoAbstraction for Arduino and mbed summary

PlatformIO Test License: Apache 2.0 GitHub release davetcc JSC TechMinds

TcMenu organisation made this framework available for you to use. It takes significant effort to keep all our libraries current and working on a wide range of boards. Please consider making at least a one off donation via the sponsor button if you find it useful. In forks, please keep text to here intact.

This library provides several useful extensions that make programming Arduino / mbed for non-trivial apps simpler. There are many different practical and familiar examples packaged with it in the examples folder. Below I cover each of the main functions briefly with a link to more detailed documentation. The API is almost identical between Arduino and mbed making it easier to port between the two.

Documentation and questions

Along with ths quick start guide and the examples also see:

Community questions can be asked in the discussions section of this repo, or using the Arduino forum. We generally answer most community questions but the responses will not be timely. Before posting into the community make sure you've recreated the problem in a simple sketch, and please consider making at least a one time donation (see links further up):

<a href="https://www.buymeacoffee.com/davetcc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>

Installation for Arduino IDE

This library is available in library manager on both Arduino and PlatformIO, this is the best choice for most people. It should automatically install the dependencies, TaskManagerIO and SimpleCollections. If for some reason it does not install the dependency, please also install it manually. It is highly recommended that you install the libraries using your library manager.

Installation for PlatformIO (Arduino or mbed)

Use the platformIO library manager to get the library. It's called 'IoAbstraction'. It should automatically include "TaskManagerIO" and "SimpleCollections" as it's a dependency.

This library is based on TaskManagerIO and SimpleCollections

Take a look at the TaskManagerIO repo for more information about how task manager works, this library relies heavily on task manager.

Also, this library uses SimpleCollections within switches and a few other areas.

BasicIoAbstraction - Arduino like interface to pins, PCF8574, PCF8575, MCP23017, AW9523 and shift registers.

Lets you choose to use Arduino pins, shift register Input/Output, I2C PCF8574/PCF8575, AW9523, and MCP23017 in an inter-changable way. Use it in your sketch to treat shift registers or i2c expanders like pins. There's even an abstraction that can combine together Arduino pins and one or more other expander! See the documentation (link further up) for more details.

If you are building a library and want it to work with either Arduino pins, shift registers or an IO expander for IO, then this library is probably a good starting point.

A simple example:

If we want to use the i2c wire based ioFrom8574 we must include the wire header file

#include <IoAbstractionWire.h>

At the global level (outside of any function) we create an i2c expander on address 0x20:

PCF8574IoAbstraction io8574(0x20, 0);
PCF8574IoAbstraction io8575(address, interruptPin, wireInstance, mode16Bit, invertedLogic);

If you use an I2C variant, you must initialise wire before using

Wire.begin();  

Or for Arduino pins instead simply call the following:

internalDigitalDevice()

You can convert any Io-device object into an IoAbstractionRef as follows

IoAbstractionRef myRef = asIoRef(io8574);

And lastly for DfRobot LCD shield input we use (requires library V1.3.2 at least):

DfRobotInputAbstraction dfRobotKeys(dfRobotAvrRanges); // or dfRobotV1AvrRanges as appropriate

To configure pin direction on any expander, you use the following, although some devices are read or write only, you should still call pinMode:

ioExpander.pinMode(0, INPUT);

And then later we read from it, in this case as we are doing a single read, use the 'S' version of the method as it removes the need to call the sync method. The only limitation is we must synchronize the device state. This allows us to be efficient where possible, setting several pins, syncing and then reading pins.

int valueRead = ioDeviceDigitalReadS(ioExpander, 0); // read pin 0 on ioExpander

Let's now say we wanted to write one value and read two items on the same device, in this case we don't use the 'S' version of the method, because otherwise it would sync three times.

ioExpander.digitalWrite(outputPin, HIGH);
ioExpander.sync();
int read1 = ioExpander.digitalRead(inputPin1);
int read2 = ioExpander.digitalRead(inputPin2);

SwitchInput - buttons that are debounced with event based callbacks

This class provides an event based approach to handling switches and rotary encoders. It fully de-bounces encoders and switches before calling back your event handler, while handling press, release, repeat key and held down states. It is however important to make sure you have no long-running tasks, or you'll miss the delayed rise. Note that this component also uses task manager.

Before doing anything else, you must add taskManager's run loop to your loop method, and your loop method must not do any long delay calls.

void loop() {
	taskManager.runLoop();
}

Here's a simple example using a switch:

In setup we initialise it telling it to use arduino pins for IO, we could use shift registers or an i2c expander, and we also add a switch along with the event that should be:

// our next task is to initialise swtiches, do this BEFORE doing anything else with switches.
// We choose to initialise in poll everything (requires no interrupts), but there are other modes too:
// (SWITCHES_NO_POLLING - interrupt only) or (SWITCHES_POLL_KEYS_ONLY - encoders on interrupt) 
switches.init(boardIo, SWITCHES_POLL_EVERYTHING, true);

// NO_REPEAT is optional, sets the repeat interval in 100s of second.
switches.addSwitch(spinwheelClickPin, onClicked, NO_REPEAT); 

Then we create a function for onClicked, this will be called when the button is pressed:

void onClicked(uint8_t pin, bool heldDown) {
	// pin: the pin that was pressed
    // heldDown: if the button has been held down
}

It is also possible to use initialiseInterrupt instead of initialise, when using this mode the library does not poll the switches unless a button is pressed down. Its use is interchangeable with initialise().

RotaryEncoder - hardware and button emulation, even available with i2c IO expanders

Switch input also fully supports rotary encoders (and simulated rotary encoders using up / down buttons). For this you just initialise the rotary encoder, and if you choose "poll everything" mode then PIN_A doesn't need an interrupt. However, in any other mode you must register an interrupt for PIN_A (even if the interrupt is shared). No debouncing is needed, the library will switch on pull up resistors too, but you may need lower resistance pull-ups will long wire runs.

For more see https://www.thecoderscorner.com/products/arduino-libraries/io-abstraction/arduino-switches-handled-as-events/.

First we must register the callback function that will be called when there's a change

void onEncoderChange(int newValue) {
	// do something with new value..
}

Then we create an encoder using one of the three examples below

// Example 1, Real encoder, we need to set up the pins that the encoder uses and provide a callback
setupRotaryEncoderWithInterrupt(encoderAPin, encoderBPin, onEncoderChange);

// Example 2, Up / down buttons acting like an encoder
setupUpDownButtonEncoder(pinUpBtn, pinDownBtn, onEncoderChange);

// Example 3, advanced usage, same as example 1, but with two encoders
HardwareRotaryEncoder* firstEncoder = new HardwareRotaryEncoder(firstEncoderAPin, firstEncoderBPin, onFirstEncoderChange);
HardwareRotaryEncoder* secondEncoder = new HardwareRotaryEncoder(secondEncoderAPin, secondEncoderBPin, onSecondEncoderChange);
switches.setEncoder(0, firstEncoder);
switches.setEncoder(1, secondEncoder);

For the vast majority of encoders there is no need to provide the encoder type. If you have a quarter cycle rotary encoder, there is an extra optional constructor parameter for the encoder type, thanks go to @ddd999 for this support. The options are listed below:

View on GitHub
GitHub Stars164
CategoryCustomer
Updated1mo ago
Forks35

Languages

C++

Security Score

100/100

Audited on Feb 16, 2026

No findings