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:








11 Comentários
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é.
ResponderExcluirA minha pergunta é: Tem como fazer isso com algumas modificações nesse código?
Olá Fernando! Agradeço sua generosidade em compartilhar conhecimento.
ResponderExcluirEstou 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.
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
ExcluirOlá 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.
ExcluirTexto 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.
Para usar com Arduino Uno, é muito diferente?
ResponderExcluirAinda quero ver um Up desse projeto usando o APP FernandoK, até já comprei o Arduino Mega com wifi embutido hehe
ResponderExcluirTOP!
ResponderExcluirMuito bom esse tópico!!!
ResponderExcluirProfessor Fernando, excelente tutorial, como sempre!
ResponderExcluirPorém, no diagrama estão conectados o RX e TX nos pinos 18 e 19 e no arquivo .INO estão configurados os pinos 19 (RX) e 20 (TX).
Linha 17 - SoftwareSerial Serial1(19, 20); // RX, TX
O correto seria assim, certo?
SoftwareSerial Serial1(18, 19); // RX, TX
Falei uma bobagem enorme no post anterior... Desculpe!
ResponderExcluirBom dia Fernando ficou show o projeto, muito massa!
ResponderExcluirSó uma dúvida para mim utilizar este projeto no Mega 2560 wifi o que e preciso mudar no código?