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. Todbot,
    I can, however for hardware assy i was hoping to be able to stack my boards together and minimize the wiring. This means that the SDA/SCL pins would line up with A0 and A1 instead of A4 and A5.
    The long term goal is to incorporate the two boards into one but that’s long term.

  2. Hi Ken,

    The “Adafruit_VL53L0X” library uses the Arduino “Wire” library (hardware I2C). It doesn’t have a provision to use different I2C hardware. Why can’t you use the regular I2C pins on your Arduino?

  3. Hello,
    I am trying to use the SoftI2CMaster to change the SCL/SDA pins to use A0 and A1 and not having any luck.
    I am using the VL54LOX Time Of Flight sensor with the following code:

    #include 
    const int sdaPin = A0; // Arduino GPIO 4 is physical pin 7 of ATtiny841.
    const int sclPin = A1; // Arduino GPIO 6 is physical pin 9 of ATtiny841.
    SoftI2CMaster i2c = SoftI2CMaster( sclPin, sdaPin, 0 );
    
    #define SERIAL_BAUD_RATE  9600
    
    
    #include "Adafruit_VL53L0X.h"
    Adafruit_VL53L0X lox = Adafruit_VL53L0X();
    int Avg = 0;
    
    
    
    void setup() {
      //Serial.begin(9600);
      Serial.begin(SERIAL_BAUD_RATE);
      // wait until serial port opens for native USB devices
      while (! Serial) {
        delay(1);
      }
      if (!lox.begin()) {
        Serial.println(F("Failed to boot VL53L0X"));
        while(1);
      }
    }
    void loop() {
      VL53L0X_RangingMeasurementData_t measure;
    
      for (int i=1; i <= 10; i++)
      {
        
        lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
        Avg += measure.RangeMilliMeter;
      }
      Avg = Avg / 10;
    
      if (measure.RangeStatus != 4) {  // phase failures have incorrect data
        if (Avg  9)&&(Avg  99){Serial.println(Avg);}
    
      } else {
        Serial.println("000");
      }
      Avg = 0;  
      delay(500);
    }
    

    Can you tell me if I am forgetting something? Can this even be done?

  4. Hi Erwin,
    You need to move the line “SoftI2CMaster i2c = SoftI2CMaster(sdaPin,sclPin);” to outside of setup() and make it a global variable. The “i2c” variable as you have it is only visible within the “setup()” function and the “loop()” function cannot see it. By making it a global, both functions can see it.

  5. Thanks for letting me benefit from your work,

    I try to connect – by using your SoftI2CMaster library – more than two MCP4725 12-Bit DAC’s to an Arduino Nano (ATmega328). As this chip only allows selection of I2C-addresses 62 or 63 I need either a Hardware MUX or your smart piece of software. I wrote a tiny assembly sketch (included in full hereafter) to verify proper library usage. However, my compiler (Arduino IDE 1.6.5) keeps saying
    ” i2c was not declared in this scope”.
    Hoping that my error strikes your eyes, I would appreciate your comment.
    Thanks and regards
    Erwin

    Following the test sketch as mentioned:

    // Test application for SoftI2CMaster library (https://todbot.com/blog/2010/09/25/softi2cmaster-add-i2c-to-any-arduino-pins/)
    // **************************************************************************************************************
    
    #include  
    
    // Constants will not change:
    const byte sdaPin = 4;
    const byte sclPin = 5;
    
    // Varables will change:
    int16_t       dac1_inc = 0;                           // signed 16bit variable for DAC-1 setting increments (0-4095)
    
    void setup()  {
      SoftI2CMaster i2c = SoftI2CMaster(sdaPin,sclPin);   // define i2c
      i2c.beginTransmission(62);                          // write to decice with I2C-address dec 62  
      Serial.begin (9600);                                // start Serial Monitor with 9600baud
      Serial.println("start");                            // to check functioning of Serial Monitor
      delay(1000);                                        // Idle for 1 Sec
    }
    
    
    void loop() {
      Serial.println("starting new loop");
      i2c.write(dac1_inc);                                // write variable dac1_inc to device with I2C-address dec 62
      dac1_inc ++;                                        // increment variable adc1_inc
      if (dac1_inc > 4000)  {                             // if variable dac1_inc > 4000
        i2c.endTransmission();                            // stop sending data to device with I2C-address dec 62
      }
       delay(3000);                                        // idle for 3sec 
    }
    
  6. Hi Waylander,
    Yes, you can use a software-based I2C Master like my SoftI2CMaster to use two normal pins for a duplicate I2C peripheral. You don’t even need to set it to another address, since you’ll have multiple independent I2C busses.

    What you can also do if you need a single I2C bus (e.g. you want to use hardware I2C), is you can wire the “address select” line of your peripheral to an output pin and then set it HIGH or LOW to set the address of the peripheral.

  7. Hi todbot,

    You’ve done fantastic job, I appreciate you willingly share this library.

    I have one question though: In my project I need to communicate with multiple i2c devices that can have just one of two possible addresses – either 0x31h or 0x32h as example. I will therefore need more than one i2c bus.
    I’ve found my solution in using i2c switch (multiplexer) IC PCA9548A or similar.

    However I was wondering if I can use software solution such as your library to create more than one i2c intefrace on the same Arduino (Mega 2560), and connect the slave devices in pairs?

    Please share your thoughts.

    Cheers!

    P.S. in your first example the line “i2c.send(…” should be “i2c.write(…” as send is not a valid command in the livrary.

  8. Hi from Spain mr. Todbot

    Could you tell me if it is compatible with Arduino Gemma?

    Nice job!

  9. Hi, plz. I want to program two i2C sensor named RTC and GY-87, on the same arduno. somebody can help me?

Leave a Reply

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