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?