(full description and code for the libraries available on github - actually some parts of this section will be the same as the ones in the repo readme!)

While working with some NES Mini Controllers I started tinkering with the I2C protocol in order to connect them into some attiny85's micros I usually use on my projects (I managed to do something interesting here).

First I used a pure software implementation approach and made a bit-banging small driver to act as a primary device and being able to control multiple I2C devices I had (those controllers, some ssd1306 oled screens and so on); but then I started reading about the "native" AVR approach: the Two-Wire mode.

After more reading, poking and trying lots of external references (there're lots of I2C implementations out there: some of them worked, others didn't, another ones are more Arduino-intended, maybe a bunch of them are made on C++...) I wrote this pair of C basic libraries that allows me to manage the I2C protocol on my attiny85's both from primary and secondary sides.

Here's a simple demonstration that uses the example code and the three attiny85's mini devices I made for it:

The libraries

Introducing a couple of C-written libraries (primary and secondary) to allow I2C communication as a primary or secondary device!

Primary devices will perform the usual calls (as seen on multiple micros, Arduino-like devices, etc.): start transaction, send address, read/write some bytes and stop transaction.

Secondary devices may "listen" to write operations from the primary one (primary writes) or prepare some buffer to be "consumed" (primary reads).

(also a secondary one can perform both read and write operations, obviously!)

Please refer to the github repo to know more about the libs.

The wiring

The "hardware part" of all of this is a super standard proof of concept made of three different attiny85's built as independent devices and then hooked up to a common bus (there's a pdf with the schematic in the files section - or you can download it from here):

Needless to say the example code follows the default pin configuration I used, but it can be easily changed and adapted to different components, address values, commands...

TODOs, work in progress...

References

Here's a bunch of links I found useful when writing the libraries. Some of them pointed me in the right direction while from other examples I grab some core ideas (such as the state machine on the secondary devices):