Início
/
Software
/
Dicas de Software
/
Como Substituir delay() e millis() no Arduino com a Biblioteca NeoTimer
Como Substituir delay() e millis() no Arduino com a Biblioteca NeoTimer
Angelo Luis Ferreira | 29/05/2025
Acessos: 134
Substituir as funções de temporização com a Biblioteca NeoTimer [Guia Completo com Exemplos]
Objetivo
Este artigo tem como objetivo apresentar uma alternativa eficiente ao uso das funções delay()
e millis()
no Arduino, utilizando a biblioteca NeoTimer. Essa biblioteca oferece uma forma mais intuitiva e modular de trabalhar com temporizadores, permitindo o desenvolvimento de sistemas sem pausas, com multitarefas cooperativas e código mais legível. Ideal para projetos com LCD, sensores, atuadores e botões com debounce.
Vantagens da NeoTimer:
-
Código mais limpo e intuitivo.
-
Elimina a lógica complexa com millis()
.
-
Permite multitarefas cooperativas reais.
- Otimizar o uso de recursos: Reduzir o uso de memória, melhorar a performance.
-
Ideal para projetos com LCD, LEDs, sensores, atuadores, debounce e controle de estado.
O que a Biblioteca NeoTimer Substitui:
A NeoTimer substitui:
-
delay()
— pausa a execução do loop principal (e, consequentemente, todo o programa) pelo período de tempo especificado. Durante esse tempo de pausa, nenhuma outra tarefa pode ser executada.
-
millis()
+ lógica manual — que exige variáveis auxiliares e condicionais repetitivas complexas.
Com a NeoTimer, você pode:
-
Programar tarefas periódicas.
-
Criar vários temporizadores simultâneos.
-
Implementar debounce de botões.
-
Fazer atualizações em LCDs e monitorar sensores em tempos diferentes, tudo sem pausar o loop.
Definições
Multitarefa (multitasking) é a capacidade de executar várias tarefas ou processos de forma simultânea (ou quase simultânea) em um sistema. No Arduino, isso é tipicamente simulado, pois há apenas um núcleo de processamento.
O que parece ser a execução simultânea de várias tarefas é, na verdade, o processador alternando entre elas muito rapidamente. Essa técnica é frequentemente chamada de "time-slicing".
Multitarefa Cooperativa Real no Arduino significa que:
-
Você divide seu código em pequenas tarefas.
-
Cada tarefa não bloqueia as outras (ou seja, sem delay()
!).
-
Todas as tarefas compartilham o mesmo loop (loop()
do Arduino).
-
O controle do tempo é feito por funções como millis()
, ou bibliotecas como NeoTimer
.
Referências
Como usar temporizadores no Arduino
Bouncing e Debouncing: Entenda e Resolva Problemas de Ruído nos Botões
Instalando a biblioteca NeoTimer
1. Abra o Arduino IDE. Vá em Sketch (Rascunho) > Incluir Biblioteca > Gerenciar Bibliotecas....

2. Digite NeoTimer no campo pesquisar e clique em INSTALAR.
3. Verifique se a biblioteca foi instalada. Obs.: Caso deseje excluí-la, clique em REMOVER.

4. Para mais informações sobre a biblioteca leia README.
O que é a NeoTimer?
A NeoTimer
é uma biblioteca que simplifica o uso de temporizadores em projetos Arduino, permitindo criar múltiplos timers com métodos intuitivos como:

Observações:
Exemplos usando a biblioteca NeoTimer
Aqui vão alguns exemplos práticos do uso da biblioteca NeoTimer no Arduino em situações comuns. Todos são sem delay, e ideais para projetos com LCD, botões, LEDs, sensores, etc.
Exemplo 1:
Espera 3 segundos para apagar o led uma única vez.
#include <neotimer.h>
Neotimer timerLed(3000); // 3000 ms = 3 segundos
const byte ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
timerLed.start(); // inicia o temporizador
}
void loop() {
if (timerLed.done()) { // chegou no tempo de definido = true
digitalWrite(ledPin, LOW);
timerLed.stop(); // para o temporizador
}
}
}
Observações:
-
NeoTimer timer(3000);
: Cria um temporizador de 3000 milissegundos (3 segundos).
-
timer.start();
: Inicia o temporizador no setup()
.
-
timer.done()
: Retorna true
quando os 3 segundos se completam.
- O método
stop()
da biblioteca NeoTimer serve para interromper ou desativar o temporizador, impedindo que ele continue contando ou retorne true
em done()
após o tempo definido.
-
Após chamar stop()
, o temporizador para completamente.
-
done()
não retornará mais true
, mesmo que o tempo tenha passado.
-
É útil para eventos únicos, onde o temporizador não deve ser reutilizado sem reinicialização.
Exemplo 2:
Piscar LED com NeoTimer (intervalo de 500ms)
#include <neotimer.h>
Neotimer ledTimer(500); // Cria o timer com intervalo de 500ms
const byte ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
ledTimer.start(); // Inicia o temporizador
}
void loop() {
if (ledTimer.repeat()) { // Se tempo completou, reinicia automaticamente
digitalWrite(ledPin, !digitalRead(ledPin));
}
}
Observações:
Diferença-chave — repeat()
vs done() + start()
-
done()
apenas detecta o fim do intervalo uma vez, então você precisa de start()
para reiniciar manualmente.
-
repeat()
já faz isso automaticamente: ele retorna true
toda vez que o tempo expira e reinicia internamente o timer.
Portanto repeat() otimiza o código abaixo:
#include <neotimer.h>
Neotimer ledTimer(500); // Cria o timer com intervalo de 500ms
const byte ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
ledTimer.start(); // Inicia o temporizador
}
void loop() {
if (ledTimer.done()) { // Se tempo completou
digitalWrite(ledPin, !digitalRead(ledPin));
ledTimer.start(); // Inicia novamente o timer
}
}
Atenção:
- O método
restart()
reinicia o timer a partir do tempo da última execução. Portanto, para esse caso deve-se usar o métiodo repeat()
ou done() + start()
.
Exemplo 3:
LED aceso por 500ms e apagado por 2000ms
#include <neotimer.h>
const byte ledPin = 13;
Neotimer timerOn(500); // Tempo que o LED fica ACESO
Neotimer timerOff(2000); // Tempo que o LED fica APAGADO
void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH); // Começamos com o LED aceso
timerOn.start(); // Inicia o timer de LED aceso
}
void loop() {
if (digitalRead(ledPin) && timerOn.done()) {
digitalWrite(ledPin, LOW); // Desliga o LED
timerOff.start(); // Inicia o timer de LED apagado
}
if (!digitalRead(ledPin) && timerOff.done()) {
digitalWrite(ledPin, HIGH); // Liga o LED
timerOn.start(); // Inicia o timer de LED aceso
}
}
-
No setup()
, o LED é iniciado como HIGH (aceso).
-
O timerOn
é iniciado primeiro (tempo que o LED ficará aceso inicialmente).
Usando o NeoTimer para multitarefas
Exemplo 1:
Dois LEDs piscando em intervalos diferentes (500ms e 1000ms)
- Exemplo de multitarefa cooperativa em Arduino, controlando múltiplos eventos temporizados sem usar
delay()
.
#include <neotimer.h>
const byte led1Pin = 12;
const byte led2Pin = 13;
Neotimer timerLed1(500); // LED 1 pisca a cada 500ms
Neotimer timerLed2(1000); // LED 2 pisca a cada 1000ms
void setup() {
pinMode(led1Pin, OUTPUT);
pinMode(led2Pin, OUTPUT);
timerLed1.start();
timerLed2.start();
}
void loop() {
if (timerLed1.repeat()) {
digitalWrite(led1Pin, !digitalRead(led1Pin)); // Inverte estado do LED 1
}
if (timerLed2.repeat()) {
digitalWrite(led2Pin, !digitalRead(led2Pin)); // Inverte estado do LED 2
}
}
Observações:
-
A cada vez que o intervalo termina (repeat()
retorna true
), o LED correspondente troca de estado (acende ou apaga).
-
!digitalRead(...)
inverte o estado atual do LED, simulando o piscar.
Usando o NeoTimer como debounce de botões
Botões físicos possuem ruído mecânico: ao serem pressionados ou soltos, o sinal oscila rapidamente por alguns milissegundos antes de se estabilizar. Isso pode causar múltiplas leituras erradas como se o botão tivesse sido pressionado várias vezes. Leia Bouncing e Debouncing: Entenda e Resolva Problemas de Ruído nos Botões
A biblioteca NeoTimer
é uma forma prática e intuitiva de substituir o uso do delay()
e do millis()
para gerenciar temporizações sem bloquear o loop(). Ela não foi criada especificamente para debounce, mas pode ser usada para isso com uma lógica um pouco mais simples do que millis()
.
Exemplo 1:
Botão interruptor com debounce NeoTimer (Botão acende e apaga um led)
#include <neotimer.h>
// Pinos
const int buttonPin = 2;
const int ledPin = 13;
// Estado do LED
bool ledState = false;
// Timer de debounce
Neotimer debounceTimer(50);
// Estado anterior do botão (para detecção de borda)
bool lastStableState = HIGH;
bool lastReadState = HIGH;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
}
void loop() {
if (botaoPressionado()) {
ledState = !ledState;
digitalWrite(ledPin, ledState);
}
}
// --- Função que retorna true apenas quando o botão é pressionado com debounce ---
bool botaoPressionado() {
bool current = digitalRead(buttonPin);
// Detecta mudança de estado
if (current != lastReadState) {
debounceTimer.start(); // Inicia o timer de debounce
lastReadState = current; // Atualiza a leitura anterior
}
// Se o tempo passou e o estado estável mudou
if (debounceTimer.done() && current != lastStableState) {
lastStableState = current;
// Detecta borda de descida (pressionamento)
if (current == LOW) {
return true;
}
}
return false;
}
✅ Funcionamento:
-
A função botaoPressionado()
detecta toques com estabilidade de 50 ms (configurado no Neotimer
).
-
Apenas mudanças de HIGH → LOW (toque real) disparam true
.
-
O LED responde com alternância (interruptor), sem múltiplas detecções acidentais.
Observações:
-
Usa INPUT_PULLUP
para evitar resistores externos.
-
Botão montado entre o pino 2 e o GND, com o resistor pull-up interno ativado (INPUT_PULLUP
), o comportamento será:
ATENÇÃO:
Vamos comparar o uso de NeoTimer, millis()
, e a biblioteca Bounce2 em termos de debounce de botões, sob os critérios mais relevantes:
Para saber como utilizar a biblioteca Bounce2 leia: Bouncing e Debouncing: Entenda e Resolva Problemas de Ruído nos Botões
NeoTimer vs millis()
NeoTimer:
-
-
✅ Abstrai a lógica com start()
e done()
.
-
✅ Ideal para quem quer evitar complexidade de controle manual de tempo.
-
✅ Muito útil para iniciantes ou projetos pequenos/médios com vários timers simples.
-
❌ Não possui lógica nativa de detecção de borda (↑ ou ↓), apenas temporização.
millis():
-
-
✅ Máxima flexibilidade e controle.
-
✅ Zero dependência de bibliotecas externas.
-
❌ Mais verboso: exige unsigned long
, controle de tempo manual e comparação de estados.
-
❌ Fácil cometer erros com "overflow" ou lógica de tempo incorreta.
NeoTimer é melhor que millis()
quando você quer legibilidade, simplicidade e foco no projeto (não na mecânica de tempo). Mas millis()
ainda é imbatível para máximo controle e uso avançado com precisão absoluta.
NeoTimer vs Bounce2
Bounce2:
-
-
✅ Feita especificamente para debounce de botões.
-
✅ Lógica completa: .fell()
, .rose()
, .update()
, .read()
— tudo pronto.
-
✅ Leitura por evento de borda: "pressionado", "liberado".
-
✅ Gerencia internamente o estado atual, anterior e tempo de estabilidade.
-
✅ Suporta múltiplos botões facilmente.
-
❌ Levemente mais pesada (mas nada que atrapalhe em Arduinos típicos).
NeoTimer:
-
-
✅ É uma ferramenta genérica de temporização.
-
❌ Não gerencia eventos de borda nativamente.
-
❌ Precisa de lógica manual para lidar com transições de estado de botão.
-
✅ Pode ser combinada com qualquer entrada, não apenas botões.
Para botões físicos, Bounce2 é superior ao NeoTimer, pois:
-
-
É específica para esse propósito.
-
Exige menos código para detectar toques, bordas, e fazer debounce corretamente.
-
É mais clara e mais fácil de integrar em projetos complexos.
Quando usar cada um?
-
millis(): quando você quer controle absoluto, lógica customizada, ou está otimizando tempo com precisão.
-
NeoTimer: para projetos rápidos e simples, temporizadores múltiplos, delays não-bloqueantes.
-
Bounce2: para botões físicos com confiabilidade, menor código, maior robustez e suporte a múltiplos estados.
Exemplo 2:
Botão interruptor com debounce NeoTimer (Led espera 3 segundos para acender)
#include <neotimer.h>
// Pinos
const int buttonPin = 2;
const int ledPin = 13;
// Estados
bool ledState = false;
// Timer de debounce
Neotimer debounceTimer(50);
// Timer para religar após 3 segundos
Neotimer religaTimer(3000); // 3000 ms = 3 segundos
// Estados de leitura
bool lastStableState = HIGH;
bool lastReadState = HIGH;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
}
void loop() {
if (botaoPressionado()) {
// Se LED estava ligado, apaga e inicia o timer de religa
if (ledState) {
ledState = false;
digitalWrite(ledPin, LOW);
religaTimer.start(); // Inicia temporizador de 3 segundos
}
// Se LED estava desligado, liga e para o timer (se estiver rodando)
else {
ledState = true;
digitalWrite(ledPin, HIGH);
religaTimer.stop(); // Cancela religamento automático
}
}
// Se LED estiver desligado e o timer de religamento terminou, religa o LED
if (!ledState && religaTimer.done()) {
ledState = true;
digitalWrite(ledPin, HIGH);
}
}
// --- Função: Detecta toque com debounce ---
bool botaoPressionado() {
bool currentRead = digitalRead(buttonPin);
if (currentRead != lastReadState) {
debounceTimer.start();
lastReadState = currentRead;
}
if (debounceTimer.done() && currentRead != lastStableState) {
lastStableState = currentRead;
if (currentRead == LOW) {
return true;
}
}
return false;
}
Usando as bibliotecas Neotimer e Bounce2
No exemplo abaixo vamos utilizar as bibliotecas Neotimer para o temporizador do Led e Bounce 2 para fazer o debounce do botão (push button).
Exemplo 1:
Botão interruptor com debounce Bounce2 e temporizado com Neotimer (Led espera 3 segundos para acender)
#include <Bounce2.h>
#include <neotimer.h>
// Pinos
const byte buttonPin = 2;
const byte ledPin = 13;
// Instâncias
Bounce debouncer = Bounce(); // Objeto de debounce
Neotimer religaTimer(3000); // Timer para religar após 3 segundos
bool ledState = true;
void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
// Configura botão com debounce
debouncer.attach(buttonPin, INPUT_PULLUP);
debouncer.interval(50); // 50ms de debounce
}
void loop() {
debouncer.update(); // Atualiza estado do botão
if (debouncer.fell()) { // Detecta transição de HIGH para LOW (botão pressionado)
if (ledState) {
ledState = false;
digitalWrite(ledPin, LOW);
religaTimer.start();
} else {
ledState = true;
digitalWrite(ledPin, HIGH);
religaTimer.stop(); // Cancela religamento automático
}
}
// Verifica se deve religar o LED após 3 segundos
if (!ledState && religaTimer.done()) {
ledState = true;
digitalWrite(ledPin, HIGH);
}
}
Observações:
O anúncio abaixo ajuda a manter o Squids Arduino funcionando
Comentários