I2Cwrapper library for easy implementation of I2C target devices

Something to think about:
If you really want to make this a full all encompassing wrapper, then you cannot assume that the name of the "wire" library header is called Wire.h nor can you assume that the "wire" object is called Wire.

i.e. suppose someone wants to use this on a platform that supports multiple i2c buses using say Wire and Wire2 and wants to use the Wire2 bus for the wrapped object?
or wants to use something like SoftwareWire instead of the Wire library
or the wire library object is not called Wire
or the wire library header file is not called Wire.h
or the wire library Wire object class is not TwoWire

I had a similar dilemma for a LCD library that used i2c.
I supported it using a C++ templated class.
If you haven't looked at C++ templates, templates can do some magical things.
With templated classes all the code must be in the .h file with no .cpp file.

For this library, it looks like it would require that the I2CWrapper class become a templated class.
It would need to be handed the class/name and object name of the "wire" object as template paramaters when declaring the I2CWrapper class object.

Newer versions of C++ support an auto type as well as supporting default template parameters that could eliminate having to pass in the wire object class as a template parameter and also provide some defaults for backward compatibility when no template parameters are used which would be ideal, but I ran into two issues when trying to do this.

  1. Not all Arduino platform cores are using new enough gcc tools for this support
  2. The Arduino.cc AVR platform forces the compiler to run in an older mode and thereby disables the support for this.

My solution was to ALWAYS pass in both the wire object class and the wire object as template parameters. This also means that it cannot be backward compatible with object declarations that are not passing in any template parameters.
This is very much not ideal but it does allow the template to work and adjust to any/all environments.

In your case it would change the object declaration to something like this:
I2Cwrapper<decltype(WIRE_OBJECT_NAME), WIRE_OBJECT> wrapper(i2cAddress);

you can then create a reference to the proper wire object within the object.

In my case
I declared the object in the sketch as:

hd44780_AIP31068<decltype(Wire), Wire> lcd(I2C_ADDR);

There are two template parameters.
Wire is the name of the wire object being used and the decltype() will present the class of the object.

Within the templated class header file, I declared the class as:

template <class T_WIRE_OBJ, T_WIRE_OBJ& WIRE_OBJ> class hd44780_AIP31068 : public hd44780 
{

and declared a private object reference within the template to reference the "wire" object as:

T_WIRE_OBJ& _wire = WIRE_OBJ; // wire object to use for instance

referenced the "wire" objectin the templated class as:

_wire.endTransmission();

It is nice as it allows it to work on with any "wire" compatible library and any wire object name, but it does require that the two templated parameters always be passed in so it is a bit "ugly".

--- bill