ESP32

Polling e interrupção com ESP32 usando um reed switch

Pra quem não conhece, um reed switch é um encapsulamento de vidro fechado hermeticamente sobre um par metálco. Seu contato é normalmente aberto e se fecha reagindo a um campo magnético, por exemplo, um imã. A vantagem em utilizar um reed switch é o fato de não haver contato físico, evitando atrito e aumentanto a durabilidade de um projeto. Nesse artigo vou mostrar como utilizar polling  e interrupção com esp32 usando um reed switch para exemplificar, uma vez que ele será utilizado no projeto do relógio cuco, que está próximo de ser concluído.

Polling

A técnica de polling é comumente utilizada em microcontroladores quando não há necessidade de muita precisão quando da ocorrência do evento ou quando a MCU tem a tarefa dedicada de tratar esse evento. A diferença em utilizá-lo no ESP32 é que podemos criar tasks para fazer polling, de modo que outras funções podem estar tratando de outras tarefas.

O polling consiste na varredura de um determinado conjunto de estados de variáveis ou pinos de IO. Em um loop, o estado dos pinos é checado constantemente e, em havendo o evento, inicia-se então o tratamento.

Interrupção

Escrevi montes de artigos mostrando tratamento de interrupção em PIC, Arduino, ESP8266, ESP32, Raspberry e sei lá onde mais. Você pode usar a caixa de pesquisa para procurar ou então ir até o menu aí em cima e clicar na categoria. Minha recomendação é que digite no google “interrupções dobitaobyte” ou “interrupção dobitaobyte” e selecione a partir dos resultados.

A interrupção é mais imediata que o polling, uma vez que independe do código que esteja sendo executado. Se ocorrer esse evento, uma ISR é executada, o evento é tratado e então o código é retomado de onde parou. No ESP32 é melhor do que isso, uma vez que o tratamento é paralelo.

Tasks e núcleos

Sempre devemos lembrar que o ESP32 possui 2 núcleos programáveis em C/C++ e um ULP (Ultra Low Power coprocessor), mas esse último, somente programável em assembly. Se estiver utilizando a API do Arduino para programá-lo, considere que as funções setup() e loop() rodam no núcleo 1. Isso significa que o núcleo 0 está totalmente disponível. Normalmente o utilizo para executar minhas tasks.

Nesse artigo explico como criar uma task. Quando criada, ela se executa no núcleo em que foi iniciada. Se desejar escolher o núcleo a executar a task, escrevi esse outro artigo. Recomendo a leitura por causa dos conceitos. E nesse artigo estou adicionando mais um recurso, que é o manipulador de tarefas. Vou mostrar como utilizar alguns recursos que só coloquei em teoria em outros artigos.

Ambiente de desenvolvimento

Estou utilizando Atom com PlatformIO  e Clang para auto-completation. Prefiro utilizar essa IDE porque posso abrir duas abas do mesmo código, para analisar partes diferentes do código, ou ainda, abrir uma aba com a biblioteca que está sendo utilizada para verificar alguma referência. Você poderá programar utilizando o ESP-IDF ou a API do Arduino, de modo que será como programar na IDE do Arduino, mas com bem mais comodidade. Nesse artigo explico como instalar tudo o que é necessário, é bem simples.

Como não poderia faltar, escolhi mais uma vez utilizar a placa de desenvolvimento para ESP32, que é realmente excepcional, permitindo prototipagem rápida e para quem pretende gravar ESP32 para colocar em placa própria, nada mais cômodo do que gravar seu programa nela antes de soldá-lo à placa definitiva. É essa placa da imagem de destaque, que você encontra no nosso parceiro CurtoCircuito. Para o relógio vou utilizar esse ESP32 , com um pouco mais de recursos que adicionei na placa verde de prototipagem:

polling e interrupção com ESP32

Se interessou, o link para ele é esse, e a compra é recomendada, pois trata-se do nosso parceiro MASUGUX. O regulador de tensão para alimentar o ESP32 (que está ali, soldado na placa de prototipagem) pode ser encontrado nesse link, também do nosso parceiro MASUGUX. A placa de prototipagem é essa.

Aplicação do reed switch

Eu precisei escrever código para esse reed switch porque preciso garantir a posição de uma das engrenagens do relógio, que me garantirá estar marcando o horário certo, em conjunção com a hora buscada na Internet através de uma consulta de minuto em minuto a um servidor NTP.

O ESP32 tem um RTC interno, utilizado para timing de processos internos e sua precisão não é boa. Usar um RTC externo aumentaria a complexidade do desenvolvimento, wiring e ocuparia mais espaço dentro do relógio cuco. Por isso optei pela utilização de hora da Internet, mas não é a melhor escolha porque devemos considerar indisponibilidade de rede. De qualquer modo, não pegará fogo na casa se o relógio perder o controle das horas, então não estou preocupado com isso.

Wiring

Não fiz nada de mais. O ESP32 é 3v3, não tolerante a 5v. Para gerar a interrupção, liguei um jumper do 3v3 no pino da placa de desenvolvimento a uma perna do reed switch. Na outra perna, liguei outro jumper e conectei ao IO14. Sem resistor, nem nada.

Código de polling e interrupção com ESP32

Fiz um código para controle de níveis de uma caixa d’água para um cliente. O código é uma estrutura funcional para ser implementado ao projeto, não é o projeto inteiro, por isso o peguei para exemplo, sem afetar a confidencialidade do projeto do cliente.

Para começar, incluí as bibliotecas para usar as funções do Arduino e também incluí a de controle do gpio do ESP32. Vou explicar mais direramente no código.

A parte mais interessante é o tratamento do polling. Invés de fazer um monte de ‘ifs’ para saber as condições, as defini no array de char messages[]. Na função loop() fiz o deslocamento de bits ao contrário; invés de empurrar 1 bit para a respectiva posição, empurrei o valor posicional da leitura dos pinos de polling, definidos na struct pins_of_pull. Utilizando uma máscara de bits, o valores anteriormente definidos são preservados, de modo que com 4 deslocamentos consigo analisar os 16 estados possíveis dos 4 pinos!

Os pinos de polling configurei no modo Arduino mesmo, utilizando a função pinMode(). A interrupção está sendo utilizada apenas no pino que analisa de a alimentação externa (utilizada em outra parte do projeto) está sendo fornecida ou não. Esse tratamento é um pouco mais elaborado.

IRAM_ATTR

Criei um semáforo para controlar a interrupção, na eclaração da ISR.

 

enableInterrupts()

Essa é a declaração da configuração da interrupção. Configura-se o modo do pino, instala-se o serviço de ISR, a borda de interrupção, define-se o pino a interromper  e finalmente, configura-se o manipulador.

O modo do pino de interrupção foi configurado para INPUT no setup, mas exclusivamente esse pino eu utilizei o modo ESP32 para configurar, já que a interrupção foi toda criada assim.

Para testar, comentei o print dos níveis e deixei exclusivamete o da interrupção (porque o delay no loop é 3s no código original e para pegar a interrupção, deixei apenas 10ms). Após subir o programa para o ESP32, fiz o teste com um pequeno imã de neodímio, que já está grudado atrás da engrenagem de segundos do relógio cuco nesse momento. Funcionou lindamente, a sensibilidade é bem alta.

polling e interrupção com ESP32

Melhorando a eficiência

Nesse artigo vimos um pouco do poder do deslocamento de bits (para ver mais, sugiro esse artigo). Também vimos como criar uma interrupção com recursos do ESP32, além dos conceitos de polling e interrupção. Com esses recursos, o código ficou reduzido e preciso. Já a interrupção não está respondendo de imediato, o que a torna ineficiente nesse caso. Como resolver? – Simples; basta criar uma task para responder à requisição. Para tal, podemos fazer o seguinte:

Funciona bem. Só que ainda não é a maneira correta de fazê-lo. Mas vou deixar para um próximo artigo, esse já está ficando longo. Simples ou não?