SoftI2CMaster: Add I2C to any Arduino pins

[update 20150128: changed links to SoftI2CMaster project on Github]

Ever wanted to use any pair of pins for I2C on Arduino, not just the dedicated pins on Analog 4 & 5? Me too, so I made a quick little Arduino library called “SoftI2CMaster”, available in the “blinkm-projects” Googlecode repository.

Get it here: SoftI2CMaster on github

It’s still a work in progress, but it can write data pretty successfully and do it over longer cables than normal.

For the VIMBY/Scion Hackerspace Challenge, I created an array of BlinkM MaxM-powered accent lights for the device we made. Because the I2C cable was longer than a few feet, the normal Wire library that BlinkM_funcs.h uses to communicate with BlinkMs couldn’t be used. This is because the Wire library assumes a perfect bus. If there is any noise or other bus problems, the Wire library will currently lock up. For the SoftI2CMaster library, I wanted it to be very tolerant, even lazy, about bus problems and also have more tunable timing to let you slow the bus down. Of course, you still need pull-up resistors on the two lines. I’ve found using 2.2k resistors to be good.

The SoftI2CMaster API follows Wire’s API pretty closely:

  • SoftI2CMaster(sdaPin,sclPin) — create an new SoftI2CMaster for the two pins specified
  • beginTransmission(address) — begin sending data
  • write(data) — send some data (byte or byte arrays)
  • endTransmission() — stop sending data

In use it looks something like this:

#include "SoftI2CMaster.h"
const byte sdaPin = 7;
const byte sclPin = 6;
SoftI2CMaster i2c = SoftI2CMaster( sdaPin,sclPin );

i2c.beginTransmission( 9 );  // write to address 9
i2c.write('c');
i2c.write(255);
i2c.write(0);
i2c.write(200);
i2c.endTransmission();

There is a simple demo for BlinkMs that this library currently lives in. It’s called “BlinkMSoftI2CDemo” and shows off a simplified BlinkM_funcs called “BlinkM_funcs_soft.h“. The entirely of BlinkMSoftI2CDemo is shown below.

const byte sdaPin = 7;  // digital pin 7 wired to 'd' on BlinkM
const byte sclPin = 6;  // digital pin 6 wired to 'c' on BlinkM

#include "SoftI2CMaster.h"
SoftI2CMaster i2c = SoftI2CMaster( sdaPin,sclPin );

// must define "i2c" before including BlinkM_funcs_soft.h
#include "BlinkM_funcs_soft.h"

byte blinkm_addr = 9;

//
void setup()
{
  Serial.begin( 19200 );
  Serial.println("BlinkMSoftI2CDemo");

  BlinkM_off(0);

  for( int i=0; i< 100; i++ ) {  // flash the blinkms
    BlinkM_setRGB( blinkm_addr, 255,255,255 );
    delay(10);
    BlinkM_setRGB( blinkm_addr, 0,0,0 );
    delay(10);
  }
}

void loop()
{
  byte r = random(255);
  byte g = random(255);
  byte b = random(255);
  
  BlinkM_setRGB( blinkm_addr, r,g,b );
  delay(10);
  BlinkM_fadeToRGB( blinkm_addr, 0,0,0 );
  delay(1000);
}

void BlinkM_off(byte addr)
{
  BlinkM_stopScript( addr );
  BlinkM_setFadeSpeed(addr,20);
  BlinkM_setRGB(addr, 0,0,0 );
}

50 Replies to “SoftI2CMaster: Add I2C to any Arduino pins”

  1. Hi,

    i’m starting work with this shield https://www.sparkfun.com/products/12081
    for obtain humidity and temperature values.
    I ran this code

    #include
    #include “HTU21D.h”

    //Create an instance of the object
    HTU21D myHumidity;

    void setup()
    {
    Serial.begin(9600);
    Serial.println(“HTU21D Example!”);

    myHumidity.begin();
    }

    void loop()
    {
    float humd = myHumidity.readHumidity();
    float temp = myHumidity.readTemperature();

    Serial.print(“Time:”);
    Serial.print(millis());
    Serial.print(” Temperature:”);
    Serial.print(temp, 1);
    Serial.print(“C”);
    Serial.print(” Humidity:”);
    Serial.print(humd, 1);
    Serial.print(“%”);

    Serial.println();
    delay(1000);
    }

    Furthermore i get always the same value, 998.
    I think that means time out.
    How do i implement correctly this program to get correctly values?

    Best Regards
    Denis

  2. Hi Martin,
    I’m not sure what you’re referring to. The SoftI2CMaster takes care of setting the read/write bit for you (look at implementation of SoftI2CMaster::beginTransmission() and SoftI2CMaster::requestFrom(). Both of these you give the 7-bit I2C address (unshifted and without read/write bit). Also, there’s no variable or define used by SoftI2CMaster matching “I2C_WRITE” or “I2C_READ”.

    Could you give an example sketch using SoftI2CMaster showing what you mean?

  3. It might be worth mentioning a few things about using this library.

    You have to define or declare I2C_WRITE as being ‘0’ and I2C_READ as being ‘1’.
    (I don’t see that in the example sketches)

    i2c-adresses are given as 7-bit addresses
    If for example an OLED-display data-sheet says it has address 0x3C (7 bit: 0111100) you have to say to the library it has address 0x77 (01111000). The last bit then gets ored with I2C_WRITE or I2C_READ to make it a read/write operation.

    It took me ages to find that out……

  4. I have a quick question. And I am new to the Arduino so please pardon me if this is plain. But if I employ your SoftI2CMaster, will it take precedence over the existing I2C that the Uno does? In the infamous words of the original Highlander, can there be only one?

  5. Hi Kurt, thanks for the great piece of code and thanks for sharing this very useful library.
    However, I have run into the exact same trouble as jferguson. How can the code be modified to get the all the bytes from the register? It would be really helpful if you can provide a response.

    Also, hey jferguson, if you have come up with a solution kindly share it here.

    Thanks
    Jimi.

  6. Hi Tod,
    I found SoftwareI2C ron github and it has the traditional requestFrom (address, number of bytes to read) function. And it even works. it’s the handiwork of Luvvee who works for Seeed Technologies in Shanghai.

    this certainly is a great time to be alive. I was pretty sure i could add this to your library but expected it would take me a couple of weeks of fooling around. I’m not adept at new code.

    thanks for your own work.

    john

  7. Hi Tod,
    At your suggestion, I downloaded library. And the MMA84520 compiled

    Here is the part of the hardware I2C (Wire) code which I’m trying to replicate with SoftI2CMaster.
    It reads four bytes from register one, It appears that the Wire library “requestFrom” is able to specify how many bytes to read. And, of course, it works

    //send a request
    Wire.beginTransmission(HONEYWELL1); // "Hey, HONEYWELL @ 0x28! Message for you"
    Wire.write(1);  // send a bit asking for register one, the data register (as specified by the pdf)
    Wire.endTransmission(); // "Thanks, goodbye..."
    // now get the data from the sensor
    delay (20);
    
    Wire.requestFrom(HONEYWELL1, 4);
    while(Wire.available() == 0);
    byte a     = Wire.read(); // first received byte stored here ....Example bytes one: 00011001 10000000
    byte b     = Wire.read(); // second received byte stored here ....Example bytes two: 11100111 00000000
    byte c     = Wire.read(); // third received byte stored here
    byte d     = Wire.read(); // fourth received byte stored here
    
    Serial.println(a, BIN);
    Serial.println(b, BIN);
    Serial.println(c, BIN);
    Serial.println(d, BIN);
    

    This defective code (my inadequacy) returns only first byte of 4 byte register on each read.

    i2c.beginTransmission(HONEYWELL1); // HONEYWELL1 is 0x28 - i2c address of this sensor
    i2c.write(1);  // send a bit asking for register one, the data register (as specified by the pdf)
    i2c.endTransmission(); // "Thanks, goodbye..."
    // now get the data from the sensor
    delay (20);
    
    i2c.requestFrom(HONEYWELL1);
    byte a     = i2c.read(); // first received byte stored here ....Example bytes one: 00011001 10000000
    byte b     = i2c.read(); // second received byte stored here ....Example bytes two: 11100111 00000000
    byte c     = i2c.read(); // third received byte stored here
    byte d     = i2c.read(); // fourth received byte stored here
    i2c.endTransmission();
    

    So I need a suggestion on how to read each of the 4 bytes in this register. You’ll not I took out the While(Wire.available) line since there is no “available” function in the SoftI2CMaster linbrary.

    I have no idea whether this is a reasonable question, but I guess it comes down to needing to know how to read each of the 4 bytes in the register – could be basic, but I can’t figure it out.

    And many thanks for any help you can give me.

    FWIW, this is another one of those things where Honeywell led me to believe that their I2C pressure sensors were available with different I2C addresses. They weren’t. i thought of other ways to solve this, but they all involve a second arduino. Much better to get up to speed on SoftI2CMaster.

    and thanks again.

    john

  8. Hi John,
    This post is pretty old (over 4 years now!). Apologies for not updating it to point to the new location of SoftI2CMaster. I have just now.

    The current version of my SoftI2CMaster is on Github: https://github.com/todbot/SoftI2CMaster
    It’s a standard Arduino library that has two different examples showing how it’s used.

    I don’t know anything about any other versions of a software-based I2C library, so I can’t help you there.

  9. Tod, It looks as thought you have cooked up your own version of a SoftI2Cmaster. Not a bad idea, but for those of us struggling to migrate working i2c sketches to SoftI2CMaster find ourselves a bit perplexed by the 3 maybe 4 versions of this code that are out there, all listed under the same name.

    the most useful to we who are less skilled would be a version which accepts the same commands so that you can establish i2c as the proxy for SoftI2CMaster and then do a global substitution for Wire such that you’d get i2c.read instead of Wire.read and it would work.

    It looks like that is what you tried to do.

    By any chance do you have any sketch which actually complies using your library files? Sketches which are self contained?

    I can guess this isn’t simple.

    and thanks for your efforts?

    best,

    john

  10. Hi, I’m newer in I2C and Arduino Uno.

    I’m testing the first example described in this page, just sending the byte ‘c’.

    I’m trying to read this in another Arduino Uno, but I don’t understand how the READER I2C program will work.

    1- Could you please send me a simple example to READ something using I2C? (can be the original Arduino code for I2C (Wire Lib) or using I2CSoft).

    2- When I use I2CSoft, I put:

    i2c.beginTransmission( 9 ); // send to address 9

    This means that the Arduino READER must be set using the I2C Address 9? How Can I do it?

    my e-mail: nyckmaia@gmail.com

    Thank you,
    Nyck

  11. Hi Wai,
    Yes, you can use A4 & A5 pins, you can even reference them symbolically by using the pin names “A4” and “A5”.

  12. Can I test that the SoftI2CMaster is working with the rest of my code by using A4 and A5 pins? I think that A4 and A5 pins are represented by const byte 18 and 19 respectively.

    Thank you.

  13. Can I use SoftI2CMaster to creat two I2C to control two devices which have a same device address ? For example :
    #include “SoftI2CMaster.h”
    const byte sdaPin1 = 7;
    const byte sclPin1 = 6;
    const byte sdaPin2 =3;
    const byte sclPin2 =2;
    SoftI2CMaster i2c1 = SoftI2CMaster( sdaPin1,sclPin1 );
    SoftI2CMaster i2c2 = SoftI2CMaster( sdaPin2,sclPin2 );

  14. I am using your library to run a IMU digital combo board (6 degrees) with an adxl345 accel and ITG3200 gyro, on an Arduino LilyPad Simple board. The gyro works fine with your code but I am not able to read values from the accelerometer. I tested it on an Arduino Mega board with the Wire library and it worked fine. Can you please suggest what might be the problem? I really need help urgently…
    IMU board link – http://www.protocentral.com/breakout-boards/212-imu-digital-combo-board-6-degrees-of-freedom-itg3200adxl345.html?search_query=imu&results=2

  15. Does anyone have a simple example of this library working. e.g. See slave device and display text from slave device in serial monitor.

    Thanks!

    – John

  16. Great, you have resold the problem of white noise controlling tea5767 module both lcd i2c module on LCD refresh data, thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *