banner

Ir para o Forum

Arduino Mega + WiFi = Automação



Uma montagem que foi sugerida por muitos seguidores: Arduino Mega + ESP. Hoje, temos um projeto de automação com 16 relés, três sensores, sem deixar de mencionar o smartphone. Vamos, então, fazer um Web Server para o Arduino Mega utilizando comunicação serial com o ESP8266, na versão ESP-01. Vou ainda mostrar uma página com os valores de sensores e botões para modificarmos o estado de relés.



Montagem

Deixo aqui o esquemático, que mostra o DHT22, o DS18B20, além de um terceiro sensor,  o LDR, que é o de luminosidade, estes ligados aos relés pelos pinos 36 ao 51.



Demonstração

Veja no vídeo a demonstração do projeto em funcionamento. Na montagem você confere uma placa com 16 relés, portanto, 16 fios, a qual está ligada diretamente nas portas de um Arduino Uno. Este Uno eu usei só para alimentar os 3v3 do ESP-01. Tenho ainda um LDR, que é o meu sensor de luminosidade, um termômetro Ds18b20, além do DHT22, que colhe os dados de umidade e temperatura. Temos, então, no smartphone, um aplicativo que vai exibir os dados colhidos por estes sensores e enviá-los ao celular pelo Arduino Mega, através do ESP, que seria a ponte serial, ou seja, o WiFi.  
Na montagem, temos Leds que, quando estão acesos, indicam que os respectivos relés estão desligados. Esse processo também é controlado pelo smartphone.




Bibliotecas

Para o nosso projeto de hoje, vamos precisar de certas libs:

Biblioteca WiFiEsp

Na IDE do Arduino vá em Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Instale WiFiEsp



Biblioteca DallasTemperature

Na IDE do Arduino vá em Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Instale DallasTemperature



Biblioteca OneWire

Na IDE do Arduino vá em Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Instale OneWire



Biblioteca DHT22 sensor library by Adafruit


Na IDE do Arduino vá em Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...

Instale DHT sensor library by Adafruit




Código Fonte


MEGAESP_01.ino

Vamos iniciar incluindo as bibliotecas e definindo os pinos ligados aos sensores. Apontamos ainda o pino onde estará o primeiro relé e quantos pinos, a partir deste primeiro, serão utilizados.

#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
#include <WiFiEsp.h>

//Pinos onde estão os sensores
#define PIN_DS18B20 7 
#define PIN_DHT22 8
#define PIN_LUMINOSITY A0

#define FIRST_PIN 36 //Pino onde está o primeiro relê
#define PINS_COUNT 16 //Quantos pinos a partir do primeiro serão utilizados


Prosseguimos tratando do sensor de temperatura DS18B20 e do sensor de temperatura e umidade DHT22. Partimos, então, para definições que envolvem a rede WiFi, como SSID e senha para o ESP se conectar. Apontamos o servidor que receberá as requisições na porta 80 (porta padrão http), bem como as variáveis para armazenar os valores dos sensores.

//Sensor de Temperatura DS18B20
OneWire oneWire(PIN_DS18B20);
DallasTemperature sensors(&oneWire);
DeviceAddress sensor;

//Sensor de temperatura e umidade DHT22
DHT dht(PIN_DHT22, DHT22);

//SSID e senha da rede wifi para o ESP se conectar
char ssid[] = "SSID";
char pass[] = "12345678";
char ip[] = "192.168.0.109";

//Servidor que receberá as requisições na porta 80 (porta padrão http)
WiFiEspServer server(80);

//Variáveis para armazenar os valores dos sensores
float temperatureDS18B20 = 0;
float temperatureDHT22 = 0;
float humidityDHT22 = 0;
int luminosity = 0;

//Mantém o estado atual dos pinos (HIGH ou LOW)
int pinsStatus[PINS_COUNT];


MEGAESP_01.ino - setup

Inicializamos o monitor serial e a serial onde está o ESP-01 com firmware AT, além dos pinos, dos sensores DS18B20 e DHT22. Para o sensor de luminosidade, apenas precisamos ler o pino analógico. Inicializamos ainda o WiFi e conectamos à rede. Por fim, inicializamos o server.

void setup()
{
    //Serial para o monitor serial
    Serial.begin(115200);

    //Serial onde está o ESP-01 com firmware AT
    Serial1.begin(115200);

    //Inicializa os pinos
    setupPins();

    //Inicializa o sensor DS18B20
    setupDS18B20();

    //Inicializa o sensor DHT22
    dht.begin();

    //Para o sensor de luminosidade apenas precisamos ler o pino analógico
    pinMode(A0, INPUT);

    //Inicializa WiFi e conecta à rede
    setupWiFi();

    //Inicializa o server
    server.begin();
}


MEGAESP_01.ino - setupPins

Nesta etapa, colocamos os pinos que estão ligados aos relés como saída.

void setupPins()
{
    //Coloca os pinos que estão ligados os relês como saída
    for(int i=0; i<PINS_COUNT; i++)
    {
        pinsStatus[i] = LOW;
        int pinNumber = FIRST_PIN + i;
        pinMode(pinNumber, OUTPUT);
        digitalWrite(pinNumber, pinsStatus[i]);
    }
}


MEGAESP_01.ino – setupWiFi

Aqui, executamos uma função que inicializa a serial onde está o ESP-01 com o firmware AT já instalado. Aguardamos conectar à rede WiFi, configuramos o IP e verificamos esse mesmo IP.

void setupWiFi()
{
    //Serial onde está o ESP-01 com o firmware AT já instalado
    WiFi.init(&Serial1);

    Serial.print("Conectando a ");
    Serial.println(ssid);

    int status = WL_IDLE_STATUS;

    //Aguarda conectar à rede WiFi
    while (status != WL_CONNECTED)
    {
        status = WiFi.begin(ssid, pass);
    }

    Serial.println();
    Serial.println("Conectado");

    //Configura o IP
    IPAddress ipAddress;
    ipAddress.fromString(ip);
    WiFi.config(ipAddress);

    //Veririca o IP
    IPAddress localIP = WiFi.localIP();
    Serial.print("IP: ");
    Serial.println(localIP);
}



MEGAESP_01.ino - setupDS18B20

Inicializamos o sensor DS18B20.

//Inicializa o sensor DS18B20
void setupDS18B20()
{
    sensors.begin();   

    if (!sensors.getAddress(sensor, 0)) 
    {
        Serial.println("Sensor não encontrado!");
    }
}


MEGAESP_01.ino – Loop

No Loop, verificamos se há um novo cliente. Fazemos a leitura da requisição e, se a requisição não for para o favicon, executamos a ação com o valor passado na requisição. Fazemos, então, a leitura dos sensores e enviamos a resposta ao cliente. Determinamos um tempo para o navegador receber os dados e fechamos a conexão com o cliente.

void loop()
{
    WiFiEspClient client = server.available();

    //Verifica se há um novo cliente
    if (client)
    {
        Serial.println("Novo cliente conectou");
        
        //Faz a leitura da requisição
        char* request = readRequest(client); 

        //Se a requisição não for para o favicon
        if(strstr(request, "favicon") == NULL)
        {
            //Executamos a ação com o valor passado na requisição
            execute(getAction(request), getValue(request));

            //Faz a leitura dos sensores
            readSensorDS18B20();
            readSensorDHT22();
            readSensorLuminosity();

            //Envia a resposta ao cliente
            sendResponse(client);

            //Tempo para o navegador receber os dados
            delay(100);
        }
        
        //Fecha a conexão com o cliente
        client.stop();
    }
}


MEGAESP_01.ino – readRequest 

O navegador do smartphone envia a requisição inteira (igual abaixo), fazemos a leitura da requisição inteira, porém guardamos apenas a primeira linha, que é somente o que nos interessa.

GET /?on=1 HTTP/1.1\r\n
Host: 192.168.3.154\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Linux; Android 8.0.0; SM-G955F Build/R16N ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Referer: http://192.168.3.154/\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: en-US,en;q=0.9\r\n
\r\n


Temos aqui, então, a leitura da primeira linha da requisição.

//Faz a leitura da primeira linha da requisição
char* readRequest(WiFiEspClient client)
{
    bool currentLineIsBlank = true;
    char request[50];
    int i = 0;
    bool firstLine = true;

    while (client.connected()){
        if(client.available()){
            char c = client.read();
            Serial.write(c);
            
            //Apenas a primeira linha da requisição nos interessa
            if(firstLine){
                request[i] = c;
                i++;
            }


Vemos que a última linha da requisição é um \r\n sozinho, após o \r\n da linha anterior, Portanto, se chegamos aqui é porque a requisição foi lida por completo. Ainda, se leu qualquer caractere que não seja \n e \r, significa que a linha não está em branco.

            if (c == '\n'){
                //A última linha da requisição é um \r\n sozinho, após o \r\n da linha anterior
                if(currentLineIsBlank){
                    //Se chegou aqui é porque a requisição foi lida por completo
                    break;
                }

                currentLineIsBlank = true;
                firstLine = false;
            }
            else if (c != '\r'){
                //Se leu qualquer caracter que não for \n e \r significa que a linha não está em branco
                currentLineIsBlank = false;
            }
        }
    }

    return request;
}


MEGAESP_01.ino – sendResponse

Essa função envia o HTML para o cliente. Envia ainda o cabeçalho HTTP, bem como o cabeçalho e o corpo do HTML.

//Envia o HTML para o cliente
void sendResponse(WiFiEspClient client)
{
    //Envia o cabeçalho HTTP
    client.print(
    "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/html; charset=UTF-8\r\n"
    "Connection: close\r\n"
    "Refresh: 10; URL=/\r\n" //Para fazer requisação a raiz do server a cada 10 segundos
    "\r\n");

    client.println("<!DOCTYPE HTML>");
    client.println("<html>");
    head(client);//Envia o cabeçalho do HTML
    body(client);//Envia o corpo do HTML
    client.println("</html>");
}


MEGAESP_01.ino – head

Enviamos o CSS para modificar a aparência da página.

//Envia o CSS para modificar a aparência da página
void head(WiFiEspClient client)
{
    client.println(F("<head>"
    "<style>"
        "body{"
            "text-align: center;"
            "font-family: sans-serif;"
            "font-size: 14px;"
        "}"
        "p{"
            "color:#555;"
            "font-size: 12px;"
        "}"
        ".button{"
            "outline: none;"
            "display: block;"
            "border: 1px solid #555;"
            "border-radius:18px;"
            "width: 150px;"
            "height: 30px;"
            "margin: 10px;"
            "margin-left: auto;"
            "margin-right: auto;"
            "cursor: pointer;"
        "}"
        ".button_off{"
            "background-color:#FFF;"
            "color: #555;"
        "}"
        ".button_on{"
            "background-color:#2C5;"
            "color: #fff;"
        "}"
    "</style>"
"</head>"));
}



MEGAESP_01.ino - body

Seguimos, então, para exibição dos dados dos sensores e criamos os botões, estes para cada pino que possuir um relé.

//Exibe os dados dos sensores e cria os botões
void body(WiFiEspClient client)
{
    client.println(
    "<body>"
    "DS18B20 Temperature: " + String(temperatureDS18B20) + " °C"
    "<br>"
    "DHT22 Temperature: " + String(temperatureDHT22) + " °C"
    "<br>"
    "DHT22 Humidity: " + String(humidityDHT22) + "%"
    "<br>");

    String buttons = "";

    //Cria um botão para cada pino que possui um relê
    for(int i=0; i<PINS_COUNT; i++)
    {
        buttons.concat(button(i));
    }

    client.println(buttons);
    client.println("</body>");
}


Os "<br>" não foram exibidos pelo HTML do Blog. Sendo assim, inserimos aqui a imagem do código completo para não deixar dúvidas.
Outro detalhe, pessoal: esqueci de printar a luminosidade. O .ino já foi corrigido. O certo seria:


MEGAESP_01.ino – button

Criamos um botão com a aparência e ação correspondente ao estado atual do relé.

//Cria um botão com a aparência e ação correspondente ao estado atual do relê
String button(int number)
{
    String label = String(number + 1);
    String className = "button ";
    className += pinsStatus[number] == HIGH ? "button_on" : "button_off";
    String action = pinsStatus[number] == HIGH ? "off" : "on";
    return "<button class=\"" + className + "\"onclick=\"location.href='?" + action + "=" + label + "'\">" + label + "</button>";
}


MEGAESP_01.ino – sensors

Vamos fazer a leitura dos sensores DS18B20, DHT e o de luminosidade.

//Faz a leitura do sensor DS18B20
void readSensorDS18B20()
{
  sensors.requestTemperatures();
  temperatureDS18B20 = sensors.getTempC(sensor);
}

//Faz a leitura  do sensor DHT
void readSensorDHT22()
{
    humidityDHT22 = dht.readHumidity();
    temperatureDHT22 = dht.readTemperature();
}

//Faz a leitura do sensor de luminosidade
void readSensorLuminosity()
{
    luminosity = analogRead(PIN_LUMINOSITY);
}



MEGAESP_01.ino – getAction getValue

Retornamos a ação que o cliente deseja executar (on/off), bem como o valor (numero do relé) que a ação será executada.

//Retorna a ação que o cliente deseja executar (on off)
String getAction(char *request)
{
    return getStringBetween(request, '?', '=');
}

//Retorna o valor (numero do relê) que a ação será executada
String getValue(char *request)
{
    return getStringBetween(request, '=', ' ');
}


MEGAESP_01.ino – getStringBetween

Prosseguimos, desta vez, retornando a string que fica entre o primeiro caractere “start” e o primeiro caractere “end”. Retornamos o endereço de memória do caractere “start”. Se não achar o caractere, vai para o próximo, enquanto não chegar ao caractere “end” ou ao final da string.

//Retorna a string que fica entre o primeiro caractere 'start' e o primeiro caractere 'end'
String getStringBetween(char *input, char start, char end)
{
    String str = "";
    //retorna o endereço de memória do caractere 'start'
    char *c = strchr(input, start);

    //Se não achou o caractere
    if(c == NULL)
    {
        return "";
    }

    //Vai para o próximo caractere
    c++;

    //Enquanto não chegar ao caractere 'end' ou ao final da string
    while(*c != end && *c!='\0')
    {
        str += *c;
        c++;
    }

    return str;
}


MEGAESP_01.ino – execute

Por fim, pessoal, executada a ação junto ao valor (número do relé), observamos se é uma das duas ações que esperamos On ou Off. Os relés são numerados a partir do 1, mas o array começa do 0. Então, tiramos 1.
O número do pino será o índice mais o número do pino onde os relés começam. Os relés devem estar em sequência a partir do pino inicial.

//Executada a ação junto ao valor (número do relê)
void execute(String action, String value)
{
    //Se é uma das duas ações que esperamos
    if(action == "on" || action == "off")
    {
        //Os relês são numerados a partir do 1, max o array começa do 0
        //então tiramos 1
        int index = value.toInt() - 1;
        //O número do pino será o índice mais o número do pino onde os relês
        //começam. Os relês devem estar em sequência a partir do pino inicial (FIRST_PIN)
        int pinNumber = FIRST_PIN + index;
        int status = action == "on" ? HIGH : LOW;
        digitalWrite(pinNumber, status);
        pinsStatus[index] = status;
    }
}



Faça o download dos arquivos:



5 comentários:

  1. Muito bom Fernando, parabéns!. Eu pretendo usar um programa mais simples com arduino uno e ESP01 sem os sensores de temperatura e umidade usando apenas controles para 8 relés liga e desliga. também uma função de pulso em um relé.
    A minha pergunta é: Tem como fazer isso com algumas modificações nesse código?

    ResponderExcluir
  2. Olá Fernando! Agradeço sua generosidade em compartilhar conhecimento.
    Estou com um problema na parte da IDE e no firmeware: instalei a IDE da Arduino, configurei para uso com o Mega2560 (placa e processador), baixei as bibliotecas como no tutorial acima e baixei o arquivo .ino. Ao compilar o firmeware apresenta erros que me parece das bibliotecas.
    Diante disso, qual a dica que devo conferir em um possível erro na instalação?
    Agradeço a atenção.

    ResponderExcluir
    Respostas
    1. Jardelson Martins acho que tenha faltado a biblioteca DHT. Na IDE do arduino onde você baixa as bibliotecas procure por DHT e instale DHT sensor library by Adafruit

      Excluir
    2. Olá Fernando! Após ter pedido essa ajuda eu percebi que não tinha essa biblioteca e também baixei, mas o problema persistiu! Estou iniciando com Mega.
      Texto do erro:

      Arduino: 1.8.6 (Windows 10), Placa:"Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

      In file included from C:\Users\Jardelson\Documents\Arduino\libraries\DHT_sensor_library\DHT_U.cpp:22:0:

      Foram encontradas múltiplas bibliotecas para «OneWire.h»
      C:\Users\Jardelson\Documents\Arduino\libraries\DHT_sensor_library\DHT_U.h:25:29: fatal error: Adafruit_Sensor.h: No such file or directory

      Utilizado: C:\Users\Jardelson\Documents\Arduino\libraries\OneWire
      compilation terminated.

      Não utilizado: C:\Users\Jardelson\Documents\Arduino\libraries\DallasTemperature
      exit status 1
      Erro ao compilar para a placa Arduino/Genuino Mega or Mega 2560.

      Este relatório teria mais informação com a
      opção «Mostrar mensagens detalhadas durante a
      compilação» seleccionada nas Preferências.

      Excluir
  3. Para usar com Arduino Uno, é muito diferente?

    ResponderExcluir

Tecnologia do Blogger.