Skip to content
A software I2C implementation to create multiple I2C buses using GPIO lines
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Multi BitBang I2C library
Copyright (c) 2019 BitBank Software, Inc.
Written by Larry Bank (
Project started 1/1/2018

The purpose of this code is to provide a simple C library which can bit-bang the I2C protocol on multiple GPIO pins on any system. The I2C protocol doesn't require any special functionality of the pins beyond standard GPIO features. The reason I wrote it was for getting easy access to I2C devices on various microcontrollers that don't necessarily have exposed I2C interfaces. This has come in handy on a variety of projects including AVR, ESP32, and nRF5 micrcontrollers.

The pin access functions can be wrapper functions for the native versions (e.g. on the nRF5 SDK) On AVR micros, the digitalWrite/digitalRead/pinMode functions are somewhat slow because they check the pin numbers against tables and do other tasks. This library includes logic to speed that up. By specifying pin numbers as the port name + bit, the library will run considerably faster on AVR microcontrollers. For example, On the Arduino Uno (ATmega328P), I/O pin 9 is actually I/O Port B, bit 1. To use the direct pin method, you would specify the pin number as 0xB1.

Start by initializing the library with the desired pin numbers for SDA/SCL along with the desired clock frequency. These are passed as lists (one set for each I2C bus). The SCL (clock) lines can be shared, so for example, 4 unique I2C buses can be created from only 5 GPIO Pins. Frequencies above 400Khz are possible, but not necessarily accurate. Luckily I2C devices don't really care about the exact clock frequency, only that the signals are stable within the given periods.

Instead of exposing functions to start and stop I2C transactions, I decided to make it simpler by providing composite functions that hide the details of I2C protocol. For scanning the I2C bus for devices, I provide the Multi_I2CScan() function which returns a bitmap (16 bytes x 8 bits) with a bit set for every device it finds. Call it like this:

unsigned char ucMap[16];
uint8_t bus;

// scan 4 buses
for (bus=0; bus<4; bus++)
Multi_I2CScan(iBus, ucMap);
//display found devices from map

For reading and writing data to the I2C device, use the following functions:

Multi_I2CRead(uint8_t bus, uint8_t addr, uint8_t *pu8Data, int iLength);
Multi_I2CReadRegister(uint8_t bus, uint8_t addr, uint8_t u8Register, uint8_t *pData, int iLen);
Multi_I2CWrite(uint8_t bus, uint8_t addr, uint8_t *pData, int iLen);

If you find this code useful, please consider buying me a cup of coffee

You can’t perform that action at this time.