Multiprocessamento - ESP32
Multiprocessamento com ESP32
As vezes é necessário que façamos o uso de sistemas multicores para as mais diversas finalidades, como por exemplo, fazer verificações de dados pela Serial, I2C ou sensores, enquanto o outro processador (Core) faz uma outra atividade em que não possa ser interrompida ou seja indesejado esse tipo de ação. Aprenderemos o que é multiprocessamento e usaremos o incrível ESP32 que tem ao todo três cores, para criar dois LOOP(), permitindo que você rode dois códigos ao mesmo tempo!
Lembrando: O terceiro core do ESP32 é um ULP, que é apenas programado em Assembly. Também é possível programa-lo para as mais diversas finalidades. Será mostrado apenas a programação do core principal.
Para conhecer mais sobre o ESP32 você pode conferir nosso tutorial: Conhecendo o ESP32
[toc]
Computação paralela - Multiprocessamento
Computação paralela é um assunto extremamente complicado, porem podemos simplificar de um método prático:
Você tem um supermercado com um caixa e em horários de pico, o caixa não é rápido suficiente para atender os clientes, então é necessário contratar outro funcionário que fique em outro caixa, fazendo assim que a velocidade de espera seja até 50% menor. (será atendido o dobro de clientes em relação a apenas um caixa)
Um sistema computacional paralelo, permite realizar diversos cálculos (tarefas, algoritmos, etc) simultaneamente. Há diversas maneiras de se paralelizar um código que é normalmente escrito em sequência: em bit, instrução, de dado ou de tarefa. O método mais simples é por exemplo dividir um FOR() pela metade a atribuir cada metade em cores diferentes, diminuindo o tempo de execução em até 50%. Apesar de parecer ser extremamente útil, há inúmeros problemas relacionados, como condição de corrida: o acesso simultâneo da mesma variável pode gerar erros em cálculos ou se o próprio calculo for dependentes de outros resultados, a paralelização disto é inconveniente, uma vez que utilizar semáforos para sincronizar as tarefas (exclusão mutua), pode ser mais lento que o simples código em sequencial. A questão à se pensar com uso de semáforos para sincronia de processos, deve ser analisada com o grau de granulação, uma vez que o uso excessivo de semáforos, pode deixar o processo mais lento que o código sequencial (single core).
Mãos à obra
Componentes necessários
- 1x ESP32
- Arduino IDE
Código do projeto
int tempo;//Variavel que armazena o tempo. void setup() { Serial.begin(115200);//Inicia a comunicaçao serial pinMode(2, OUTPUT);//Define o led Onboard como saída Serial.printf("\nsetup() em core: %d", xPortGetCoreID());//Mostra no monitor em qual core o setup() foi chamado xTaskCreatePinnedToCore(loop2, "loop2", 8192, NULL, 1, NULL, 0);//Cria a tarefa "loop2()" com prioridade 1, atribuída ao core 0 delay(1); } void loop()//O loop() sempre será atribuído ao core 1 automaticamente pelo sistema, com prioridade 1 { Serial.printf("\n Tempo corrido: %d", tempo++); delay(1000);//Mantem o processador 1 em estado ocioso por 1seg } void loop2(void*z)//Atribuímos o loop2 ao core 0, com prioridade 1 { Serial.printf("\nloop2() em core: %d", xPortGetCoreID());//Mostra no monitor em qual core o loop2() foi chamado while (1)//Pisca o led infinitamente { digitalWrite(2, !digitalRead(2)); delay(100); } }
Colocando para funcionar
Podemos observar tanto no Serial Monitor quanto no ESP32, o funcionamento esperado do código. Enquanto o core 1 faz a contagem do tempo e espera 1seg para repetir a mensagem (o que o deixa em IDLE [travado]), o core 0 pisca o led, mostrando que um não interfere no outro.
Entendendo a fundo
Software
-Função xTaskCreatePinnedToCore()
xTaskCreatePinnedToCore(loop2, "loop2", 8192, NULL, 1, NULL, 0);
Esta função cria uma tarefa e atribuí a um especifico processador. O FreeRTOS pode definir automaticamente em qual core a tarefa será rodada, para isto, use xTaskCreate() (Mais informações no site FreeRTOS).
Neste caso, criamos a tarefa loop2, com "tamanho" de 8192 Bytes (words), nenhum parâmetro, prioridade 1 e atribuída ao core 0.
Vamos esclarecer os parâmetros em ordem (da esquerda à direita):
xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID)
pxTaskCode: Ponteiro para a tarefa, apenas o nome da tarefa. loop2.
pcName: O nome (String) da tarefa (entre aspas), é usado para facilitar o debug. "loop2".
usStackDepth: O tamanho de Stack reservada à tarefa, mais informações clique aqui. 8192.
pvParameters: O valor a ser passado para a tarefa no momento de criação. Nossa tarefa não precisa de parâmetros, então: NULL.
uxPriority: A prioridade da tarefa. É comum se usar 1 para tarefas simples, já que funções de delay (IDLE) tem prioridade 0. 1.
Se duas tarefas com mesma prioridade estiverem na fila, a primeira da fila irá ser executada e depois a próxima. Caso uma tarefa com prioridade 1 esteja na fila, porém uma tarefa com prioridade 2 também, será executado a tarefa com prioridade 2 e depois as outras.
pxCreatedTask: Valor opcional caso seja necessário manipulação das tarefas (Handle). NULL.
xCoreID: Atribuí a tarefa a um core especifico. 0.
Observações:
- Se você por exemplo criar dois loops, ao chamar uma subrotina (função), ela será executada no core em que foi chamada.
- Caso você não crie uma tarefa "infinita", será necessário deletar a tarefa com xTaskDelete().
- No caso do loop2() criado, é necessário o uso de pelo menos delay(1) dentro do loop, para que o Task Watchdog não seja ativado. Há maneiras de contornar isso, mas precisa fazer alterações no BootLoader.
-Função xPortGetCoreID()
xPortGetCoreID()
Esta função retorna o core em que a tarefa esta sendo executada.
Fechamento
A computação paralela se mostra útil quando necessário alto poder computacional ou monitoramento de itens específicos. Este assunto é gigantesco, complicado e intrigante, foi mostrado apenas o básico sobre o assunto; se você quer se aprofundar mais, veja os PDFs que foram citados no começo.
Dúvidas? Sugestões? Críticas? Comente abaixo!
Conhecendo o ESP32
Conhecendo o ESP32
Vamos conhecer mais a respeito do incrível "irmão mais novo" do nosso querido ESP8266, o ESP32. Este novo microcontrolador da Espressif é uma melhoria do seu antecessor, com mais poder de processamento (Triple core), memória e novos recursos, incluindo Bluetooth e sensores de touch capacitivo. Veja abaixo uma lista de características do ESP32.
Características do ESP-WROOM32
- Processador principal: LX6 32-bit Dual-core, operando 2-240 MHz.
- Processador secundário: ULP (Ultra Low Power coprocessor) 8MHz e consome 150uA.
- FLASH: 4MB.
- RAM: 520kB.
- GPIO: 34, com 3.3V e 12mA.
- ADC: 18, com resolução de 12-bit.
- DAC: 2, com resolução 8-bit.
- WiFi: 2,4 GHz, 802.11 b/g/n.
- Bluetooth: Bluetooth Low Energy v4.2 (BLE).
- Acelerador via hardware para encriptações, hash e afins. (AES, RSA, SHA e ECC).
- True Random Number Generator (TRGN).
- 4 Timers de 64-bit.
- 4 Watchdogs.
- 10 Sensores de Touch Capacitivo.
- 1 Sensor de temperatura interno.
- 1 Sensor de efeito Hall.
O ULP é um processador de baixo consumo que pode ser usado até em Deep Sleep, e com isso, conseguimos acordar o micro controlador das mais diversas formas, como por exemplo dados na Serial, informação de algum sensor ou os próprios sensores de toque capacitivo. Vários erros que aconteciam com o ESP8266 foram corrigidos e/ou melhorados, por exemplo, as tarefas de background, interrupções, yield() e watchdogs. Ainda é cedo para dizer (no momento) se foram corrigidos totalmente ou apenas melhorados para que gerem menos inconvenientes.
Podemos programa-lo de diversas formas, inclusive na Arduino IDE. Porem, a inclusão das funções do ESP32 ainda está atrasada em relação a esp-IDF, há varias funções que ainda não foram portadas para o Core do Arduino, a principal até o momento, é o Bluetooth, que ainda não é possível usufruir corretamente. Se você precisa usar o Bluetooth ou alguma função que ainda não foi incluída à Arduino IDE, sugerimos que use a esp-IDF.
Chega de papo e vamos programar esta belezinha, o nosso novo brinquedo. Iremos usar a Arduino IDE por já estarmos familiarizado com os comandos e funções.
Instalando o ESP32 na Arduino IDE
Instalando o Python no seu computador
Talvez seja necessário a instalação do Python em sua maquina caso ainda não tenha instalado. Se você já programou o ESP8266, provavelmente já tem o Python instalado e não precisara instalar novamente.
- Baixando o instalador do Python
Entre em ( https://www.python.org/downloads/ ) e Baixe o Python 2.7.XX
- Instalando o Python
Abra o instalador e siga até a terceira tela. Na terceira tela ative a opção "Add Python.exe to Path" clicando em "Will be installed on local hard drive" e termine a instalação.
Instalando o Driver (Core) do ESP32 na IDE Arduino
1 - Baixe o ZIP do ESP32, disponível no site do GitHub. ( https://github.com/espressif/arduino-esp32 )
2- Extraia os arquivos dentro da pasta Arduino/hardware/espressif/esp32/ , que se encontra em seu HD. Ficara igual o nosso:
3- Vá em esp32/tools e execute o arquivo GET.exe . Irá começar o download de novos arquivos e isso pode demorar um pouco. Ao fim, a janela se fechara automaticamente.
4- Abra a Arduino IDE, e veja se as placas do ESP32 estão disponíveis para seleção.
5- Plugue seu ESP32 no computador e espere a instalação automática do driver CP2102. Alguns usuários relatam que não foi possível a instalação automática, sendo necessário a instalação manualmente do driver CP2102. Após a instalação do driver, selecione a porta COM respectiva do seu ESP32, se estiver em duvida qual seja, desconecte o ESP32 do computador e veja quais portas estão ativas, agora conecte novamente e veja a porta que apareceu.
Mãos à obra
Componentes necessários
- 1x ESP32 (Usaremos o NodeMCU32).
Programando
Iremos fazer o primeiro upload para nossa placa com o clássico Blink. O Blink é um código para piscar LEDs de forma bem simples. Na placa NodeMCU há um LED OnBoard que iremos piscar, então não é preciso usar um LED externo. Caso você queira usar um LED externo, não se esqueça de alterar os pinos no código.
Código do projeto
void setup() { pinMode(LED_BUILTIN, OUTPUT);//Habilita o LED onboard como saída. } void loop() { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));//Faz o LED piscar (inverte o estado). delay(250);//espera 250mS para inverter o estado do LED. }
Colocando para funcionar
Após o upload do código, nosso micro controlador começara a piscar, veja o nosso:
Entendendo a fundo
Software
- Função Setup
void setup() { pinMode(LED_BUILTIN, OUTPUT);//Habilita o LED onboard como saída. }
A função de Setup do Arduino é feita da mesma forma, ao ligar/resetar o micro controlador, esta função é executada uma unica vez, por isso o nome "setup", referenciando uma função que configure nosso micro controlador. Normalmente as configurações iniciais, como por exemplo definição de pino (Input/Output) só é necessária ser feita uma vez, logo, adicionamos ela ao setup. Diferentemente do Loop, que é aonde nosso código é executado infinitamente.
- Função pinMode
pinMode(LED_BUILTIN, OUTPUT);//Habilita o LED onboard como saída.
A função pinMode(pino, estado) é usada para definir qual o estado de um pino do micro controlador, no caso, iremos controlar um LED, então foi declarado OUTPUT, para que possamos alterar o nivel lógico do pino (LOW ou HIGH).
- Função digitalWrite
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));//Faz o LED piscar (inverte o estado).
A função digitalWrite(pino, estado) é usada para "escrever" um valor no pino do micro controlador. Você pode usar LOW e HIGH, para acender ou apagar um LED, ligar ou desligar motores e etc. Também foi usado a função digitalRead(pino) que faz a leitura do atual estado do pino, e após pegar este valor, nós invertemos com a exclamação " ! ", que se encontra antes da função digitalRead(). Fazendo isto, nós sempre iremos alternar o estado do LED, ou seja, se esta ligado, desligamos; e se esta desligado, ligamos.
- Função Delay
delay(250);//espera 250mS para inverter o estado do LED.
A função delay(mS) é usada para adicionar um tempo entre as tarefas. Se ela não for usada, o LED irá piscar muito rapidamente, impossibilitando a visualização. Logo, usamos 250mS para que possamos ver o LED piscando lentamente.
Fechamento
Então chegamos ao fim de mais um tutorial, aprendemos a instalar e dar o primeiro upload neste incrivel micro controlador. Nos proximos tutoriais, iremos ensinar sobre como dividir tarefas entre os 2 processadores, permitindo assim, que você rode 2 códigos ao mesmo tempo.
Você também pode optar por instalar a ESP-IDF, um ambiente completo de desenvolvimento pro ESP32 que suporta todas configurações e features do microcontrolador: https://portal.vidadesilicio.com.br/instalando-esp-idf-no-windows-esp32/
Se houver dúvidas ou sugestões, fique à vontade para comentar!