fbpixel
Etiquetas: , , , ,

Estabelecer uma comunicação entre diversos dispositivos pode ser prático, sobretudo em projectos de domótica. Uma técnica empregada com frequência é o protocolo I2C (ou TWI). O protocolo I2C é um método que permite conectar diversas placas mestres e diversas placas escravas, de modo a comunicar até 128 dispositivos. Ele possibilita conexões assíncronas entre diversos componentes, para compartilhar informações por meio de um “barramento comum”.  Já falamos anteriormente sobre a comunicação pela porta serial (dita UART), utilizada para enviar o código ao Arduino através de um computador ou para conectar dois dispositivos em particular via Bluetooth.

Obs.: Vale observar que a comunicação I2C é originalmente concebida para a comunicação entre placas. Assim, ela não é adaptada à comunicação à distância (>1m).

Equipamento

  • Computador
  • Arduino UNO (2 ou mais)
  • Cabos jumper M/M (3 vezes a quantidade de placas)

Esquema de ligação do barramento I2C entre placas Arduino

O protocolo I2C também possibilita estabelecer comunicação entre diferentes sistemas (sensores, ecrã LCD, Raspberry Pi, etc.). Um exemplo interessante é a comunicação entre diversas placas Arduino. Para isso, é preciso escrever ao menos dois programas, um para a placa mestre e outro para as placas escravas.

A comunicação I2C é definida por um barramento de dois fios (também chamado de TWI, Two Wire Interface) e um endereço. Os pinos utilizados pela comunicação I2C normalmente são fixos para cada dispositivo. Há um pino para o envio dos dados (SDA Serial Data Line) e outro para o relógio de sincronização (SLC Serial Clock Line).

Pinos I2C/TWI:

  • Uno, Ethernet A4 (SDA), A5 (SCL)
  • Mega2560 20 (SDA), 21 (SCL)
  • Leonardo 2 (SDA), 3 (SCL)
  • Due 20 (SDA), 21 (SCL), SDA1, SCL1

No exemplo abaixo, utilizamos uma placa Arduino Uno, e portanto os pinos A4 e A5.

Para que as duas placas comuniquem-se entre si, é preciso ligá-las corretamente (A4 com A4 e A5 com A5) e não esquecer de conectar as terras (GND) conforme indicado no esquema a seguir.

Atenção: Se os pinos A4 e A5 forem conectados aos pinos de uma placa não alimentada, o código irá travar no momento da transmissão.

connection-i2c-arduino Gerir vários Arduinos com um barramento I2C

Em geral, uma das placas envia as informações (Writer) e uma outra as recebe (Reader).

Código para configuração do barramento I2C

A biblioteca Wire.h permite definir a comunicação serial no barramento I2C com facilidade. As funções são similares às da biblioteca Serial.

  • Wire.begin() permite configurar o endereço do dispositivo. O argumento da função pode ser vazio para os dispositivos mestres.
  • Wire.write() permite o envio de bytes.
  • Wire.requestFrom() gerencia a função de recepção de solicitações
  • Wire.beginTransmission() inicia a transmissão de dados e define o receptor.
  • Wire.endTransmission termina a transmissão de dados
  • Wire.onRequest() gerencia a função de recepção de solicitações
  • Wire.onRecieve() gerencia a função de recepção de dados

Código da placa Mestre

#include <Wire.h>

# define I2C_SLAVE1_ADDRESS 11
# define I2C_SLAVE2_ADDRESS 12

#define PAYLOAD_SIZE 2

int n = 0;

void setup()
{
  Wire.begin();
  Serial.begin(9600);

  Serial.println(F("-------------------------------------I am the Master"));
  delay(1000);

  //Request value of n to slave
  Wire.requestFrom(I2C_SLAVE1_ADDRESS, 1);
  n = Wire.read();
  Serial.print(F("recieved value : "));
  Serial.println(n);

  //Send value 12 to slave
  Wire.beginTransmission(I2C_SLAVE1_ADDRESS);
  Wire.write(12);
  Serial.print(F("sending value : "));
  Serial.println(12);
  Wire.endTransmission();

  Serial.print(" ");

  //Request value of n to slave after change
  Wire.requestFrom(I2C_SLAVE1_ADDRESS, 1);
  n = Wire.read();
  Serial.print(F(" new recieved value : "));
  Serial.println(n);
}


void loop()
{
  delay(100);
}

Código da placa Escrava

#include <Wire.h>

# define I2C_SLAVE_ADDRESS 11 // 12 pour l'esclave 2 et ainsi de suite

#define PAYLOAD_SIZE 2

void setup()
{
  Wire.begin(I2C_SLAVE_ADDRESS);
  Serial.begin(9600);
  Serial.println("-------------------------------------I am Slave1");
  delay(1000);
  Wire.onRequest(requestEvents);
  Wire.onReceive(receiveEvents);
}

void loop() {}

int n = 0;

void requestEvents()
{
  Serial.println(F("---> recieved request"));
  Serial.print(F("sending value : "));
  Serial.println(n);
  Wire.write(n);
}

void receiveEvents(int numBytes)
{
  Serial.println(F("---> recieved events"));
  n = Wire.read();
  Serial.print(numBytes);
  Serial.println(F("bytes recieved"));
  Serial.print(F("recieved value : "));
  Serial.println(n);
}



Abra o monitor serial das placas escravas antes do monitor da placa mestre.

No monitor serial da placa mestre:

communication-i2c-maitre Gerir vários Arduinos com um barramento I2C

No monitor serial da placa Escrava 1:

communication-i2c-esclave Gerir vários Arduinos com um barramento I2C

Podemos observar que as duas placas trocam informações. É muito simples estender este exemplo para diversas placas Arduino (Leonardo, Mini, etc.), adaptando-se as ligações e o endereço do componente no código escravo.

.

Código para identificar os periféricos conectados ao barramento I2C

Um bom teste para saber se os seus dispositivos comunicam-se bem entre si é utilizar o código abaixo (I2CScanner), que retorna os endereços de todos os dispositivos conectados à placa mestre.

#include <Wire.h>


void setup()
{
  Wire.begin();
  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println(F("\nI2C Scanner"));
}


void loop()
{
  byte error, address;
  int nDevices;

  Serial.println(F("Scanning..."));

  nDevices = 0;
  for (address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();


    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error == 4)
    {
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0)
    Serial.println(F("No I2C devices found\n"));
  else
    Serial.println(F("done\n"));

  delay(5000);           // wait 5 seconds for next scan
}

Se tiver qualquer dificuldade para estabelecer uma comunicação I2C entre diferentes dispositivos, deixe um comentário abaixo ou contacte-nos.

Fontes