Irrigação Automática com ESP32

, , ,

Olá, como estão? A princípio iremos demonstrar como utilizar o KIT de irrigação automática com ESP32 da WJ Componentes.

Sensor Umidade Solo Anticorrosivo
ESP32

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 ConsumoUnidadeCusto de Água (R$)Custo de Esgoto (R$)Custo Total (R$)
De 0 a 10 (Mínimo)Mês17,0515,7732,82
De 11 a 15m32,562,374,39
De 16 a 20m33,733,457,18
De 21 a 25m35,415,0010,41
De 26 a 30m35,955,5011,45
De 31 a 40m36,245,7712,01
De 41 a 50m36,566,0612,62
De 51 a 75m36,916,3913,30
De 76 a 100m37,076,5413,61
De 101 a 200m38,487,8416,32
De 201 a 300m310,179,4119,58
Acima de 300m312,2011,2923,49

Montagem do Projeto Irrigação Automática


Logo após compra dos componentes na WJ Componentes, vamos à montagem!

Pinout Sensor Vazão de Água

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.

Pinout Sensor Umidade Solo Anticorrosivo

Contudo segue abaixo uma imagem que demonstrando a montagem do circuito.

Montagem do Circuito Eletrônico na Protoboard - Irrigação Automática

Diagrama Esquemático do Projeto Irrigação Automática


Diagrama do Circuito Eletrico - 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.

Como Importar Arquivos para ESP32

Então agora no IDE do Arduíno e verificamos se possuímos a ferramenta ESP32 Sketch Data Upload instalada no IDE.

ESP32 Sketch Data Upload

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.

Site - Irrigação Automática

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

    Julio Cesar Bonow Manoel

    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

    O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *