Conhecendo o Protocolo I2C com Arduino
Olá, tudo bem? Hoje iniciaremos o nosso estudo sobre os protocolos de comunicação. Dessa forma, hoje vamos estar conhecendo o protocolo I2C com arduino. Isso porque a sua utilização é muito grande em projetos devido a sua facilidade tanto de programação quanto de conexões !
Sendo assim, hoje vamos nos aprofundar um pouco mais e conhecer o funcionamento do protocolo I2C usando o arduino e saber como ele trabalha para poder utilizá-lo em nossos projetos. Vamos nessa então?
Funcionamento do Protocolo I2C com Arduino
Criado pela empresa Phillips por volta dos anos 80, este protocolo foi criado com o propósito de padronizar (em outras palavras, facilitar) a comunicação entre diferentes dispositivos.
Devido a isso, ele necessita apenas de dois pinos (SDA – para dados, SCL – nosso clock) para poder conectar até 112 dispositivos para barramentos de 7 bits. É surreal, mas é possível de acontecer. Contudo, ele foi feito para distâncias curtas, e com uma velocidade média de operação (por volta de 400KHz).
Em virtude dessa sua facilidade de utilização, diversos componentes e módulos foram desenvolvidos voltados para este tipo de interface. Você pode encontrar Display LCD, RTCs entre outros. Tudo isso na loja Autocore, então para encontrá-los, só clicar no botão abaixo!
Funcionamento na vida real
Vamos conhecer o protocolo I2C com Arduino e entender como é na vida real. Bom, basicamente o protocolo I2C funciona com a ideia de hierarquia, onde temos o Mestre e seus Escravos. Assim, nosso arduino normalmente é o nosso mestre e todos os dispositivos e sensores conectados nele, os escravos. Como na imagem abaixo:
Entretanto, uma dúvida que surge é: como a comunicação entre mestre – escravo acontece? Para responder tal pergunta, é necessário conhecer os três tipos de comunicação de dados que existe, são els: Simplex, Half Duplex, Full Duplex. Que raios é isso? Relaxa e observe a imagem abaixo:
Basicamente, cada nome define como a comunicação irá ocorrer. Então, no primeiro temos apenas o envio de dados e pronto. No segundo, temos o envio e recebimento dos dois lados, porém, é preciso esperar um enviar para o outro responder. No último, ambos podem receber e enviar dados ao mesmo tempo.
Dessa forma, o protocolo I2C trabalha no modo Half-Duplex. Porém, visando não ocorrer esse conflito de dados, o dispositivo mestre do circuito é quem vai realizar a coordenação de toda a comunicação. Uma vez que ele, tem a capacidade de enviar e solicitar informações aos escravos do barramento.
Conhecendo sobre os endereços do I2C no Arduino
Pois bem, já conhecemos o modo de operação de nosso barramento, mas como saber quantos escravos eu tenho no barramento e como enviar dados para cada um individualmente? É nesse momento que entra os endereços!
O endereço tem como função informar onde tal objeto ou lugar se encontra. Um exemplo disso é o nosso endereço de casa. Tranquilo. Mas quando trabalhamos com o protocolo I2C, esse endereço é dado no formato hexadecimal, por exemplo #03F.
Mas, assim como o nosso endereço de casa, cada escravo do barramento, tem o seu próprio e único endereço. No entanto, tem muitos casos onde o endereço de dois módulos totalmente diferentes, são iguais. E ai?
Como resultado disso, precisamos recorrer a uma pesquisa prévia a respeito do sensor para saber se tem como fazer essa mudança de endereço soldando certos pinos dele. Observe a imagem abaixo:
Na imagem acima, temos um módulo I2C para Display LCD. Perceba que temos nele as conexões A0, A1, A2. Caso este módulo tenha o endereço de algum outro sensor do barramento, basta soldar e conectar os dois pinos do A0, ou A1 por exemplo.
Entretanto, vale ressaltar que não são todos módulos I2C que você conseguirá fazer essa alteração física, por isso é necessário a pesquisa prévia, beleza?
Envio de dados no protocolo I2C
Depois de já conhecido a forma como o mestre reconhece e envia os dados para os seus escravo no barramento, vamos entender como o dado é enviado e recebido dentro do circuito. Para isso, observe a imagem abaixo:
A transmissão inicia-se com um bit de start (S), enviado pelo mestre. Em seguida, ele envia o endereço físico do escravo (aquele hexadecimal que citei) e no bit LSB, o mestre informa o que ele quer fazer: Uma leitura (HIGH) ou uma escrita (LOW).
Após o escravo ser identificado pelo o endereço enviado no barramento, ele envia um bit de reconhecimento (ACK) deixando o seu pino de SDA LOW. Ou seja ele diz “estou aqui mestre, a seu dispor”.
Assim, o mestre inicia o envio de dados que é feito em duas partes. Primeiro é enviado 4 bits e espera-se o bit de confirmação do escravo para enviar os outros 3 bits de dados. Por fim, se o escravo receber tudo corretamente, ele permanece em LOW e o mestre manda a condição de STOP, caso contrário o pino de SDA muda para HIGH, alegando que não reconheceu o dado enviado.
Essa condição de START e STOP é gerada para que o barramento saiba se está tendo uma comunicação de dados naquele momento. Assim, para isso ocorrer, é preciso que os pinos de SDA e SCL do mestre sigam os estados da imagem abaixo:
Pinagem de I2C dos Arduinos
Então, agora que já aprendemos sobre o funcionamento do barramento I2C e como é feita a comunicação dentro dele, chegou a hora de aprender a como conectar o arduino (nosso mestre) aos outros escravos do circuito.
Para isso, temos que saber quais pinos do nosso arduino são SDA e SCL. Assim, é muito importante prestar atenção nesse ponto, pois esses pinos mudam para cada versão de arduino. Combinado?
Por exemplo, para encontrar as pinagem de seu arduino, basta fazer uma pesquisa rápida pelo termo “diagram pinout + modelo do Arduino”. Porém, na tabela abaixo você pode encontrar as pinagens para cada modelo de Arduino:
Modelo | SDA | SCL |
Arduino UNO | A4 | A5 |
Arduino MEGA | 44 | 43 |
Arduino DUE | 20 | 21 |
Arduino YÚN | 19 | 18 |
Arduino MICRO | 19 | 18 |
Arduino NANO | 27 | 28 |
Arduino PRO MINI | A4 | A5 |
Pontos importantes sobre o protocolo I2C
Alguns detalhes importantes que precisamos saber e lembrar na hora de trabalhar com protocolo I2C são:
- Pode haver atrasos: Isso porque como já vimos, o nosso barramento é do tipo Half Duplex. Dessa forma, quando o mestre enviar os dados (de comprimento de 1 byte) pode acontecer do escravo não conseguir trabalhar os dados recebidos, assim, ele pode forçar o mestre a entrar num estado de espera.
- Ligação Direta: Além disso, um outro ponto importante é com relação a sua ligação, diferente do protocolo UART (do nosso módulo Bluetooth) onde invertemos os pinos TX e RX. Aqui, não podemos fazer isso pois se trata de barramento, assim, o pino de SDA do mestre, precisa estar no pino SDA do escravo, o meu vale para o pino SCL. Beleza?
Componentes do Projeto
Agora, chegou a hora de montar o nosso circuito de hoje para testar todo o conhecimento adquirido. Para isso, separa os seguintes componentes:
Montagem do Circuito
Depois de separar todos os componentes da lista, realize a ligação de todos eles de acordo com a imagem do esquemático de ligação abaixo:
Código do Projeto
Dispositivo Metre
<code>/* ================================================================================================== Projeto: Treinando a comunicação I2C no Mestre Autor: Danilo Nogueira Data: 22/08/2019 Autor: Danilo Nogueira // ==================================================================================================*/ // --- Declaração Bibliotecas --- #include <Wire.h> // Incluindo a biblioteca para realizar a comunicação I2C bool LED_estado; // Variável que aramazena o estado no LED que o mestre irá enviar // ================================================================================================== // --- Definicando o void setup() --- void setup() { Wire.begin(); // Inicia a comunicação I2C } // ================================================================================================== // --- Definicando o void loop() --- void loop() { Wire.beginTransmission(0x08); // Estamos iniciando a comunicação com o escravo no endereço 0x08 Wire.write(LED_estado); // Estamos enviando para o escravo o estado do LED que queremos Wire.endTransmission(); // Encerrando a comunicação com o escravo LED_estado = !LED_estado; // Alteramos o estado do LED para o próximo envio de dados. delay(800); } // ================================================== FIM ===========================================</code>
Dispositivo Escravo
<code>/* ================================================================================================== Projeto: Treinando a comunicação I2C no Escravo Autor: Danilo Nogueira Data: 22/08/2019 Autor: Danilo Nogueira // ==================================================================================================*/ // --- Declaração Bibliotecas --- #include <Wire.h> bool LED_estado; // Variável que aramazena o estado no LED que o mestre irá enviar bool estado; int pino_LED = 4; // ================================================================================================== // --- Definindo void setup() --- void setup() { Wire.begin(0x08); // Iniciando a comunicação com o mestre através do endereço 0x08 Wire.onReceive(Dado_Recebido); // Quando o escravo receber algo no barramento, ele chama a função 'Dado_Recebido' pinMode(pino_LED,OUTPUT); // Colocando o LED como saida } // ================================================================================================== // --- Definindo void setup() --- void loop() { delay(100); } // ================================================================================================== // --- Definindo as funções criadas --- void Dado_Recebido(int leitura) { estado = Wire.read(); // Lê os dados eviados if (estado == 1){ digitalWrite(pino_LED,HIGH); } else{ digitalWrite(pino_LED,LOW); } } // ================================================ FIM ==============================================</code>
Explicação do Código
Em primeiro lugar, iremos começar pela definição da nossa biblioteca, a qual tem funções que gerenciam a comunicação entre dispositivos I2C. A biblioteca que usaremos será “Wire.h”, e chamaremos ela nos dois códigos através de:
<code>#include <Wire.h> // Incluindo a biblioteca para realizar a comunicação I2C</code>
Analisando Código Mestre
No código do nosso arduino mestre, temos a definição da variável booleana que guardará o estado do led e dentro do void setup(), já iniciamos a comunicação I2C através do comando Wire.begin():
<code>void setup() { Wire.begin(); // Inicia a comunicação I2C }</code>
Um detalhe importante que na função Wire.begin(), precisamos colocar o endereço do canal (ou barramento) de comunicação. Contudo, o mestre do circuito não necessita colocar, porém todos os escravos sim!
Logo em seguida, temos que dizer qual escravo iremos nos conectar e enviar os dados. Para isso utilizamos o comando “Wire.beginTransmission(endereço do escravo)”, este comando serve para basicamente especificar, dentro de todo o barramento, qual o escravo o mestre quer se comunicar.
Depois de especificar o escravo, está na hora de enviar os dados. Para isso, utilizamos a função “Wire.write(dados)”. Logo depois de enviar os dados, precisamos encerrar a comunicação para que uma nova seja criada como já falamos, assim, utilizamos a função “Wire.endTransmission()”.
<code>void loop() { Wire.beginTransmission(0x08); // Estamos iniciando a comunicação com o escravo no endereço 0x08 Wire.write(LED_estado); // Estamos enviando para o escravo o estado do LED que queremos Wire.endTransmission(); // Encerrando a comunicação com o escravo LED_estado = !LED_estado; // Alteramos o estado do LED para o próximo envio de dados. delay(800); }</code>
Analisando Código Escravo
Em nosso dispositivo escravo, começamos com a definição da nossa biblioteca e declaração das variáveis que iremos trabalhar. Uma para o estado do led, outra para analisar o valor enviado pelo mestre (0 e 1) e uma variável para dizer o pino do LED.
<code>bool LED_estado; // Variável que aramazena o estado no LED que o mestre irá enviar bool estado; int pino_LED = 4;</code>
Entrando no void setup(), temos alguns comandos importantes. Iniciamos com o “Wire.begin(endereço)”, e perceba que aqui já temos o endereço do Arduino.
Então, a ideia é a seguinte, cada escravo tem o número de seu “apartamento” (nosso endereço) que precisamos inicializar para dizer ao mestre que ele pode se comunicar. Assim, quando o mestre for enviar algo, ele irá colocar o endereço apenas no “Wire.beginTransmission” e não no “Wire.begin”. Beleza?
Em seguida temos o “Wire.onReceive(função)”, o qual verifica se algo foi enviado pelo barramento e pega esse dado e joga na função que estiver dentro dele. No nosso caso ele jogou o dado para a função criada chamada “Dado_Recebido”.
<code>void setup() { Wire.begin(0x08); // Iniciando a comunicação com o mestre através do endereço 0x08 Wire.onReceive(Dado_Recebido); // Quando o escravo receber algo no barramento, ele chama a função 'Dado_Recebido' pinMode(pino_LED,OUTPUT); // Colocando o LED como saida }</code>
No void loop(), temos apenas um delay() de segurança, o qual a documentação do Arduino aconselha colocar. Já no final do código, temos a criação da nossa função “Dado_Recebido”, que será chamada sempre que houver alguma informação enviada pelo mestre.
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.
<code>void Dado_Recebido(int leitura) { estado = Wire.read(); // Lê os dados eviados if (estado == 1){ digitalWrite(pino_LED,HIGH); } else{ digitalWrite(pino_LED,LOW); } }</code>
Resultado do Projeto
Confira abaixo o resultado da aplicação do nosso protocolo I2C em nosso canal do Youtube!
Mas e ai? Gostou do post? Caso tenha ficado algo incompleto para você, comente abaixo 📝
Dicas? Dúvidas? 🤔 Críticas? 😱 Só comentar abaixo! 👇
Forte abraço e compartilha se gostou! 🤘
Excelente Post. Me ajudou bastante no meu projeto!
Olá Ronan, obrigado pelo feedback!
Espero que venham muitos outros e qualquer coisa só chamar!
Grande abraço!
tem alguma forma deixa a comunicação sem fio? por exemplo tenho um arduino no quarto com sensor x toda vez que esse sensor mandar informação vai acender os led dos arduinos da sala e da cozinha.
Olá Renan, espero que esteja bem!
Já pensou na possibilidade de utilizar módulo bluetooth?
Como é um ambiente da casa, acredito que a comunicação deve ocorrer normalmente!
Temos um artigo explicando sobre ele caso tenha o interesse
[link]
Qualquer dúvida só escrever,
Abraços!
Boa noite, estou com dois sensores q utilizam o protocolo I2C: O BMP280 e o MPU6050. Há uma maneira de eu fazer um código para a leitura dos dois com apenas um arduíno?
Olá Laila, espero que esteja bem!
Você pode sim, fique atenta apenas ao endereço de cada um deles. Contudo pode utilizar os mesmos pinos!
Além disso, é necessário tomar cuidado com os resistores pull up dos módulos que você comprou!
Vou deixar dois links de referencia para você se basear neles!
[Entendendo o problema]
[Código do projeto]
Qualquer dúvida, por favor escrever!
Grande abraço e boa sorte nos projetos!
Parabéns pelo excelente trabalho.
Peço sua autorização para usar como base de apresentação do seu material em curso técnico
Gostei da descrição e diretividade.
Fico feliz sempre que encontro profissionais com interesse em compartilhar
Grande Abraço
Olá Luiz,
Obrigado pelo feedback e com toda certeza pode utilizar sim!
Um grande abraço,