banner

Spoiler do curso de IOT


Uma parte do curso de IOT que estou desenvolvendo. É o que mostro hoje para vocês: uma aula sobre Multitask, ou seja, sobre como ser profissional programando “multitarefas”. Adianto que dá para fazer isso com o ESP32 e também com o Arduino, embora com menor velocidade. Então, vamos aprender a programar tasks utilizando FreeRTOS, em um exemplo que utiliza os 2 cores do ESP32.






PROGRAMAÇÃO MULTICORE

Introdução

O ESP32 é dual core, ou seja, possui 2 cores que são definidos como core 0 e core 1.
Quando executamos o código na Arduino IDE, o core 0 inicialmente não possui processos definidos enquanto o core 1 possui os processos das funções setup e loop.


Para saber em qual core o código está sendo executado existe a função xPortGetCoreID().
Basta chamar essa função dentro do escopo da função desejada. Você pode usar o Serial.println(xPortGetCoreID()); e ver no monitor serial o número indicado, que corresponde ao core 0 ou 1.



Caso o Serial.println estivesse em uma task iniciada no core 0, então o número 0 seria exibido.
No próximo slide veremos como iniciar uma task.



Criando uma task

Para criar tarefas que serão executadas para sempre (loop infinito), o código utilizado é o seguinte:


Para tarefas que são temporárias, ou seja, serão executadas só uma vez, é necessário criar um manipulador de tarefa. Conforme o código abaixo.




Encerrando uma task

Abaixo temos um exemplo de iniciação e encerramento de uma task temporária


Visualizando o monitor serial:




Watchdog timer tasks

Todas as tasks são automaticamente vigiadas por um watchdog e temos que resetar este contador chamando a função esp_task_wdt_reset.




Watchdog

É possível desabilitar esse watchdog, porém você deve entender que o seu projeto estará sujeito a possíveis problemas de travamento.
Desabilitando o Watchdog timer (core 0):




Parâmetros

Mostraremos um exemplo de como passar uma variável por parâmetro. É possível passar também uma struct ou até mesmo um objeto por exemplo.






MULTITASK - EXEMPLO

Recursos usados

  • ESPWROOM-32 DevKit de 38 pinos
  • Led verde
  • Led vermelho
  • 2 Resistores 220 ohm ou 330 ohm
  • Jumpers
  • Protoboard





Montagem





CÓDIGO

Fluxograma




Funcionamento

Neste nosso exemplo estamos utilizando duas tasks no core 0 e também o loop no core 1.
A task 1 (taskLedVerde) é encarregada de piscar o led verde a cada 100 ms.
A task 2 (taskLedVermelho) é encarregada de piscar o led vermelho a cada 1000 ms.
O loop é encarregado de reiniciar o ESP 10 segundos após a sua inicialização.?
A saída no monitor serial é feita de acordo com a imagem abaixo, é a função loop que exibe estas mensagens.




Código

Declarações e Variáveis

// Biblioteca com as funções de watchdog
#include "esp_task_wdt.h"

// Pinos dos leds
const int ledVerde = 32, ledVermelho = 33;

// Segundos que serão contados para reiniciar o ESP, a partir do momento que ele iniciar
const int secondsRestart = 10; 

// Variável auxiliar usada na função loop
int auxSeconds = -1;

// Variável que guarda o valor de "millis()" ao iniciar o ESP
int startMillis;


Setup


void setup()
{
  // Habilitamos o watchdog com timeout de 15 segundos
  esp_task_wdt_init(15, true);

  // Setamos os pinos dos leds como saída
  pinMode(ledVerde, OUTPUT);
  pinMode(ledVermelho, OUTPUT);

  // Iniciamos a serial com velocidade de 115200
  Serial.begin(115200);

  // Pulamos uma linha ao iniciar/reiniciar o ESP para separar o lixo de memória do primeiro Serial.println
  Serial.println();

  // Deixamos os leds apagados indicando a reiniciação do ESP, caso contrário não é possível ver os leds "parando" no momento em que o ESP é reiniciado
  digitalWrite(ledVerde, LOW);
  digitalWrite(ledVermelho, LOW);

  // Exibimos que o esp irá iniciar em 3 segundos
  Serial.println("Starting in 3 seconds...");
  // Aguardamos 3 segundos



// Iniciamos uma task para o led verde
  xTaskCreatePinnedToCore(  taskLedVerde, // Função que implementa a tarefa
                            "taskLedVerde", // Nome da tarefa
                            10000, // Número de palavras a serem alocadas na pilha (stack)
                            NULL, // Parâmetro de entrada para a tarefa
                            1, // Prioridade da tarefa
                            NULL, // Referência para a tarefa
                            0 // Núcleo em que a task rodará (0 ou 1)
                          );

  // Iniciamos uma task para o led vermelho
  xTaskCreatePinnedToCore(taskLedVermelho, "taskLedVermelho", 10000, NULL, 1, NULL, 0);   

  // Atribuímos o valor de millis para nossa variável de referência que será usada no contador de tempo
  startMillis = millis();
}



TaskLedVerde


// Task que pisca o led verde a cada 100ms
void taskLedVerde(void *p)
{ 
  esp_task_wdt_add(NULL);
  // Loop infinito
  while(true)
  {    
    esp_task_wdt_reset();
    // Mudamos de estado do led
    digitalWrite(ledVerde, !digitalRead(ledVerde));
    // Aguardamos 100ms   
    delay(100);
  }
}


TaskLedVermelho


// Task que pisca o led vermelho a cada 1000ms
void taskLedVermelho(void *p)
{    
  esp_task_wdt_add(NULL);

  // Loop infinito
  while(true)
  {
    esp_task_wdt_reset();
    // Invertemos o estado do led e aguardamos 1 segundo, piscando o led
    digitalWrite(ledVermelho, !digitalRead(ledVermelho));      
    delay(1000);
  }
}


Loop


// O loop será responsável por contar o tempo decorrido e caso este tempo ultrapassar os segundos definidos pela variável "secondsRestart" o ESP será reiniciado
void loop() 
{
  // Variável que recebe o tempo decorrido em segundos
  int seconds = (millis() - startMillis)/1000;

  // Caso o tempo ultrapassou os segundos definidos pela variável "secondsRestart"
  if(seconds >= secondsRestart)
  {
    // Exibimos reiniciando
    Serial.println("\t\t\tRestarting...");
    // Reiniciamos o ESP
    ESP.restart();
  }    

  // Exibimos de segundo em segundo o tempo restante para o ESP reiniciar
  if(seconds != auxSeconds)
  {
    // Exibimos na serial os segundos restantes
    Serial.println("Seconds remaining to restart: "+String(secondsRestart-seconds));    
    // Atribuímos para a variável auxiliar o valor de "seconds"
    auxSeconds = seconds;
  }
}




FAÇA O DOWNLOAD DOS ARQUIVOS





2 comentários:

  1. Olá. Quando o curso será lançado ? Tenho grande interesse na área de programação para RTOS e venho acompanhando seu canal a um bom tempo, parabéns pela iniciativa !!!

    ResponderExcluir
  2. gostaria tb de participar. wbhenriques@gmail.com

    ResponderExcluir

Tecnologia do Blogger.