Protocolo I2C – Comunicação entre Arduinos

É indiscutível que os protocolos de comunicação são alguns dos elementos mais importantes de um sistema dotado de dispositivos que possuem a necessidade de interagir entre si para que um determinado resultado possa ser obtido, pois, estes possibilitam a troca de informações entre os mesmos através do estabelecimento de regras, padrões e modelos que visam permitir a conexão entre os dispositivos citados. Neste tutorial, você aprenderá sobre um protocolo de comunicação suportado pelas placas Arduino e também por diversos módulos para Arduino, o protocolo de comunicação I2C. Além de apresentá-lo, iremos utilizar o mesmo para estabelecer uma comunicação entre 2 placas Arduino.

[toc]

O protocolo de comunicação I2C

O modo de funcionamento do protocolo I2C é baseado na interação entre elementos seguindo a hierarquia mestre/escravo, ou seja, quando temos vários dispositivos se comunicando segundo esta premissa, pelo menos um destes deve atuar como mestre e os demais serão escravos. A função do mestre consiste em realizar a coordenação de toda a comunicação, pois, ele tem a capacidade de enviar e requisitar informações aos escravos existentes na estrutura de comunicação, os quais, devem responder às requisições citadas.

A estrutura na qual o protocolo I2C atua é uma estrutura de barramento, que por sua vez, consiste em um arranjo em que todos os elementos encontram-se conectados a um ramal principal.

 

 

Na figura acima pode-se ver um modelo simplificado da estrutura de barramento citada anteriormente. Note que, neste arranjo, o barramento de comunicação I2C pode ser dividido em dois barramentos. Um deles é denominado SDA (Serial Data), o qual, é responsável pela troca de dados entre os dispositivos do arranjo e o outro barramento, denominado SCL (Serial Clock), possui a função de sincronizar os dispositivos e garantir a confiabilidade do sistema.

Na prática, como o protocolo I2C funciona?

Segundo a literatura, podemos ter a presença de até 127 dispositivos escravos anexados no barramento citado, no entanto, algumas fontes ressaltam que este valor, embora fornecido pela literatura oficial, é apenas teórico, alegando que, na prática, é possível utilizar apenas 112 dispositivos em um barramento I2C. Isso se deve o fato de que dos 128 endereços possíveis, 16 estão reservados, os quais vão de 0x00 a 0x07 e 0x78 a 0x7F, em outras palavras, os endereços possíveis estão localizados no intervalo de 0x08 a 0x77.

Exemplo prático de barramento I2C
Exemplo prático de barramento I2C

A vantagem da utilização deste tipo de estrutura é nítida quando levamos em conta o número de pinos utilizados pelos elementos e por consequência, a quantidade de fios, além de promover uma organização maior do conjunto quando temos muitos dispositivos interagindo entre si.

Endereço

Para que o mestre converse com cada escravo ele precisa saber qual o endereço do escravo a ser contactado. É importante frisar que o endereço de cada integrante de um barramento I2C deve ser único.

Geralmente, cada módulo possui um endereço padrão que em alguns casos pode ser alterado por algumas opções.

É muito comum que haja mais de um módulo, mesmo com funcionalidades totalmente diferentes, com o mesmo endereço padrão. Quando for assim, pesquise se é possível e como mudar o endereço de pelo menos um deles.

No tutorial Display LCD 20×4 e LCD 16×2 com Adaptador I2C, mostramos que existem módulo I2C para LCD que permitem que o endereço seja alterado dentro de 8 opções. Clique aqui para entender mais sobre o endereço do módulo I2C para LCD.

Comunicação entre Arduinos

Você pode conectar ao barramento diversos Arduinos. Tal como os módulos que suportam I2C, o Arduino também precisa ter um endereço cujo o qual você definirá via software.

Nesse tutorial aprenderemos como criar uma comunicação entre dois Arduinos usando I2C. Tal aplicação pode gerá infinitas opções de projetos práticos, podendo integrar varias placas Arduino e módulos compatíveis com o protocolo I2C para montar um sistema de automação.


Mãos à obra – Utilizando um Arduino Micro para fazer um Arduino UNO acionar um led 

Nesta seção iremos demonstrar todos os passos que você deve seguir para aprender a utilizar o protocolo I2C.

Componentes necessários

Para reproduzir este projeto, você irá precisar dos seguintes componentes:

Montando o projeto

 

Hardware utilizado com 2 placas Arduino comunicando através do protocolo I2C, um mestre e outro escravo
Hardware utilizado.

 

Na montagem deste hardware devemos garantir que os pinos SDA, SCL e GND de ambos os dispositivos estejam respectivamente conectados entre si. No Arduino UNO, os pinos SDA e SCL são os pinos analógicos A4 e A5 respectivamente, ao passo que, no Arduino Micro, estes são os pinos D2 e D3.

Programando

Conforme apresentado no tópico anterior, o projeto a ser desenvolvido neste tutorial utiliza duas placas Arduino, portanto, teremos a presença de dois códigos para serem gravados em ambas as placas. Primeiramente, apresentamos o código a ser gravado no Arduino Micro, que por sua vez, será o mestre do barramento

#include <Wire.h>

bool estado_LED;

void setup() {
  Wire.begin();
}

void loop() {
  Wire.beginTransmission(0x08); 
  wire.write(estado_LED);            
  Wire.endTransmission();
  
  estado_LED = !estadoLED;   

  delay(1000);
}

Em seguida, apresentamos o código a ser gravado no Arduino UNO, que em nosso projeto, se comportará como escravo.

#include <Wire.h>

void setup() {
  Wire.begin(0x08);                
  Wire.onReceive(receiveEvent);     
  pinMode(4,OUTPUT);     
}

void loop() {
  delay(100);
}


void receiveEvent(int leitura) {
  
  bool estado = Wire.read();    // receive byte as an integer

  if (estado == 1){
    digitalWrite(4,HIGH);
  }
  else{
    digitalWrite(4,LOW);
  }
}

 


Entendendo a fundo

Software – Arduino Micro (Mestre)

Neste momento, iremos demonstrar o passo a passo como funciona o código que deve ser gravado no Arduino Micro, que por sua vez, atuará como mestre no barramento

– Incluindo a biblioteca que será utilizada

Primeiramente, devemos incluir a biblioteca que será utilizada no código, portanto, através da diretiva #include, dizemos ao Arduino Micro que iremos utilizar a biblioteca Wire.h, responsável por conter as funções necessárias para gerenciar a comunicação entre os dispositivos através do protocolo I2C.

#include <Wire.h>

– Declarando a variável do projeto

Em seguida, declaramos uma variável booleana, isto é, que pode armazenar apenas dois valores (0 ou 1) , com o intuito de que esta seja responsável por conter a informação que será enviada ao Arduino UNO através do protocolo I2C para que o LED seja acionado.

bool estado_LED;

– Inicializando a comunicação através do protocolo I2C

Dentro da função Setup() utilizamos a sentença Wire.begin() para que a comunicação através do protocolo I2C seja iniciada. Como este dispositivo será o elemento mestre, não é necessário adicionar parâmetro algum à função citada (veremos adiante como proceder quando o dispositivo se comportar como escravo).

void setup() {
  Wire.begin();
}

– Iniciando a transmissão de dados

Será dentro da função loop() que iremos proceder com a transmissão de dados entre as placas Arduino. O primeiro passo que deve ser realizado para que uma informação possa ser enviada de um dispositivo mestre para um dispositivo escravo é a inicialização da transmissão com o elemento-alvo. Para isto, utilizamos a sentença Wire.beginTransmission(), tendo como parâmetro, o endereço do escravo com o qual queremos que o mestre se comunique (na configuração do dispositivo escravo veremos como pode-se definir o endereço do mesmo).

Wire.beginTransmission(0x08);

– Enviando os dados desejados

Para enviar os dados para o escravo definido pelo endereço determinado na utilização da função anterior devemos utilizar a sentença Wire.write(). O parâmetro desta função é justamente a informação que nós queremos que o mestre envie para o escravo, neste caso, o valor armazenado na variável estado_LED.

wire.write(estado_LED);

– Finalizando a transmissão

Após o envio da informação, finalizamos a transmissão utlilizando a sentença Wire.endTransmission().

Wire.endTransmission();

– Atualização da variável estado_LED

Por fim, atualizamos a variável estado_LED fazendo com que o seu valor seja o inverso do que era anteriormente, ou seja, caso seu valor seja 0, na próxima iteração (lembrando que o conteúdo da função loop() é executado repetidamente) ele deverá ser 1. Além disso, utilizamos a função delay() para que este envio de informações aconteça a cada 1 segundo.

estado_LED = !estadoLED;
delay(1000);

Veja como ficou nossa função loop():

void loop() {
  Wire.beginTransmission(0x07); 
  wire.write(estado_LED);            
  Wire.endTransmission();
  
  estado_LED = !estadoLED;   

  delay(1000);
}

Software – Arduino UNO (Escravo)

Agora iremos demonstrar o passo a passo como funciona o código que deve ser gravado no Arduino UNO, que por sua vez, atuará como escravo no barramento

– Incluindo a biblioteca que será utilizada

Assim como no caso anterior, devemos incluir a biblioteca que será utilizada no código, portanto, através da diretiva #include, dizemos ao Arduino Micro que iremos utilizar a biblioteca Wire.h, responsável por conter as funções necessárias para gerenciar a comunicação entre os dispositivos através do protocolo I2C.

#include <Wire.h>

– Definindo as configurações iniciais

Dentro da função setup() utilizamos a sentença Wire.begin() para inicializar a comunicação do Arduino UNO através do protocolo I2C, no entanto, desta vez, utilizamos o valor 0x07 como parâmetro, em outras palavras, quando fazemos isto, estamos dizendo que a placa Arduino em questão estará presente no barramento como escravo e que o endereço do mesmo será 0x07.

 Wire.begin(0x08);

Além disso, através da sentença Wire.onRecieve() determinamos qual função deverá ser executada quando chegar alguma informação proveniente do elemento-mestre do barramento. Neste caso, quando ocorrer a situação prevista, a função chamada será a receiveEvent() (note que esta função pode ter qualquer nome, utilizamos este somente pelo fato de que na literatura oficial esta é denominada desta forma na maioria das vezes).

Wire.onReceive(receiveEvent);

Por último, determinando que o pino 4 do Arduino UNO atue como uma saída digital através da função pinMode().

 pinMode(4,OUTPUT);

Veja como ficou nossa função setup()

void setup() {
  Wire.begin(0x07);                
  Wire.onReceive(receiveEvent);   
  pinMode(4,OUTPUT);     
}

– Lendo os dados recebidos

Em um primeiro momento, devemos ressaltar que não foi necessário utilizar nenhum procedimento dentro da função loop(), pois, o tratamento dos dados recebido será demonstrado posteriormente. Sendo assim, utilizamos apenas a função delay() em virtude de este procedimento ser aconselhado nos exemplos oficiais do Arduino

void loop() {
  delay(100);
}

No final do código apresentamos a função receiveEvent(), que por sua vez, será chamada sempre que houver alguma informação proveniente do mestre, chegando. Nesta função utilizamos a sentença Wire.read() para obter o valor relativo ao estado do led (enviado pelo mestre) e armazená-lo na variável estado, de modo que, conforme o conteúdo da mesma, optamos através de uma função if() se devemos acender ou apagar o led.

void receiveEvent(int leitura) 
{  
  bool estado = Wire.read();   

  if ( estado == 1){
    digitalWrite(4,HIGH);
  }
  else{
    digitalWrite(4,LOW);
  }
}

Considerações finais

Neste tutorial, demonstramos como você pode utilizar o protocolo de comunicação I2C para fazer com que duas placas Arduino troquem informações entre si. Este foi apenas um conteúdo para que você tenha uma noção de como das os primeiros passos com o protocolo citado, portanto, esperamos que continue nos acompanhando e sinta-se à vontade para nos dar sugestões, críticas ou elogios. Lembre-se de deixar suas dúvidas nos comentários abaixo.

Privacy Preference Center