Come collegare due Arduino usando il bus I2C.

Una caratteristica che reputo molto interessante è la possibilità di espandere Arduino tramite l’uso di shield. La maggior parte di queste schede aggiuntive sono però progettate per compiere una funzione specifica, non programmabile.
Esistono alcuni metodi che permettono di comunicare con altri dispositivi utilizzando come canale di trasmissione dati i protocolli seriali (I2C, SPI o RS232). Questo rende possibile demandare ad una scheda slave funzioni di elaborazione gravose, che non vogliamo far eseguire alla scheda master. Ho realizzato questo piccolo tutorial usando il bus I2C per creare uno scambio dati tra un Arduino ed un chip ATmega328 standalone.

Bus i2c

Il bus I2C, basandosi su due fili, non permette la comunicazione contemporanea tra Master e Slave. Lo scambio dati deve essere gestito dal Master tramite gli indirizzi (univoci) degli slave. Il flusso può essere sintetizzato in questo modo

  1. Il Master invia sul bus un bit di start
  2. Il Master invia sul bus l’indirizzo dello slave con cui vuole comunicare
  3. Il Master decide se scrivere o leggere dal dispositivo
  4. Lo Slave legge o scrive in base alla richiesta del Master

La libreria Wire dispone di tutte le funzioni necessarie alla realizzazione Master-Slave tra due schede Arduino. Lo schema del circuito che ho realizzato è visibile nella figura seguente.

Collegamento Master Slave tra due Arduino UNO

Come detto precedentemente lo schema si basa su Arduino Uno impiegato come Master e con un ATmega328 impiegato come slave.

Il codice permette di inviare un dato numerico allo slave, che provvederà ad incrementarlo per poi rispedirlo al Master.

//MASTER
#include <Wire.h>

byte x = 0;
byte num = 0;

void setup()
{
  //inizializzo la libreria Wire come Master
  Wire.begin();

  //init seiale
  Serial.begin(9600);
  //avviso che il programma è avviato
  Serial.println("START");

}

void loop()
{
  //invio sul bus I2C un byte al device
  //che ha come indirizzo il valore 0x04
  //start trasmissione
  Wire.beginTransmission(0x04);
  //invio un byte
  Wire.write(x);
  //fine trasmissione
  Wire.endTransmission();

  delayMicroseconds(500);

  //richiedo un byte allo slave che ha indirizzo 0x04
  Wire.requestFrom(0x04, 1);

  //attendo la disponibilità di dati sul bus i2c
  while(Wire.available())
  {
    //quando è presente un dato avvia
    //la lettura
    num = Wire.read();
  }

  //incrementa il valore del byte
  x++;

  //verifico che il byte letto dallo slave sia stato
  //incrementato
  if(num != x)
    Serial.println("ERRORE");

  delay(5);

}

Il codice seguente è relativo allo Slave

//SLAVE
#include <Wire.h>

byte x = 0;

void setup()
{
  //inizializzo la libreria
  //imposto l'indirizzo dello slave
  Wire.begin(0x04);

  //eventi per la ricezione del dato
  //e per la richiesta del dato
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}

void loop()
{
  //esegui qualcosa
  delay(1000);
}

void receiveEvent(int data)
{
 //questo evento viene generato quando sul bus
 //è presente un dato da leggere

 //eseguo la lettura
  x = Wire.read();

  //elaboro il dato letto
  x++;
}

void requestEvent()
{
  //questo evento viene generato quando il master
  //richiede ad uno specifico slave
  //una richiesta di dati

  //spedisco il dato al Master
  Wire.write(x);

}

I commenti presenti nel codice permettono di capire ciò che avviene tra i due dispositivi.

Di seguito uno screenshot dei dati scambiati sul bus

Scambio dati master slave su bus i2c

Di seguito invece la trasmissione del Master

Trasmissione i2c Master