Olá, como estão? A princípio iremos demonstrar como utilizar o KIT de irrigação automática com ESP32 da WJ Componentes.
Componentes Utilizados
OU
- 1x KIT IRRIGAÇÃO COM ESP32
Funcionamento do Projeto Irrigação Automática
Nesse projeto nosso objetivo será realizar irrigação automática de plantas, então mostrar um relatório. Entretanto para sabermos se o solo está seco, iremos utilizar o sensor de umidade de solo com sonda anticorrosivo HD-38. Então para saber a quantidade de água gastas nas irrigações utilizamos um sensor de vazão YF-S401 com um ESP32. Dessa forma ele receberá informações dos sensores e irrigar quando for necessário. Contudo o relatório será feito através de uma página HTML que iremos importar para o ESP32. Contudo ele conterá quantas irrigações foram realizadas, a quantidade de água gastas, o custo médio da conta de água nas últimas 24 horas e todas as plantas cadastradas e a respectiva quantidade de irrigação de cada uma. Então as tecnologias que foram abordada nesse projeto foi SPIFFS, WiFi e NTP.
Obs.: A conta de água pode haver grandes diferenças de valor pelos seguintes motivos:
- Pois foi calculado somente o consumo de água das plantas.
- O valor cobrado pela distribuidora de água.
- A forma que a distribuidora cobra, pois calculamos somente a quantidade de água gasta no dia. Segue tabela de preço da distribuidora SAEE Sorocaba.
Faixa de Consumo | Unidade | Custo de Água (R$) | Custo de Esgoto (R$) | Custo Total (R$) |
---|---|---|---|---|
De 0 a 10 (Mínimo) | Mês | 17,05 | 15,77 | 32,82 |
De 11 a 15 | m3 | 2,56 | 2,37 | 4,39 |
De 16 a 20 | m3 | 3,73 | 3,45 | 7,18 |
De 21 a 25 | m3 | 5,41 | 5,00 | 10,41 |
De 26 a 30 | m3 | 5,95 | 5,50 | 11,45 |
De 31 a 40 | m3 | 6,24 | 5,77 | 12,01 |
De 41 a 50 | m3 | 6,56 | 6,06 | 12,62 |
De 51 a 75 | m3 | 6,91 | 6,39 | 13,30 |
De 76 a 100 | m3 | 7,07 | 6,54 | 13,61 |
De 101 a 200 | m3 | 8,48 | 7,84 | 16,32 |
De 201 a 300 | m3 | 10,17 | 9,41 | 19,58 |
Acima de 300 | m3 | 12,20 | 11,29 | 23,49 |
Montagem do Projeto Irrigação Automática
Logo após compra dos componentes na WJ Componentes, vamos à montagem!
Então o sensor de vazão possui três pinos sendo eles VCC, GND e digital. Além disso o sensor de umidade de solo HD-38 possui quadro pinos sendo eles VCC, GND, digital e analógico. Entretanto não iremos utilizar o pino digital para esse circuito, os dois utilizam como alimentação 5V. Sabendo disso iremos conectar o pino VIN e GND do ESP32 na protoboard e assim alimentando os sensores. Em seguida conectamos o pino digital do sensor de vazão no pino GPIO35 do ESP32. Contudo o sensor de umidade de solo HD-38 conectamos o pino analógico no pino GPIO34 do ESP32.
Contudo segue abaixo uma imagem que demonstrando a montagem do circuito.
Diagrama Esquemático do Projeto Irrigação Automática
Código-fonte do Projeto Irrigação Automática
Em seguida segue abaixo o código-fonte completo do projeto.
//Bibliotecas ----------------------------------------------------------------------------------------
#include <WiFi.h> // Responsável pelo WiFi
#include <AsyncTCP.h> // Responsável por permitir múltiplas conexões
#include <ESPAsyncWebServer.h> // Responsável pelas funções de requisições
#include <NTPClient.h> // Facilitar o uso dos Servidores NTP
#include <SPIFFS.h> // Responsável por Arquivar em memoria Flash
//WiFi -----------------------------------------------------------------------------------------------
const char* SSID = "**********"; // Nome da rede WiFi
const char* password = "***************"; // Senha do WiFi
AsyncWebServer server(80); // Declaração do servidor
//NTP - Horário --------------------------------------------------------------------------------------
const char* ntpServer = "a.st1.ntp.br"; // Servidor NTP
const int fusoHorario = -3 * 3600; // Fuso horário que será aplicado
WiFiUDP udp; // Objeto para Update do time
NTPClient ntp(udp, ntpServer, fusoHorario, 60000); // Objeto "NTP"
//Definições -----------------------------------------------------------------------------------------
#define bomba 13 // Bomba
#define sensorVazao 35 // Sensor de vazão
//Variáveis Globais ----------------------------------------------------------------------------------
int cont = 0, contDia = 0, taxaQtde = 0; // Variáveis de controle
int dia, contPulso; // Variáveis de controle
float custo = 0, custoDia = 0; // Variáveis de controle
float taxaCusto = 0, vazao, ml; // Variáveis de controle
float litros = 0, litrosDia = 0; // Variáveis de controle
float taxaLitros = 0; // Variáveis de controle
typedef struct hort { // Structs Hortaliças
String nome; // Nome da Hortaliças
uint8_t sensor[2]; // Sensores
uint8_t umidSolo; // Umidade do Solo
int cont = 0; // Contador
} hort; // Define novo nome para struct
hort hortalicias[2]; // Inicialização da struct com vetor
//Funções Auxiliares ---------------------------------------------------------------------------------
void notFound(AsyncWebServerRequest *request); // Responsável por retornar erro 404
String processor(const String& var); // Responsável por enviar os textos para o HTML
void custoConta(); // Responsável por calcular o custo da conta
void incrpulso (); // Responsável por contar os pulsos
//Setup ----------------------------------------------------------------------------------------------
void setup() {
//Alface
hortalicias[0].nome = "Alface"; // Atribui nome a struct de vetor [0]
hortalicias[0].sensor[0] = 34; // Atribui sensor[0] a struct de vetor [0]
hortalicias[0].umidSolo = 23; // Atribui umidade do solo a struct de vetor [0]
//Soja
hortalicias[1].nome = "Soja"; // Atribui nome a struct de vetor [0]
hortalicias[1].umidSolo = 13; // Atribui umidade do solo a struct de vetor [0]
/* Caso queira adicionar novas hortalicias ou plantas só *
seguir o exemplo assim e alterar o tamanho dos vetores */
Serial.begin(115200); // Inicialização da Serial
if (!SPIFFS.begin(true)) { // Verificação e inicialização do SPIFFS
Serial.println("Erro ao Iniciar SPIFFS"); // Imprime na Serial erro
while (1) {}; // Loop infinito
}
WiFi.begin(SSID, password); // Inicia o Wi-Fi com o SSID e password
if (WiFi.waitForConnectResult() != WL_CONNECTED) { // Verifica se conseguiu conectar
Serial.printf("WiFi Failed!\n"); // Imprime na Serial erro
while (1) {}; // Loop infinito
}
Serial.print("IP Address: "); // Imprime o IP do ESP no monitor Serial
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { // Inicializa arquivo HTML
request->send(SPIFFS, "/index.html", "text/html", false /*dowload*/, processor);
});
server.serveStatic("/style.css", SPIFFS, "/style.css"); // Inicializa arquivo CSS
server.serveStatic("/script.js", SPIFFS, "/script.js"); // Inicializa arquivo JS
server.onNotFound(notFound); // Error 404
server.begin(); // Inicia Servidor
for (byte i = 0; i < (sizeof(hortalicias) / sizeof(hort)); i++) { // Loop hortalicias
for (byte j = 0; j < (sizeof(hortalicias[i].sensor) / sizeof(uint8_t)); j++) { // Sensor
pinMode(hortalicias[i].sensor[j], INPUT); // Define sensores como INPUT
}
}
pinMode(sensorVazao, INPUT); // Define sensor de vazão como INPUT
pinMode(bomba, OUTPUT); // Define bomba como OUTPUT
ntp.begin(); // Inicializa NTP
ntp.forceUpdate(); // Força um Update da Hora
dia = ntp.getDay(); // Armazena o dia atual
attachInterrupt(sensorVazao, incrpulso, RISING); // Configura a porta para interrupção
}
//Loop -----------------------------------------------------------------------------------------------
void loop() {
if (dia != ntp.getDay()) { // Verifica se o dia mudo
custoConta(); // Chama a função custoConta();
taxaQtde = cont - contDia; // Descobre taxa de quantidade
contDia = cont; // Atribui a contDia o valor de cont
cont = 0; // Atribui 0 a cont para recomeçar a contagem
taxaLitros = litros - litrosDia; // Descobre taxa de litros
litrosDia = litros; // Atribui a litrosDia o valor de litros
litros = 0; // Atribui 0 a litros para recomeçar a contagem
taxaCusto = custo - custoDia; // Descobre taxa custo
custoDia = custo; // Atribui a custoDia o valor de custo
custo = 0; // Atribui 0 a custo para recomeçar a contagem
}
contPulso = 0; // Atribui 0 a custoPulso
for (byte i = 0; i < (sizeof(hortalicias) / sizeof(hort)); i++) { // Loop hortalicias
for (byte j = 0; j < (sizeof(hortalicias[i].sensor) / sizeof(uint8_t)); j++) { // Sensor
Serial.println(hortalicias[i].sensor[j]); // Imprime o valor do sensor
float umidade = analogRead(hortalicias[i].sensor[j]); // Lê o sensor e armazena
umidade = map(umidade, 0, 4095, 100, 0); // Remapeia para o valor correspondente
Serial.print("Loop Umidade : "); // Imprime no monitor Serial
Serial.println(umidade); // Umidade remapeada
if (umidade < hortalicias[i].umidSolo) { // Verifica se o solo esta seco
while (umidade < hortalicias[i].umidSolo) { // Verifica se o solo continua seco
umidade = analogRead(hortalicias[i].sensor[j]); // Lê o sensor e armazena
umidade = map(umidade, 0, 4095, 100, 0); // Remapeia para o valor correspondente
sei(); // Liga interrupção
digitalWrite(bomba, HIGH); // Liga o bomba
};
digitalWrite(bomba, LOW); // Desliga o bomba
cli(); // Desliga interrupção
vazao = contPulso / 5.5; // Litros por min
ml = vazao / 60; // Descobre ml
litros += ml; // E armazena em litros
hortalicias[i].cont++; // Incrementa o contador da hortaliça
cont++; // Incrementa o contador
}
}
}
}
//Erro 404 -------------------------------------------------------------------------------------------
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
//Processo Site --------------------------------------------------------------------------------------
String processor(const String& var) {
Serial.println(var); // Imprime na Serial var
if (var == "QTDE") { // Verifica se var é igual a QTDE
return String(contDia);
}
else if (var == "TAXAQTDE") { // Verifica se var é igual a TAXAQTDE
if (taxaQtde >= 0 ) { // Verifica se maior ou igual a 0
return "+ " + String(taxaQtde);
}
else {
return " " + String(taxaQtde);
}
}
else if (var == "QTDEAGUA") { // Verifica se var é igual a QTDEAGUA
return String(litrosDia) + " L";
}
else if (var == "TAXAQTDEAGUA") { // Verifica se var é igual a TAXAQTDEAGUA
if (taxaQtdeAgua >= 0 ) { // Verifica se maior ou igual a 0
return "+ " + String(taxaLitros);
}
else {
return " " + String(taxaLitros);
}
}
else if (var == "CUSTO") { // Verifica se var é igual a CUSTO
return "R$ " + String(custoDia);
}
else if (var == "TAXACUSTO") { // Verifica se var é igual a TAXACUSTO
if (taxaQtdeAgua >= 0 ) { // Verifica se maior ou igual a 0
return "+ " + String(taxaCusto);
}
else {
return " " + String(taxaCusto);
}
}
else if (var == "TABELA") { // Verifica se var é igual a TABELA
String htmlTabela;
const char tabela1 [] = "<div class=\"preview-item border-bottom\"><div class=\"preview-item-content d-sm-flex flex-grow\"><div class=\"flex-grow\"><h6 class=\"preview-subject\">";
const char tabela2 [] = "</h6><p class=\"text-muted mb-0\">Necessita de uma Umidade de ";
const char tabela3 [] = "</p></div><div class=\"mr-auto text-sm-right pt-2 pt-sm-0\"><p class=\"text-muted\">";
const char tabela4 [] = "</p><p class=\"text-muted mb-0\">";
const char tabela5 [] = " irregações</p></div></div></div>";
for (byte i = 0; i < (sizeof(hortalicias) / sizeof(hort)); i++) { // Loop com qtde planta
htmlTabela += tabela1 + hortalicias[i].nome + tabela2 + hortalicias[i].umidSolo + tabela3 + /*Timer*/ + tabela4 + hortalicias[i].cont + tabela5;
}
return htmlTabela;
}
return String();
}
//Custo da Conta de Agua -----------------------------------------------------------------------------
void custoConta() {
int litrosCub = litros / 1000; // Converte litros para m3
if (litrosCub >= 0 && litrosCub <= 10) {
custo = 32.82;
}
else if (litrosCub >= 11 && litrosCub <= 15) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 4.93;
}
}
else if (litrosCub >= 16 && litrosCub <= 20) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 7.18;
}
}
else if (litrosCub >= 21 && litrosCub <= 25) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 10.41;
}
}
else if (litrosCub >= 26 && litrosCub <= 30) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 11.45;
}
}
else if (litrosCub >= 31 && litrosCub <= 40) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 12.01;
}
}
else if (litrosCub >= 41 && litrosCub <= 50) {
custo = 32, 82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 12.62;
}
}
else if (litrosCub >= 51 && litrosCub <= 75) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 13.30;
}
}
else if (litrosCub >= 76 && litrosCub <= 100) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 13.61;
}
}
else if (litrosCub >= 101 && litrosCub <= 200) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 16.32;
}
}
else if (litrosCub >= 201 && litrosCub <= 300) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 19.58;
}
}
else if (litrosCub > 300) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 23.49;
}
}
}
//Incrementa Pulso -----------------------------------------------------------------------------------
void incrpulso () {
contPulso++; // Incrementa a variável de pulsos
}
Vamos dar uma olhada mais de perto no código:
Incluindo Bibliotecas
Primeiramente temos que incluir as bibliotecas que usamos em nosso projeto.
//Bibliotecas ----------------------------------------------------------------------------------------
#include <WiFi.h> // Responsável pelo WiFi
#include <AsyncTCP.h> // Responsável por permitir múltiplas conexões
#include <ESPAsyncWebServer.h> // Responsável pelas funções de requisições
#include <NTPClient.h> // Facilitar o uso dos Servidores NTP
#include <SPIFFS.h> // Responsável por Arquivar em memoria Flash
Definições WiFi
Logo após definimos o nome, senha do WiFi e um objeto chamado server.
//WiFi -----------------------------------------------------------------------------------------------
const char* SSID = "**********"; // Nome da rede WiFi
const char* password = "***************"; // Senha do WiFi
AsyncWebServer server(80); // Declaração do servidor
Definições de Data e Hora
Entretanto para conseguir receber informações sobre horários declaramos duas constantes e dois objetos. Sendo elas, ntpServer (servidor onde será consultado), fusoHorario, udp (atualizar) e ntp (objeto que irá conter as informações).
//NTP - Horário --------------------------------------------------------------------------------------
const char* ntpServer = "a.st1.ntp.br"; // Servidor NTP
const int fusoHorario = -3 * 3600; // Fuso horário que será aplicado
WiFiUDP udp; // Objeto para Update do time
NTPClient ntp(udp, ntpServer, fusoHorario, 60000); // Objeto "NTP"
Definições de Pinos
Então definimos os pinos da bomba e do sensor de vazão.
//Definições -----------------------------------------------------------------------------------------
#define bomba 13 // Bomba
#define sensorVazao 35 // Sensor de vazão
Variáveis Globais
Em seguida declaramos algumas variáveis globais.
//Variáveis Globais ----------------------------------------------------------------------------------
int cont = 0, contDia = 0, taxaQtde = 0; // Variáveis de controle
int dia, contPulso; // Variáveis de controle
float custo = 0, custoDia = 0; // Variáveis de controle
float taxaCusto = 0, vazao, ml; // Variáveis de controle
float litros = 0, litrosDia = 0; // Variáveis de controle
float taxaLitros = 0; // Variáveis de controle
typedef struct hort { // Structs Hortaliças
String nome; // Nome da Hortaliças
uint8_t sensor[2]; // Sensores
uint8_t umidSolo; // Umidade do Solo
int cont = 0; // Contador
} hort; // Define novo nome para struct
hort hortalicias[2]; // Inicialização da struct com vetor
Declarações de Structs
Contudo também declaramos uma estrutura que será responsável por armazenar as informações sobre as plantas e hortaliças.
typedef struct hort { // Structs Hortaliças
String nome; // Nome da Hortaliças
uint8_t sensor[2]; // Sensores
uint8_t umidSolo; // Umidade do Solo
int cont = 0; // Contador
} hort; // Define novo nome para struct
hort hortalicias[2]; // Inicialização da struct com vetor
Funções Auxiliares
Logo após declaramos as funções auxiliares que usamos no projeto.
//Funções Auxiliares ---------------------------------------------------------------------------------
void notFound(AsyncWebServerRequest *request); // Responsável por retornar erro 404
String processor(const String& var); // Responsável por enviar os textos para o HTML
void custoConta(); // Responsável por calcular o custo da conta
void incrpulso (); // Responsável por contar os pulsos
Funções Setup
Então agora na função setup (), atribuímos os nomes, sensores e umidade do solo para planta e hortaliça.
//Setup ----------------------------------------------------------------------------------------------
void setup() {
//Alface
hortalicias[0].nome = "Alface"; // Atribui nome a struct de vetor [0]
hortalicias[0].sensor[0] = 34; // Atribui sensor[0] a struct de vetor [0]
hortalicias[0].umidSolo = 23; // Atribui umidade do solo a struct de vetor [0]
//Soja
hortalicias[1].nome = "Soja"; // Atribui nome a struct de vetor [0]
hortalicias[1].umidSolo = 13; // Atribui umidade do solo a struct de vetor [0]
Obs.: Caso queira realizar só o incremento de novas plantas basta ir à inicialização da struct e adicionar +1 ao vetor.
Em seguida realizamos a inicialização da Serial, SPIFFS e WiFi. Da mesma forma Também inicializamos os arquivos CSS e JS como serveStatic e inicializamos o HTML com a função processor.
Serial.begin(115200); // Inicialização da Serial
if (!SPIFFS.begin(true)) { // Verificação e inicialização do SPIFFS
Serial.println("Erro ao Iniciar SPIFFS"); // Imprime na Serial erro
while (1) {}; // Loop infinito
}
WiFi.begin(SSID, password); // Inicia o Wi-Fi com o SSID e password
if (WiFi.waitForConnectResult() != WL_CONNECTED) { // Verifica se conseguiu conectar
Serial.printf("WiFi Failed!\n"); // Imprime na Serial erro
while (1) {}; // Loop infinito
}
Serial.print("IP Address: "); // Imprime o IP do ESP no monitor Serial
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { // Inicializa arquivo HTML
request->send(SPIFFS, "/index.html", "text/html", false /*dowload*/, processor);
});
server.serveStatic("/style.css", SPIFFS, "/style.css"); // Inicializa arquivo CSS
server.serveStatic("/script.js", SPIFFS, "/script.js"); // Inicializa arquivo JS
server.onNotFound(notFound); // Error 404
server.begin(); // Inicia Servidor
Logo após iremos fazer um loop verificando o tamanho da struct, dentro do loop realizamos outro loop verificando o quantos sensores temos. Então consigamos configurar todos os pinos dos sensores como INPUT.
for (byte i = 0; i < (sizeof(hortalicias) / sizeof(hort)); i++) { // Loop hortalicias
for (byte j = 0; j < (sizeof(hortalicias[i].sensor) / sizeof(uint8_t)); j++) { // Sensor
pinMode(hortalicias[i].sensor[j], INPUT); // Define sensores como INPUT
}
}
Continuação Funções Setup
Contudo também configuramos o pino da bomba como OUTPUT e o sensor de vazão como INPUT, inicializamos o ntp (relógio) e forçamos uma atualização. Também armazenamos o dia atual para que possamos utilizar futuramente e configuramos o pino do sensor de vazão para interrupção.
pinMode(sensorVazao, INPUT); // Define sensor de vazão como INPUT
pinMode(bomba, OUTPUT); // Define bomba como OUTPUT
ntp.begin(); // Inicializa NTP
ntp.forceUpdate(); // Força um Update da Hora
dia = ntp.getDay(); // Armazena o dia atual
attachInterrupt(sensorVazao, incrpulso, RISING); // Configura a porta para interrupção
}
Funções Loop
Agora na função void loop, em seguida verificamos se o dia mudou. Entretanto caso tenha mudado chamamos função custoConta para calcular o custo da conta de água. Então realizamos a conta para saber as taxas se aumentaram ou diminuíram em 24 horas. Logo após atribuímos a variável contDia, litrosDia e cutoDia o valor que foi obtido nas últimas 24 horas e zeramos a variáveis de controle.
//Loop -----------------------------------------------------------------------------------------------
void loop() {
if (dia != ntp.getDay()) { // Verifica se o dia mudo
custoConta(); // Chama a função custoConta();
taxaQtde = cont - contDia; // Descobre taxa de quantidade
contDia = cont; // Atribui a contDia o valor de cont
cont = 0; // Atribui 0 a cont para recomeçar a contagem
taxaLitros = litros - litrosDia; // Descobre taxa de litros
litrosDia = litros; // Atribui a litrosDia o valor de litros
litros = 0; // Atribui 0 a litros para recomeçar a contagem
taxaCusto = custo - custoDia; // Descobre taxa custo
custoDia = custo; // Atribui a custoDia o valor de custo
custo = 0; // Atribui 0 a custo para recomeçar a contagem
}
Continuação Função Loop
Logo depois atribuímos 0 a variável contPulso que é responsável por contar os números de pulsos que o sensor de vazão envia. Logo após realizamos um loop verificando o tamanho da struct hort e dentro outro loop verificando a quantidade de sensores. Assim imprimimos o número do sensor no monitor Serial e lemos e armazenamos na variável umidade.
contPulso = 0; // Atribui 0 a custoPulso
for (byte i = 0; i < (sizeof(hortalicias) / sizeof(hort)); i++) { // Loop hortalicias
for (byte j = 0; j < (sizeof(hortalicias[i].sensor) / sizeof(uint8_t)); j++) { // Sensor
Serial.println(hortalicias[i].sensor[j]); // Imprime o valor do sensor
float umidade = analogRead(hortalicias[i].sensor[j]); // Lê o sensor e armazena
Continuação Função Loop
Mapeamos a variável para que ela possua o mesmo valor, porém entre 0 até 100, imprimimos o novo valor no monitor Serial e verificamos se a umidade é menor que a umidade da planta ou hortaliça que foi definida assim. Entretanto caso for iremos criar um loop até que seja diferente de menor, dentro do loop realizamos a leitura novamente e o mapeamento, porém dessa vez também ligamos a interrupção e a bomba.
umidade = map(umidade, 0, 4095, 100, 0); // Remapeia para o valor correspondente
Serial.print("Loop Umidade : "); // Imprime no monitor Serial
Serial.println(umidade); // Umidade remapeada
if (umidade < hortalicias[i].umidSolo) { // Verifica se o solo esta seco
while (umidade < hortalicias[i].umidSolo) { // Verifica se o solo continua seco
umidade = analogRead(hortalicias[i].sensor[j]); // Lê o sensor e armazena
umidade = map(umidade, 0, 4095, 100, 0); // Remapeia para o valor correspondente
sei(); // Liga interrupção
digitalWrite(bomba, HIGH); // Liga o bomba
};
Continuação Função Loop
Então quando o sair do loop desligamos a bomba e a interrupção. Em seguida realizamos os cálculos para saber a quantidade em litros, também incrementamos as variáveis hortalicias[i].cont e a variável cont.
digitalWrite(bomba, LOW); // Desliga o bomba
cli(); // Desliga interrupção
vazao = contPulso / 5.5; // Litros por min
ml = vazao / 60; // Descobre ml
litros += ml; // E armazena em litros
hortalicias[i].cont++; // Incrementa o contador da hortaliça
cont++; // Incrementa o contador
}
}
}
}
Função Not Found
Agora passamos para a função void notFound, essa função é responsável por retornar o erro 404 quando não encontrar a página solicitada ou arquivo.
//Erro 404 -------------------------------------------------------------------------------------------
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
Função Processor
Passamos para a função void processor, aonde ela é responsável por substituir no HTML os textos, primeiramente imprimimos a variável var no monitor Serial. Após verificamos se var é igual a QTDE, TAXAQTDE, QTDEAGUA, TAXAQTDEAGUA, CUSTO, TAXACUSTO ou TABELA, caso seja retornarmos a o texto ou variável correspondente.
//Processo Site --------------------------------------------------------------------------------------
String processor(const String& var) {
Serial.println(var); // Imprime na Serial var
if (var == "QTDE") { // Verifica se var é igual a QTDE
return String(contDia);
}
else if (var == "TAXAQTDE") { // Verifica se var é igual a TAXAQTDE
if (taxaQtde >= 0 ) { // Verifica se maior ou igual a 0
return "+ " + String(taxaQtde);
}
else {
return " " + String(taxaQtde);
}
}
else if (var == "QTDEAGUA") { // Verifica se var é igual a QTDEAGUA
return String(litrosDia) + " L";
}
else if (var == "TAXAQTDEAGUA") { // Verifica se var é igual a TAXAQTDEAGUA
if (taxaQtdeAgua >= 0 ) { // Verifica se maior ou igual a 0
return "+ " + String(taxaLitros);
}
else {
return " " + String(taxaLitros);
}
}
else if (var == "CUSTO") { // Verifica se var é igual a CUSTO
return "R$ " + String(custoDia);
}
else if (var == "TAXACUSTO") { // Verifica se var é igual a TAXACUSTO
if (taxaQtdeAgua >= 0 ) { // Verifica se maior ou igual a 0
return "+ " + String(taxaCusto);
}
else {
return " " + String(taxaCusto);
}
}
else if (var == "TABELA") { // Verifica se var é igual a TABELA
String htmlTabela;
const char tabela1 [] = "<div class=\"preview-item border-bottom\"><div class=\"preview-item-content d-sm-flex flex-grow\"><div class=\"flex-grow\"><h6 class=\"preview-subject\">";
const char tabela2 [] = "</h6><p class=\"text-muted mb-0\">Necessita de uma Umidade de ";
const char tabela3 [] = "</p></div><div class=\"mr-auto text-sm-right pt-2 pt-sm-0\"><p class=\"text-muted\">";
const char tabela4 [] = "</p><p class=\"text-muted mb-0\">";
const char tabela5 [] = " irregações</p></div></div></div>";
for (byte i = 0; i < (sizeof(hortalicias) / sizeof(hort)); i++) { // Loop com qtde planta
htmlTabela += tabela1 + hortalicias[i].nome + tabela2 + hortalicias[i].umidSolo + tabela3 + /*Timer*/ + tabela4 + hortalicias[i].cont + tabela5;
}
return htmlTabela;
}
return String();
}
Função Custo da Conta
Na função void custoConta inicializamos outra variável para que possamos converter litros em metros cúbicos sem perder a variável litros. Logo após verificamos se o litrosCub estão entre 0 a 10, 11 a 15, 16 a 20, 21 a 25, 26 a 30, 31 a 40, 41 a 50, 51 a 75, 76 a 100, 101 a 200, 201 a 300 ou mais que 300. Entretanto caso esteja, será calculado a conta referente ao consumo de água.
//Custo da Conta de Agua -----------------------------------------------------------------------------
void custoConta() {
int litrosCub = litros / 1000; // Converte litros para m3
if (litrosCub >= 0 && litrosCub <= 10) {
custo = 32.82;
}
else if (litrosCub >= 11 && litrosCub <= 15) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 4.93;
}
}
else if (litrosCub >= 16 && litrosCub <= 20) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 7.18;
}
}
else if (litrosCub >= 21 && litrosCub <= 25) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 10.41;
}
}
else if (litrosCub >= 26 && litrosCub <= 30) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 11.45;
}
}
else if (litrosCub >= 31 && litrosCub <= 40) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 12.01;
}
}
else if (litrosCub >= 41 && litrosCub <= 50) {
custo = 32, 82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 12.62;
}
}
else if (litrosCub >= 51 && litrosCub <= 75) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 13.30;
}
}
else if (litrosCub >= 76 && litrosCub <= 100) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 13.61;
}
}
else if (litrosCub >= 101 && litrosCub <= 200) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 16.32;
}
}
else if (litrosCub >= 201 && litrosCub <= 300) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 19.58;
}
}
else if (litrosCub > 300) {
custo = 32.82;
litrosCub -= 10;
for (byte i = 0; i < litrosCub; i++) {
custo += 23.49;
}
}
}
Função Incrpulso
Por fim temos a função void incrpulso a função dela é somente ficar incrementando a variável contPulso toda vez que o sensor de vazão realizar uma interrupção.
//Incrementa Pulso -----------------------------------------------------------------------------------
void incrpulso () {
contPulso++; // Incrementa a variável de pulsos
}
E assim finalizamos todas as funções e o código do ESP32.
Arquivos do Site
Para o website utilizamos três arquivos sendo eles HTML (corpo), CSS (estilo) e Javascript (funcionalidade).
HTML (Corpo)
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Irregação Automatica</title>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<div class="main-panel">
<div class="content-wrapper">
<div class="row">
<div class="col-sm-4 grid-margin">
<div class="card">
<div class="card-body">
<h5>Qtde Irregações</h5>
<div class="row">
<div class="col-8 col-sm-12 col-xl-8 my-auto">
<div class="d-flex d-sm-block d-md-flex align-items-center">
<h2 class="mb-0">%QTDE%</h2>
<p id="qtde" class="text-success ml-2 mb-0 font-weight-medium">%TAXAQTDE%</p>
</div>
<h6 class="text-muted font-weight-normal">Ultimas 24 horas</h6>
</div>
<div class="col-4 col-sm-12 col-xl-4 text-center">
<span id="qtdeIcon" class="material-icons icon icon-success">south_east</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin">
<div class="card">
<div class="card-body">
<h5>Qtde Media de Água Gasta</h5>
<div class="row">
<div class="col-8 col-sm-12 col-xl-8 my-auto">
<div class="d-flex d-sm-block d-md-flex align-items-center">
<h2 class="mb-0">%QTDEAGUA%</h2>
<p id="qtdeAgua" class="text-success ml-2 mb-0 font-weight-medium">%TAXAQTDEAGUA%</p>
</div>
<h6 class="text-muted font-weight-normal">Ultimas 24 horas</h6>
</div>
<div class="col-4 col-sm-12 col-xl-4 text-center">
<span id="qtdeAguaIcon" class="material-icons icon icon-success">south_east</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin">
<div class="card">
<div class="card-body">
<h5>Valor Médio da Conta de Água</h5>
<div class="row">
<div class="col-8 col-sm-12 col-xl-8 my-auto">
<div class="d-flex d-sm-block d-md-flex align-items-center">
<h2 class="mb-0">%CUSTO%</h2>
<p id="custo" class="text-success ml-2 mb-0 font-weight-medium">%TAXACUSTO%</p>
</div>
<h6 class="text-muted font-weight-normal">Ultimas 24 horas</h6>
</div>
<div class="col-4 col-sm-12 col-xl-4 text-center">
<span id="custoIcon" class="material-icons icon icon-success">south_east</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<div class="d-flex flex-row justify-content-between">
<h4 class="card-title mb-1">Tipos de Hortalicias</h4>
<p class="text-muted mb-1">Ultimas Irrigações</p>
</div>
<div class="row">
<div class="col-12">
<div class="preview-list">
%TABELA%
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
CSS (Estilo)
@media (min-width: 768px){
.d-md-flex {
display: flex !important;
}
}
body {
color: #ffffff;
background-color: #000000;
font-family: sans-serif;
margin: 0px;
overflow-x: hidden;
}
h4 {
font-size: 1.125rem;
}
h5 {
font-size: 1rem;
}
h6 {
font-size: 0.9375rem;
}
h2, h4, h5, h6 {
font-weight: 500;
line-height: 1.2;
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
}
.main-panel{
width: 100%;
}
.content-wrapper{
padding: 1.875rem 1.75rem;
}
.row {
display: flex;
margin-right: -0.75rem;
margin-left: -0.75rem;
}
.grid-margin {
margin-bottom: 1.5rem;
}
.col-4, .col-8, .col-12, .col-sm-4, .col-sm-12, .col-xl-4, .col-xl-8 {
position: relative;
width: 100%;
padding-right: 0.75rem;
padding-left: 0.75rem;
}
.card {
background-color: #191c24;
border-radius: 0.25rem;
border: 1px solid rgba(0, 0, 0, 0.125);
}
.card .card-body {
padding: 1.75rem 1.5625rem;
}
.card .card-title {
color: #ffffff;
text-transform: capitalize;
}
.align-items-center {
align-items: center !important;
}
.ml-2 {
margin-left: 0.5rem !important;
}
.mb-0 {
margin-bottom: 0 !important;
}
.mb-1 {
margin-bottom: 0.25rem !important;
}
.text-success {
color: #00d25b !important;
}
.text-muted {
color: #6c7293 !important;
}
.text-danger {
color: #fc424a !important;
}
.justify-content-between {
justify-content: space-between !important;
}
.d-flex {
display: flex !important;
}
.preview-list .preview-item {
display: flex;
flex-direction: row;
align-items: flex-start;
padding: 1rem 0rem;
font-size: .875rem;
}
.border-bottom {
border-bottom: 1px solid #2c2e33 !important;
}
.preview-list .preview-item .preview-item-content p {
margin-bottom: 10px;
line-height: 1;
}
.flex-grow {
flex-grow: 1;
}
.text-sm-right {
text-align: right !important;
}
.pt-sm-0 {
padding-top: 0 !important;
}
.d-sm-flex {
display: flex !important;
}
.text-center {
text-align: right;
margin-top: -40px;
align-self: center;
}
.icon {
font-size: 40px !important;
border-radius: 10px;
padding: 10px;
}
.icon-success {
color: rgb(0, 199, 0);
background-color: rgba(0, 128, 0, 0.479);
}
.icon-danger {
color: rgb(199, 0, 0);
background-color: rgba(128, 0, 0, 0.479);
}
JavaScript (Funcionalidade)
var qtde = parseFloat(document.getElementById("qtde").innerText.substr(2));
var qtdeAgua = parseFloat(document.getElementById("qtdeAgua").innerText.substr(2));
var custo = parseFloat(document.getElementById("custo").innerText.substr(2));
if(qtde>0){
document.getElementById("qtde").classList.remove("text-success");
document.getElementById("qtde").classList.add("text-danger");
document.getElementById("qtdeIcon").classList.remove("icon-success");
document.getElementById("qtdeIcon").classList.add("icon-danger");
document.getElementById("qtdeIcon").innerHTML = 'north_east'
}
if(qtdeAgua>0){
document.getElementById("qtdeAgua").classList.remove("text-success");
document.getElementById("qtdeAgua").classList.add("text-danger");
document.getElementById("qtdeAguaIcon").classList.remove("icon-success");
document.getElementById("qtdeAguaIcon").classList.add("icon-danger");
document.getElementById("qtdeAguaIcon").innerHTML = 'north_east'
}
if(custo>0){
document.getElementById("custo").classList.remove("text-success");
document.getElementById("custo").classList.add("text-danger");
document.getElementById("custoIcon").classList.remove("icon-success");
document.getElementById("custoIcon").classList.add("icon-danger");
document.getElementById("custoIcon").innerHTML = 'north_east'
}
Como Importar Arquivos para ESP32
Contudo para realizar a importação dos arquivos para o ESP32. Temos que criar uma pasta chamada data no mesmo local esta salvo o sketch do ESP32, e colocar os arquivo dentro dela.
Então agora no IDE do Arduíno e verificamos se possuímos a ferramenta ESP32 Sketch Data Upload instalada no IDE.
Contudo caso esteja instalada basta clicar nela, escolher SPIFFS e clicar OK. Logo após apertamos no botão BOOT do ESP32. Entretanto caso esteja instalado faça o download do arquivo, abra o Explorador de Arquivos e vá em Documentes -> Arduino -> tools -> ESP32FS -> tool. Em seguida coloque o arquivo, caso não tenha as pastas pode cria elas.
Obs.: Todos os arquivos que estiverem dentro da pasta data do ESP32 serão apagados
Resultado do Projeto Irrigação Automática
Esse foi o resultado obtido com nosso projeto.
Agradecemos sua Presença
Por fim, espero que tenham gostado e aprendido. Compartilhe com seus colegas e deixe um comentário de qual projeto deveria ser o próximo aqui no Blog da WJ Componentes!!
Enfim estarei deixando o arquivo Arduíno, software e sites utilizados e deixarei os arquivos os Fritzing dos componentes.
Fique à vontade para tirar suas dúvidas nos comentários.
Software e Sites Utilizados
Fritzing dos Componentes
GitHub das Bibliotecas Utilizadas
Post Relacionados
Posts mais Recentes
- Programando Relé RF 4331 -Instruções de emparelhamento do modo de alternância Modo de… Leia mais: Programando Relé RF 433
- Acionando motor DC por meio do pino “Touch” do ESP-32Neste projeto, vamos explorar uma aplicação ainda mais dinâmica e… Leia mais: Acionando motor DC por meio do pino “Touch” do ESP-32
- Acendendo led RGB por meio do pino “Touch” do ESP-32No último post, exploramos o fascinante mundo dos pinos touch… Leia mais: Acendendo led RGB por meio do pino “Touch” do ESP-32
- Utilizando os Pinos touch do ESP32Nesse projeto Vamos aprender a usar os pinos touch´s do… Leia mais: Utilizando os Pinos touch do ESP32
- Como Programar e Localizar o Endereço de uma Tela OLED I2C com Arduinoeste guia, vou levá-lo através dos passos para programar uma… Leia mais: Como Programar e Localizar o Endereço de uma Tela OLED I2C com Arduino
Julio Cesar Bonow Manoel
Cursando Engenharia da Computação pelo Centro Universitário Facens e atua no desenvolvimento de projetos na WJ Componentes. Participante da equipe de robótica Omegabotz.
Deixe um comentário