I2C Configurações avançadas entre master e slave.

Ola Pessoal. Percebo que vários leitores chegam aqui através de duas buscas principais: I2C e automação residencial, ambas utilizando Arduino. Não parei com automação, informo que instalei o setup dos interruptores, esta funcionando, e estou preparando posts para atualizar vocês e também o projeto. Enquanto isso, vou fazer uma série de posts com um uso um pouco mais avançado de I2C, onde utilizaremos comunicação entre 3 Arduinos, Raspberry, e algum outro componente que seja do interesse.

Conectando 2 Arduinos com I2C

Hoje vou falar da conexão entre dois Arduinos utilizando I2C, onde o master escreve no slave alterando seu comportamento. Para nos auxiliar utilizaremos um buzzer conectado na porta 13, além é claro dos 2 Arduinos.

O esquema do setup que utilizaremos será o seguinte:

I2C Arduino master slave

I2C – Arduino Master e Slave

Qual a finalidade deste exemplo?

A intenção aqui é mostrar como o master pode enviar uma informação ao slave, e este alterar seu comportamento de acordo com a informação recebida. Falando de outra forma, a ideia é que o master comande o slave. Para isso vamos criar algumas funções no slave, e cada função será executada sem parar entre os loops, até que o master “mande” o slave fazer outra coisa, ou executar outra função.

Vamos ver o código do slave?

#include <Wire.h>
#define i2c_address 0x1
char comando = 1;

void setup(){
  Serial.begin(9600);
  Wire.begin(i2c_address);
  Wire.onReceive(receivEvent);
}

void loop(){
  switch (comando){
    case 1:
    comportamento001();
    break;
    case 2:
    comportamento002();
    break;
    case 3:
    comportamento003();
    break;
    case 4:
    comportamento004();
    break;
    default:
    break;
  }
  delay(1000);
}

void receivEvent(int x){
  comando=Wire.read();
}

void comportamento001(){
  int i = 0;
  for (i=0;i<1;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void comportamento002(){
  int i = 0;
  for (i=0;i<2;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void comportamento003(){
  int i = 0;
  for (i=0;i<3;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

void comportamento004(){
  int i = 0;
  for (i=0;i<4;i++){
    tone(13, 800, 50); // play the tone
    delay(100);
      }
}

Começamos incluindo a biblioteca necessária para trabalharmos com I2C, a Wire.h. Na sequência definimos a variável i2c_address, onde armazenamos o endereço I2C que vamos utilizar, o 0x1. Por fim criamos uma variável chamada comando, com o valor inicial 1.

Em setup() nós inicializamos a serial do Arduino(embora não vamos utilizá-la aqui). Na sequência inicializamos o barramento I2C, passando a variável i2c_address como parâmetro. Quando inicializamos o barramento I2C informando um parâmetro, estamos dizendo que este é um slave, e deve “escutar” informações destinadas ao endereço informado. Isso não significa que este Arduino esta condenado a ser eternamente um slave, vamos ver situações onde ele pode sim atuar como master. Finalizando o setup, temos a declaração de um event handler(ou manipulador de eventos), chamado receivEvent.

Eventos???

Se lembrem que as duas operações principais do protocolo I2C são a leitura e a escrita. A biblioteca Wire.h implementa estas duas operações para um slave na forma de eventos. Para a escrita nós temos o evento onReceive, e para a leitura nós temos o evento onRequest. Notem que os nomes dos eventos são bem apropriados para as operações. De suma importância em setups avançados, cada um destes eventos utiliza uma interrupção. Não vou falar de interrupção por hora(mesmo porque manjo muito pouco 😀 ), mas gravem na memória(da sua cabeça) que nada mais é executado enquanto uma interrupção esta em andamento.

Com Wire.onReceive(receivEvent) estamos então criando um manipulador para o evento onReceive, que será então tratado pela função receivEvent. Esta função é declarada logo depois do bloco loop(), e ela simplesmente lê a informação que foi enviada, armazenando o valor na variável comando. Notem que a função receivEvent tem declarada uma variável do tipo int, que no caso dei o nome de x. Quando acontece o evento onReceive, o Arduino chama a função declarada no manipulador e passa um parâmetro para ela. Este parâmetro é o número de bytes recebidos do master.

Antes de falar do bloco loop, vejam que existem outras 4 funções, nomeadas comportamento00X. Estas funções serão executadas no loop, conforme a vontade do master. Estas funções são bem simples, basicamente tocam um bip X vezes, com intervalo de 100 milisegundos entre cada bip. Notem que o número de bips depende diretamente da função chamada, por exemplo a função comportamento003 toca 3 bips na sequência.

Por fim, vamos ao loop().

Aqui selecionamos qual função executar verificando o valor da variável comando através de um bloco switch(basicamente um IF mais rebuscado), executamos a função selecionada, e então esperamos 1 segundo. Este é o nosso sketch. Vejam que em momento algum do loop alteramos o valor da variável comando, logo executaremos sempre a mesma função, a não ser que ocorra o evento onReceive, e a função receivEvent altere o valor da variável comando.

Vamos ver qual o código e o comportamento do todo-poderoso master?

#include <Wire.h>
#define SLAVE01 0x1

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

void loop() {
  int comando=random(1,5);
  Wire.beginTransmission(SLAVE01);
  Wire.write(comando);
  Wire.endTransmission();
  delay(5000);
}

O código é curto e simples. Iniciamos dando um include na biblioteca Wire.h e então definimos uma variável chamada SLAVE01, onde armazenamos o endereço I2C do slave.

Em setup() nós inicializamos a interface serial, mesmo que sem utilizá-la por enquanto, e então inicializamos o barramos I2C com Wire.begin(). Note que sem parâmetro algum, indicando que este é um master.

Por fim, no loop(), nós criamos uma variável comando, do tipo int, e atribuímos a ela um valor aleatório entre 1 e 4. Então iniciamos a escrita no barramento I2C informando o endereço do slave, enviando na sequencia um byte representando o valor da variável comando. Encerramos a transmissão e esperamos 5 segundos. Bem simples não é 😉

Resumo dos sketchs

Com as conexões efetuadas e feito o upload dos sketches para os Arduinos, hora de testar. Se tudo ocorreu bem, o Arduino Slave vai apitar X vezes, esperar 1 segundo e apitar X vezes novamente. Conforme a função random retornar um número entre 1 e 4, o master irá alterar a função chamada no slave. Percebam que o intervalo entre os bips é de 1 segundo, e no master temos um intervalo de 5 segundos entre cada loop. Portanto, os bips se repetirão sempre em múltiplos de 5.

Este exemplo particularmente não tem utilidade nenhuma. A minha intenção é que vocês entendam a ideia principal, que é a possibilidade de termos vários “comportamentos” configurados no slave, e alterarmos o comportamento conforme nossa necessidade. Seria como se cada função fosse um sketch, e o master fosse “ativando” os sketches. No master utilizamos a função random em intervalos de 5 segundos para ativar as funções, mas poderíamos, por exemplo, ter um sensor configurado, e conforme a leitura do sensor ativarmos uma determinada função.

Bem, se tiverem dúvidas, por favor perguntem.

Trabalhando com vários Arduinos.

Para finalizar, vou mostrar como trabalhar com vários Arduinos de uma vez.

Os dois Arduinos estão conectados ao notebook, cada um em uma porta USB, como na imagem abaixo:

Meu setup I2C com 2 Arduinos.

Setup utilizando 2 Arduinos em I2C – master/slave

Mas que bagunça!!! Cada um deles usa uma porta com, e para sabermos qual é qual, conecte o primeiro Arduino e verifique na IDE do Arduino(Ferramentas–> Porta:) qual a porta associada. Conecte o segundo Arduino, e verifique novamente. Quando for fazer o upload do sketch, selecione a porta correta e OK.

Seleção de Arduino - I2C

Seleção da porta serial correspondente ao Arduino correto

Bem pessoal, vou parar este post por aqui, mas aguardem que vem mais para destrincharmos de vez o I2C. Qualquer dúvida ou sugestão, fiquem a vontade nos comentários. Abs a todos vocês.

Deixe uma resposta

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

dezenove + quinze =