Projeto: Sistema de irrigação automático integrado ao Telegram com Arduino!
Hoje vou apresentar um projeto que desenvolvi junto a alguns amigos. Um sistema capaz de controlar a irrigação de uma plantação de forma autônoma, integrada a um bot no Telegram.
Tutoriais relacionados:
- NodeMCU: Introdução (em breve)
- Como conectar um NodeMCU a Internet (em breve)
- Como integrar o Arduino ao Telegram (em breve)
- Arduino OTA: Atualize o código sem fio! (em breve)
Introdução:
Alguns amigos estavam terminando o curso técnico em eletrotécnica e precisavam desenvolver um projeto de conclusão, porém estavam com dificuldades com a programação, aí que entrei na história.
A ideia era desenvolver um projeto que fosse capaz de controlar um sistema de irrigação de uma plantação, porém não queríamos algo que só ligasse e desligasse uma válvula.
Por isso pensamos que seria interessante que os dados de umidade coletados fossem entregues por um chatbot.
A ideia principal desse post não é ser um tutorial que explicará passo-a-passo, mas sim apresentar o projeto para que você se inspire, copie e o melhore. Em breve postarei os tutoriais detalhando cada função.
A primeira coisa que fizemos foi definir as funcionalidades que o projeto deveria ter para sabermos o que deveríamos fazer:
- Ele deveria ser capaz de identificar a umidade do solo;
- Controlar um relé para que fosse possível acionar uma bomba/válvula;
- O protótipo deveria ter algumas luzes de indicação para sabermos quando a umidade estivesse crítica e se o sistema de irrigação está ligado;
- Exibir a umidade atual da plantação no Telegram, e permitir que o usuário ligasse a irrigação de forma manual;
- O protótipo deveria ser seguro, por isso além da luz de indicação de quando o sistema de irrigação estivesse ligado, deveria possuir um botão de emergência que desligasse o relé;
- Função sem necessidade, mas que tomei como desafio pessoal: O sistema deveria permitir a atualização do código sem fio.
Programação
Por gosto pessoal (e um pouco de mania), costumo separar o código em partes, assim fica mais fácil de encontrar um possível bug ou fazer uma melhoria.
O código foi dividido em 6 partes:
- Main;
- Bot;
- WiFi;
- OTA;
- Pinagem;
- Configurações básicas.
Configurações básicas e pinagem:
Na minha opinião, a vantagem de separar os parâmetros e configurações básicas é que se torna bem mais simples à modificação de qualquer um deles, sem a necessidade de ter que procura-los no meio do código.
//Configurações de WiFi (Rede que a placa vai se conectar (tem que ter acesso à Internet)) const char* ssid = "nome da rede"; //Rede WiFi const char* password = "senha"; //Senha do WiFi //Configurações do BOT do Telegram const char* botToken = "token"; //Token do BOT #define ligarsistem 50 //em %umidade #define desligarsistem 70 //em %umidade #define muitoalto 90 //em %umidade #define muitobaixo 30 //em %umidade #define maxleitura 1023 #define minleitura 0
Aqui definimos coisas como a rede na qual a placa vai se conectar e o token do bot que será utilizado. Além disso também estão aqui alguns parâmetros fundamentais para o funcionamento do sistema, como em qual porcentagem a umidade será considerada crítica.
#define sensor A0 #define rele 16 //D0 #define ledligado 5 //D1 #define leddesligado 4 //D2 #define botaoemergencia 14 //D5 #define ledalta 13 //D7 #define ledbaixa 12 //D6
A pinagem deste código é bem simples, alguns pinos declarados acabaram não sendo utilizados na versão final do código, mas acabaram ficando declarados de herança.
Uma observação sobre o NodeMCU:
Os números escritos na placa indicando os pinos não correspondem aos pinos GPIO da placa, por exemplo, o pino D0 indicado na placa corresponde ao pino 16 na programação.
(Nessa parte não tem muita coisa interessante na programação)
Agora começamos a parte funcional, onde configuramos as funcionalidades do código.
WiFi:
#include <ESP8266WiFi.h>
void setupWiFi() {
// Define o modo de operação apenas como STA (cliente)
WiFi.mode(WIFI_STA);
// Conectando-se à rede Wi-Fi
Serial.print("Conectando à rede Wi-Fi");
WiFi.begin(ssid, password);
// Aguarda a conexão com a rede Wi-Fi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Printa o IP
Serial.println();
Serial.print("Conectado! IP: ");
Serial.println(WiFi.localIP());
// Remove a verificação SSL (caso esteja utilizando o cliente seguro)
client.setInsecure();
}Como vimos no nosso tutorial (ainda não postado, paciência que estamos começando) de NodeMCU,a configuração de conexão ao WiFi é muito simples.
OTA:
O over the air (sob o ar), ou OTA para os íntimos é uma maneira de atualizarmos o código da placa sem uma conexão física, usando a rede local.
#include <ArduinoOTA.h>
void setupOTA() {
//Configuração do OTA
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else {
//U_SPIFFS
type = "filesystem";
}
Serial.println("Iniciando atualização: " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nAtualização Concuída!");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progresso: %u%%\r", (progress * 100) / total);
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Erro[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Falha na autenticação");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Falha ao iniciar");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Erro de conexão");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Erro ao receber");
} else if (error == OTA_END_ERROR) {
Serial.println("Erro ao finalizar");
}
});
ArduinoOTA.begin();
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
void loopOTA() {
ArduinoOTA.handle();
}Sem duvidas o OTA foi o que mais deu trabalho (e problemas) nesse projeto.
Como a placa ficaria dentro de uma caixa alimentada por uma bateria, não seria pratico ter que desmontar todo o protótipo para ter acesso ao circuito e atualizar o código. Por isso surgiu a ideia de fazer isso sem fio, de uma forma.
A ideia original era que pudéssemos carregar o código em algum lugar como um repositório e quando a placa ficasse on-line procurasse a atualização e a instalasse, independente do local e da rede que ela estivesse atualizada.
Tentamos usar o GitHub para isso, mas acabou não dando certo, a placa ficava presa em um loop tentando instalar o arquivo binário.
Como tínhamos um tempo limitado e essa função não era realmente necessária decidimos abandona-la, mas para não cortamos por completos optamos por um modo mais simples que permite a atualização apenas em redes locais.
Em breve vou fazer um tutorial dedicado ao OTA e fazer essa ideia de atualização remota funcionar.
Bot:
#include <WiFiClientSecure.h> //WiFi Telegram
#include <UniversalTelegramBot.h> //BOT Telegram
WiFiClientSecure client;
UniversalTelegramBot bot(botToken, client);
unsigned long lastTimeBotRan = 0;
const long botInterval = 1000; //Intervalo para verificações
//Void Mensagens BOT
void handleNewMessages(int numNewMessages) {
for (i = 0; i < numNewMessages; i++) {
String chat_id = String(bot.messages[i].chat_id); //Obtem ID dos usuarios
String text = bot.messages[i].text; //Obtem as mensagens enviadas
String from_name = bot.messages[i].from_name; //Obtem o nome do remetente
if (from_name == "")
from_name = "Usuário"; //Define Nome padrão caso o usuario não preencha no Telegram
//Define funcionalidade do /Start
if (text == "/start") {
String resposta = "Olá " + from_name + ", bem-vindo(a)!\nComandos:\n/umidade - Informa a umidade atual. \n/liga - Liga o sistema. \n/desliga - Desliga o sistema. \n/estado - Informa o estado atual do sistema.\n/auto - Liga/desliga o modo automático do sistema.";
bot.sendMessage(chat_id, resposta, "");
}
//Define a funcionalidade do /umidade
else if (text == "/umidade" || text == "Umidade" || text == "umidade") {
String resposta = from_name + ", a umidade atual é " + String(umidade) + "%."; //Mensagem que o BOT enviará no comando /umidade
bot.sendMessage(chat_id, resposta, "");
}
//Define a funcionalidade do /liga
else if (text == "/liga" || text == "Liga" || text == "liga") {
liga();
String resposta = from_name + ", a irrigação está **LIGADA**"; //Mensagem que o BOT enviará no comando /liga
bot.sendMessage(chat_id, resposta, "");
}
//Define a funcionalidade do /desliga
else if (text == "/desliga" || text == "Desliga" || text == "desliga") {
desliga();
String resposta = from_name + ", a irrigação está **DESLIGADA**"; //Mensagem que o BOT enviará no comando /desliga
bot.sendMessage(chat_id, resposta, "");
}
//Define a funcionalidade do /estado
else if (text == "/estado" || text == "Estado" || text == "estado") {
String resposta = from_name + ", a irrigação está: " + String(estado) + "."; //Mensagem que o BOT enviará no comando /estado
bot.sendMessage(chat_id, resposta, "");
}
//Define a funcionalidade do /auto
else if (text == "/auto" || text == "Auto" || text == "auto") {
autom = !autom;
String resposta = from_name + ", o modo automatico foi alterado, ele está: " + String(autom) + "."; //Mensagem que o BOT enviará no comando /estado
bot.sendMessage(chat_id, resposta, "");
}
//The Capybara Society
else if (text == "/capivara" || text == "Capivara" || text == "capivara") {
String resposta = "The Capybara Society gives its blessing to this code!";
bot.sendPhoto(chat_id, "https://imgur.com/clwMZsF", resposta);
}
//Comandos não reconhecidos
else
bot.sendMessage(chat_id, "Comando não reconhecido!");
}
}
//Fim Void Mensagens BOT
void loopBOT() {
//Verifica se tem novas mensagens
if (millis() - lastTimeBotRan > botInterval) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages) {
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
lastTimeBotRan = millis();
}
//Fim Verifica se tem novas mensagens
}
//Fim do loopBOTA integração entre o bot e o Arduino está detalhado no nosso tutorial (que também não foi publicado), mas de forma simplificada o código coleta 3 dados do Telegram:
- O ID do usuário: É como se fosse o “CPF” da sua conta no Telegram, ele é fundamental para sabermos quem enviou a mensagem e para quem devemos responde-la.
- O nome do usuário: Através do ID conseguimos identificar o nome do usuário cadastrado na conta do Telegram, assim podemos deixar as mensagens mais personalizadas e o bot menos artificial (se isso for possível)
- A mensagem enviada: A informação coletada mais importante, já que é através dela que saberemos qual ação o Arduino deve executar, seja acionar o sistema de irrigação ou informar a umidade atual.
Sempre que possível tento esconder uma capivara como easter-egg (talvez seja meu animago), por isso o comando /capivara envia uma foto de uma capivara. Para simplificar esse envio preferi hospedar a imagem on-line para não sobrecarregar a memoria do NodeMCU.
Esse código poderia ser melhor otimizado, talvez fazendo com que existisse apenas um comando para enviar a resposta e não separadamente como está, mas como disse, não tínhamos um prazo longo, então o importante foi estar funcional e não otimizado (o bom e velho XGH).
//Inclusão
#include "configuracoes_basicas.h" //Chamas as configurações básicas
#include "Pinagem.h" //Inclui pinos
//Declarando variaveis
int i; // Variavel de repetição
int umidade; //Variavel de umidade
bool estado;
bool autom = HIGH;
//Fim Declarar variaveis
//Void Setup
void setup() {
setupOTA(); //Chama funções OTA
setupWiFi(); //Chama funções WiFi
Serial.begin(9600);
Serial.println("Inicializando...");
//Setup funcional
//Define função dos pinos
pinMode(sensor, INPUT);
pinMode(rele, OUTPUT);
pinMode(ledligado, OUTPUT);
pinMode(leddesligado, OUTPUT);
pinMode(ledalta, OUTPUT);
pinMode(ledbaixa, OUTPUT);
pinMode(botaoemergencia, INPUT_PULLUP);
//Fim setup funcional
}
//Fim void Setup
//Void Loop
void loop() {
loopOTA(); //Chama Funções do OTA
loopBOT(); //Chama funções do BOT
//mapeia a umidade de 0 a 100
umidade = map(analogRead(sensor), minleitura, maxleitura, 100, 0);
if (umidade < 0)
umidade = 0;
if (umidade > 100)
umidade = 100;
Serial.println(umidade);
//Desliga o sistema caso ultrapasse o limite
if (umidade >= desligarsistem && autom == HIGH)
desliga();
//Liga o sistema caso esteja baixa
if (umidade <= ligarsistem && autom == HIGH)
liga();
//Liga o led de atenção caso a umidade esteja muito baixa ou muito alta
if (umidade >= muitoalto || umidade <= muitobaixo) {
digitalWrite(ledalta, HIGH);
} else
digitalWrite(ledalta, LOW);
//Faz a leitura do botão de emergencia
if (digitalRead(botaoemergencia) == LOW)
desliga();
}
//Fim Void Loop
void liga() {
estado = HIGH;
digitalWrite(rele, HIGH);
digitalWrite(ledligado, HIGH);
digitalWrite(leddesligado, LOW);
}
void desliga() {
estado = LOW;
digitalWrite(rele, LOW);
digitalWrite(ledligado, LOW);
digitalWrite(leddesligado, HIGH);
}É aqui que a mágica funciona, no main todos os códigos são integrados e onde está toda a parte lógica do projeto como o mapeamento do valor lido em porcentagem de umidade e as funções de ligar e desligar a irrigação.
Circuito:
Itens usados:
- NodeMCU com ESP8266;
- Sensor de umidade de solo;
- LEDs;
- Muitos fios e cola quente.
Comprando através desse link, você não paga nada a mais e ajuda a manter esse projeto vivo.
O circuito desse projeto é simples, com apenas a placa, alguns LEDs, o sensor de umidade do solo.
Sempre que possível, uso os botões em modo PULL_UP, dessa forma é necessário colocar nenhum resistor ou ligação mais complexa, ligando apenas entre o GND e o pino da placa.
Novamente, como esse projeto era um protótipo conceitual e não um projeto final e tínhamos um prazo curto para executa-lo não otimizamos o circuito, o que implicou na forma de alimentação dele. Optamos em usar um power bank para alimentar o Arduino, apesar de não ser o ideal, era o que tínhamos disponível no momento.
Como fiquei responsável apenas pelo software não tenho o circuito final detalhado, nem fotos do mesmo ou o projeto finalizado, mas dou a minha palavra que ele funcionou e eles tiveram o projeto aprovado com louvor.
Foram utilizadas algumas bibliotecas externas, que podem ser instaladas pela própria IDE do Arduino, como ensinado nesse post: (que ainda não existe mas vai vir).
Esse é o primeiro post de verdade do nosso blog, ainda estou pegando o jeito, por isso seu feedback é muito importante, você pode nos encontrar no Instagram, Thingverse, GitHub.
Agradeço ao meu grande amigo Raul que confiou em mim para ajudar nesse projeto e permitiu que eu o compartilhasse aqui.
Espero que tenha sido útil e que tenha te inspirado a desenvolver a sua própria versão (ou algo completamente diferente).







Publicar comentário