Arduino millis() vs delay(): temporização sem bloquear o código
Aprenda o millis() do Arduino: por que o delay() congela o sketch, piscar sem delay, várias tarefas ao mesmo tempo, temporização segura contra o estouro de 49 dias, micros(), ESP32 e Pico.
A função millis() do Arduino retorna o tempo desde que a placa começou a funcionar, em milissegundos, e é o jeito padrão de temporizar coisas sem delay(). Quando você escreve delay(1000), o Arduino não faz mais nada durante um segundo inteiro: um toque no botão passa despercebido, os sensores não são lidos e o display não é atualizado. Com millis() você pergunta “quanto tempo passou desde a última tarefa?”; roda as tarefas que estão na hora, pula as que não estão, e o loop() nunca para. Assim, um único Arduino consegue piscar LEDs, ler botões, medir sensores e enviar relatórios pela porta serial, tudo ao mesmo tempo.
Neste guia você vai aprender por que o delay() trava o programa, o famoso exemplo blink without delay (piscar sem delay), como rodar várias tarefas ao mesmo tempo, como passar com segurança pelo estouro que acontece depois de 49,7 dias, a função micros(), que conta microssegundos, e padrões de temporizador reutilizáveis. Nas últimas seções vamos ver como a mesma lógica é escrita no ESP32 e no Raspberry Pi Pico (MicroPython).

O que é o delay() e por que ele para o programa?
O delay(ms) mantém o processador girando em um laço até passar o tempo informado. Durante esse tempo a próxima linha do loop() não é alcançada; isso se chama espera bloqueante. Isso serve para piscar um único LED, mas o problema começa no momento em que você coloca um botão no projeto. O código abaixo pisca um LED uma vez por segundo e também verifica se um botão está apertado:
1const int LED_PIN = 13; // dahili LED / built-in LED
2const int BUTON_PIN = 2; // buton, diğer ucu GND / button, other leg to GND
3
4void setup() {
5 pinMode(LED_PIN, OUTPUT);
6 pinMode(BUTON_PIN, INPUT_PULLUP);
7 Serial.begin(9600);
8}
9
10void loop() {
11 digitalWrite(LED_PIN, HIGH);
12 delay(1000); // 1 sn bekle, buton okunmaz / wait 1 s, button is not read
13 digitalWrite(LED_PIN, LOW);
14 delay(1000);
15 if (digitalRead(BUTON_PIN) == LOW) { // 2 saniyede bir bakılır / checked once every 2 s
16 Serial.println(F("Butona basildi / Button pressed"));
17 }
18}Neste código o botão só é percebido uma vez a cada dois segundos, e só se por acaso estiver apertado exatamente naquele instante; um toque curto se perde entre as duas chamadas de delay(). As interrupções continuam rodando durante o delay(): o millis() continua contando e os bytes que chegam pela porta serial ficam guardados no buffer de 64 bytes. Mas o seu código espera. Conforme você acrescenta display, sensores e buzzer, cada delay() atrasa todas as outras tarefas e o aparelho fica lento. A solução é parar de esperar e começar a medir o tempo.
O que é o millis() e como ele funciona?
O millis() retorna o número de milissegundos desde que a placa foi ligada ou reiniciada, como um unsigned long (32 bits, sem sinal). No Arduino Uno esse contador é atualizado em segundo plano pela interrupção de estouro do timer de hardware Timer0. A 16 MHz o Timer0 estoura a cada 1,024 ms; o código do core acumula os 0,024 ms extras e, mais ou menos a cada 43 ms, aumenta o contador em 2 em vez de 1. Ou seja, o millis() pula alguns valores, e é por isso que comparações de igualdade como millis() == 5000 não são confiáveis; use sempre >=. Repare também que o millis() não avança enquanto as interrupções estão desativadas (por exemplo, dentro de uma rotina de interrupção).
| Função | O que faz | Resolução (Uno, 16 MHz) | Estoura depois de | Bloqueia o programa? |
|---|---|---|---|---|
| delay(ms) | Espera os milissegundos informados | 1 ms | - | Sim |
| delayMicroseconds(us) | Espera curta (precisa de 3 a 16383 µs) | Cerca de 1 µs | - | Sim |
| millis() | Milissegundos desde a inicialização | 1 ms (às vezes avança de 2 em 2) | Cerca de 49,7 dias | Não |
| micros() | Microssegundos desde a inicialização | 4 µs | Cerca de 71,6 minutos | Não |
Blink without delay: piscando um LED com millis()
- Crie uma variável unsigned long para lembrar quando a tarefa foi feita pela última vez (oncekiZaman).
- A cada passada pelo loop(), leia o tempo atual com millis().
- Verifique se passou tempo suficiente com a condição simdi - oncekiZaman >= ARALIK.
- Se o tempo acabou, faça a tarefa (inverta o LED) e atualize o oncekiZaman.
- Se não acabou, não faça nada e deixe o loop() seguir; é exatamente nesse momento que as suas outras tarefas rodam.
1const int LED_PIN = LED_BUILTIN; // Uno'da 13 / pin 13 on the Uno
2const unsigned long ARALIK = 500; // yanıp sönme aralığı (ms) / blink interval (ms)
3
4unsigned long oncekiZaman = 0; // son değişim anı / time of the last toggle
5bool ledDurumu = false;
6
7void setup() {
8 pinMode(LED_PIN, OUTPUT);
9}
10
11void loop() {
12 unsigned long simdi = millis(); // şu anki zaman / current time
13 if (simdi - oncekiZaman >= ARALIK) { // taşmaya karşı güvenli / overflow-safe
14 oncekiZaman += ARALIK; // kaymayı önler / prevents drift
15 ledDurumu = !ledDurumu;
16 digitalWrite(LED_PIN, ledDurumu);
17 }
18 // burada başka işler beklemeden çalışır / other work runs here without waiting
19}No código escrevemos oncekiZaman += ARALIK em vez de oncekiZaman = simdi. A diferença é pequena, mas importante: se o loop() às vezes atrasa alguns milissegundos, o primeiro método soma esse atraso toda vez e o LED vai se desviando com as horas, enquanto o segundo avança o horário-alvo em passos fixos e o período médio fica exatamente em 500 ms. Use += para relógios, metrônomos e medições regulares. Se o seu programa pode ficar bloqueado por muito tempo (por exemplo, esperando o Wi-Fi), o += vai tentar recuperar os passos perdidos um atrás do outro; nesse caso, = simdi é mais seguro. Se você também quer mudar o brilho do LED sem parar o código, dê uma olhada no artigo sobre PWM e analogWrite.
Fazendo várias coisas ao mesmo tempo: multitarefa com millis()
O Arduino Uno tem um processador de um núcleo e não consegue de verdade fazer duas coisas ao mesmo tempo. Mas, como o loop() pode rodar dezenas de milhares de vezes por segundo, dar a cada tarefa alguns microssegundos quando chega a vez dela parece “simultâneo” para quem vê de fora. Isso se chama multitarefa cooperativa. A regra é simples: cada tarefa tem a própria variável de tempo e o próprio intervalo, e nenhuma tarefa espera por muito tempo. No exemplo abaixo, quatro tarefas rodam lado a lado:
1const int KIRMIZI_LED = 8; // 300 ms'de bir yanıp söner / blinks every 300 ms
2const int YESIL_LED = 9; // 1000 ms'de bir yanıp söner / blinks every 1000 ms
3const int SARI_LED = 10; // butonla açılıp kapanır / toggled by the button
4const int BUTON = 2; // diğer ucu GND / other leg to GND
5
6unsigned long kirmiziZaman = 0, yesilZaman = 0, butonZaman = 0, seriZaman = 0;
7bool kirmizi = false, yesil = false, sari = false;
8int sonOkuma = HIGH; // son ham okuma / last raw reading
9int kararliDurum = HIGH; // titreşimsiz durum / debounced state
10
11void setup() {
12 pinMode(KIRMIZI_LED, OUTPUT);
13 pinMode(YESIL_LED, OUTPUT);
14 pinMode(SARI_LED, OUTPUT);
15 pinMode(BUTON, INPUT_PULLUP);
16 Serial.begin(9600);
17}
18
19void loop() {
20 unsigned long simdi = millis();
21
22 // Görev 1: kırmızı LED / Task 1: red LED
23 if (simdi - kirmiziZaman >= 300) {
24 kirmiziZaman = simdi;
25 kirmizi = !kirmizi;
26 digitalWrite(KIRMIZI_LED, kirmizi);
27 }
28
29 // Görev 2: yeşil LED / Task 2: green LED
30 if (simdi - yesilZaman >= 1000) {
31 yesilZaman = simdi;
32 yesil = !yesil;
33 digitalWrite(YESIL_LED, yesil);
34 }
35
36 // Görev 3: 50 ms debounce ile buton / Task 3: button with 50 ms debounce
37 int okuma = digitalRead(BUTON);
38 if (okuma != sonOkuma) { // değişti, sayacı yeniden başlat / changed, restart the timer
39 butonZaman = simdi;
40 sonOkuma = okuma;
41 }
42 if (simdi - butonZaman >= 50 && okuma != kararliDurum) {
43 kararliDurum = okuma;
44 if (kararliDurum == LOW) { // basıldı / pressed
45 sari = !sari;
46 digitalWrite(SARI_LED, sari);
47 }
48 }
49
50 // Görev 4: saniyede bir seri rapor / Task 4: serial report once per second
51 if (simdi - seriZaman >= 1000) {
52 seriZaman = simdi;
53 Serial.print(F("Calisma suresi (s) / Uptime (s): "));
54 Serial.println(simdi / 1000);
55 }
56}| Tarefa | Pino | Intervalo | O que faz |
|---|---|---|---|
| LED vermelho | 8 | 300 ms | Pisca rápido |
| LED verde | 9 | 1000 ms | Pisca devagar |
| Botão + LED amarelo | 2 e 10 | Debounce de 50 ms | Inverte o LED amarelo a cada toque |
| Relatório serial | - | 1000 ms | Mostra o tempo de funcionamento em segundos |
A lógica do botão também é baseada em millis(): cada vez que a leitura bruta muda, o temporizador recomeça, e, se a leitura fica estável por 50 ms, o toque é aceito como real. Assim, o repique mecânico do botão não transforma um toque em dezenas, e o código nunca espera. Para os detalhes da ligação do botão e do resistor de pull-up interno, veja Como ligar um botão no Arduino. Um resistor de 220 Ω basta para os LEDs; para outras cores e correntes, use a calculadora de resistor para LED.
O estouro de 49,7 dias e comparações seguras contra estouro
Um unsigned long guarda no máximo 4.294.967.295. Quando o millis() chega a esse número, depois de cerca de 49 dias, 17 horas e 2 minutos, ele volta a contar do 0. Se a sua placa fica ligada por semanas (controle de estufa, aquário, sistema de alarme), esse momento vai chegar. A boa notícia: o código escrito com subtração, na forma simdi - onceki >= aralik, não é afetado pelo estouro. A subtração sem sinal é feita módulo 2 elevado a 32. Por exemplo, se onceki = 4.294.967.000 e, depois do estouro, simdi = 200, então simdi - onceki não dá um número negativo; dá o resultado correto, 496. Então, desde que o intervalo que você espera seja menor que 49,7 dias, a comparação sempre funciona.
| Código | Resultado | Por quê? |
|---|---|---|
| if (millis() - onceki >= aralik) (forma com subtração) | Correto | A subtração sem sinal dá a diferença certa mesmo depois do estouro |
| if (millis() >= onceki + aralik) (forma com soma) | Errado | Quando onceki + aralik estoura, vira um número pequeno e a condição dispara cedo demais |
| if (millis() == hedef) | Errado | O millis() às vezes avança de 2 em 2, então o valor exato pode nunca aparecer |
| int onceki = millis(); (int de 16 bits) | Errado | No Uno o int tem 16 bits e guarda valores errados depois de 32,7 segundos |
| long onceki = millis(); (long com sinal) | Arriscado | Um tipo com sinal fica negativo depois de 24,8 dias; use unsigned long para evitar confusão |
Medindo microssegundos com micros()
O micros() funciona do mesmo jeito, mas retorna os microssegundos desde a inicialização. Em placas de 16 MHz como o Arduino Uno e o Nano a resolução é de 4 µs (8 µs em placas de 8 MHz), e ele estoura a cada 71,6 minutos mais ou menos; a regra da subtração vale aqui também. Ele é usado em tarefas precisas, como medir quanto tempo um trecho de código leva, cronometrar pulsos curtos ou gerar som. O exemplo abaixo mede, duas vezes por segundo, quanto tempo leva uma chamada de analogRead(); no Uno você vai ver cerca de 110–120 µs:
1unsigned long raporZaman = 0;
2
3void setup() {
4 Serial.begin(9600);
5}
6
7void loop() {
8 if (millis() - raporZaman >= 500) { // yarım saniyede bir ölç / measure every half second
9 raporZaman = millis();
10 unsigned long basla = micros(); // başlangıç (µs) / start (µs)
11 int deger = analogRead(A0);
12 unsigned long sure = micros() - basla; // geçen süre (µs) / elapsed time (µs)
13 Serial.print(F("A0 = "));
14 Serial.print(deger);
15 Serial.print(F(" sure / time: "));
16 Serial.print(sure);
17 Serial.println(F(" us"));
18 }
19}Para intervalos mais longos, prefira millis() a micros(); um período de estouro de 71 minutos é curto demais para esperas longas. O delayMicroseconds() serve só para esperas curtas e inevitáveis de alguns microssegundos, como enviar o pulso de disparo de 10 µs ao pino TRIG do sensor de distância HC-SR04. Esperas tão curtas não atrasam de forma perceptível as suas outras tarefas.
Padrões de temporizador: periódico, de disparo único e máquina de estados
- Tarefa periódica: uma tarefa que se repete em um intervalo definido, como um LED piscando, ler um sensor a cada 2 segundos ou atualizar um display a cada segundo. Você já viu esse padrão acima.
- Temporizador de disparo único (timeout): roda uma vez, um tempo definido depois de um evento. A luz de escada é um bom exemplo: acenda a luz quando houver movimento, guarde o horário e apague se nenhum movimento novo chegar em 30 segundos. Renovar o horário a cada movimento novo prolonga o período.
- Espera com limite de tempo: ao esperar uma resposta ou um sinal, você define um limite máximo para nunca ficar preso para sempre; por exemplo, mostre um erro se nenhum dado chegar em 2 segundos.
- Máquina de estados: você guarda a etapa atual do aparelho em uma variável e passa para a próxima etapa quando o tempo de cada uma acaba. Semáforos, programas de máquina de lavar e fases de jogos são escritos assim.
- Detecção de toque longo: medindo com millis() quanto tempo um botão fica apertado, você pode dar funções diferentes a toques curtos e longos.
1// Yeniden kullanılabilir zamanlayıcı / Reusable timer
2struct Zamanlayici {
3 unsigned long baslangic = 0;
4 unsigned long sure = 0;
5
6 void kur(unsigned long ms) { // süreyi şimdiden başlat / start the period from now
7 baslangic = millis();
8 sure = ms;
9 }
10 bool doldu() const { // süre doldu mu? / has it expired?
11 return millis() - baslangic >= sure;
12 }
13};
14
15const int KIRMIZI = 11, SARI = 12, YESIL = 13;
16enum Durum { DUR, HAZIRLAN, GEC, YAVASLA }; // trafik lambası adımları / traffic light states
17Durum durum = DUR;
18Zamanlayici isik;
19
20void lambalar(bool k, bool s, bool y) {
21 digitalWrite(KIRMIZI, k);
22 digitalWrite(SARI, s);
23 digitalWrite(YESIL, y);
24}
25
26void setup() {
27 pinMode(KIRMIZI, OUTPUT);
28 pinMode(SARI, OUTPUT);
29 pinMode(YESIL, OUTPUT);
30 lambalar(true, false, false);
31 isik.kur(5000); // kırmızı 5 sn / red for 5 s
32}
33
34void loop() {
35 if (isik.doldu()) {
36 switch (durum) {
37 case DUR: durum = HAZIRLAN; lambalar(true, true, false); isik.kur(1500); break;
38 case HAZIRLAN: durum = GEC; lambalar(false, false, true); isik.kur(5000); break;
39 case GEC: durum = YAVASLA; lambalar(false, true, false); isik.kur(2000); break;
40 case YAVASLA: durum = DUR; lambalar(true, false, false); isik.kur(5000); break;
41 }
42 }
43 // loop serbest: buton, sensör, ekran... / loop stays free: buttons, sensors, display...
44}Neste código, uma pequena estrutura (struct) chamada Zamanlayici reduz a uma linha só o ato de iniciar um período e perguntar se ele terminou. O semáforo é uma máquina de estados com quatro etapas: em cada etapa as lâmpadas são definidas e o tempo da próxima transição é iniciado. Como o loop() nunca espera, você pode acrescentar facilmente na mesma placa um botão de pedestre, um sensor de luz LDR para um modo noturno ou um display com o tempo restante. Se você precisar de muitos temporizadores, as bibliotecas de timer prontas do Gerenciador de Bibliotecas funcionam com o mesmo princípio.
millis(), delay() e Ticker no ESP32
No core Arduino do ESP32, millis() e micros() têm os mesmos nomes e o mesmo tipo unsigned long de 32 bits; os valores são calculados a partir do contador de 64 bits esp_timer_get_time(). Isso significa que os períodos de estouro de 49,7 dias e 71,6 minutos valem no ESP32 também, e você usa a mesma regra da subtração. A diferença importante está no delay(): no ESP32, o delay() chama o vTaskDelay() do FreeRTOS. O processador não fica girando à toa; outras tarefas, como Wi-Fi e Bluetooth, rodam enquanto você espera. Mas o seu loop() continua esperando, então tarefas como um servidor web, MQTT ou a leitura de botões atrasam. Para tarefas periódicas, a biblioteca Ticker, que vem com o core do ESP32, também é prática:
1#include <Ticker.h>
2
3const int LED1 = 2; // çoğu DevKit'te dahili LED / built-in LED on most DevKits
4const int LED2 = 4;
5
6Ticker ticker;
7bool led1Durum = false;
8bool led2Durum = false;
9unsigned long oncekiZaman = 0;
10
11void led1Degistir() { // kısa tut, uzun iş yapma / keep it short, no long work
12 led1Durum = !led1Durum;
13 digitalWrite(LED1, led1Durum);
14}
15
16void setup() {
17 Serial.begin(115200);
18 pinMode(LED1, OUTPUT);
19 pinMode(LED2, OUTPUT);
20 ticker.attach_ms(250, led1Degistir); // her 250 ms'de çağır / call every 250 ms
21}
22
23void loop() {
24 unsigned long simdi = millis();
25 if (simdi - oncekiZaman >= 1000) { // Arduino ile aynı desen / same pattern as on Arduino
26 oncekiZaman = simdi;
27 led2Durum = !led2Durum;
28 digitalWrite(LED2, led2Durum);
29 int64_t us = esp_timer_get_time(); // 64 bit, pratikte taşmaz / 64-bit, practically never overflows
30 Serial.printf("millis=%lu esp_timer=%lld us\n", simdi, us);
31 }
32}A função que você passa ao Ticker é chamada por uma tarefa de timer em segundo plano; não faça nada lento nela (requisições HTTP, saídas seriais longas, delay), só levante uma flag ou mude um pino. O esp_timer_get_time() de 64 bits não estoura por centenas de milhares de anos, então você pode usá-lo em medições que precisam de precisão de microssegundos em um aparelho que fica ligado por meses. O pino do LED embutido pode variar de placa para placa; confira o mapa de pinos da sua placa na ferramenta de pinagem. Se você está começando com o ESP32, o artigo O que é ESP32? é um bom começo para configuração e escolha de pinos.
Raspberry Pi Pico (MicroPython): ticks_ms() e ticks_diff()
No MicroPython você usa time.sleep(), time.sleep_ms() e time.sleep_us() em vez de delay(), e time.ticks_ms() em vez de millis(). Mas atenção: os valores de tick não dão a volta em 2 elevado a 32, como no Arduino, e sim em um limite menor que depende da plataforma. No Pico esse limite é de 2 elevado a 30 ms, cerca de 12,4 dias (você pode conferir na sua placa com time.ticks_add(0, -1)). Por isso, nunca subtraia nem compare dois valores de tick diretamente; obtenha a diferença com time.ticks_diff(new, old) e use time.ticks_add() para calcular um horário futuro. O código abaixo pisca o LED da placa do Pico sem bloquear, enquanto um botão no GP14 inverte um LED no GP15:
1from machine import Pin
2import time
3
4led = Pin("LED", Pin.OUT) # dahili LED (Pico ve Pico W) / onboard LED (Pico and Pico W)
5dis_led = Pin(15, Pin.OUT) # GP15'e harici LED / external LED on GP15
6buton = Pin(14, Pin.IN, Pin.PULL_UP) # GP14 buton, diğer ucu GND / GP14 button, other leg to GND
7
8ARALIK = 500 # ms
9onceki = time.ticks_ms()
10son_okuma = 1 # son ham okuma / last raw reading
11kararli = 1 # titreşimsiz durum / debounced state
12degisim = time.ticks_ms() # son değişim anı / time of the last change
13
14while True:
15 simdi = time.ticks_ms()
16 if time.ticks_diff(simdi, onceki) >= ARALIK: # taşmaya karşı güvenli / wrap-safe
17 onceki = time.ticks_add(onceki, ARALIK) # kaymasız / drift-free
18 led.toggle()
19 okuma = buton.value()
20 if okuma != son_okuma: # değişti, sayacı yeniden başlat / changed, restart the timer
21 degisim = simdi
22 son_okuma = okuma
23 if time.ticks_diff(simdi, degisim) >= 50 and okuma != kararli: # 50 ms debounce
24 kararli = okuma
25 if kararli == 0: # basıldı / pressed
26 dis_led.toggle()
27Para tarefas periódicas, o MicroPython também tem o machine.Timer: a linha Timer(period=500, mode=Timer.PERIODIC, callback=function) chama a sua função a cada 500 ms. Mantenha esses callbacks curtos, assim como no Ticker. Se você programa o Pico pela Arduino IDE (com o core arduino-pico), millis() e micros() funcionam exatamente como no Arduino. Para instalar o MicroPython no Pico e conhecer os pinos dele, veja o artigo O que é Raspberry Pi Pico?.
Ideias de projetos com millis()
- Teste de reação: acenda um LED em um momento aleatório, meça com millis() o tempo até o botão ser apertado e mostre o resultado em milissegundos.
- Luz de escada: acenda a luz quando um sensor PIR detectar movimento e apague 30 segundos depois do último movimento, enquanto o código continua fazendo outras tarefas.
- Sensor de estacionamento que acelera: encurte o intervalo dos bipes do buzzer conforme a distância medida pelo HC-SR04 diminui.
- Melodia sem bloqueio: encadeie as notas em um buzzer com millis() em vez de delay(), para que os botões continuem funcionando enquanto a música toca.
- Sensor e display juntos: leia o sensor DHT11 a cada 2 segundos, mas atualize o display a cada 250 ms.
- Cronômetro: monte um cronômetro com botões de iniciar, parar e zerar que mostra o tempo em um display de 7 segmentos.
Erros comuns e como corrigir
| Erro | Sintoma | Solução |
|---|---|---|
| Variável de tempo declarada como int | O programa enlouquece depois de uns 33 segundos | Faça de toda variável de tempo um unsigned long |
| Escrever millis() >= onceki + aralik | A temporização quebra depois de 49,7 dias | Passe para a forma millis() - onceki >= aralik |
| Verificar millis() == hedef | O evento às vezes nunca dispara | Use >= em vez de igualdade |
| Esquecer de atualizar o onceki | Depois que o tempo acaba, a tarefa se repete a cada passada | Escreva onceki = simdi ou onceki += aralik dentro da condição |
| Um delay() esquecido dentro do código do temporizador | As outras tarefas continuam atrasando | Divida as esperas longas em uma máquina de estados |
| Esperar com millis() dentro de uma interrupção | A placa congela | Nunca espere dentro de uma ISR; só levante uma flag |
| Um produto int como 60 * 1000 | O intervalo sai negativo ou curto demais | Escreva 60000UL ou 60UL * 1000 |
Quando você pega o jeito do millis(), seus projetos mudam completamente: LEDs, botões, sensores e displays trabalham juntos sem esperar uns pelos outros. Para o próximo passo, você pode escolher um projeto que precise de temporização na lista de ideias de projetos com Arduino. No app BlueGrays você explora passo a passo projetos como Semáforo, Estrobo de polícia, Iluminação inteligente de escada e corredor e Teste de reação com alerta sonoro, com esquema do circuito e código pronto, e testa na sua própria placa.
Perguntas frequentes
O que é o millis() no Arduino?
O millis() é uma função que retorna o tempo desde que o Arduino foi ligado ou reiniciado, em milissegundos. O valor é um unsigned long e recomeça do zero depois de cerca de 49,7 dias. Ele é usado para temporizar coisas sem parar o programa.
Qual é a diferença entre millis() e delay()?
O delay() pausa o programa pelo tempo informado, então nada mais, como ler botões ou atualizar um display, pode acontecer nesse meio-tempo. O millis() só diz quanto tempo passou e nunca espera. Verificando o tempo decorrido e rodando a tarefa que estiver na hora, você roda várias tarefas ao mesmo tempo.
Meu programa quebra quando o millis() estoura depois de 49 dias?
Não, se você verificar o tempo com subtração, na forma millis() - onceki >= aralik; a subtração sem sinal dá a diferença certa mesmo depois do estouro. Os problemas só aparecem com comparações escritas com soma, como millis() >= onceki + aralik, e com variáveis int.
Dá para zerar o millis() no Arduino?
Não há uma função oficial para zerar o millis(), e mexer na variável interna do core não é recomendado. Nem é preciso: guarde em uma variável o momento a partir do qual você quer contar e calcule o tempo decorrido a partir dele. Reiniciar a placa naturalmente faz o millis() recomeçar do 0.
O Arduino consegue fazer duas coisas ao mesmo tempo?
Como o Uno tem um núcleo só, ele não consegue executar fisicamente duas instruções ao mesmo tempo, mas, se você dividir cada tarefa em passos curtos com millis(), o loop() roda milhares de vezes por segundo e dá a vez a cada tarefa. Para quem vê de fora, o LED, o botão, o sensor e o display parecem funcionar todos juntos. O ESP32 também tem dois núcleos e tarefas do FreeRTOS.
O millis() é preciso o bastante para fazer um relógio?
O millis() depende do oscilador da placa; por causa do ressonador cerâmico do Arduino Uno, ele pode se desviar até alguns minutos por dia. Serve bem para medir intervalos curtos, mas, para um relógio que precisa ficar certo por dias, um módulo RTC como o DS1307 ou o DS3231, ou pegar a hora por NTP em um ESP32, é mais preciso.
Projetos para montar depois desta leitura
Ver todos os projetos
LED piscante
O primeiro contato com a eletrônica: acender e apagar um único LED. O Arduino acende e apaga o LED em intervalos curtos, fazendo a luz piscar rapidamente. Você vê da forma mais simples como a placa co

Semáforo
Uma pequena versão do semáforo que você vê na rua todos os dias. O vermelho fica aceso por dois segundos, o amarelo acende por um instante, o verde fica aceso por dois segundos e depois volta ao verme

Luzes de polícia com LEDs
Como as luzes de uma viatura: o LED vermelho pisca três vezes rapidamente, depois o LED azul pisca três vezes, e a sequência se repete sem parar. Basta ligar dois LEDs e dois resistores ao Arduino. Um

Sequencial de LEDs com velocidade ajustável
Seis LEDs acendem um após o outro, fazendo a luz parecer correr de uma ponta à outra. Ao girar o botão do potenciômetro, a velocidade muda na hora: para um lado, a luz corre rapidamente; para o outro,

Ligar e desligar um LED com botão
Pressione o botão uma vez para acender a luz e outra vez para apagar, como o interruptor do seu quarto. O Arduino detecta cada toque e inverte o estado do LED, aguardando um breve instante para que as

Iluminação inteligente de escadas e corredores
Ao entrar na escada ou no corredor, as luzes acendem uma a uma no sentido em que você caminha. Há um sensor de distância em cada extremidade; ao passar por um deles, os oito LEDs acendem a partir daqu

Teste de reação com Arduino e aviso sonoro: meça seus reflexos em milissegundos (LCD + buzzer + 3 LEDs)
Seus reflexos são rápidos? Aperte o botão: o LED vermelho diz preparar, o amarelo diz apontar e começa uma espera aleatória de 1,5 a 3,5 segundos. Assim que o LED verde acende e o buzzer apita, aperte de novo; seu tempo de reação aparece em milissegundos no LCD I2C, com seu recorde da sessão e sua média embaixo. Apertou antes do verde? A tela mostra TOO EARLY! e toca um som de derrota. Segure o botão para um novo jogador com a mesma ordem de esperas.

Relógio digital com display de 7 segmentos de 4 dígitos
Um relógio digital luminoso de quatro dígitos, parecido com o de um forno de cozinha. O módulo de relógio DS1302 mantém a hora e, a cada segundo, o Arduino lê as horas e os minutos e os exibe no displ
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

