Executar uma função sem atrapalhar o funcionamento de outra é algo extremamente necessário em sistemas que começam a ter uma leve complexidade. Ainda mais no caso do NodeMcu que é uma plaquinha poderosa que pode fazer muitas coisas. Portanto, vamos aprender a executar duas funções ao mesmo tempo utilizando o módulo tmr.
Na aula anterior vimos como fazer a leitura analógica e digital dos pinos do NodeMcu.
Considerações do NodeMcu
No post que expliquei sobre como executar duas coisas ao mesmo tempo no Arduino, comentei sobre a importância desse assunto. Para não ser repetitivo, vou apenas falar a importância aplicado ao caso do NodeMcu.
Como o NodeMcu se conecta a internet, podem existir inúmeros sistemas que vão precisar dessa técnica. Por exemplo: imagine que o NodeMcu está comandando um display que atualiza a hora a cada segundo e também verifica constantemente a temperatura por dados da internet e mostra no display. Essas duas funções, de atualizar a hora e a temperatura, podem conflitar uma com a outra. Porque criar um delay de 1 minuto para atualizar a hora iria congelar o programa, impossibilitando o código de verificar a temperatura na internet.
Sendo assim, nós podemos utilizar um módulo já existente na plaquinha chamado tmr.
Módulo tmr
O modulo tmr possui uma série de funções que auxiliam nas tarefas que envolvem o tempo. Meu objetivo não é citar todas, mas sim aquelas que importam para a multi-tarefa. Então, quem tiver curiosidade, é só entrar na página da documentação e conhecer melhor sobre o módulo.
Por exemplo, podemos utilizar a função tmr.now() para nos dizer qual é o tempo atual do timer em microssegundos. Dessa forma, é possível criar exatamente o mesmo código que ensinei para resolver o problema de executar duas coisas ao mesmo tempo no Arduino. Existe também a função tmr.time() que é igual, mas retorna o tempo em segundos.
Além disso, temos a função delay: tmr.delay(100) com o parâmetro sendo o tempo em microssegundos. Certo, mas comentei sobre essas funções a titulo de curiosidade sobre o módulo, pois a função que realmente vai nos importar é:
Função tmr.alarm()
Essa função combina outras duas funções (tmr.register() e tmr.start()) para criar uma espécie de alarme como o próprio nome sugere.
Para esse alarme funcionar, você precisa associar uma função a ele e precisa definir um tempo em milissegundos. Quando ele é chamado, ele espera o tempo em milissegundos passar e, após isso, executa a função associada. Ele faz isso sem atrapalhar o andamento do restante do código. Ou seja, ele é basicamente um despertador que você configura a hora de despertar. E, quando desperta, a forma dele avisar isso é entrar na função associada.
Parâmetros
A função recebe os parâmetros da seguinte forma:
tmr.alarm(id, intervalo, modo, função())
- Id: é o parâmetro responsável por dizer qual timer estamos nos referindo
- Pode ser de 0 a 6
- Existe uma solução alternativa utilizando o tmr.create(), que está explicada no exemplo 4 do tópico abaixo
- Intervalo: tempo para o alarme ser acionado e é dado em milissegundos
- Modo: Existem três modos distintos para usar com a função. São eles:
- tmr.ALARM_SINGLE: é um alarme que é acionado apenas uma vez
- tmr.ALARM_SEMI é um alarme que sua repetição deve ser feita manualmente chamando a função tmr.start()
- tmr.ALARM_AUTO é um alarme que se repete automaticamente
- Ou você pode escrever o nome do modo no parâmetro, ou pode utilizar 0 para o primeiro modo e 1 para o modo automático.
- O timer pode ser parado com a função tmr.stop()
- Função(): É basicamente a função associada
- Para escrever a função, o parâmetro deve começar com function() e terminar com end. O que vai no meio dos dois são os comandos.
Exemplos
Para entender como utilizar a função alarm, vamos ver alguns exemplos. Alguns deles foram retirados do site oficial do NodeMcu e foram levemente modificados.
- Exemplo 1
1 | tmr.alarm(0,1000,0,function() print("alarm 0") end) |
O comando acima cria um alarme no timer 0, que será acionado em 1 segundo. Além disso, ele utiliza o modo 0 que é o ALARM_SINGLE (único acionamento). Por fim, a função associada foi escrita dentro do próprio parâmetro. Para isso, primeiro escrevemos function(), em seguida escrevemos o comando que queremos e depois terminamos a função com um ‘end’.
Sendo assim, a função acima irá apenas escrever “alarm 0” na tela depois que se passar 1 segundo do acionamento do alarme. Vale lembrar que todo o restante do código funciona sem nenhum problema.
- Exemplo 2
Agora, se colocarmos um alarme seguido do outro, eles funcionam de forma independente (desde que o id usado não seja o mesmo):
1 2 3 | tmr.alarm(0,1000,0,function() print("alarm 0") end) tmr.alarm(2,2000,1,function() print("alarm 2") end) |
Mesma coisa do exemplo 1. A diferença é que 1 segundo após o primeiro alarme escrever “alarm 0”, o segundo alarme irá escrever “alarm 2”. Outro detalhe importante é que o alarme 2 está com o modo ‘1’, então ele ficará repetindo eternamente.
- Exemplo 3
1 2 3 4 5 | function separada() print("Alarme 0") end tmr.alarm(0,1000,0, function() separada() end) |
Mesma estrutura de antes. A diferença é que a função associada foi separada. Então, criei a função fora do timer e depois só chamei ela dentro do parâmetro.
- Exemplo 4
1 2 3 4 5 | timer = tmr.create() timer:alarm(1000, 1, function() timer:stop() print("ola") end) |
O alarme é o mesmo de antes, a diferença é que agora criei um objeto timer (1ª linha do código). Dessa forma, posso manipular o alarme de maneira controlada (mandar ele parar). Sendo assim, quando o alarme é acionado, o programa força a parada do timer e printa “ola” na tela.
Executando duas coisas ao mesmo tempo
Tendo em vista o que foi explicado sobre a função tmr.alarm(), praticamente já sabemos executar duas coisas ao mesmo tempo. Por garantia, vou mostrar como fazer isso utilizando o mesmo exemplo do post do Arduino. Que no caso é ficar comutando dois LEDs , cada um com um tempo de comutação diferente.
Circuito
É só ligar o negativo dos LEDs em um resistor, e a outra ponta do resistor no GND do NodeMcu. O pino positivo de cada LED deve ser ligado em uma porta digital do NodeMcu. No meu caso, liguei o verde no pino 0 e o azul no pino 1.
Assim como antes:
- O LED verde tem que comutar de estado a cada 1 segundo (se estava aceso irá apagar e vice-versa)
- O LED azul tem que comutar de estado a cada 3 segundos (se estava aceso irá apagar e vice-versa)
Programação
A lógica é bem simples. Vamos começar definindo nossos pinos e setando eles como saída:
ledVerde = 0
ledAzul = 1
gpio.mode(ledVerde, gpio.OUTPUT)
gpio.mode(ledAzul, gpio.OUTPUT)
Agora basta criar um alarme para cada LED (usar ID’s diferentes). Como é uma comutação indefinida, vou utilizar o modo 1 para ficar repetindo infinitamente.
tmr.alarm(0,1000, 1, function() ‘comando’ end)
O LED verde terá um alarme de 1000ms de intervalo e o azul de 3000ms. Por fim, nós temos que colocar um comando que altere o estado do LED para o contrário do estado atual. Ou seja, se estava em alta, temos que apagá-lo, e, se estava em baixa, temos que acendê-lo. Para isso, cheguei ao seguinte comando:
gpio.write(ledVerde, 1-gpio.read(ledVerde))
Esse comando aciona o LED da seguinte maneira: Se o estado atual do LED for ‘alto’, o gpio.read() irá retornar 1, portanto 1-1 dá 0 e o LED se apaga. No caso contrário (led em estado baixo), o gpio.read() retorna 0, portanto 1-0 dá 1 e o LED acende. É essencialmente uma comutação de estados.
Por fim, basta juntar isso tudo que você terá seu código pronto.
Código completo
1 2 3 4 5 6 7 8 9 10 11 12 13 | ledVerde = 0 ledAzul = 1 gpio.mode(ledVerde, gpio.OUTPUT) gpio.mode(ledAzul, gpio.OUTPUT) tmr.alarm(0,1000, 1, function() gpio.write(ledVerde, 1-gpio.read(ledVerde)) end) tmr.alarm(1,3000, 1, function() gpio.write(ledAzul, 1-gpio.read(ledAzul)) end) |