Atualmente, as mais variadas soluções que envolvam dispositivos eletrônicos podem contar com a maior oferta de conectividade já vista, havendo um significativo número de possibilidades para enviar dados para a nuvem, de forma que sejam disponibilizados a sistemas e plataformas online para futura análise e processamento. Dentre as opções atuais do mercado em redes LPWAN para esta finalidade, uma que ganha destaque é o LoRaWAN, devido ao baixo custo anual de conectividade, grande disponibilidade no Brasil, baixíssimo consumo energético, fácil acesso e baixo custo de módulos LoRaWAN no mercado.

 Aproveitando-se das facilidades que as LPWAN oferecem, um já bem conhecido problema pode ser resolvido: monitoramento remoto de instrumentos de medição diversos, como de consumo de água, gás e energia elétrica. Este artigo mostra uma sugestão de como desenvolver um equipamento para permitir o monitoramento a distância destes tipos de medidores, usando LoRaWAN como conectividade.

 

Como obter as medições que os equipamentos geram?

Hoje, na grande maioria dos casos, dependemos de leituras manuais (feitas mensalmente, por profissionais leituristas) para termos conhecimento de nosso consumo de água/gás/energia, tarifações e para analisar tais consumos atrás de irregularidades, como vazamentos, por exemplo. Esse tempo é muito significativo e pode significar aumento de desperdícios (em casos de vazamentos) e, ainda, pode atrasar ações que poderíamos fazer para reduzir o consumo.

Felizmente, hoje em dia contamos com uma grande oferta de medidores de consumo de água, gás e energia que disponibilizam saídas pulsadas, onde através destes pulsos informam, aos dispositivos eletrônicos que estiverem lendo tais saídas, qual o consumo por eles medido. Nestes casos, não é preciso se fazer nenhum tipo de calibração, uma vez que os pulsos representam a própria medição feita no medidor (logo, possuem a mesma confiabilidade que o medidor em si). Ainda, tais pulsos são gerados a partir de imãs que fecham contato de um reed-switch, sendo, portanto, saídas de contato seco. Veja na figura 1 um exemplo de um hidrômetro que disponibiliza tal saída pulsada.

 

 

 

Figura 1 - hidrômetro que disponibiliza saída pulsada através do fio cinza (fonte da figura: https://www.hidrauconex.com/hidrometro-multijato-de-1-polegada-saida-pulsada-conexoes)
Figura 1 - hidrômetro que disponibiliza saída pulsada através do fio cinza (fonte da figura: https://www.hidrauconex.com/hidrometro-multijato-de-1-polegada-saida-pulsada-conexoes )

 

 

Ainda como exemplo, na figura 2 é mostrado o medidor de consumo de gás G4.0, do fabricante DAEFLEX, que também disponibiliza saída pulsada.

 

 

FIgura 2 - medidor de consumo de gás G4.0 do fabricante DAEFLEX, que disponibiliza saída pulsada (fonte da imagem: https://fgsbrasil.com.br/produto/medidor-de-gas-g-4-0/ )
FIgura 2 - medidor de consumo de gás G4.0 do fabricante DAEFLEX, que disponibiliza saída pulsada (fonte da imagem: https://fgsbrasil.com.br/produto/medidor-de-gas-g-4-0/  )

 

 

Considerando a presença desta saída pulsada, para se ter acesso a tais medições bastaria dispor de um dispositivo eletrônico capaz de ler tais pulsos, contabilizar as medições de consumo informadas e, periodicamente, enviar tais medições de consumo via LoRaWAN para a nuvem. Dessa forma, é possível se ter acesso a tais dados de consumo via Internet.

É importante ressaltar que cada medidor possui um padrão de pulsos gerados por recurso consumido (água, gás ou energia), a depender do modelo e marca do medidor em questão. Desta forma, como não há uma uniformidade nesta quantidade de pulsos gerados, é recomendável no equipamento eletrônico (que captura tais pulsos) simplesmente se contar a quantidade de pulsos gerados e, em nuvem (na plataforma IoT, por exemplo), se fazer o cálculo para saber o real consumo, uma vez que o operador da plataforma IoT (o próprio usuário final, em muitos casos) conhece / pode ter acesso a relação de pulsos / consumo do medidor que originou tais pulsos.

 

Projeto para leitura de pulsos, contagem de pulsos e transmissão do contador via LoRaWAN

Como exemplo prático de um projeto para leitura de pulsos, contagem de pulsos e transmissão do contador via LoRaWAN, segue o link do repositório de um projeto de minha autoria: https://github.com/phfbertoleti/contador_pulsos_lorawan  .Para baixar a versão mais recente do projeto, acesse diretamente o link: https://github.com/phfbertoleti/contador_pulsos_lorawan/archive/refs/heads/main.zip  .

Este é um projeto de um contador de pulsos com conectividade LoRaWAN, usando como principais itens de hardware um ESP32 modelo C3 e módulo LoRaWAN SMW SX1262M0 do fabricante Smart Modular. Este projeto foi originalmente desenvolvido para a placa DevKit ESP32-C3 LoRaWAN, cuja imagem pode ser vista na figura 3.

 

 

Figura 3 - placa DevKit ESP32-C3 LoRaWAN
Figura 3 - placa DevKit ESP32-C3 LoRaWAN | Clique na imagem para ampliar |

 

 

O projeto desenvolvido é capaz de:

 

Contabilizar pulsos de duas entradas pulsadas, vindas de dois medidores distintos, compondo, portanto, dois contadores de pulsos em um só dispositivo. Tais pulsos devem, na prática, chavear o GND para os GPIOs 3 e 4.

Enviar periodicamente (para fins de teste, a cada 15 segundos, tempo definido em TEMPO_MIN_ENTRE_ENVIOS_LORAWAN_MS no seu código-fonte) a contabilização dos pulsos. O payload LoRaWAN tem no total apenas 8 bytes, sendo 4 bytes para cada contador de pulsos.

O limite máximo de pulsos contabilizados por contador é de 4.294.967.296 pulsos.

A cada certo número de envios (definido por NUM_ENVIOS_PARA_GRAVAR_CONTADORES_NVS no código-fonte), é feito o salvamento dos valores dos contadores na partição NVS do ESP32. Desse modo, caso o módulo perder a alimentação, o número de pulsos contados será resgatado e a contagem não será perdida.

Neste projeto, utiliza-se o LoRaWAN operando em classe A, com spread factor em 12, no modo ABP e sem utilizar confirmação de envio, visando menor tráfego possível de mensagens e maior alcance possível entre dispositivo e gateway LoRaWAN.

 

 

Como preparar o ambiente para programação do ESP32-C3?

Este projeto faz uso da extensão do ESP-IDF para o Visual Studio Code (uma das mais populares e suportadas IDEs atualmente). Esta extensão permite programar o ESP32-C3 via Visual Studio Code, utilizando 100% do que o framework oficial da Espressif (ESP-IDF) - fabricante do ESP32 - oferece.

Para instalar esta extensão, siga o procedimento abaixo. O procedimentoconsidera que você já possui o Visual Studio Code instalado no seu computador. Caso ainda não possuir o Visual Studio Code, baixe-o em https://code.visualstudio.com/  ou via loja de software do seu sistema operacional.

1) No Visual Studio Code, na barra lateral esquerda, clique sobre o ícone de extensões 
 

2) No campo de texto de busca de extensões, procure por espressif

3) Faça download da extensão chamada Espressif IDF

4) Após a instalação, é hora de baixar a versão do ESP-IDF para a extensão utilizar. Para isso, no Visual Studio Code, pressione o botão F1 do seu teclado, digite configure esp-idf extension e, em seguida, selecione a opção ESP-IDF: Configure ESP-IDF extension.

5) Será exibida a tela para instalação do ESP-IDF a ser instalado (para uso da extensão referida). Estarão disponíveis três opções: Express, Advanced e Use existing setup. Selecione a opção Express.

6) Na tela de opções que surgir, em Select ESP-IDF version, selecione a versão 4.4 (versão na qual este projeto foi desenvolvido) e clique em Install.

7) Aguarde alguns minutos e a instalação estará completa.

 

Após este procedimento, o seu computador estará preparado para desenvolver software para o ESP32 via Visual Studio Code com o ESP-IDF.

 

Projeto: como funciona?

A seguir, serão explicados em detalhes nos tópicos "Como são feitas as leituras de pulsos no ESP32-C3 do projeto?", “Persistência dos valores dos contadores de pulsos” e “Envio LoRaWAN dos valores dos contadores” como as principais partes do projeto funcionam, de forma a facilitar o entendimento completo do código-fonte do projeto. Esta modularização foi feita devido a extensão do código-fonte, para permitir que você compreenda rapidamente como o todo funciona. Dessa forma, é possível compreender como o projeto funciona lendo tais tópicos, pois estes falam das partes mais importantes do software deste projeto.

De forma complementar, se você deseja saber em detalhes como tudo funciona neste projeto, é altamente recomendável que você faça a leitura atenta e completa de todo o código-fonte do mesmo.

 

Como são feitas as leituras de pulsos no ESP32-C3 do projeto?

Neste projeto, o ESP32-C3 faz as leituras de ambas entradas pulsadas (GPIOS 3 e 4) via interrupção, com acionamento em borda de descida. Ou seja, a interrupção é gerada toda vez que o sinal do pulso vai do nível alto, 3V3, para o nível baixo, GND. Cada entrada pulsada possui sua função de tratamento de interrupção - contador_1_isr_handler() e contador_2_isr_handler().

Veja na listagem abaixo a configuração dos GPIOS 3 e 4 como entradas (com pull-up, para permitir pulsos com nível alto em 3V3), configuração da interrupção em borda de descida (GPIO_INTR_NEGEDGE) e associação das funções de tratamento de interrupção (contador_1_isr_handler() e contador_2_isr_handler()) a cada uma das entradas pulsadas.

 

 

/* Configura GPIOs que receberão os pulsos */
    /* Contadores: input, com pull-up interno e interrupção na borda de descida */
    io_conf_contadores.intr_type = GPIO_INTR_NEGEDGE;
    io_conf_contadores.pin_bit_mask = GPIO_INPUT_CONTADOR_1_PIN_SEL;
    io_conf_contadores.mode = GPIO_MODE_INPUT;
    io_conf_contadores.pull_up_en = 1;
    gpio_config(&io_conf_contadores);

    io_conf_contadores.intr_type = GPIO_INTR_NEGEDGE;
    io_conf_contadores.pin_bit_mask = GPIO_INPUT_CONTADOR_2_PIN_SEL;
    io_conf_contadores.mode = GPIO_MODE_INPUT;
    io_conf_contadores.pull_up_en = 1;
    gpio_config(&io_conf_contadores);

    //Instala ISR dos GPIOs
    ESP_LOGI(CONTADORES_PULSOS_TAG, "Instalando ISRs dos contadores");
    tempo_ref_contador_1 = esp_timer_get_time() / 1000;
    tempo_ref_contador_2 = esp_timer_get_time() / 1000;
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    gpio_isr_handler_add(GPIO_CONTADOR_1, contador_1_isr_handler, (void*) GPIO_CONTADOR_1);
    gpio_isr_handler_add(GPIO_CONTADOR_2, contador_2_isr_handler, (void*) GPIO_CONTADOR_2);

 

As funções de tratamento de interrupção de cada entrada pulsada são mostradas na listagem a seguir. Em ambos os casos, seu funcionamento é muito simples: é feito um processo de debounce (para não ser contabilizada a trepidação do sinal, algo muito comum em botões ou sistemas eletromecânicos, como o reed-switch de medidores de consumo de água, energia elétrica e gás) e, constatado que o sinal capturado é um pulso válido, o contador de pulsos referente a entrada pulsada em questão é incrementado em 1.

Para finalidades de se obter maior qualidade de código e segurança do valor contado, tais contadores são armazenados em filas unitárias, de forma a poderem ser acessados pelo ESP32 em qualquer ponto do código sem risco de corromper o valor do contador.

 

/*
 *  Handlers das ISR dos contadores de pulsos
 */
static void IRAM_ATTR contador_1_isr_handler(void* arg)
{
    int64_t tempo_atual = esp_timer_get_time() / 1000;

    /* Tempo debounce: 100ms */
    if ( (tempo_atual - tempo_ref_contador_1) >= TEMPO_DEBOUNCE_PULSOS)
    {
        contador_pulsos_1++;
        xQueueOverwriteFromISR(fila_contador_pulsos_1, &contador_pulsos_1, NULL);
        tempo_ref_contador_1 = esp_timer_get_time() / 1000;
    }
}

static void IRAM_ATTR contador_2_isr_handler(void* arg)
{
    int64_t tempo_atual = esp_timer_get_time() / 1000;

    /* Tempo debounce: 100ms */
    if ( (tempo_atual - tempo_ref_contador_2) >= TEMPO_DEBOUNCE_PULSOS)
    {
        contador_pulsos_2++;
        xQueueOverwriteFromISR(fila_contador_pulsos_2, &contador_pulsos_2, NULL);
        tempo_ref_contador_2 = esp_timer_get_time() / 1000;
    }
}

 

Persistência dos valores dos contadores de pulsos

Os valores dos contadores de pulsos neste projeto possuem persistência, ou seja, periodicamente (a cada número de envios via LoRaWAN do contador, configuração esta definida em NUM_ENVIOS_PARA_GRAVAR_CONTADORES_NVS) tais valores são salvos na memória não-volátil (NVS) do ESP32-C3. Dessa forma, se o projeto perder a alimentação (ou se, por qualquer motivo, o ESP32 reiniciar), o último valor de contadores salvo é resgatado.

O salvamento do valor dos contadores de pulsos é feito no código mostrado na listagem abaixo. Quando o total de envios chegar ao valor definido em NUM_ENVIOS_PARA_GRAVAR_CONTADORES_NVS, será feito o salvamento dos valores dos contadores de pulsos via função grava_valor_contador_nvs().

 

/* Verifica se é momento de salvar na NVS os valores dos contadores */
 if (total_de_envios == NUM_ENVIOS_PARA_GRAVAR_CONTADORES_NVS)
{
            grava_valor_contador_nvs(CHAVE_NVS_CONTADOR_1, contador_1);
            grava_valor_contador_nvs(CHAVE_NVS_CONTADOR_2, contador_2);
            total_de_envios = 0;
}

 

Já a leitura / resgate desse número de contadores é feito no código referente à listagem abaixo:

 

ESP LOGI(CONTADORES_PULSOS_TAG, "Filas criadas / alocadas com sucesso"); le_valor_contador_nvs(CHAVE_NVS_CONTADOR_1, &contador_pulsos_1);
le_valor_contador_nvs(CHAVE_NVS_CONTADOR_2, &contador_pulsos_2);
while (xQueueOverwrite(fila_contador_pulsos_1, (void *)&contador_pulsos_1) != pdPASS);
while (xQueueOverwrite(fila_contador_pulsos_2, (void *)&contador_pulsos_2) != pdPASS);

 

É importante ressaltar que, caso a função le_valor_contador_nvs() não consiga ler da NVS o valor de um determinado contador de pulsos, ela retorna zero como consumo lido. Isso é útil no caso extremo de corrompimento da NVS ou, ainda, se não existirem valores de contadores de pulsos gravados na NVS (como no caso da primeira inicialização, por exemplo).

A manipulação da NVS do ESP32-C3 neste projeto pode ser verificada em detalhes no módulo nvs_rw deste projeto: https://github.com/phfbertoleti/contador_pulsos_lorawan/tree/main/contador_pulsos_lorawan/main/nvs_rw

 

Envio LoRaWAN dos valores dos contadores

O envio dos valores de contadores por LoRaWAN é feito no módulo envios_lorawan do projeto, acessível em: https://github.com/phfbertoleti/contador_pulsos_lorawan/tree/main/contador_pulsos_lorawan/main/envios_lorawan  .

Neste módulo, é feito o envio de forma periódica, de acordo com o definido em TEMPO_MIN_ENTRE_ENVIOS_LORAWAN_MS. Toda a tarefa / rotina de envio é mostrada na listagem abaixo, incluindo o resgate/leitura dos valores dos contadores e condensação destes em um array de bytes, conforme mostrado na codificação a seguir. Note que cada contador é armazenado em 4 bytes. Como a mensagem LoRaWAN a ser enviada contém, como payload, apenas os contadores, o payload LoRaWAN utilizado é de somente 8 bytes.

 

 

static void envios_lorawan_task(void *arg)
{
    char bytes_para_enviar[8] = {0};
    uint32_t contador_1 = 0;
    uint32_t contador_2 = 0;
    int qtde_bytes = 0;
    int i;
    char * pt_byte_contador;
    int64_t tempo_atual = 0;
    int64_t tempo_ref = 0;

    esp_task_wdt_add(NULL);

    tempo_ref = esp_timer_get_time() / 1000;

    while (1)
    {        
        /* Aguarda momento do envio */
        tempo_atual = esp_timer_get_time() / 1000;

        if ( (tempo_atual - tempo_ref) < TEMPO_MIN_ENTRE_ENVIOS_LORAWAN_MS )
        {
            esp_task_wdt_reset();
            continue;
        }
        else
        {
            tempo_ref = esp_timer_get_time() / 1000;
        }
        
        /* Le contadores de pulsos */
        qtde_bytes = 8;
        while (xQueuePeek(fila_contador_pulsos_1, &contador_1, TEMPO_MAX_PARA_LER_DADO_FILA) != pdPASS)
        {
            esp_task_wdt_reset();
        }

        while (xQueuePeek(fila_contador_pulsos_2, &contador_2, TEMPO_MAX_PARA_LER_DADO_FILA) != pdPASS)
        {
            esp_task_wdt_reset();
        }
        
        pt_byte_contador = (char *)&contador_1;
        bytes_para_enviar[0] = *pt_byte_contador;
        pt_byte_contador++;
        bytes_para_enviar[1] = *pt_byte_contador;
        pt_byte_contador++;
        bytes_para_enviar[2] = *pt_byte_contador;
        pt_byte_contador++;
        bytes_para_enviar[3] = *pt_byte_contador;

        pt_byte_contador = (char *)&contador_2;
        bytes_para_enviar[4] = *pt_byte_contador;
        pt_byte_contador++;
        bytes_para_enviar[5] = *pt_byte_contador;
        pt_byte_contador++;
        bytes_para_enviar[6] = *pt_byte_contador;
        pt_byte_contador++;
        bytes_para_enviar[7] = *pt_byte_contador;

        ESP_LOGI(ENVIOS_LORAWAN_TAG, "Payload a ser enviado:");
        for(i=0; i<8; i++)
        {
            ESP_LOGI(ENVIOS_LORAWAN_TAG, "Byte %d: %02X", i, bytes_para_enviar[i]);
        }

        envia_mensagem_binaria_lorawan_ABP(bytes_para_enviar, qtde_bytes);
        total_de_envios++;
        ESP_LOGI(ENVIOS_LORAWAN_TAG, "Envio #%d LoRaWAN feito. Envios faltantes para o salvamento na NVS: %d", total_de_envios,
                                                                                                               NUM_ENVIOS_PARA_GRAVAR_CONTADORES_NVS - total_de_envios);

        /* Verifica se é momento de salvar na NVS os valores dos contadores */
        if (total_de_envios == NUM_ENVIOS_PARA_GRAVAR_CONTADORES_NVS)
        {
            grava_valor_contador_nvs(CHAVE_NVS_CONTADOR_1, contador_1);
            grava_valor_contador_nvs(CHAVE_NVS_CONTADOR_2, contador_2);
            total_de_envios = 0;
        }
    }
}

 

 

Note que a listagem acima não compreende a formação dos comandos AT a serem enviados ao módulo LoRaWAN SMW SX1262M0. Tal formatação é feita internamente na função envia_mensagem_binaria_lorawan_ABP(). Para maiores informações de como formatar tais comandos AT, consulte o módulo LoRaWAN do projeto (acessível em: https://github.com/phfbertoleti/contador_pulsos_lorawan/tree/main/contador_pulsos_lorawan/main/LoRaWAN  ) e também o manual oficial de comanos AT do módulo LoRaWAN SMW SX1262M0 acessível em https://1494345701-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fk4vm1BAonZb0lJir85Kt%2Fuploads%2FQkG7MKtIZl7jsVARqcBi%2FManual_LoRa_AT_Command_v1.0_en%20(v2.2).pdf?alt=media&token=e9705c48-a8e5-452f-860d-67ee7baeb5a9

 

Importante:As credenciais LoRaWAN (chaves e endereço do dispositivo) devem ser substituídas no arquivo LoRaWAN.c do módulo LoRaWAN do projeto, no trecho destacado na listagem a seguir. Antes de usar o projeto, substitua tais credenciais pelas suas (obtidas com seu distribuidor de conectividade LoRaWAN).

 

/* Credenciais LoRaWAN */
static const char DEVADDR[] = "00:00:00:00";
static const char APPSKEY[] = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
static const char NWKSKEY[] = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00";
static const char APPEUI[] = "00:00:00:00:00:00:00:00";

 

Abrindo, compilando, gravando e testando o projeto

Primeiramente, é preciso que você tenha em seu computador o projeto. Se ainda não tiver, baixe-o de forma compactada (.zip) diretamente o link: https://github.com/phfbertoleti/contador_pulsos_lorawan/archive/refs/heads/main.zip  . Uma vez baixado, basta descompactar em qualquer pasta de fácil acesso no seu computador.

Com o projeto baixado e descompactado, abra-o no Visual Studio Code clicando em File > Open Folder. Você deve mandar abrir a pasta na qual descompactou o projeto.

Para compilar o projeto, clique no botão 
na barra azul inferior da tela do Visual Studio Code. O processo pode demorar alguns minutos, a depender das configurações de seu computador e como está o uso de CPU do mesmo no momento da compilação. Após a compilação, será confirmada que esta foi bem sucedida na tela do terminal exibida na parte inferior da tela do Visual Studio Code, conforme mostrado na figura 4.

 

Figura 4 - finalização e confirmação de compilação bem sucedida do projeto
Figura 4 - finalização e confirmação de compilação bem sucedida do projeto | Clique na imagem para ampliar |

 

Uma vez compilado com sucesso, você pode gravar no ESP32-C3 o software e testá-lo. Para gravação, conecte o ESP32-C3 no seu computador via cabo USB e clique no botão  
na barra azul inferior da tela do Visual Studio Code. Serão exibidas mensagens sobre a gravação, aguarde a finalização do processo. Ao fim do processo de gravação, será exibida a mensagem:

 

Leaving...

Hard resetting via RTS pin...

 

Agora, você já pode testar o projeto verificando todas as mensagens de debug que ele vai gerando conforme funciona. Para isso, clique no botão 
na barra azul inferior da tela do Visual Studio Code. Aplique pulsos (de forma a ser um contato seco, chaveando GND) nos GPIOs 3 e 4 e verifique, via mensagens de debug, os contadores sendo incrementados.

 

Conclusão

Neste artigo, foi possível aprender que, normalmente, instrumentos de medição de consumo de água, gás e energia elétrica disponibilizam ao mundo externo (demais dispositivos eletrônicos) suas medições via saídas pulsadas. Isso torna possível o desenvolvimento de dispositivos capazes de contabilizar e enviar a nuvem as medições destes instrumentos, dispositivo este alvo do exemplo prático abordado no artigo. Isso possibilita um cenário onde medições remotas para fins de tarifação e detecção de irregularidades no consumo (vazamentos, por exemplo) podem ser feitas com rapidez, precisão e exatidão, sem intervenção humana.

Com base no projeto aqui apresentado, é possível se expandir para projetos muito interessantes, como a tarifação na individualização de consumo (gás, água e energia) em condomínios e a detecção de vazamentos de água em residências, condomínios e distribuidores de água.

 

Referências

Projetos com ESP32 e LoRa