Automação Residencial

Fonte Bebedouro para Gatos com ESP8266

Eletrogate 23 de agosto de 2022

Introdução

Bichanos são, frequentemente, vistos como pets independentes. E não faltam motivos para isso! Eles cuidam da própria higiene, enterram suas necessidades e não precisam fazer caminhadas diárias. Entretanto, em algumas questões, os felinos precisam de ajuda. Uma delas é a hidratação.

Afinal, como fazer o gato beber água?

Nesse projeto, criamos um bebedouro que cria um fluxo de água corrente, evitando deixar água parada, o que é muito mais atrativo para os felinos, além de evitar que acumule lodo ou sujeira. E, como pode funcionar de maneira automática, é muito fácil de utilizar! Basta colocar a água e esperar o bichano se refrescar!

Esse projeto serve, também, para bebedouro de cachorro, mas será necessário fazer algumas modificações.


Como Funciona?

O objetivo do projeto é chamar a atenção do gato para ele se hidratar. Para isso, o bebedouro pode operar de 4 formas diferentes:

  1. Ligada permanentemente;
    • Bomba ligada todo o tempo.
  2. Acionamento por intervalo de tempo;
    • A bomba irá ligar após um intervalo predefinido no código.
  3. Acionamento manual pela página web;
    • A bomba só será acionada quando clicar no botão na interface web.
  4. Acionamento manual e por tempo em conjunto.
    • Acionamento combinando o modo 2 e 3.

Você pode alternar entre os modos de operação. Mostrarei como fazer isso a seguir.

Após acionar a bomba de uma das formas citadas acima, o ESP8266 fecha o relé que aciona o circuito da bomba e faz a água jorrar.

ATENÇÂO: O sistema não detecta, da forma como está, o nível de água e, por isso, há o risco de queima da bomba por volume insuficiente de água no recipiente. Verifique, periodicamente, o nível de água para evitar a queima. O nível não pode ser menor que a altura da bomba.

Circuito

O circuito deve ser alimentado com uma fonte de 5V e 1A.


Impressões 3D

As partes foram impressas em PLA, mas podem ser impressas em ABS ou PETG. Os respectivos arquivos podem ser baixados por este link, que contém os arquivos editáveis (.ipt) e os arquivos para impressão (.stl). Imprima com as configurações da impressora que deseja.


Código

Para compilar o código do bebedouro, são necessárias as seguintes bibliotecas:

  • ESPAsyncTCP.h – Biblioteca TCP assíncrona, disponível em: Github
  • ESPAsyncWebServer.h – Biblioteca para criar um servidor assíncrono HTTP e WebSocket para ESP8266, disponível em: Github

Bebedouro.ino

/**
 * @file Bebedouro.ino
 * Criado por: Saulo Aislan
 * @brief Firmware para controlar o motor bomba do bebedouro para gato.
 * @version 0.1
 * 
 * @copyright Copyright (c) 2022
 * 
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

#define PIN_MOTOR 4 // Pino D2 do Wemos

const char* ssid     = "Bebedouro"; // Nome da rede
const char* password = "123456789"; // Senha da rede

// AsyncWebServer objeto na porta 80
AsyncWebServer server(80);

bool buttonStatus = false; // Habilitando ou não o botão na página
bool motorStatus = false; // Status do motor bomba
uint8_t paramMotorStatus = 0;
uint8_t controlMode = 0;
uint16_t interval = 60; // Intervalo de acionamento do motor no modo automático (timeControl) em minutos

/* Prototipo da funcao */
void withOutControl(void);
void controlManual(void);
void timeControl(int interval);
String processor(const String& var);

/**
 * @brief Função que aciona a bomba, sem controle de tempo ou manual, acionamento constante
 */
void withOutControl(void){
  digitalWrite(PIN_MOTOR, HIGH);
  buttonStatus = false;
}

/**
 * @brief Função para o controle manual da bomba, habilitando o botão na página
 */
void controlManual(void) {
  Serial.println("Controle Manual");
  buttonStatus = true;
}

/**
 * @brief Função de acionamento por tempo do motor bomba
 * @param int Intervalo em minutos de desligada para ligada 
 */
void timeControl(int i_interval){
  i_interval = i_interval*60*1000;
  digitalWrite(PIN_MOTOR, LOW);
  delay(i_interval);
  digitalWrite(PIN_MOTOR, HIGH);
  delay(15000); // Tempo que vai permanecer ligada 15s
}

/**
 * @brief Função para o controle manual da bomba,
 * recebe o status recebido e aciona o motor bomba
 * @param AsyncWebServerRequest ponteiro para o request HTTP_GET
 */
void manualControl(AsyncWebServerRequest* request) {
  bool st;

  if (request->hasParam("st"))
    st = request->getParam("st")->value().toInt();
  else
    st = 0;

  if(st){
    digitalWrite(PIN_MOTOR, HIGH);
    Serial.println("Motor: ON");
    request->send(200, "text/html", SendHTML()); 
  } else {
    digitalWrite(PIN_MOTOR, LOW);
    Serial.println("Motor: OFF");
    request->send(200, "text/html", SendHTML());     
  }
}

/**
 * @brief Função que cria a página (HTML) de configuração
 * @return String com o HTML da página
 */
String SendHTML(){
  String ptr = "<!DOCTYPE html><html>";
  ptr.concat(F("<head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">")); 
  ptr.concat(F("<title>Bebedouro para Gato</title>")); 
  ptr.concat(F("<style>html {font-family: Arial, Helvetica, sans-serif; display: inline-block; margin: 0px auto; text-align: center;}")); 
  ptr.concat(F("body{margin: 0;font-size: 1rem; color: #1e4659;background-color: #f2f2f2;} h3 {color: #444444;margin-bottom: 50px;}")); 
  ptr.concat(F(".topnav{overflow: hidden; background-color: #04448c; text-align: center; color: white;}")); 
  ptr.concat(F("form{border-radius: 5px;border-radius: 5px;padding: 20px;}")); 
  ptr.concat(F("input {width: 100%;padding: 12px 20px;margin: 8px 0;display: inline-block;border: 1px solid #ccc;border-radius: 4px;box-sizing: border-box;}")); 
  ptr.concat(F(".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}")); 
  ptr.concat(F(".button-on {background-color: #4caf50;}")); 
  ptr.concat(F(".button-off {background-color: #f44336;}")); 
  ptr.concat(F("table{border-collapse: collapse;} td{border: 1px solid #ffc107;padding:8px;}"));    
  ptr.concat(F("</style></head>")); 
  ptr.concat(F("<body><div class=\"topnav\"><h1>Bebedouro para Gato</h1></div>")); 
  ptr.concat(F("<div><form name=\"formMode\"><div style=\"border: 1px solid #ffc107;\"><div style=\"background-color: #ffc107; padding: 10px; font-weight: bold;\"><label for=\"mode\">Modos de operação</label></div>")); 
  ptr.concat(F("<table style=\"width: 100%;\"><tr><td style='width: 50%'><b>Modo atual:</b></td>")); 
  if(paramMotorStatus == 0)
  {
    ptr.concat(F("<td>-</td></tr>")); 
  } else if(paramMotorStatus == 1)
  {
    ptr.concat(F("<td>Ligada direto</td></tr>")); 
    buttonStatus = false;
  } else if(paramMotorStatus == 2)
  {
    ptr.concat(F("<td>Acionamento por intervalo de tempo</td></tr>")); 
    buttonStatus = false;
  } else if(paramMotorStatus == 3)
  {
    ptr.concat(F("<td>Acionamento manual</td></tr>"));
    buttonStatus = true;
  } else if(paramMotorStatus == 4)
  {
    ptr.concat(F("<td>Acionamento manual ou por intervalo de tempo</td></tr>")); 
    buttonStatus = true;
  }
  
  ptr.concat(F("<tr><td colspan=\"2\"><b>Escolha o Modo de operação:</b></td></tr>")); 
  ptr.concat(F("<tr><td colspan=\"2\"><select onchange=\"setmode()\" name=\"opMode\" id=\"mode\">")); 
  ptr.concat(F("<option value=\"-\">-</option>")); 
  ptr.concat(F("<option value=\"/mode?op=1\">Ligada</option>")); 
  ptr.concat(F("<option value=\"/mode?op=2\">Por tempo</option>")); 
  ptr.concat(F("<option value=\"/mode?op=3\">Manualmente</option>")); 
  ptr.concat(F("<option value=\"/mode?op=4\">Por tempo e manualmente</option></select></td></tr></table></form></div>")); 

  if(buttonStatus)
  {
    if(digitalRead(PIN_MOTOR) == LOW)
    {
      ptr.concat(F("<div><div style='background-color:#ffc107;padding: 10px;font-weight: bold;margin: auto;margin-bottom: 2%;width: 50%;'>Bebedouro: <span style='color: #f44336;'>Desligado</span></div><a class='button button-on' href=\"/bomba?st=1\">Ligar</a></div>"));
    } else {
      ptr.concat(F("<div><div style='background-color:#ffc107;padding: 10px;font-weight: bold;margin: auto;margin-bottom: 2%;width: 50%;'>Bebedouro: <span style='color: #4caf50;'>Ligado</span></div><a class='button button-off' href='/bomba?st=0'>Desligar</a></div>"));
    }
  } else {
    ptr.concat(F("<div><a class='button button-on' style='background-color:#c3c3c3;'> - </a></div>"));
  }

  ptr.concat(F("<script type=\"text/javascript\">function setmode(){"));
  ptr.concat(F("location=document.formMode.opMode.options[document.formMode.opMode.selectedIndex].value"));
  ptr.concat(F("}</script>"));
  ptr.concat(F("</body></html>"));
    
  return ptr;
}

/******************* Função em setup (setup) *********************/
void setup(){
  Serial.begin(115200);
  pinMode(PIN_MOTOR, OUTPUT);
  
  Serial.print("Access Point…");
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
    request->send(200, "text/html", SendHTML()); 
  });

  server.on("/mode", HTTP_GET, [](AsyncWebServerRequest *request){
    if (request->hasParam("op"))
    {
      paramMotorStatus = request->getParam("op")->value().toInt();
      controlMode = paramMotorStatus;
    }
    else
    {
      paramMotorStatus = 0;
    }
    Serial.print("Modo: ");
    Serial.println(paramMotorStatus);

    request->send(200, "text/html", SendHTML());
  });

  server.on("/bomba", HTTP_GET, manualControl);
  
  // Start server
  server.begin();
}

/******************* Função em loop (loop) *********************/
/**
 * @brief Máquina de estados para os modos de acionamento do motor bomba
 */
void loop(){
  switch(controlMode){
    case 1:
      withOutControl(); // Acionamento direto, motor ligado todo o tempo
      break;
    case 2:
      timeControl(interval); // Acionamento por tempo (minutos)
      break;
    case 3:
      controlManual(); // Acionamento manual
      break;
    case 4: // Caso 4 o motor poderá ser acionado manualmente e pelo tempo
      controlManual(); // Acionamento manual
      timeControl(interval); // Acionamento direto, motor ligado todo o tempo
      break;
    default:
      timeControl(30); // Acionamento por tempo (30 minutos)
  }
}

Montagem e Configurações

Monte todo o circuito e bomba nas respectivas cases junto com o tubo como nas figuras abaixo, use cola quente para fixar melhor os componentes:

         

         

Coloque a bomba dentro do recipiente (algo como um pote de sorvete, por exemplo) e fixe, de alguma forma, a bomba no fundo do recipiente e o circuito na lateral de fora do recipiente para evitar contato com a água.

Ao ligar o bebedouro, ele irá criar uma rede como Access-Point com o nome de “Bebedouro”. Com um celular ou computador, acesse a rede criada e insira a senha que está no código na linha “const char* password“. Em seguida, clique em conectar.

Uma vez conectado à rede, acesse o IP: 192.168.4.1 para abrir a página de configuração. Ao acessa-lo, será aberta uma página como essa:

     

Os modos de operação podem ser selecionados através da página web de configuração na caixa de seleção “Escolha o Modo de operação”. Se o modo escolhido for o 3 ou o 4, o botão Ligar/Desligar irá aparecer para o acionamento manual.

ATENÇÂO: O sistema não detecta, da forma como está, o nível de água e, por isso, há o risco de queima da bomba por volume insuficiente de água no recipiente. Verifique, periodicamente, o nível de água para evitar a queima. O nível não pode ser menor que a altura da bomba.

Conclusão

Nesse tutorial, aprendemos a montar um bebedouro para ajudar seu amigo de pelos tomar água. Esse projeto é totalmente Open-source e você pode modificar o que quiser, adicionar ou remover componentes, conectar o bebedouro direto na sua rede local. Deixe sua imaginação fluir. Para mais material como este, continue acompanhando as postagens semanais do blog e não deixe de visitar nossa loja. Lá, você encontra todos os componentes necessários para desenvolver esse e muitos outros projetos!

Que a força estejam com vocês e até mais!


Sobre o Autor


Saulo Aislan

Graduando em Tecnologia em Telemática pelo IFPB – Campus de Campina Grande – PB. Tenho experiência com os microcontroladores da família Arduino, ESP8266, ESP32, STM32 e microprocessador Raspberry Pi. Tenho projetos na áreas de IoTs voltada para a indústria 4.0, agroindústria e indústria aeroespacial civil utilizando LoRa, Bluetooth, ZigBee e Wi-Fi. Atualmente estudando e desenvolvendo em FreeRTOS para sistemas em tempo real com ESP32 e LoRaWan para Smart City e compartilhando alguns projetos no blog da Eletrogate.


Eletrogate

23 de agosto de 2022

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Assine nossa newsletter e
receba  10% OFF  na sua
primeira compra!