Manual

do

Maker

.

com

Irrigação com ESP32 : multi-protocolo

Irrigação com ESP32 : multi-protocolo

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.

Projeto de irrigação com ESP32

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.

ESP32 no modo AP e STA

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.

ESP32 com LoRa

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.

Irrigação com ESP32 através de botão

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.

Resumo das interfaces

Apenas um repasse agora:

  • MQTT - ficará para outra ocasião, mas previsto.
  • Client AP - funcionalidade inicial, reimplementada.
  • LoRa - ficará para outra versão.
  • Botão - Em ambas as placas, sendo que na placa da AFEletronica enviará um comando via socket.

Recursos utilizados

Como estou utilizando diversos recursos, vou explicar as partes e ao final disponibilizo o código completo.

NTP

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.

Socket server para fazer irrigação com ESP32

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.

Instância do objeto

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:

Parâmetros através de tasks

Controle de tasks

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.

Modo AP para fazer irrigação com ESP32 rapidamente

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.

Pinout da CLP i4.0 da VDC

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:

vdc-001.png
vdc-002.png
vdc-003.png

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.

Código para irrigação com ESP32 usando a VDC i4.0

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 repositório é esse.

Vídeo de irrigação com ESP32

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.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.