Um projeto de automação com
ESP32 como Web Server! Hoje, vamos utilizar o ESP32 para mostrar uma página com
os valores de temperatura, umidade e botões para modificarmos o estado de relés.
Ainda, vamos utilizar o MCP23017 para aumentar a quantidade de pinos
disponíveis no ESP, o que nos possibilita ligar até 128 relés.
Quero destacar, pessoal, que
neste vídeo você vai perceber que não vale a pena fazer um projeto usando o
ESP-01 com Arduino Mega, como neste vídeo: Arduino Mega + WiFi = Automação,
que eu fiz a pedido de vários seguidores. Isso, é claro, na minha opinião, pois
considero que com o ESP32 fica muito melhor, por conta do domínio total que
esse microcontrolador te possibilita.
ESP32 Pinout
Montagem
Nesta imagem você observa que
eu só usei dois pinos para ligar 16 relés, facilidade possibilitada pelo MCP23017,
que você pode conhecer melhor neste vídeo: Expansor de IOs para ESP32,ESP8266 e Arduino, mas na versão MCP23016, que utiliza a mesma Lib. Tenho ainda
neste projeto de hoje nosso velho conhecido DHT22.
Demonstração
Na imagem,
você vê nossa montagem e um celular conectado ao nosso aplicativo com IP local,
sendo que as placas, tanto a de relés quanto a do ESP32, estão sendo
alimentadas pela USB.
Através do
aplicativo, realizamos a leitura de temperatura e umidade com os dados captados
pelo DHT22.
Biblioteca SimpleDHT
Na IDE do
Arduino vá em Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Instale
SimpleDHT
Biblioteca WebServer
Vá em
C:\Users\<SEU_USUÁRIO>\Documents\Arduino\hardware\espressif\esp32\libraries
e verifique se possui a pasta WebServer. Se não possuir reinstale o core do
ESP32 na IDE o Arduino, pois as versões mais novas já vem com a lib.
ESP32_WebServer.ino
Vamos incluir
as bibliotecas necessárias e definir o endereço i2c do MCP23017, bem como dos
registradores.
#include <WiFi.h> #include <WiFiClient.h> #include <WebServer.h> #include <SimpleDHT.h> #include <Wire.h> #include <FS.h> #include <SPIFFS.h> //endereço I2C do MCP23017 #define MCP_ADDRESS 0x20 //ENDEREÇOS DE REGISTRADORES #define GPA 0x12 // DATA PORT REGISTER A #define GPB 0x13 // DATA PORT REGISTER B #define IODIRA 0x00 // I/O DIRECTION REGISTER A #define IODIRB 0x01 // I/O DIRECTION REGISTER B #define PINS_COUNT 16 //Quantidade total de pinos #define DATA_PATH "/pin_data.bin" //Arquivo onde serão salvos os status dos pinos #define DHTPIN 5 //Pino one está o DHT22
Temos aqui as
variáveis que envolvem ssid, senha e IP. Criamos um server na porta padrão e
apontamos o objeto que faz a leitura de temperatura e umidade. Temos ainda as
variáveis para guardar esses valores lidos. Seguimos guardando o estado das
duas portas do MCP23017 e executamos o controle do temporizador do Watchdog
(assista o vídeo sobre watchdog: Travou! E agora?).
const char *ssid = "TesteESP"; const char *password = "12345678"; const char *ip = "192.168.0.154"; //Criamos um server na porta padrão o http WebServer server(80); //Objeto que faz a leitura da temperatura e umidade SimpleDHT22 dht; //Variáveis para guardar os valores de temperatura e umidade lidos float temperature = 0; float humidity = 0; //Guarda o estado atual das duas portas do MCP23017 (8 bits cada) uint8_t currentValueGPA = 0; uint8_t currentValueGPB = 0; //faz o controle do temporizador do watchdog (interrupção por tempo) hw_timer_t *timer = NULL;
ESP32_WebServer.ino - setup
Inicializamos os valores dos
pinos do MCP23017, e tratamos da inicialização também do SPIFFS e do WiFi.
Definimos que, sempre que recebermos uma requisição na raiz do webserver, a
função handleRoot será executada. Inicializamos ainda o server e o watchdog.
void setup() { Serial.begin(115200); //Inicializa os valores dos pinos do MCP23017 setupPins(); //Tenta inicializar SPIFFS if(SPIFFS.begin(true)) { loadPinStatus(); } else { //Se não conseguiu inicializar Serial.println("SPIFFS Mount Failed"); } //inicializa WiFi setupWiFi(); //Sempre que recebermos uma requisição na raiz do webserver //a função handleRoot será executada server.on("/", handleRoot); //Se recebermos uma requisição em uma rota que nao existe server.onNotFound(handleNotFound); //Inicializa o server server.begin(); //Inicializa o watchdog setupWatchdog(); }
ESP32_WebServer.ino – setupPins
Inicializamos o Wire nos pinos
SDA e SCL padrões do ESP32 e apontamos a velocidade de comunicação. Configuramos,
então, todos os pinos das duas portas do MCP23017 como saída.
void setupPins() { //Inicializa o Wire nos pinos SDA e SCL padrões do ESP32 Wire.begin(); //Velocidade de comunicação Wire.setClock(200000); //Configura todos os pinos das duas portas do MCP23017 como saída configurePort(IODIRA, OUTPUT); configurePort(IODIRB, OUTPUT); }
ESP32_WebServer.ino – readDHT
Temos aqui uma função para
recuperar o estado atual dos pinos no arquivo para que estes permaneçam após um
eventual reboot.
//Função para recuperar o estado atual dos pinos no //arquivo para que estes permaneçam após um eventual reboot void loadPinStatus() { //Abre o arquivo para leitura File file = SPIFFS.open(DATA_PATH, FILE_READ); //Se arquivo não existe if(!file) { //Na primeira vez o arquivo ainda não foi criado Serial.println("Failed to open file for reading"); //Coloca todos os pinos das duas portas do MCP23017 em LOW writeBlockData(GPA, B00000000); writeBlockData(GPB, B00000000); return; } //Faz a leitura dos valores file.read(¤tValueGPA, 1); file.read(¤tValueGPB, 1); //fecha o arquivo file.close(); //Envia os valores para o MCP23017 writeBlockData(GPA, currentValueGPA); writeBlockData(GPB, currentValueGPB); }
ESP32_WebServer.ino – setupWiFi
Nesta etapa, colocamos como
modo station e conectamos à rede. Configuramos o IP e exibimos este endereço
para abrir no navegador.
void setupWiFi() { //Coloca como modo station WiFi.mode(WIFI_STA); //Conecta à rede WiFi.begin(ssid, password); Serial.println(""); //Enquanto não conectar while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //Se chegou aqui está conectado Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); //Configura o IP IPAddress ipAddress; ipAddress.fromString(ip); WiFi.config(ipAddress, WiFi.gatewayIP(), WiFi.subnetMask()); //Exibe o endereço de IP para abrir no navegador Serial.print("IP address: "); Serial.println(WiFi.localIP()); }
ESP32_WebServer.ino – WatchDog
Aqui temos a função que o
temporizador irá chamar para reiniciar o ESP32, bem como a função que configura
o temporizador.
//função que o temporizador irá chamar, para reiniciar o ESP32 void IRAM_ATTR resetModule(){ ets_printf("(watchdog) reboot\n"); esp_restart_noos(); //reinicia o chip } //função que configura o temporizador void setupWatchdog() { timer = timerBegin(0, 80, true); //timerID 0, div 80 //timer, callback, interrupção de borda timerAttachInterrupt(timer, &resetModule, true); //timer, tempo (us), repetição timerAlarmWrite(timer, 5000000, true); timerAlarmEnable(timer); //habilita a interrupção //enable interrupt }
ESP32_WebServer.ino – loop
No Loop, temos a função que reseta
o temporizador (alimenta o watchdog) e a que verifica se existe alguma
requisição.
void loop() { //reseta o temporizador (alimenta o watchdog) timerWrite(timer, 0); //Verifica se existe alguma requisição server.handleClient(); }
ESP32_WebServer.ino – handleRoot
Nesta fase, verificamos se a
montagem recebeu argumentos na requisição e executamos uma ação. Fazemos a
leitura de temperatura e umidade, geramos o html e o enviamos. Por fim, salvamos
o status dos pinos para voltar assim no próximo reboot.
void handleRoot() { //Se recebeu argumentos na requisição if(server.args() > 0) { //Executa a ação (on ou off) no pino do argumento execute(server.argName(0), server.arg(0)); } //Faz a leitura da temperatura e umidade readDHT(); //Gera o html e o envia String html = "<html>"; html.concat(head()); html.concat(body()); html.concat("</html>"); server.send(200, "text/html; charset=UTF-8", html); //Salva o status dos pinos para voltar assim no próximo reboot savePinStatus(); }
ESP32_WebServer.ino – savePinStatus
Aqui trabalhamos com a função
para salvar o estado atual dos pinos em arquivo para que este permaneça após um
eventual reboot.
//Função para salvar o estado atual dos pinos em //arquivo para que estes permaneçam após um eventual reboot void savePinStatus() { //Abre o arquivo para escrita File file = SPIFFS.open(DATA_PATH, FILE_WRITE); //Se não conseguiu abrir/criar o arquivo if(!file) { Serial.println("Failed to open file for writing"); return; } //Escreve os valores dos pinos no começo do arquivo file.seek(0); file.write(¤tValueGPA, 1); file.write(¤tValueGPB, 1); //Fecha o arquivo file.close(); }
ESP32_WebServer.ino – handleNotFound
Já esta função serve para enviar
para o navegador a informação que a rota não foi encontrada.
void handleNotFound() { //Envia para o navegador a informação que a rota não foi encontrada server.send(404, "text/plain", "Not Found"); }
ESP32_WebServer.ino – execute
Esta, executa a ação junto ao
valor (número do relé).
//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, mas o array começa do 0 //então tiramos 1 int index = value.toInt() - 1; int status = action == "on" ? HIGH : LOW; digitalWriteMCP(index, status); } }
ESP32_WebServer.ino – head
Retornamos o cabeçalho da
página com a informação do tempo para atualizar a página sozinha e a aparência.
//Retorna o cabeçalho da página com a informação do tempo //para atualizar a página sozinho e a aparência String head() { return (F("<head>" "<meta name='viewport' content='width=device-width, initial-scale=1.0'>" "<meta http-equiv='refresh' content='10;URL=/'>" //refresh a cada 10 segundos "<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>")); }
ESP32_WebServer.ino – body
Agora, esta função exibe os
dados dos sensores e cria os botões, sendo um para cada pino que possui um relé.
//Exibe os dados dos sensores e cria os botões String body() { String b = "<body>" "<p>Temperature: " + String(temperature) + " °C</p>" "<p>Humidity: " + String(humidity) + "%</p>"; //Cria um botão para cada pino que possui um relê for(int i=0; i<PINS_COUNT; i++) { b.concat(button(i)); } b.concat("</body>"); return b; }
ESP32_WebServer.ino – button
Temos aqui a criação de 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 += getPinStatus(number) == HIGH ? "button_on" : "button_off"; String action = getPinStatus(number) == HIGH ? "off" : "on"; return "<button class=\"" + className + "\"onclick=\"location.href='?" + action + "=" + label + "'\">" + label + "</button>"; }
ESP32_WebServer.ino – getPinStatus
Verificamos se o relé está
ligado ou desligado.
uint8_t getPinStatus(int pin) { uint8_t v; //de 0 a 7 porta A, de 8 a 15 porta B if(pin < 8) { v = currentValueGPA; } else { v = currentValueGPB; pin -= 8; } return !!(v & (1 << pin)); }
ESP32_WebServer.ino – configurePort
Aqui, configuramos o modo dos
pinos das portas (GPA ou GPB). Como parâmetro, passamos:
port: GPA ou GPB
type: INPUT para todos os
pinos da porta trabalharem como entrada
OUTPUT para todos os pinos da porta
trabalharem como saída
//Configura o modo dos pinos das portas (GPA ou GPB) //como parametro passamos: // port: GPA ou GPB // type: // INPUT para todos os pinos da porta trabalharem como entrada // OUTPUT para todos os pinos da porta trabalharem como saída void configurePort(uint8_t port, uint8_t type) { if(type == INPUT) { writeBlockData(port, 0xFF); } else if(type == OUTPUT) { writeBlockData(port, 0x00); } }
ESP32_WebServer.ino – digitalWriteMCP
Mudamos o estado de um pino
desejado (de 0 a 15), salvamos os valores dos bits da porta correspondente e
enviamos os dados para o MCP.
//muda o estado de um pino desejado void digitalWriteMCP(int pin, int value) { uint8_t port; uint8_t v; //de 0 a 7 porta A, de 8 a 15 porta B if(pin < 8){ port = GPA; v = currentValueGPA; }else{ port = GPB; v = currentValueGPB; pin -= 8; } if (value == LOW){ v &= ~(B00000001 << (pin)); // muda o pino para LOW } else if (value == HIGH){ v |= (B00000001 << (pin)); // muda o pino para HIGH } //Salva os valores dos bits da porta correspondente if(port == GPA){ currentValueGPA = v; }else{ currentValueGPB = v; } //envia os dados para o MCP writeBlockData(port, v); }
ESP32_WebServer.ino – writeBlockData
Enviamos, então, os dados para
o MCP23017 através do barramento i2c.
//envia dados para o MCP23017 através do barramento i2c void writeBlockData(uint8_t port, uint8_t data) { Wire.beginTransmission(MCP_ADDRESS); Wire.write(port); Wire.write(data); Wire.endTransmission(); delay(10); }
ESP32_WebServer.ino – readDHT
Executamos a leitura da
temperatura e umidade
//Faz a leitura da temperatura e umidade void readDHT() { float t, h; int status = dht.read2(DHTPIN, &t, &h, NULL); //Apenas altera as variáveis se a leitura foi bem sucedida if (status == SimpleDHTErrSuccess) { temperature = t; humidity = h; } }
Faça o download dos arquivos:
24 Comentários
Boa noite ; obrigado por este projeto vou montar ele.
ResponderExcluirE obrigado pelas aulas , é um aprendizado muito bom.
Bom dia Fernando, estou a alguns dias acompanhando os seus vídeos, são muito úteis, bem estruturados, muito bem explicados, parabéns e obrigado por disponibilizar seu tempo em preparar estes vídeos que nos trazem muitas e importantes informações. Grande abraço e sucesso.
ResponderExcluirBoa tarde fernandok . Fiz esse projeto com o esp3266 da marca lolin ,mas ta dando erro na copilação sera que a pinagem é diferente, vc pode me ajudar ?
ResponderExcluirBoa noite FernandoK, sou novo em automação mas preciso terminar um projeto para escola. Consigo controlar reles e servo no mesmo codigo?Com a Esp32
ResponderExcluircarlos costa27 de setembro de 2018 14:02
ResponderExcluirBoa tarde fernandok . Fiz esse projeto com o esp3266 da marca lolin ,mas ta dando erro na copilação sera que a pinagem é diferente, vc pode me ajudar ?
Boa noite, Fernando, montei esse projeto mas ligando o resistor 4.7k no 3.3 junto com o pinto de sinal do mcp23017 ele para de funcionar e se ligar sem esse resistor funciona, mas as vezes os reles param de operar, tem ideia do que pode ser? obrigado
ResponderExcluiro meu está funcionando de boa, só não entendo porque neste projeto liga-se os resistores de 4,7K, enquanto no outro projeto usando mcp23016 não precisou dos resistores no canal i2c, alguem sabe responder ?? grato
Excluirpoderia explicar como usa esse mesmo projeto mas usando o node red ?
ResponderExcluirÓtimo projeto! Mas e se eu quiser colocar 16 pulsadores, para poder acionar o relé, estou precisando dessa parte... Onde o relé pode ser acionado via web, mas caso tiver uma interação pelo pulsador ele atualizar o status do rele!
ResponderExcluirPoderia ligar em triway, mas no final nunca iria saber se a lâmpada estaria realmente ligada ou não...
tenho a mesma pergunta !!!!
Excluirmontei td igual, e não esta funcionando, única coisa que tiver que mudar foi na linha 150, "esp_restart_noos();" para "esp_restart();" mas ao acessar o ip não esta aparecendo temperatura e nem acionando os relés
ResponderExcluirTive o mesmo problema, mais consegui resolver através da tua pergunta.
ExcluirEu também tive o mesmo problema e resolvemos assim.
ExcluirFoi o único problema que também encontrei, mas foi resolvido graças a sua dica, Rafael. Valeu!
ExcluirBoa tarde
ResponderExcluirno codigo quando mando verificar a biblioteca #include , aparece em destaque acredito que seja por não ter no PC, alguém sabe onde posso baixar ??
Antonio Sousa
Ops!! faltou qual biblioteca,
ResponderExcluir#include
Att
Anotonio Sousa
???????? não esta indo ????????
ResponderExcluirSPIFFS.h
Alguem sabe dizer se funciona no ESP8266?????
ResponderExcluirDesistam.. ele não responde Mais, nem aqui, nem no Fórum, nem no Youtube..
ResponderExcluirgood morning, i have done the project and work well, only mod i made is on watchdog instruction, i put the new one set, but i need to change name at the buttons, can you help me?
ResponderExcluirthanks Giovanni from italy
Boa noite Sr. Fernando estou acompanhando seus videos gostaria de saber se consigo fazer com esp 32 esse projeto com comando de voz no Google assistente desde já parabenizalo pelos videos
ResponderExcluiralguem poderia comentar para que servem aqueles resistores ligados às portas i2c ? ainda estou engatinhando na eletronica digital, vejo alguns projetos do proprio canal usando o mcp23016 sem resistor, este projeto com mcp23017 usando resistor, não entendi porque.. grato
ResponderExcluirPrimeiramente parabéns pelo conteúdo me ajudou muito em meu projeto.
ResponderExcluirEu implementei o projeto acima da mesma forma, mas estou tendo problemas com o modulo rele, não sei se é alguma interferência ou outra coisa mas depois de um tempo com o sistema funcionando alguns reles que não estavam armados começam a ficar meio loucos armando e desarmando muito rapidamente depois param e ficam o mesmo estado de outro rele. Exemplo estou apenas com o rele 14 armado após algum tempo o rele 16 começa a se comportar de forma estranha e depois ele segue o mesmo sinal que o 14, então se eu desarmo o rele 14 o 16 também desarma se eu armo o rele 14 o 16 também sofre a mesma ação e vice e versa se faço a mesma atividade com o rele 16 o 14 também sobre a mesma ação como se o pino de sinal dos dois estivessem interligados.
O mais estranho é, se removo o fio de sinal do pino 16 mesmo assim ao ativar o rele 14 o 16 também é ativado.
Se alguém puder me ajudar ficarei muito grato. Obrigado.
Olá a todos, seguindo o passo a passo do projeto eu agarrei ao incluir a biblioteca Webserve.
ResponderExcluirReinstalei o core do ESP32 e mesmo assimnao apareceu a biblioteca.
Alguem saberia o porque?