Faltam apenas

/Desenvolvimento

voltar
/Desenvolvimento

Comunicando o Arduino com o ESP8266 por I2C

PorPedro Minatel em

Já fui questionado inúmeras vezes a respeito de como fazer a comunicação entre o ESP8266 e o Arduíno por serial (UART). Este questionamento é muito comum quando o ESP por algum motivo não consegue atender todos os requisitos de hardware, seja por falta de IO, entrada analógica ou até mesmo um projeto legado.

O que ocorre é que em alguns casos, a comunicação por UART não é viável, como por exemplo quando a UART é utilizada para debug ou outra aplicação.

Tendo em vista que a UART não possa ser utilizada no seu projeto, vou mostrar como usar a comunicação por I2C para enviar comandos entre o ESP8266 e o Arduino.

Comunicação I2C

A interface I2C, ou Inter-Integrated Circuit, é um tipo de comunicação serial, bi-direcional e por barramento, muito utilizada em diversos equipamentos, sensores, displays, memorias e muitos outros periféricos. Usualmente em dispositivos de baixa velocidade e pode operar em vários modos, como master-slave, multi-slave, multi-master entre outros.

O I2C possui duas linhas, uma de dados (SDA) e outra de clock (SCL), . Para o barramento funcionar, é necessário resistores, usualmente de 10k de pull-up na linha de clock e data.

Para saber detalhes de como funciona o I2C, mas recomendo a leitura desse material:

Utilização

Essa técnica pode ser muito útil em casos onde já exista um projeto Arduino pronto, ou um projeto com o ESP em que mais portas digitais ou entradas analógicas são necessárias. Isso pode reduzir o custo de um projeto ou simplificá-lo.

Comunicação master-slave ou mestre-escravo

Para entender o papel do master (mestre), ele é quem envia os comandos para o slave (escravo) responder. O slave não pode iniciar a comunicação, e sim esperar o master perguntar.

Nesse modo, apenas um master pode existir e cada slave terá um endereço, não podendo repetir esse endereço no mesmo barramento. O endereço pode variar entre 7 a 10bits, o que significa que em um único barramento, pode existir algo entre 128 a 1024 slaves

Para esse exemplo, vou utilizar um Arduino como slave e um ESP8266 (Wemos) como master, onde serão enviados comandos para o Arduino através do ESP8266. Não será possível utilizar o ESP como slave neste artigo.

Existe uma limitação na utilização do ESP8266 como slave, mas não devido ao hardware e sim o firmware. Vou tentar abordar esse tema mais para frente utilizando o SDK com NONOS (desenvolvimento nativo).

Hardware

É importante lembrar que no ESP8266 o nivel logico é de 3.3V e no Arduino é de 5V. Para resolver isso sem o medo de queimar o ESP é usar um level shifter, que faz a conversão do sinal logico entre as duas tensões de trabalho.

No caso do uso de level shifter, os resistores de 10k não são necessários, já que o circuito já possui, conforme o esquema abaixo:

Abaixo segue o esquema completo de ligação:

Código

Nesse exemplo, vou mostrar como piscar o LED do Arduino Nano e devolver na resposta do comando o estado dele para que o LED da Wemos também pisque. Se o barramento for desconectado, ambos param de piscar.

Vamos utilizar a IDE Arduino versão 1.8.2 (Linux Manjaro).

Para o lado do master, temos o seguinte código:

/*
I2C Master
Author: Pedro Minatel
*/

#include <Wire.h>

//Estado do LED
uint8_t led_status = 0;

void setup() {
  Wire.begin();
  //LED embarcado na placa
  pinMode(LED_BUILTIN, OUTPUT);
  //Serial para debug
  Serial.begin(115200);
}

void loop() {

  //Inicia a transmissão para o endereço 2
  Wire.beginTransmission(2);
  //Escreve no barramento o estado do LED
  Wire.write(led_status);
  //Termina a transmissão
  Wire.endTransmission();    // stop transmitting

  //Espera a resposta do escravo
  int data_available = Wire.requestFrom(2, 1);
  //Se houver resposta
  if(data_available == 1) {
    //Lê o byte de resposta
    uint8_t slaveResp = Wire.read();
    //Controla o LED de acordo com a resposta do escravo
    if(slaveResp==0){
      digitalWrite(LED_BUILTIN, LOW);
      Wire.write(led_status);
    } else if(slaveResp==1){
      digitalWrite(LED_BUILTIN, HIGH);
      Wire.write(led_status);
    }
    Serial.println(slaveResp);
    //Inverte o estado do LED
    led_status = !led_status;
  } else {
    //Se caso a resposta do escravo for diferente
    Serial.print("Erro de bytes recebidos: ");
    Serial.println(data_available);
  }
  
  delay(100);
}

E para o(s) slave(s):

/*
I2C Slave
Author: Pedro Minatel
*/

#include <Wire.h>

//Dado recebido do mastre
uint8_t rec_value = 0;

void setup() {
  //Inicia como escravo no endereço 2
  Wire.begin(2);
  //Callback para dados recebidos do mestre
  Wire.onReceive(receiveCallback);
  //Callback para requisições recebidas do mestre
  Wire.onRequest(requestCallback);
  //LED embarcado na placa
  pinMode(LED_BUILTIN, OUTPUT);
  //Serial para debug
  Serial.begin(115200);
}


void loop() {
}

//Callback para dados recebidos do mestre
void receiveCallback(int bytes)
{
  //Se tiver dados recebidos
  if(Wire.available() != 0)
  {
    for(int i = 0; i< bytes; i++)
    {
      //Lê os dados
      rec_value = Wire.read();
      Serial.print("Recebido: ");
      Serial.println(rec_value);
    }
    //Controla o LED de acordo com o dado recebido
    if(rec_value==0){
      digitalWrite(LED_BUILTIN, LOW);
    } else if(rec_value==1){
      digitalWrite(LED_BUILTIN, HIGH);
    }
  }
}

//Callback para requisições recebidas do mestre
void requestCallback(int bytes) {
  //Retorna para o mestre o valor recebido
  Wire.write(rec_value);
}

Vale lembrar que esse é um simples exemplo que pode ser customizado de acordo com a necessidade de cada projeto.

Conclusão

A grande vantagem deste modo, é que a serial pode ser utilizada para o debug da placa sem a necessidade de tratamento dos pacotes de comunicação, além do fato de não ter o inconveniente dos dados que são enviados pela serial no processo de boot do ESP.

Esse método ainda pode ser útil em inúmeros casos e situações, ficando assim mais uma solução na manga no momento do aperto!

Happy Hacking!

Deixe um comentário! 3

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Comentando como Anônimo

  1. Parabéns pelo post, realmente deu pra entender o funcionamento perfeitamente.

    Fiz a automação do meu quarto utilizando módulos relé Wi-Fi com esp8266 + raspberry pi3 controlados pelo iPhone através do aplicativo de voz “Siri” e agora pretendo construir minha residência toda automatizada e gostaria muito da sua ajuda.

    Vi que com o iC2 eu economizo muitos pinos da minha placa o q diminuiria muito o custo com arduínos.

    Pretendo utilizar módulos relés optoacoplacos de 8 e 16 canais + iC2 + NodeMCU (esp12f) controlados através do meu Iphone (Siri) pela raspberry.

    Gostaria de saber se é possível fazer a automação usando essa minha idéia e como eu faria essa comunicação/programação entre o NodeMCU (master) e os módulos relés (Slaves) através do i2c.

    Desde já o meu muito obrigado.

    Obs: esse tema daria um excelente vídeo.

    1. Boa noite Gustavo, tudo bem?
      Obrigado por ter gostado do artigo!
      Para usar os módulos de relé por I2C, você vai precisar usar um expansor de portas por I2C, como o MCP23017 ou MCP23008, mas devo alertá-lo que I2C é utilizado para uma comunicação a curta distância, de no máximo uns 2 metros.
      Para o seu caso, o ideal seria usar RS485 (MAX485 + Arduino), que permite comunicação a distancias bem maiores.

      Espero ter te ajudado de alguma forma.

      Abrs,
      Pedro

  2. Olá Pedro, sou novato e estou aprendendo mto com seus artigos principalmente aqueles que usam pouca biblioteca. Eu gostaria de saber se há possibilidade de usar somente a biblioteca SoftwareSerial na comunicação entre 2 ESP-01 e 2 arduino com butão e led, se sim nos de exemplos ou fazer um tutorial. Desde já agradeço seja qual for sua decisão, eu e meus amigos com a mesma dúvida, continuaremos lendo os seus artigos, grande abraço!

leia mais