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.
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:
No monitor serial da placa Escrava 1:
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
- Referência na biblioteca Wire do Arduino
- Nota sobre o conceito I2C Master Writer e Master Reader