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

NodeMcu circuito para comutar dois LEDs

É 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)

Como criar um Web Server com NodeMcu – Aula 6 – NB