Hoy os traigo una guía acerca de cómo hacer una comunicación I2C bidireccional con Arduino. Recientemente he comenzado a trastear con este protocolo debido a que me interesa para un proyecto que tengo en mente, y tras unas pequeñas pruebas he podido comprobar que se pueden comunicar entre dos Arduino maestros sin problemas.

Consideraciones previas

A pesar de ser posible comunicarse entre varios Arduino, hay que tener una serie de consideraciones antes de proceder para evitar problemas:

  • i2C es un protocolo de comunicación del tipo OAAT (One At A Time), lo cual quiere decir que sólo un master puede enviar datos a la vez.
  • Si haces mucho ruido enviando datos de continuo, es posible que interfieras con el correcto funcionamiento de los Arduino. Si por ejemplo pones un envío de datos en un loop de un Arduino sin poner delay, spamearas al otro y le impedirás ejecutar el hilo principal, deteniendo su programación.
  • Cada vez que recibes datos en el slave este sale del loop principal, por lo que es importante no cargar mucho la función de recepción, ya que retrasará la ejecución del código principal.
  • Cuanto más Masters mandando, más complejo es de gestionar el Bus i2C.

Mi intención por ejemplo es crear un registro automático de Slaves en el que cuando uno entra en el grupo y se enciende, este se conecta como Master y solicita al Master general un ID, y en cuando lo recibe se convierte en Slave con dicho ID. Esto no provocaría mucha carga en el Master general, y en el fondo el resto serían Slaves. El código lo pondré más adelante cuando consiga mi propósito, el cual incluye además persistencia en la ID recibida tras el apagado.

Que diferencia a un Master de un Slave

La principal y única diferencia entre un Master y un Slave es que el Master carece de ID, mientras que el Slave tiene un ID único en el bus.
En el momento en el que se le pone un ID a un Master, este adquiere la capacidad de recibir datos como si de un Slave se tratase. Es por esta razón que simplemente con darle un ID a los Masters, puedes comunicarte bidireccionalmente entre ellos.

Cómo se consigue la comunicación bidireccional

La comunicación bidireccional se consigue convirtiendo los dos Arduinos en Slaves. Para ello le tienes que asignar un ID a cada uno y ponerles una función que se lance cuando reciban datos.Al hacer ambos Slaves los pones en escucha para recibir datos, pero al no perder la capacidad de enviar, pueden enviar datos al otro Slave de forma alterna.

Código y Conexión

La conexión en si misma no tiene ningún misterio, ya que usa la misma conexión i2C:

Y en referencia al código, es el mismo y tan sólo cambia el ID del Arduino en i2C.
Los voy a poner por separado para poder poner textos diferentes, pero notaréis que no cambia (realmente cambio x por y).

#include <Wire.h>

void setup()
{
  // Los master pueden obviar este ID, pero al querer recibir datos, tendremos que ponerlo
  Wire.begin(0); // Se une al bus i2C con la ID #0
  Wire.onReceive(receiveEvent); // Función a ejecutar al recibir datos
  Serial.begin(9600);
}

byte x = 0;

void loop()
{
  Wire.beginTransmission(1); // enviar a Slave #1
  Wire.write("x is ");       // envía cinco bytes
  Wire.write(x);             // envía un byte  
  Wire.endTransmission();    // deja de enviar

  x++;
  delay(500);
}

// Esta función se ejecutará al recibir datos, lo cual provocará que se salga del loop principal.
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // hacemos loop por todos los bytes salvo el último
  {
    char c = Wire.read();    // recibe un byte como carácter
    Serial.print(c);         // imprime el carácter
  }
  int x = Wire.read();       // recibe el último byte como número
  Serial.println(x);         // imprime el número
}
#include <Wire.h>

void setup()
{
  // Los master pueden obviar este ID, pero al querer recibir datos, tendremos que ponerlo
  Wire.begin(1); // Se une al bus i2C con la ID #1
  Wire.onReceive(receiveEvent); // Función a ejecutar al recibir datos
  Serial.begin(9600);
}

byte y = 0;

void loop()
{
  Wire.beginTransmission(0); // enviar a Slave #0
  Wire.write("y is ");       // envía cinco bytes
  Wire.write(y);             // envía un byte  
  Wire.endTransmission();    // deja de enviar

  y++;
  delay(500);
}

// Esta función se ejecutará al recibir datos, lo cual provocará que se salga del loop principal.
void receiveEvent(int howMany)
{
  while(1 < Wire.available()) // hacemos loop por todos los bytes salvo el último
  {
    char c = Wire.read();    // recibe un byte como carácter
    Serial.print(c);         // imprime el carácter
  }
  int y = Wire.read();       // recibe el último byte como número
  Serial.println(y);         // imprime el número
}

Con estos ejemplos ya podremos comunicarnos entre los dos Arduinos de forma bidireccional:

Hasta aquí la guía de cómo realizar una comunicación I2C entre dos Arduino, de forma bidireccional. Espero que os haya servidor y como siempre, se aceptan comentarios y sugerencias.

¡Un saludo!

Daniel Carrasco

DevOps con varios años de experiencia, y arquitecto cloud con experiencia en Google Cloud Platform y Amazon Web Services. En sus ratos libres experimenta con Arduino y electrónica.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.