Usando o Dual Core do ESP32 – Processamento Paralelo

Neste artigo, faremos 2 Leds piscarem simultaneamente com delay, evidenciando o dual core, com o objetivo de fazer um registrador de energia contínuo, tendo 1 core para registrar e calcular a potência, e o segundo core gerenciando a publicação dos dados.


Entenda mais do funcionamento do Dual Core: https://randomnerdtutorials.com/esp32-dual-core-arduino-ide/


Vimos no artigo acima que o ESP32 vem com 2 microprocessadores Xtensa 32-bit LX6. Normalmente os exemplos na IDE do Arduino consideram 1 processador somente, sendo assim executa 1 atividade por vez, e nós queremos executar atividades em paralelo.


SOFTWARE:

Então rodando o código abaixo:

https://github.com/casaautomacao/Exemplo_Dual_CoreESP32


Na primeira aba temos:

Nas linhas 1 e 2 a criação das duas tasks (atividades), e nas linhas 5 e 6 a escolha das saídas que acionarão os Leds, terminando a declaração fora das funções.


Dentro da função Setup() nós vamos escolher o Core (processador) que rodará cada Task. Sendo possível o core 0 ou o core 1.


Não rodaremos nada na função loop(). Sabendo que, quando tem código no loop(), este roda no Core 1 normalmente.


Na segunda aba temos:

Onde Piscaremos o led do pino 17 do ESP.


E na terceira aba, piscaremos o Led do pino 16:


Em uma aplicação normal, chamaríamos as funções “Task1Code” e “Task2Code” dentro do “loop” e 1 Led ascenderia após o outro, mas como podemos evidenciar no vídeo abaixo, o Leds estão piscando simultaneamente, mostrando que as 2 atividades estão acontecendo ao mesmo tempo.


HARDWARE:

Para rodar o exemplo, serão necessários os seguintes materiais:

1 – ESP32 ou uma CPB32

2 – Resistores de 1KOhm

2 – Leds

1 – Placa de prototipagem (protoboard)

4 – Jumpers MM


IMPORTANTE AO FAZER MÁQUINA:


Um ponto importe que nós comentamos sempre aqui na Crescer é que “uma coisa é rodar exemplo, e outra é fazer máquina”.


Vamos dividir ainda neste blog, algo que constatamos ao implementar o medidor de energia, mas como está relacionado ao Dual Core, focaremos neste blog.


Já testando a lógica para medir e calcular a potência, uma premissa é rodar o mais rápido possível e sem interrupção, por isto 1 Core somente para tal atividade.


Sem saber qual Core escolher, coloquei aleatoriamente no Core 0 e no Core 1, deixei a lógica de piscar o led (futura lógica de envio de dados).


Com isto, o Core 0 estava resetando a cada 5 segundos e coloquei um delay de 1ms e parou de resetar. Pensei então que pode ser algumas Tasks que estão rodando no Core 0 em baixo nível, já que a ESP32 que usamos tem memória flash externa por exemplo, e este acesso pode ser gerenciado pelo Core 0 e não está sendo mostrado para nós que usamos a IDE do Arduino para programá-lo. (CONTINUE A LEITURA, POIS ESTA AÇÃO MELHOROU, MAS NÃO RESOLVEU DEFINITIVAMENTE O PROBLEMA)


Ao colocar a lógica rápida no Core 1, parou de resetar, então vou organizar o projeto em, Core 0 fazendo a conexão e publicação na nuvem. Deixando o Core 1 para medir e calcular a potência.


Quando começamos a inserir a lógica de publicação no google sheets e rodando a CPB32 por mais tempo, percebemos que após 20 min (e variando a cada falha), ocorria a seguinte falha:


TasE (10179) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (10179) task_wdt: - IDLE0 (CPU 0)
E (10179) task_wdt: Tasks currently running:
E (10179) task_wdt: CPU 0: Task1
E (10179) task_wdt: CPU 1: loopTask


E a partir daí começamos a entrar em uma série de fóruns internacionais para tentar resolver e vou começar a listar para vocês as ações que tomamos até chegar na última que nos deixou a aplicação rodando por dias ininterruptamente



Trabalhamos um bom tempo com as seguintes funções:


Delay( 10 ); // pausa de 10 milesegundos 
VTaskDelay( 10 ); // outra maneira de pausar recomendada em muitos fóruns
Yield( ); // seria uma função para verificar se tem algo precisando ser executado
vTaskDelete(NULL); // esta função dentro do loop()


Pare se ter uma ideia, nós considerarmos colocar as funções acima dentro dos 3 loopings, Loop(), Task1code() e Task2code().

Com As combinações mais variadas possíveis e o melhor resultado foi de 110 minitos sem a falha.

Para criar uma aplicação, dependendo do caso, não seria um problema, pois o ESP32 retorna a funcionar. Mas não é uma boa prática ter em projetos, este tipo de ocorrência frequente, então continuamos a pesquisar.


O resultado acima foi obtido com:

Aba 1 do software:

Aba 2 do software:

Aba 3 do software:

Este contexto não me deixou satisfeito, então encontramos já no final da pesquisa uma forma de desabilitar o Watchdog no Core 0. Na mensagem de falha, pode-se verificar que, nestes momentos ocorre uma segurança de Watchdog no Core 0, então inserimos o seguinte código no loop:

E retirei o yield() e vTaskDelay dos outros 2 loops.

Tendo a aplicação rodando 2 dias sem dar falha, e na performance máxima que cada código disponibiliza, pois não tem qualquer tipo de delay agora.

Caso no futuro o Core 0 comece a travar e precise habilitar o Watchdog, poderemos criar uma variável que altere o valor no loop do Core 0, e se o Core 1 detectar que o Core 0 parou, poderemos habilitar o Watchdog ou mesmo resetar o ESP32.


Agora é aplicar nos projetos e evoluir...


E aí o que achou deste material? Tens algum projeto em mente onde 2 códigos rodando simultaneamente dariam mais performance para sua aplicação?


Posts recentes

Ver tudo