Manual
do
Maker
.
com
No artigo anterior relacionado dispus minha necessidade real a a forma simplória que utilizei para resolver rapidamente o problema. Nesse artigo trato novamente o tema sobre irrigação com ESP32, mas agora de forma um pouco mais elaborada, implementando multi-protocolo em uma placa da VDC, a CLP i4.0, distribuída pela AFEletronica, podendo ser encontrada nesse link. Para fazer a comunicação com ele, estou usando outra placa da AFEletronica, que é a AFSmartRadio ESP32, desenvolvida em parceria com o blog e tem honrosamente a descrição "AFEletronica & Dobitaobyte" estampada. Essa placa é uma edição especial, portanto se você é maker colecionador, não perca a chance de ter essa exclusividade! Pra finalizar, gostaria de lembrar que as placas da AFELetronica são industriais, de altíssima qualidade e não ganho um centavo pela divulgação ou venda. Desse modo, pode confiar no que estou dizendo: Essa é a melhor placa que você pode ter. A AFSmartRadio ESP32 pode ser adquirida aqui.
Já fiz review de ambas, mas a feita em parceria com a AFEletronica deixou a desejar no quesito interface, porque ela é programável apenas através de um adaptador USB-Serial. Mas se você já tem o adaptador, recomendo fortemente. Se não tem, apenas recomendo; a placa não deixa mais nada a desejar.
Estava muito bacana como fiz, adicionando um ESP32 e um módulo relé. Daí, ao voltar de viagem decidi tirar a automação e o ligamento era feito ao conectar-se ao ESP32 no modo AP. Não é nada mal essa opção, porque posso estar deitado na rede e de repente achar que o jardim precisa ser irrigado. Basta conectar à rede AP do ESP32 e pronto! Porém, quando estou no estúdio e é hora da rega, preciso descer e estar próximo do ESP32 para me conectar a ele. Não que seja mal, porque a intenção é justamente desligar a mente do trabalho, mas em alguns momentos pode não ser possível sair do estúdio de imediato e a rega pode acabar sendo deixada de lado. Para resolver essa questão, adicionei outros protocolos para fazer a rega. Os detalho a seguir.
O ESP32 funciona simultaneamente no modo AP e STA, se desejado. No modo AP uso como gatilho o número de estações conectadas a ele. Se houver 1, faz a rega. desse modo não preciso configurar um servidor web para usar um mero botão de liga e desliga, então economizo trabalho no código e recursos da MCU.
No modo STA, conecto o ESP32 à Internet. Uma das possibilidades é adicionar o protocolo MQTT, mas aí depende de broker e não acho que seja tão interessante para um trabalho doméstico, por isso implementei socket TCP.
Estando na rua ou eventualmente sem Internet, ainda será possível comunicar-se à distância com o irrigador, utilizando LoRa. Ambas as placas usam o módulo SX1276 915.0 MHz. A biblioteca utilizada é a LoRa, do autor Sandeep Mistry. Procure-a no gerenciador de bibliotecas da IDE do Arduino. Iniciei a implementação, mas ainda estou com bug, então ficará para uma versão 2.0 desse artigo, porque mais do que preguiça, estou bastante cansado do trabalho da semana e tenho até deixado de escrever por conta disso.
Ambas as placas tem um botão de uso livre. Na CLP da VDC vou utilizá-lo para acionar diretamente o relé, adicionando o mesmo tipo de controle dos demais modos, como você poderá ver no código mais adiante. Na placa feita em parceria com a AFEletronica, o botão disparará o evento LoRa para acionar a irrigação. Agora a curiosidade; a própria placa "AFE & Dobitaobyte" tem um módulo relé, mas não estava cogitando usá-la até o momento em que decidi implementar LoRa também. Que coisa, hum? Quando o código já estava praticamente concluído, adicionei o recurso. Quando o botão da placa da AFEletronica for pressionado, enviará um comando via socket para a placa da VDC. A intenção inicial era mandar o comando via LoRa, que poderá ser implementada em algum momento.
Apenas um repasse agora:
Como estou utilizando diversos recursos, vou explicar as partes e ao final disponibilizo o código completo.
Já escrevi sobre a utilização de um servidor de horas da Internet, chamado NTP. É bastante útil, mas um RTC pode ser fundamental, dependendo do quão disponível o serviço deva estar. Se não há grande prejuízo em falhas por falta de conectividade, utilizar apenas NTP é uma opção de menor custo.
Utilizando o recurso da biblioteca time.h*,*** basta termos uma struct com a disposição das variáveis relacionadas e iniciar o serviço. Na verdade, nem precisa ser struct. Como gosto de manter uma certa organização no código, fiz a seguinte implementação:
//No início do programa, inclua a biblioteca:
#include <time.h>
//Em algum ponto antes de setup(), inclua a struct:
struct tntp {
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0;
const int daylightOffset_sec = -3600 * 3;
} ntp_server;
//Ainda antes de setup, declare a função que consultará a hora:
uint8_t getHourFromNTPserver(){
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("no NTP for now");
return 0;
}
Serial.println(timeinfo.tm_hour);
// Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
//TODO: definir a hora prioritária por MQTT
if ((timeinfo.tm_hour-3 == 8 || timeinfo.tm_hour-3 == 20) && last_water_execution != timeinfo.tm_hour-3){
water_flowing = true;
water_started_at = millis();
last_water_execution = timeinfo.tm_hour-3;
return 1;
}
if (water_flowing){
if ((millis()-water_started_at) > water_time_secs){
water_flowing = false;
return 0;
}
return 1;
}
return 0;
}
//Agora dentro de setup(), coloque essa linha (pode ser a primeira de setup):
void setup(){
configTime(ntp_server.gmtOffset_sec, ntp_server.daylightOffset_sec, ntp_server.ntpServer);
...
}
Na definição acima, se for 8 da manhã ou 20 horas, deve ligar. De outro modo, desligar. Ficará ligado pelo tempo determinado em water_time_secs.
Já escrevi sobre socket com ESP32 em alguns artigos, mas nada como rememorar.
Para criarmos um socket TCP, utilizamos o recurso da própria biblioteca WiFi.h, de maneira bastante simples. Mas no caso, estou deixando uma tarefa dedicada ao socket no núcleo 0 do ESP32, por isso temos alguns conceitos importantes a abordar agora.
Como é necessário alguma interação no setup, criei uma instância do objeto WiFiServer. Essa instância é um ponteiro para o tipo WiFiServer, da biblioteca WiFi.h. Lá em algum lugar no início do código (antes do setup e fora de qualquer função) declaramos:
WiFiServer *sockServ;
Dentro da função setup() fazemos então a conexão WiFi AP e STA:
WiFi.mode(WIFI_MODE_APSTA);
WiFi.softAP(ssid, password);
WiFi.begin(sta_ssid,sta_pwd);
E antes da última linha de setup(), criamos a instância com new e iniciamos o socket. Isso deve ser feito depois de estabelecer a conexão WiFi, por isso o objeto está sendo manipulado desse jeito.
sockServ = new WiFiServer(123); //socket aberto na porta 123
sockServ->begin();
Não é usual em Arduino, apesar de ser algo trivial na linguagem C++, mas utilizamos o operador new. A regra é que tudo que for criado com new, deve ser excluído com delete. Isso significa que se estivéssemos utilizando um programa no desktop, causaríamos um memory leak, caso o objeto fosse deixa em algum momento. Porém em nossa MCU o processo tende a ser perpétuo, de modo que não precisaremos nos preocupar com isso. Iniciaremos agora uma task, como chamada a thread em sistemas operacionais de tempo real. A task (assim como as threads em sistemas operacionais x86, ARM, MIPS etc) executa um processo de forma assíncrona. Desse modo, o que estiver sendo executado no núcleo 1, que é o núcleo da função main(), a partir de onde são executados setup() e loop(), rodará sem interrupções. Do mesmo modo a task que deixaremos no núcleo 0, que estará dedicada ao gerenciamento da comunicação via socket.
Para executar uma task especificando o núcleo, usamos essa última linha na função setup():
xTaskCreatePinnedToCore(taskSocket,"taskSocket",10000,NULL,0,NULL,0);
taskSocket é a referência da função que criamos para ser executada. O mesmo nome dentro de aspas, dessa vez para ser identificado na fila de tarefas do FreeRTOS. O resto deixe como está, mas se quiser saber mais sobre tasks no ESP32, sugiro alguns artigos:
Selecionar CPU para executar tasks
E tem outros mais, citados dentro dos próprios artigos!
Pra finalizar, faltou declararmos a função taskSocket. Ela fica desse jeito para ser usada como task:
void taskSocket(void *pvParameters){
while (true){
String payload_from_socket = "";
WiFiClient client2 = sockServ->available();
if (client2) {
while (client2.connected()) {
while (client2.available()>0) {
char c = client2.read();
//client.write(c);
payload_from_socket += c;
}
//delay(10);
}
client2.stop();
Serial.println("Fim da conexao");
state_machine.ap_rf_mqtt_ntp_sock[pos_sock] = payload_from_socket.indexOf("^1$") < 0 ? 0 : 1;
Serial.printf("Payload from socket: %s",payload_from_socket);
}
}
}
Não se preocupe com o conteúdo do código acima. Tudo está devidamente declarado em seu lugar, só estou mostrando que parte do código faz o quê.
Por fim, se quiser testar o socket, podemos fazer de diversas maneiras. Em breve mostrarei como fazê-lo no Windows, mas se você tem Linux, é muito simples fazer a comunicação socket sem programar nenhuma linha, como exemplifico esse e outros recursos nesse artigo. Abra um terminal e digite:
echo ^1$ >/dev/tcp/192.168.1.64/123
Isso deverá acionar o relé. Para desligar, use a mesma linha, trocando apenas 1 por 0:
echo ^0$ >/dev/tcp/192.168.1.64/123
No vídeo faço as demonstrações, iniciando na bancada e depois no jardim.
O modo AP é o mais simples. Como o único papel é ligar e desligar a solenoide, não faz sentido uma página web com um botão. Basta pegar o número de hosts conectados ao ESP32. Se for igual a 1, aciona a solenoide.
Esse é o trecho de código pertinente ao modo AP:
WiFi.mode(WIFI_MODE_APSTA); //porque estamos usando o socket server também
WiFi.softAP(ssid, password); //cria a rede AP
A rede aparecerá com o nome definido por você. Na função loop devemos colocar apenas isso:
state_machine.sm_time_to_water = WiFi.softAPgetStationNum();
state_machine.ap_rf_mqtt_ntp_sock[pos_ap] = state_machine.sm_time_to_water == 1 ? 1 : 0;
Essa condicional ternária atribui o valor 0 ou 1 à variável que analisa se deve ser acionado o relé. A análise dessa variável está mais pra frente e foi prolongada com outras condições para que fosse possível apenas um ponto controlar o relé através de qualquer origem.
O código completo (para utilizar com a VDC - para outras, adapte) está no github, através desse link.
Se não leu o artigo relacionado a essa CLP i4.0 da VDC, recomendo a leitura. No artigo, cito como fazer execução assíncrona, que é algo bastante interessante.
A placa deve ser alimentada por fonte externa de 12 à 28 VDC. O pinout é bem recheado, como pode ser visto a seguir:
Já notou o pinout de 0x20 e 0x27? São duas PCF8574, um expansor de IO do qual falo amplamente e sempre que possível, retomo. E recomendo o artigo Dominando PCF8574. Mas para fazer a manipulação dos bits, estou usando a biblioteca EasyPCF8574que você encontra no repositório oficial de bibliotecas. A propósito, essa é uma das bibliotecas que criei, e já mostrei como criar bibliotecas para Arduino.
Se passou sem ler, vou dar uma colher de chá e colocar o link do repositório com o código para irrigação com ESP32 mais uma vez. Como citei, estou bastante cansado do trabalho e não queria deixar de escrever algo para não parecer que o blog está "abandonado". As partes dispostas com explanação são funcionais. A parte de LoRa e MQTT eu resolvi deixar para quando tiver mais energia.
O vídeo deve sair pelo meio da semana. Farei meu melhor para apresentar um vídeo bem feito. Aproveite para se inscrever no canal e aquecer meu coração para que eu, mesmo cansado, continuar produzindo conteúdo com carinho!
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.