Este bus de comunicación I2C (Inter Integrated circuits) que usan en Arduino fue diseñado por los ingenieros de Philips en los años 80 para simplificar él envió de datos entre dispositivos que soportan este protocolo de comunicación, además de poder tener varios maestros y esclavos conectados entre sí al mismo tiempo.
Para iniciar la comunicación entre dispositivos siempre la debe iniciar el maestro y no el esclavo, además este tipo de comunicación es síncrona y half dúplex.
El bus I2C en Arduino necesitamos generar dos señales, una señal es usada por los pulsos de reloj para sincronizar los cambios de flancos de bajada y subida, y la otra señal es de datos.
Señales usadas por el bus de comunicación I2C / TWI en Arduino
Como ya mencionamos este tipo de comunicación solo requiere dos señales, el SCL y SDA, esto lo hace muy fácil de implementar para nuestros proyectos.
SCL (Serial Clock Line).- Usada para determinar la frecuencia de reloj.
SDA (Serial Data Line).- Con esta línea de señal podemos intercambiar datos entre nuestros dispositivos.
Velocidades con el bus de comunicación I2C / TWI en Arduino
Las velocidades de transferencia de datos son relativamente bajas, pero podemos trabajar hasta los 3.4 Mbit/s con un bus bidireccional y 5 Mbit/s con un bus unidireccional.

Pines del bus I2C / TWI en Arduino
Lo más recomendable para saber que pin usar para este tipo de conexión es ir directamente a la hoja de datos del microcontrolador que utiliza nuestra placa de Arduino, pero la documentación de Arduino nos indica que pines usar y se lo detalla a continuación:

Conexión de maestro esclavo para la comunicación con el bus I2C / TWI en Arduino
En este tipo de conexión podemos tener hasta 128 dispositivos interconectados asignándole una dirección única a cada dispositivo, esto porque las direcciones trabajan con 7 bit lo que nos da un rango de 0 a 127 direcciones posible, el bit restante se usa para determinar si el dispositivo está leyendo o escribiendo, para tener una buena interconectividad se recomienda usar una resistencia pull-up, además como dijimos podemos trabajar con uno o varios maestros.
Conexion para tener varios esclavos con un bus I2C

Conexion para tener varios maestros con un bus I2C

Librería Wire para usar la comunicación I2C / TWI con Arduino
La librería Wire que viene preinstalado en el IDE de Arduino nos permite trabajar de manera fácil y ordenada con el bus de comunicación I2C, en la cabecera de nuestro código se inserta la siguiente instrucción: #include <Wire.h>, con esto ya estaremos listos para utilizar sus métodos de esta librería que se detallan a continuación:
Wire.begin()
Esta función nos permite inicializar la conexión como maestro o esclavo.
Si ponemos dirección del dispositivo se configurara como esclavo y en caso contrario será maestro el dispositivo.
Wire.requestFrom()
Esta función es usada para recuperar información desde el esclavo, tiene tres argumentos, en el primer argumento debemos colocar la dirección del dispositivo esclavo, en el segundo argumento debemos colocar la cantidad de bits que solicitaremos, y en el tercer argumento colocamos una booleano “TRUE” que nos permitirá terminar la conexión después de la solicitud o “FALSE” para mantener la conexión.
Wire.beginTransmission(dirección)
Inicia la transmisión de datos con una dirección de 7 bits del dispositivo como argumento.
Wire.endTransmission()
Termina la transmisión de datos, los bytes en cola en la función write() seran enviadas, también podemos definir un argumento booleano “True” para liberar el bus y “false” para seguir usando el bus.
Wire.write()
Envía datos desde el maestro al esclavo, puede tener un argumento donde puede ser un byte o una cadena, también podemos enviar datos con respectiva longitud y para esto usaremos dos argumentos.
Wire.available()
Esta función nos permite determinar si existen bytes para ser leídos.
Wire.read()
Es usada para leer los bytes enviados.
Wire.setClock(frecuencia del reloj)
Es usada para colocar la frecuencia de reloj y poder trabajar con la velocidad que necesitemos según los modos ya mencionados anteriormente, esto dependerá si soporta o no el dispositivo que vayamos a usar, para esto debemos hacer uso de sus hojas de datos para saber si es posible trabajar con estas velocidades.
Wire.onReceive(función)
Con esta función podemos hacer referencia a otra función para realizar alguna acción cuando recibamos alguna transmisión de datos dese el maestro.
Wire.onRequest(funcion)
Es usada para responder con algún mensaje al maestro cuando lo requiera, el mensaje lo podemos programar en una función propia y colocarla como argumento.