RTOS com ESP32: Como Programar Multitarefas

Introdução

Se você está lendo este post, muito provavelmente já conferiu nossa postagem sobre a teoria por trás do RTOS. Se não, não perca tempo e acesse já este link para aprender mais sobre essa maravilhosa ferramenta que é o Sistema Operacional de Tempo Real! Feito isso, vamos mostrar para você quais são as funcionalidades do ESP32 dentro da programação em tempo real. Se o leitor não estiver familiarizado com o componente ESP32, é aconselhável que confira nossa explicação sobre seu funcionamento e sua aplicação no ambiente da programação. Basta clicar nos links abaixo.

Conhecendo o ESP32 – Introdução (1)

Conhecendo o ESP32 – Usando Arduino IDE (2)

Como programar Multitasking

Criando Tarefas

Para agendar uma tarefa, é necessário fazer duas coisas: criar uma função que contenha o código que deseja executar e, em seguida, criar uma tarefa que chame essa função.

Por exemplo, digamos que eu queira acender e apagar um LED continuamente.

Primeiro, será definido o pino ao qual o LED está conectado e seu modo para OUTPUT:

Posteriormente, será criada uma função que se tornará a base da tarefa. O comando digitalWrite() é usado para ligar e desligar o LED, e vTaskDelay (ao invés de delay()) é usado para pausar a tarefa por 500 ms entre mudanças de estados:

Essa foi a primeira tarefa. Algumas coisas a considerar:

Sim, foi criado um loop infinito for (;;) e isso pode parecer um pouco estranho. Como podemos realizar multitarefas se escrevermos uma tarefa que dura para sempre? O truque é vTaskDelay, que informa ao planejador que essa tarefa não deve ser executada por um determinado período. O planejador irá pausar o loop for e executar outras tarefas (se houver).

Por último, mas não menos importante, temos que informar ao planejador sobre nossa tarefa. Podemos fazer isso na função setup ():

Portanto, é isso aí! Quer piscar outro LED em um intervalo diferente? Basta criar outra tarefa e relaxar enquanto o planejador cuida da execução de ambas.

Criando uma Tarefa Única

Você também pode criar tarefas que são executadas apenas uma vez. Esses tipos de tarefas não precisam de um loop for (;;) sem fim; em vez disso, elas têm a seguinte aparência:

É possível perceber uma semelhança muito grande com uma função C ++ comum, exceto para o vTaskDelete (). Depois de chamá-lo, o FreeRTOS sabe que a tarefa foi concluída e não deve ser reprogramada. (Nota: não se esqueça de chamar esta função, ou fará com que o watchdog reinicie o ESP32).

Escolha qual núcleo rodará a tarefa

Quando xTaskCreate () é utilizado, o planejador fica livre para escolher em qual núcleo ele executará sua tarefa. A princípio, esta é a solução mais flexível.

No entanto, é possível fixar uma tarefa em um núcleo específico com xTaskCreatePinnedToCore (). É como xTaskCreate () e leva um parâmetro adicional, o núcleo no qual você deseja executar a tarefa:

Verifique qual núcleo está sendo usado

A maioria das placas ESP32 tem processadores dual-core, então como saber em qual núcleo sua tarefa está sendo executada?

Basta chamar xPortGetCoreID () de dentro da sua tarefa:

Quando há tarefas suficientes, o planejador irá despachá-las para os dois núcleos.

Encerrando Tarefas

Agora, e se você adicionou uma tarefa ao agendador, mas deseja interrompê-la? Duas opções: você exclui a tarefa de dentro dela mesma ou usa um identificador de tarefa. Encerrar uma tarefa por dentro já foi discutido antes (use vTaskDelete).

Para interromper uma tarefa de outro lugar (como outra tarefa ou seu loop principal), temos que armazenar um identificador de tarefa:

Bastou apenas definir o identificador e passá-lo como o último parâmetro de xTaskCreate (). Agora podemos eliminá-lo com vTaskDelete ():

Prioridade de Tarefas

Ao criar tarefas, devemos atribuir-lhes uma prioridade, e isso é feito através do quinto parâmetro da função xTaskCreate (). As prioridades são importantes quando duas ou mais tarefas estão competindo por tempo de CPU. Quando isso acontece, o planejador executa primeiro a tarefa de prioridade mais alta, o que faz sentido.

No FreeRTOS, um número de prioridade mais alta significa que uma tarefa é mais importante. Isso pode parecer estranho, já que uma “prioridade 1” parece mais importante do que uma “prioridade 2”, mas no FreeRTOS, é justamente o contrário.

Quando duas tarefas compartilham a mesma prioridade, o FreeRTOS compartilhará o tempo de processamento disponível entre elas.

Cada tarefa pode ter uma prioridade entre 0 e 24. O limite superior é definido por configMAX_PRIORITIES () no arquivo FreeRTOSConfig.h.

Projeto

A publicação deste post está sendo feito na primavera, e já estamos nos aproximando do verão, época mais quente do ano, e, por isso, nosso blog decidiu desenvolver um projeto voltado justamente a ajudar nossos leitores a passar por esse calor todo da maneira mais confortável possível, e, lógico, com a ajuda do Arduino. Neste post, iremos desenvolver um projeto que contém duas tarefas, as quais serão realizadas simultaneamente pelos dois núcleos do ESP32. A primeira tarefa será a automatização do controle de um ventilador a partir da temperatura ambiente, e a segunda tarefa será o desenvolvimento de um lembrete diário para beber água. Este projeto é extremamente benéfico principalmente para quem está fazendo home office ou EAD, já que permite o leitor a focar completamente no trabalho/estudo sem se preocupar em esquecer de beber água ou passar calor. A automação do ventilador também é ótima para usar a noite, enquanto dorme, já que, nos períodos em que a temperatura cai, o ventilador desliga sozinho, economizando energia.

Lista de Materiais

Os materiais a seguir compõem o projeto desenvolvido aqui.

Diagrama

O diagrama a seguir representa a montagem do projeto:

Nota: Não consegui achar o modelo correto do sensor de temperatura no fritzing, esse foi o mais próximo que encontrei, porém ele não é muito diferente do que estamos usando aqui. Ele possui um fio a mais, o branco, que deve ser ignorado. Os fios vermelho e preto são, respectivamente, o positivo e o negativo, igual ao DS18B20, porém o fio laranja aqui é, na verdade, amarelo no que estamos usando, portanto não se esqueça de considerar essa pequena diferença na hora de montar.

É importante salientar também que, na parte da conexão do relé com o ventilador, uma extensão foi confeccionada. É possível fazer esse projeto de outras formas, cortando o fio do ventilador, por exemplo, porém essa foi a maneira escolhida neste post. As imagens a seguir mostram a extensão.

Os materiais utilizados foram:

  • Tomada fêmea;
  • Tomada macho;
  • Cabo elétrico.

Aqui, foi utilizado um módulo relé com quatro módulos, porém apenas um é necessário.

Atenção: Se você for menor de idade, peça ajuda a um adulto responsável para confeccionar essa extensão.

Código

O código a seguir compõe os comandos do projeto desenvolvido:

O código em si é auto explicativo, porém acho pertinente detalhá-lo mais uma vez. Basicamente, são criadas duas tarefas, as quais serão realizadas simultaneamente, porém em núcleos diferentes, uma no núcleo 1 e a outra no núcleo 2. A primeira tarefa (Task1code) se resume em um lembrete para beber água. Em síntese, este lembrete espera um período de tempo determinado, e, passado o tempo, ele toca o buzzer, pisca o led e avisa pelo display que o usuário deve beber água. O led continuará piscando e o display continuará aceso até o usuário apertar o push button para desativar o lembrete. É possível perceber que o período de tempo do código acima é de apenas 5 segundos, porém o leitor pode mudar esse tempo para um valor maior, o indicado é 30 minutos (delay de 1800000). Já a segunda tarefa (Task2code) se baseia em ligar ou desligar um ventilador dependendo da temperatura ambiente. Em suma, o código lê a temperatura ambiente a partir do sensor e, após printar a temperatura no monitor serial, ele toma uma decisão: se a temperatura for maior que 30 graus, ele liga o relé (ventilador), e se for menor que 29 graus, ele desliga o relé (ventilador). Obviamente, estas temperaturas são simplesmente demonstrativas e foram escolhidas com o único objetivo de mostrar o funcionamento mais facilmente. Aconselhamos o leitor a escolher temperaturas mais adequadas ao seu próprio ambiente, como, por exemplo, 27 graus para ligar o ventilador e 24 para desligá-lo.

Botando pra Rodar!

Vamos ver o funcionamento do projeto na prática!

Contextualizando o que está acontecendo no vídeo: inicialmente, o lembrete de água é acionado, e, após beber a água, basta apenas pressionar o botão até a mensagem no display mudar. No código utilizado aqui, o lembrete acontece de 5 em 5 segundos, portanto é aconselhável que o leitor mude esse tempo. Logo em seguida, são mostrados o relé, o sensor de temperatura e o ventilador, que compõem a segunda parte do projeto. Para que a temperatura capturada pelo sensor suba mais rápido, eu apenas segurei-o em minha mão para esquentá-lo, e, a partir dos valores apresentados pelo monitor serial, é possível perceber que, no momento em que a temperatura lida foi superior a 30 graus, o ventilador ligou, e quando a temperatura ficou abaixo de 29 graus, o ventilador desligou. Essas temperaturas foram escolhidas apenas para facilitar a demonstração, logo também é aconselhável que o leitor mude-as no código.

Considerações Finais

A partir do post acima, foi possível compreender um pouco mais sobre a aplicação de sistemas RTOS em componentes ESP32, bem como seu funcionamento e as vantagens de sua utilização. Se o leitor se interessou pela implementação prática do projeto, acredito que gostará dos seguintes posts relacionados:

Curtiu o post? Avalie e deixe um comentário!

Siga-nos também no Instagram e nos marque quando fizer algum projeto nosso: @eletrogate.

Até a próxima!

Referências

Avaliação: 5.0/5. De 3 votos.
Espere por favor...
Ricardo Lousada
Ricardo Lousada
Graduando em Engenharia de Controle e Automação pela UFMG. Ocupo meu tempo aprendendo cada vez mais sobre eletrônica e programação, áreas que mais gosto. Meus hobbies são cinema e livros.
Acesse nossa loja