Passing Software I2C streams by Reference to Libraries

It's not pretty but we've come up with a way to make I2C based libraries even more flexible. Now you can pass a software I2C port into a library!

Favorited Favorite 0

We write a lot of libraries for I2C based devices. And we try really hard to genericize the library so that you can use our library with Wire, but also with Wire1 or Wire2 if you’ve got a platform that has additional I2C hardware (such as the SparkFun SAMD21 boards or the Teensy). But what about SoftwareWire?

There’s been some really great advancements with software, also known as bit-banged, I2C libraries. These work well but can we write a library that can inherit a TwoWire stream such as this:

mySensor.begin(Wire1);

and do the same for a SoftwareWire stream such as this:

mySensor.begin(myWire);

It turns out it’s possible but not pretty. Please understand, I am not an advanced programmer. I know enough to write a library but when it comes to classes and C++ inheritance I am very much at a loss.

Here is my attempt at writing a genericized library for both hardware and software I2C. The .begin call is overloaded with TwoWire and SoftwareWire references. The low level I2C commands are selected based upon which .begin function was called. It compiles, and best of all, it works! The down side is that it uses duplicate code. It also requires the user to have the SoftwareWire library installed before any example sketch will compile. There’s got to be a better way.

So here’s a shout out to all you gurus: is there a way to pass a reference or pointer of a software-based I2C stream to a library in a more elegant way?

Short of a better solution, we’ll be trying out the above implementation in some of our newer I2C libraries. You can see our first attempt in the SparkFun BME280 Library.


Update!

Here’s the new code. We’ve discovered a slightly better way to do this with templates. It makes it so the user doesn’t have to have Software I2C library installed, and removes duplicate code, but it does add a slight complexity to the instantiation of the class. For hardware I2C it looks like this:

myClass <TwoWire> mySensor; //Use for hardware I2C

For software I2C it looks like this:

myClass <SoftwareWire> mySensor; //Use for software I2C

Not too bad!

Here’s the main sketch:

//Use for hardware I2C
#include <Wire.h>

//Use for software I2C
//#include <SoftwareWire.h> //SoftwareWire by Testato. Installed from library manager.
//SoftwareWire myWire(6, 7); //SDA, SCL

#include "Generic_I2C_Library.h"
myClass <TwoWire> mySensor; //Use for hardware I2C
//myClass <SoftwareWire> mySensor; //Use for software I2C

void setup()
{
  Serial.begin(9600);
  Serial.println("Example using software I2C and hardware I2C");

  byte result;

  //Softwareware I2C
  //myWire.begin();
  //mySensor.begin(myWire);

  //Hardware
  Wire.begin();
  mySensor.begin();

  result = mySensor.readRegister(0xD0); //This reads the chip ID register of the BME280: https://www.sparkfun.com/products/14348
  Serial.print("Result: 0x");
  Serial.println(result, HEX);
}


Comments 0 comments