Expansão de portas analógicas com NodeMCU e Arduino 18

O NodeMCU é uma placa de desenvolvimento muito atrativa e com ótimo custo/benefício, pois além de compacta possui conetividade wifi, I/Os e é programável pela Arduino IDE. Mas como toda placa de desenvolvimento, ela possui limitações, sendo a maior delas a dificuldade de expansão de portas analógicas, já que ela possui apenas um pino para essa função.

NodeMCU e Arduino Nano

O projeto deste post tem como objetivo justamente contornar esta limitação, utilizando para isso um Arduino para ler entradas analógicas e transmitir as leituras ao NodeMCU serialmente, ficando a cargo dele o envio dos dados à nuvem. Pode-se utilizar qualquer Arduino, porém pra este projeto escolheu-se o Arduino Nano V3.0 devido ao seu reduzido tamanho físico e por possuir 8 entradas analógicas.

Material necessário

Para fazer este projeto de expansão de portas analógicas você precisará de:

Overview do projeto de expansão de portas analógicas

O funcionamento do projeto consiste em utilizar o Arduino Nano V3.0 como uma placa de aquisição de sinais analógicos (de até 8 entradas) e, via serial, transmitir as informações lidas ao NodeMCU. Este, por sua vez, irá enviar por MQTT as leituras obtidas, de forma a permitir monitoramento remoto das entradas analógicas lidas. Observe o diagrama em blocos da figura 1.

 

Expansão de portas analógicas
Figura 1 – diagrama em blocos do projeto

Observações sobre debug e gravação

Como o Arduino Nano e NodeMCU irão se comunicar pela UART, o debug via serial monitor é impossibilitado. Uma forma de contornar isso é utilizando seriais por software. Entretanto, por reduzirem o desempenho dos dispositivos (utilizam considerável parcela de processamento), não serão abordadas nesse artigo.

Quanto à gravação, o Arduino Nano e o NodeMCU devem ser gravados em separado e fora do circuito. Isto é necessário pois a mesma serial utilizada para gravação é a que é usada para comunicação serial entre eles.

Parte 1: programação do Arduino Nano

A programação do Arduino Nano é relativamente simples: tudo que ele deve fazer é, periodicamente, ler os 8 canais de ADC, colocar as leituras numa string e enviá-la ao NodeMCU pela serial. Neste projeto de expansão de portas analógicas, a periodicidade destas ações será de 1 segundo, porém você pode mudar isso para o tempo que desejar.

Sendo assim, o código-fonte do programa que vai ser carregado no Arduino Nano é o seguinte:

/*
 * Projeto: Expansao de portas analogicas 8 canais do Arduino Nano V3.0 e envio das leituras por serial.
 * Autor: MakerHero e Pedro Bertoleti
 * Data: Janeiro/2018
 */

//Defines
#define TAMANHO_STRING_SERIAL      ((4*8) + 7 + 1)  //8 canais (de 4 bytes de informação cada, em ASCII) e 7 separadores (;) e 1 terminador de string (\0)

//Variáveis globais
int LeiturasADC[8]; //armazenará as leituras dos canais de ADC

//protótipops
void FazLeituraCanaisADC(void);
void TransmiteLeiturasADC(void);

/*
 * Implementações
 */

//Função: faz a leitura dos canais de ADC
//Parâmetros: nenhum
//Retorno: nenhum
void FazLeituraCanaisADC(void)
{
  char i;

  for(i=0; i<8; i++)
    LeiturasADC[i] = analogRead(i);
}

//Função: Transmite via serial, na forma textual/string, as leituras de ADC obtidas
//Parâmetros: nenhum
//Retorno: nenhum
void TransmiteLeiturasADC(void)
{
   char InfoCanaisADC[TAMANHO_STRING_SERIAL];

   //limpa string
   memset(InfoCanaisADC,0,TAMANHO_STRING_SERIAL);

   //coloca as leituras numa string
   sprintf(InfoCanaisADC,"%04d;%04d;%04d;%04d;%04d;%04d;%04d;%04d", LeiturasADC[0], 
                                                                    LeiturasADC[1], 
                                                                    LeiturasADC[2], 
                                                                    LeiturasADC[3], 
                                                                    LeiturasADC[4], 
                                                                    LeiturasADC[5],
                                                                    LeiturasADC[6],
                                                                    LeiturasADC[7]);

   //Transmite a string pela serial
   Serial.write(InfoCanaisADC, TAMANHO_STRING_SERIAL);                                                                     
}
 
void setup() 
{
  memset(LeiturasADC,0,sizeof(LeiturasADC));
  
  //configura o baudrate da comunicação serial em 19200
  Serial.begin(19200);   
}

void loop() {
  FazLeituraCanaisADC();  
  TransmiteLeiturasADC();
}

Parte 2: programação do NodeMCU

Nesta parte, muito do que foi abordado no artigo Controle e Monitoramento IoT com NodeMCU e MQTT será aproveitado. Portanto, é fortemente recomendada a leitura deste artigo.

Aqui, a tarefa a ser realizada também é relativamente simples: após conexão ao WiFi e ao broker MQTT, o NodeMCU vai publicar no broker (no tópico definido no código-fonte) a string de leituras analógicas recebida. Pelo fato de a recepção de strings ocorrer a cada segundo (conforme explicado no tópico anterior), as informações serão publicadas no broker a cada segundo também.

O código-fonte do programa a ser gravado no NodeMCU está abaixo:

/*
 * Projeto: Recepção de leituras analógicas pela serial e envio das mesmas por MQTT
 * Autor: MakerHero e Pedro Bertoleti
 * Data: Janeiro/2018
 */

#include <ESP8266WiFi.h> // Importa a Biblioteca ESP8266WiFi
#include <PubSubClient.h> // Importa a Biblioteca PubSubClien

//defines:
//defines de id mqtt e tópicos para publicação e subscribe
#define TOPICO_PUBLISH   "MQTTNodeMCUAnalogicoEnvia"    //tópico MQTT de envio de informações para Broker
                                                        //IMPORTANTE: recomendamos fortemente alterar os nomes
                                                        //            desses tópicos. Caso contrário, há grandes
                                                        //            chances de você controlar e monitorar o NodeMCU
                                                        //            de outra pessoa.
#define ID_MQTT  "NodeMCUAnalogico"     //id mqtt (para identificação de sessão)
                                        //IMPORTANTE: este deve ser único no broker (ou seja, 
                                        //            se um client MQTT tentar entrar com o mesmo 
                                        //            id de outro já conectado ao broker, o broker 
                                        //            irá fechar a conexão de um deles).

#define TAMANHO_STRING_SERIAL      ((4*8) + 7 + 1)  //8 canais (de 4 bytes de informação cada, em ASCII) e 7 separadores (;) e terminador de string (\0)

// WIFI
const char* SSID = "SSID"; // SSID / nome da rede WI-FI que deseja se conectar
const char* PASSWORD = "SENHA"; // Senha da rede WI-FI que deseja se conectar
 
// MQTT
const char* BROKER_MQTT = "iot.eclipse.org"; //URL do broker MQTT que se deseja utilizar
int BROKER_PORT = 1883; // Porta do Broker MQTT


//Variáveis e objetos globais
WiFiClient espClient; // Cria o objeto espClient
PubSubClient MQTT(espClient); // Instancia o Cliente MQTT passando o objeto espClient
char StringLeiturasADC[TAMANHO_STRING_SERIAL];

//Prototypes
void initSerial(void);
void initWiFi(void);
void initMQTT(void);
void reconectWiFi(void); 
void VerificaConexoesWiFIEMQTT(void);
bool VerificaSeHaInformacaoNaSerial(void);
void EnviaInformacoesMQTT(void);

/*
 * Implementações
 */

//Função: inicializa comunicação serial com baudrate 115200 (para comunicação com Arduino Nano)
//Parâmetros: nenhum
//Retorno: nenhum
void initSerial() 
{
    Serial.begin(19200);
}

//Função: inicializa e conecta-se na rede WI-FI desejada
//Parâmetros: nenhum
//Retorno: nenhum
void initWiFi() 
{
    delay(10);
    reconectWiFi();
}
 
//Função: inicializa parâmetros de conexão MQTT(endereço do broker e porta)
//Parâmetros: nenhum
//Retorno: nenhum
void initMQTT() 
{
    MQTT.setServer(BROKER_MQTT, BROKER_PORT);   //informa qual broker e porta deve ser conectado
}

//Função: reconecta-se ao broker MQTT (caso ainda não esteja conectado ou em caso de a conexão cair)
//Parâmetros: nenhum
//Retorno: nenhum
void reconnectMQTT() 
{
    while (!MQTT.connected()) 
    {
        if (MQTT.connect(ID_MQTT)) 
          break;
        else 
        {
            //aguarda 2 segundos e tenta se conectar novamente.
            delay(2000);
        }
    }
}
 
//Função: reconecta-se ao WiFi
//Parâmetros: nenhum
//Retorno: nenhum
void reconectWiFi() 
{
    //se já está conectado a rede WI-FI, nada é feito. 
    //Caso contrário, são efetuadas tentativas de conexão
    if (WiFi.status() == WL_CONNECTED)
        return;
        
    WiFi.begin(SSID, PASSWORD); // Conecta na rede WI-FI
    
    while (WiFi.status() != WL_CONNECTED) 
        delay(100);  
}



//Função: verifica o estado das conexões WiFI e ao broker MQTT. 
//        Em caso de desconexão (qualquer uma das duas), a conexão é refeita.
//Parâmetros: nenhum
//Retorno: nenhum
void VerificaConexoesWiFIEMQTT(void)
{
    if (!MQTT.connected()) 
        reconnectMQTT(); //se não há conexão com o Broker, a conexão é refeita
    
     reconectWiFi(); //se não há conexão com o WiFI, a conexão é refeita
}

//Função: verifica se há informações sendo recebidas na serial.
//        Em caso positivo, as recebe e armazena (para futuro envio por MQTT)
//Parâmetros: nenhum
//Retorno: true: ha informações recebidas
//         false: não ha informações recebidas
bool VerificaSeHaInformacaoNaSerial(void)
{
  char c;
  char i;
  
  if (Serial.available() <= 0)
    return false;

  //há dados sendo recebidos. Limpa buffer de recepção:
  memset(StringLeiturasADC,0,TAMANHO_STRING_SERIAL);  

  //pega a string toda (até \0).
  i=0;
  do {
    c = Serial.read();
    if (c != '\0') 
    {
      StringLeiturasADC[i] = c;
      i++;
    }
  } while (c != '\0');

  return true;  
}

//Função: envia informações recebidas pela serial por MQTT
//Parâmetros: nenhum
//Retorno: nenhum
void EnviaInformacoesMQTT(void)
{
  MQTT.publish(TOPICO_PUBLISH, StringLeiturasADC);
}

void setup() 
{
    initSerial();
    initWiFi();
    initMQTT();
}

void loop() 
{
  //garante funcionamento das conexões WiFi e ao broker MQTT
  VerificaConexoesWiFIEMQTT();
  
  //se recebeu informações pela serial, as envia por MQTT
  if (VerificaSeHaInformacaoNaSerial() == true)
    EnviaInformacoesMQTT();

  //keep=alive do MQTT
  MQTT.loop();  
}

Parte 3: circuito esquemático

O circuito esquemático do projeto pode ser visto abaixo. É muito importante se atentar ao seguinte:

  1. O Arduino Nano V3.0 trabalha originalmente com alimentação 5V em seu microcontrolador (ATMega 328P), enquanto o ESP8266 12-E do NodeMCU trabalha com 3,3V.
    Desta forma, conforme mostrado no circuito esquemático, é necessário que no TX do Arduino Nano para o NodeMCU haja um divisor de tensão, de forma que a tensão no pino de RX do NodeMCU não ultrapasse 3,3V. Se isto não for feito, há grandes chances de o NodeMCU ser danificado.
  2. No lugar dos potenciômetros, você pode colocar quaisquer sensores ou componentes analógicos que trabalhem com tensão 5V. Os potenciômetros estão no circuito esquemático somente para ilustrar o uso das entradas analógicas do Arduino Nano V3.0.

Circuito Nodemcu e Arduino Nano

 

Projeto em ação!

Ao se conectar com um cliente de MQTT qualquer (exemplo: MQTTLens) e dar subscribe no tópico que o NodeMCU está publicando as informações, você verá uma string com as leituras:

Saída MQTT

Portanto, o NodeMCU agora é capaz de publicar leituras de até 8 canais analógicos.

Gostou deste post sobre expansão de portas analógicas com NodeMCU? Deixe seu comentário logo abaixo.

Faça seu comentário

Acesse sua conta e participe

18 Comentários

  1. Parabéns, vou aproveitar a idéia para apenas 2 canais para um rc robot car, para não ficar usando o aplicativo no celular. Depois envio para vocês, obrigado!
    Carlos Bruni
    IFBA Campus Salvador

    Nota: tenho também os nrf24 porém, optei por usar o nodemcu com drive.

  2. Olá,
    Tentei reproduzir o projeto sem utilizar o envio dos dados com MQTT. Apenas gostaria de debugar se a string do arduino nano está sendo recebida no Node MCU porém, no Node é como se nunca eu recebesse a string do nano (que está sendo enviada com sucesso). Estou utilizando duas portas USB do computador para a alimentação das placas, isso pode estar fazendo falhar a comunicação serial? Alguma dica para resolver este problema?

  3. Olá, não estou conseguindo ler os valores na porta serial da NodeMCU, tentei ler por outro programa que escrevi, sem mandar via mqtt. Testei a leitura com uma arduino Mega ao invés da Node e obtive sucesso.
    Segue abaixo o código que usei para ler os dados da porta serial:

    #include // Importa a Biblioteca ESP8266WiFi

    void setup() {
    Serial.begin(115200);
    }

    /**
    * Função que lê uma string da Serial
    * e retorna-a
    */
    String leStringSerial(){
    String conteudo = “”;
    char caractere;

    // Enquanto receber algo pela serial
    while(Serial.available() > 0) {
    // Lê byte da serial
    caractere = Serial.read();
    // Ignora caractere de quebra de linha
    if (caractere != ‘\n’){
    // Concatena valores
    conteudo.concat(caractere);
    }
    // Aguarda buffer serial ler próximo caractere
    delay(10);
    }

    Serial.print(“Recebi: “);
    Serial.println(conteudo);

    return conteudo;
    }

    void loop() {
    // Se receber algo pela serial
    if (Serial.available() > 0){
    // Lê toda string recebida
    String recebido = leStringSerial();
    Serial.print(“Deu certo: “);
    Serial.println(recebido);
    }
    }

    1. Olá, Matheus,

      Talvez valha a pena testar a biblioteca SoftwareSerial, que transforma qualquer pino digital em TX/RX. Você pode não estar conseguindo ler os valores da NodeMCU por interferência da USB por exemplo.

      Abraços!
      Diogo – Equipe MakerHero

  4. Olá , daria para enviar dados de um sensor digital também? tipo o DHT11!

    1. Sim, é possível. Seguindo o projeto e fazendo a leitura do DHT11 no Arduino Nano, basta fazer o seguinte:

      – Leia o DHT11 no Arduino Nano e coloque a medição dele na string
      – No NodeMCU, leia a string conforme você a enviou / encaixou a medição do DHT11
      – Ainda no NodeMCU, envie a medição do DHT11 por MQTT.

      Alternativamente, você pode ler o DHT11 no próprio NodeMCU também. Seria mais fácil, inclusive.

  5. Muito bom! Excelente, Pedro. Obrigado por compartilhar.
    Os resistores do divisor de tensão são esses mesmo de 1k?

    1. Ricardo, muito obrigado.

      Sim, os resistores do divisor são ambos de 1k. O esquemático está correto, pode segui-lo.

      Atenciosamente,
      Pedro Bertoleti

      1. os resistores precisam ser de 310(R1) e 680(R2) pra reduzir de 5v p/ 3,3v

        WILSON DIEGO GOUVEA FERREIRA DE MACEDO
  6. Excelente abordagem, esse exemplo vai salvar meu projeto, porém não vou usar mqtt e não sei ao certo quais linhas desse sketch tirar, poderia postar aqui nos comentários quais linhas eu devo tirar? Obrigado.

    1. Anderson, bom dia. Obrigado pelos elogios!

      Considerando que basicamente tudo que o NodeMCU faz é obter as leituras analógicas do Arduino Nano via serial e as publicar via MQTT, o que você deseja que o NodeMCU faça especificamente?
      Em termos de código, da forma que você falou, bastaria que no loop fosse chamada a função VerificaSeHaInformacaoNaSerial() e, por consequencia, você poderia deletas aquelas funções, defines e variáveisrelacionadas a MQTT.

      Atenciosamente,
      Pedro Bertoleti

  7. Boa tarde, e se eu fizer a leitura das portas analógicas, tratar o sinal, como envio o sinal tratado para o nodemcu?
    obrigado

    ANDERSON DA SILVA ANTUNES RIBEIRO
    1. Bom dia Anderson.

      Se vocÊ fizer a aquisição do sinal em uma ou mais posições do array InfoCanaisADC e gravar o sinal tratado na mesma variável, basta fazer o tratamento do sinal no código que vai no Arduino Uno Nano (ou seja, antes de este ser enviado, o que é feito na linha “Serial.write(InfoCanaisADC, TAMANHO_STRING_SERIAL);”).
      Dessa forma o sinal já irá tratado pro NodeMCU.

      Atenciosamente,
      Pedro Bertoleti

  8. Boa Noite estou tentando enviar do arduino mega para o nodemcu, porem ja realizo algumas leituras pelo node mcu como temperatura umidade e vazao. e quero enviar 3 leituras de corrente mais dados de um inversor solar para ele. estou colocando seu codigo no lado do arduino mega, mas nao consigo ler do lado do nodemcu na serial dele. coloquei a biblioteca soft serial e utilizo os pinos d7 e d8 para isso conforme biblioteca. teria alguma maneira de enviar esses valores de uma placa para outra? são 4 valores double

    1. Juliano, boa tarde.

      Por gentileza, coloque aqui seu código (ou coloque-o em alguma ferramenta de compartilhamento de código-fonte, como o code-share, por exemplo). Dessa forma consigo entender melhor o que está ocorrendo.

      Atenciosamente,
      Pedro Bertoleti

  9. Boa tarde, sou cliente de vcs e tenho um arduino e queria saber se existe dentro do arduino o protect dos pics, ou seja para evitar a leitura..obrigado, Carlos

  10. Olá.
    Estou tentando fazer esta comunicação via I2C bus. Já tentaste?
    Estou com um pouco de dificuldade, pois acho pouco material conclusivo sobre o assunto!

    Abraço e gosto muito da sua loja, sou comprador assíduo!

    1. Felipe, boa tarde.

      Obrigado pela preferência pela loja! Ficamos felizes com seu feedback.
      Infelizmente, não tentei ainda ler entradas analógicas com I²C. Vou ficar devendo essa pra você, desculpe.

      Atenciosamente,
      Pedro Bertoleti