Guias · 15 min de leitura

Hora NTP no ESP32: relógio pela internet com fuso horário, OLED e RTC de reserva

Como obter a hora NTP no ESP32: configTzTime e fusos horários, getLocalTime, strftime, relógio OLED, DS3231 de reserva, tarefa diária agendada e ntptime no Pico W.

Obter a hora com NTP no ESP32 significa conectar a placa a servidores de hora na internet e obter a data e a hora com precisão de algumas dezenas de milissegundos, sem nenhum módulo de relógio. Depois que o ESP32 se conecta ao Wi-Fi, uma única chamada a configTzTime() obtém a hora, aplica seu fuso horário e a placa continua contando por conta própria. Relógios de mesa, irrigação programada, marcações de data e hora para registro de dados, um alarme matinal ou luzes que acendem em um horário definido deixam de precisar de um módulo RTC separado.

Neste guia, vamos ver passo a passo como o NTP funciona, configurar o fuso horário correto, escrever a data em qualquer formato com getLocalTime e strftime, mostrar a hora em um display OLED, manter a hora com um módulo DS3231 quando o Wi-Fi cai ou falta energia e executar uma tarefa em um horário específico todos os dias. Na última seção, faremos o mesmo no Raspberry Pi Pico W com o módulo ntptime do MicroPython.

Placa de desenvolvimento ESP32
ESP32 DevKit: o Wi-Fi integrado permite obter a hora de servidores NTP e manter a data e a hora sem um módulo de relógio separado.

O que é NTP e como o ESP32 obtém a hora?

NTP (Network Time Protocol) é um protocolo usado desde a década de 1980 para sincronizar os relógios de computadores e dispositivos pela internet. Os servidores são organizados em camadas chamadas estratos: os servidores de estrato 1 estão conectados diretamente a um relógio atômico ou GPS, os de estrato 2 obtêm a hora deles, e assim por diante. O ESP32 solicita a hora com um pequeno pacote pela porta UDP 123; o servidor responde com uma marcação de data e hora que contém os segundos desde 1900 e uma fração de segundo. O ESP32 considera o tempo de ida e volta do pacote e ajusta seu próprio relógio. Em uma conexão doméstica, a precisão obtida assim costuma ficar entre 10 e 50 milissegundos, mais do que suficiente para um projeto de relógio.

A pilha de rede lwIP do ESP32 usa SNTP, uma versão simplificada do NTP. Depois de receber a hora, o ESP32 a mantém como hora do sistema e continua contando com um temporizador derivado de seu cristal; ele não acessa a internet a cada segundo. Em segundo plano, consulta o servidor novamente em intervalos regulares (por padrão, a cada 3 horas no core Arduino ESP32) e corrige pequenos desvios. A hora do sistema é mantida em UTC; a hora local que você vê é calculada a partir dela, aplicando a regra do fuso horário.

ConceitoValor / explicação
Protocolo e portaSNTP, UDP 123
Marcação de data e horaSegundos desde 1900 (o ESP32 converte isso para o tempo Unix, baseado em 1970)
Precisão típica10–50 ms em uma conexão doméstica
RessincronizaçãoPadrão de 3 horas no Arduino ESP32, alterado com sntp_set_sync_interval() (mínimo de 15 s)
Máximo de servidores3 (os nomes de servidores passados a configTzTime)
Tráfego de internetA solicitação e a resposta são pacotes NTP de 48 bytes cada; no máximo alguns quilobytes por dia
O que você precisaESP32, uma rede Wi-Fi de 2,4 GHz e uma conexão com a internet

configTime e configTzTime: configurando o fuso horário

Há duas formas de iniciar o NTP no core Arduino ESP32. configTime(gmtOffset_sec, daylightOffset_sec, server1, server2, server3) recebe a diferença em relação ao UTC e o ajuste do horário de verão em segundos. Para a Turquia, por exemplo, você escreve configTime(10800, 0, "pool.ntp.org"), porque 3 horas equivalem a 10.800 segundos e a Turquia permanece em UTC+3 o ano todo, sem horário de verão, desde 2016. Esse método é simples, mas não conhece as datas do horário de verão da sua região; em lugares que o adotam, como a Europa ou os EUA, a mudança ocorre no dia errado, então use configTzTime nesses casos.

A melhor forma é configTzTime(TZ, server1, server2, server3). Essa função recebe uma string de fuso horário no formato POSIX, que também contém as regras do horário de verão, e o ESP32 faz a mudança sozinho a cada ano. Para a Turquia, a string é "<+03>-3": o +03 entre os sinais de menor e maior é a abreviação exibida na tela, e -3 significa que você subtrai 3 horas da hora local para chegar ao UTC. A tabela abaixo lista as strings de regiões comuns.

RegiãoString TZ (configTzTime)Equivalente com configTime
Turquia (UTC+3 o ano todo)<+03>-3configTime(10800, 0, ...)
Alemanha, França, Itália, EspanhaCET-1CEST,M3.5.0,M10.5.0/3Não é adequado, use configTzTime
Reino Unido, PortugalGMT0BST,M3.5.0/1,M10.5.0Não é adequado, use configTzTime
Leste dos EUA (Nova York)EST5EDT,M3.2.0,M11.1.0Não é adequado, use configTzTime
Azerbaijão, Geórgia (UTC+4)<+04>-4configTime(14400, 0, ...)
UTC (para marcações de data e hora)UTC0configTime(0, 0, ...)
O sinal em uma string de fuso horário POSIX é invertido: se você escrever "UTC+3" ou "GMT+3" para um fuso UTC+3, o ESP32 interpreta isso como “3 horas atrás do UTC” e o relógio fica com 6 horas de diferença. A string correta é "<+03>-3" ou "UTC-3". Da mesma forma, a Europa Central usa "CET-1...", e não "CET+1". Se seu relógio estiver com exatamente 3 ou 6 horas de diferença, quase sempre o problema é o sinal.

Componentes necessários e ligações

Para ver a hora no Monitor Serial, você só precisa de um ESP32 e uma rede Wi-Fi. Mais adiante no guia, usamos um display OLED SSD1306 de 0,96 polegada para mostrar a hora, um módulo RTC DS3231 para mantê-la durante quedas de energia e um módulo relé para o exemplo de tarefa agendada. O OLED e o DS3231 compartilham o mesmo barramento I2C, então ambos se conectam aos mesmos dois pinos. Você pode consultar a pinagem do ESP32 na ferramenta Pinagem.

Pino do componenteESP32Raspberry Pi Pico WObservação
OLED / DS3231 VCC3V33V3 (OUT)Ambos funcionam com 3,3 V
OLED / DS3231 GNDGNDGNDGND comum
OLED / DS3231 SDAGPIO 21GP4Linha de dados I2C
OLED / DS3231 SCLGPIO 22GP5Linha de clock I2C
IN do módulo reléGPIO 26GP15Exemplo de tarefa agendada
VCC do módulo relé5V (VIN)VBUSA maioria das bobinas de relé precisa de 5 V
O ESP32 só se conecta a redes Wi-Fi de 2,4 GHz. Se seu roteador usar o mesmo nome para 5 GHz e 2,4 GHz, ou o nome da rede contiver caracteres especiais, a conexão poderá falhar. Se a hora nunca chegar, primeiro confirme no Monitor Serial que o Wi-Fi está conectado; o NTP só funciona depois que a conexão Wi-Fi está ativa.

Passo a passo: seu primeiro sketch NTP no ESP32

  1. Verifique se o pacote de placas ESP32 está instalado na Arduino IDE e selecione ESP32 Dev Module em Ferramentas. Se não souber como, veja o artigo O que é ESP32.
  2. Nenhuma biblioteca extra é necessária: WiFi.h e time.h vêm com o core do ESP32.
  3. Substitua os valores de WIFI_SSID e WIFI_PASS pelos da sua rede, e TZ_INFO pelo seu fuso horário da tabela acima.
  4. Carregue o sketch e abra o Monitor Serial a 115200 baud.
  5. Você deverá ver primeiro os pontos da conexão Wi-Fi e, depois de alguns segundos, a data e a hora sendo atualizadas a cada segundo.
C++ (Arduino IDE)
1#include <WiFi.h>
2#include <time.h>
3
4const char* WIFI_SSID = "AgAdi";       // Wi-Fi adı / Wi-Fi name
5const char* WIFI_PASS = "Sifre123";    // Wi-Fi şifresi / Wi-Fi password
6const char* TZ_INFO   = "<+03>-3";     // Türkiye: UTC+3, yaz saati yok / Turkey: UTC+3, no DST
7
8void setup() {
9  Serial.begin(115200);
10  WiFi.begin(WIFI_SSID, WIFI_PASS);
11  Serial.print("Wi-Fi");
12  while (WiFi.status() != WL_CONNECTED) {    // bağlanana kadar bekle / wait until connected
13    delay(500);
14    Serial.print(".");
15  }
16  Serial.println(" OK");
17
18  // NTP sunucuları + saat dilimi / NTP servers + time zone
19  configTzTime(TZ_INFO, "pool.ntp.org", "time.cloudflare.com");
20
21  struct tm t;
22  while (!getLocalTime(&t)) {                // ilk eşitlemeyi bekle (5 sn deneme) / wait for the first sync (5 s per try)
23    Serial.println("NTP bekleniyor / waiting for NTP");
24  }
25  Serial.println("Saat alindi / time received");
26}
27
28void loop() {
29  struct tm t;
30  if (getLocalTime(&t)) {
31    Serial.println(&t, "%d.%m.%Y %H:%M:%S");  // ör. 16.11.2026 14:05:09 / e.g. 16.11.2026 14:05:09
32  }
33  delay(1000);
34}
35
LinhaO que faz?
configTzTime(TZ_INFO, ...)Inicia o cliente SNTP e define os servidores e o fuso horário; retorna imediatamente, e a hora chega em segundo plano
getLocalTime(&t)Preenche uma struct tm com a hora local; se ainda não houver hora, espera até 5 segundos e retorna false
Serial.println(&t, "...")A classe Print do ESP32 pode imprimir uma struct tm diretamente com um formato strftime
time(nullptr)Retorna os segundos desde 1970 (tempo Unix, UTC)

Há um detalhe importante aqui: getLocalTime() decide se a hora é “válida” verificando o ano. Quando o ESP32 inicia, a hora do sistema é 1º de janeiro de 1970, e a função retorna false até que o ano seja posterior a 2016. Assim, o código que registra dados ou executa tarefas antes de o NTP chegar não produzirá acidentalmente registros datados de 1970, desde que você verifique o valor de retorno. O segundo parâmetro é o tempo limite em milissegundos; se não quiser bloquear a execução dentro de loop(), escreva getLocalTime(&t, 0) para que faça apenas uma tentativa.

struct tm: as partes da hora

getLocalTime() grava a hora em uma struct tm, dividida em partes. Você usa esses campos para comparar horas, dias ou meses ao criar tarefas agendadas. Dois campos costumam confundir iniciantes: o mês é contado a partir de 0 e o ano a partir de 1900.

CampoIntervaloExemplo (segunda-feira, 16 de novembro de 2026, 14:05:09)
tm_sec0–59 (60 em um segundo intercalar)9
tm_min0–595
tm_hour0–2314
tm_mday1–3116
tm_mon0–11 (0 = janeiro)10
tm_yearAnos desde 1900126 (1900 + 126 = 2026)
tm_wday0–6 (0 = domingo)1
tm_yday0–365 (dia do ano, a partir de 0)319
Se a tela mostrar “16.10.2026”, mas for novembro, você esqueceu de somar 1 a tm_mon. Se o ano aparecer como 126, você precisa de tm_year + 1900. Se usar strftime, ele faz essas conversões para você.

Formatando data e hora com strftime

strftime() grava a hora de uma struct tm em um array de caracteres de acordo com um padrão. Os códigos com porcentagem no padrão são substituídos pelo valor correspondente, e todos os outros caracteres permanecem como estão. Dimensione o array para a maior saída mais um (para o zero terminador): “14:05:09” tem 8 caracteres, então o array deve ter pelo menos 9 bytes. Os códigos mais comuns:

CódigoSignificadoExemplo de saída
%H / %M / %SHora (00–23) / minuto / segundo14 / 05 / 09
%I e %pRelógio de 12 horas e AM/PM02 PM
%d / %m / %YDia / mês (01–12) / ano com quatro dígitos16 / 11 / 2026
%yAno com dois dígitos26
%A / %aNome do dia da semana, completo / abreviado (inglês)Monday / Mon
%B / %bNome do mês, completo / abreviado (inglês)November / Nov
%jDia do ano (001–366)320
%F / %TAtalhos para %Y-%m-%d / %H:%M:%S2026-11-16 / 14:05:09
%ZAbreviação do fuso horário+03

A biblioteca C do ESP32 só conhece os nomes dos dias e meses em inglês; %A sempre imprime “Monday”. Para datas em outro idioma, você mantém os nomes dos dias e meses em seus próprios arrays e usa tm_wday e tm_mon como índices dos arrays. O exemplo abaixo faz isso com nomes em turco; basta substituí-los pelos do seu idioma. Como o Monitor Serial aceita UTF-8, as letras acentuadas aparecem corretamente nele. O sketch produz tanto uma data legível quanto o formato ISO ordenável (2026-11-16 14:05:09) para registro de dados.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <time.h>
3
4const char* WIFI_SSID = "AgAdi";
5const char* WIFI_PASS = "Sifre123";
6const char* TZ_INFO   = "<+03>-3";
7
8// tm_wday: 0 = Pazar ... 6 = Cumartesi / 0 = Sunday ... 6 = Saturday
9const char* GUNLER[] = {"Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"};
10const char* AYLAR[]  = {"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran",
11                        "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"};
12
13void setup() {
14  Serial.begin(115200);
15  WiFi.begin(WIFI_SSID, WIFI_PASS);
16  while (WiFi.status() != WL_CONNECTED) delay(500);
17  configTzTime(TZ_INFO, "pool.ntp.org");
18}
19
20void loop() {
21  struct tm t;
22  if (!getLocalTime(&t)) return;             // saat henüz yok / time not set yet
23
24  char saat[9];                              // "HH:MM:SS" + '\0'
25  char iso[20];                              // "YYYY-MM-DD HH:MM:SS" + '\0'
26  strftime(saat, sizeof(saat), "%H:%M:%S", &t);
27  strftime(iso, sizeof(iso), "%Y-%m-%d %H:%M:%S", &t);
28
29  // Türkçe tarih: 16 Kasım 2026 Pazartesi / Turkish date
30  Serial.printf("%d %s %d %s  %s\n", t.tm_mday, AYLAR[t.tm_mon], t.tm_year + 1900, GUNLER[t.tm_wday], saat);
31  Serial.printf("Kayit / log: %s  epoch: %ld\n", iso, (long)time(nullptr));
32  delay(1000);
33}
34
Ao registrar dados (cartão SD, Google Sheets, servidor), sempre grave a data como %Y-%m-%d %H:%M:%S ou como tempo Unix bruto. Esse formato fica em ordem cronológica quando ordenado alfabeticamente, e o Excel e os bancos de dados o reconhecem como data. Você ainda pode usar um formato local na tela.

Mostrando a hora e a data em um display OLED

Para transformar a saída do Monitor Serial em um relógio de mesa de verdade, o display OLED SSD1306 de 0,96 polegada é ideal: é barato, conecta-se com dois fios e mostra dígitos grandes com nitidez. O sketch abaixo mostra o dia da semana e a data no topo, a hora em uma fonte grande no meio e o estado do Wi-Fi na parte inferior. Como só redesenha quando o segundo muda, e não a cada ciclo, ele não ocupa o barramento I2C sem necessidade. Instale as bibliotecas Adafruit SSD1306 e Adafruit GFX pelo Gerenciador de Bibliotecas; o display é abordado no artigo display OLED de 0,96 polegada (SSD1306).

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <Wire.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_SSD1306.h>
5#include <time.h>
6
7const char* WIFI_SSID = "AgAdi";
8const char* WIFI_PASS = "Sifre123";
9const char* TZ_INFO   = "<+03>-3";
10// OLED yazı tipinde Türkçe harf yok / the OLED font has no Turkish letters
11const char* GUNLER[] = {"Pazar", "Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi"};
12
13Adafruit_SSD1306 display(128, 64, &Wire, -1);
14int sonSaniye = -1;                          // son çizilen saniye / last drawn second
15
16void yaz(const char* mesaj) {                // ekrana tek satır / one line on screen
17  display.clearDisplay();
18  display.setTextSize(1);
19  display.setCursor(0, 0);
20  display.print(mesaj);
21  display.display();
22}
23
24void setup() {
25  Wire.begin(21, 22);                        // SDA = 21, SCL = 22
26  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
27  display.setTextColor(SSD1306_WHITE);
28  yaz("Wi-Fi baglaniyor...");
29
30  WiFi.begin(WIFI_SSID, WIFI_PASS);
31  while (WiFi.status() != WL_CONNECTED) delay(300);
32  configTzTime(TZ_INFO, "pool.ntp.org", "time.cloudflare.com");
33  yaz("NTP bekleniyor...");
34}
35
36void loop() {
37  struct tm t;
38  if (!getLocalTime(&t, 0)) {                // 0 = beklemeden dene / try without waiting
39    delay(200);
40    return;
41  }
42  if (t.tm_sec == sonSaniye) {               // saniye değişmediyse çizme / redraw only on a new second
43    delay(20);
44    return;
45  }
46  sonSaniye = t.tm_sec;
47
48  char saat[9], tarih[11];
49  strftime(saat, sizeof(saat), "%H:%M:%S", &t);
50  strftime(tarih, sizeof(tarih), "%d.%m.%Y", &t);
51
52  display.clearDisplay();
53  display.setTextSize(1);
54  display.setCursor(0, 0);
55  display.print(GUNLER[t.tm_wday]);          // gün adı / day name
56  display.setCursor(68, 0);
57  display.print(tarih);                      // 10 karakter = 60 piksel / 10 chars = 60 px
58  display.setTextSize(2);
59  display.setCursor(16, 26);
60  display.print(saat);                       // 8 x 12 = 96 piksel genişlik / 96 px wide
61  display.setTextSize(1);
62  display.setCursor(0, 56);
63  display.print(WiFi.status() == WL_CONNECTED ? "Wi-Fi: var" : "Wi-Fi: yok");
64  display.display();
65}
66

Observe que os nomes dos dias são escritos sem letras acentuadas: a fonte padrão da Adafruit GFX não tem caracteres como ş, ç ou é e mostra símbolos sem sentido no lugar deles. No tamanho de texto 2, cada caractere tem 12 pixels de largura; “14:05:09”, com 8 caracteres, ocupa 96 pixels, então começar no pixel 16 o centraliza exatamente na tela de 128 pixels. Se preferir um LCD de 16x2 caracteres, basta imprimir a mesma saída de strftime com lcd.print(); as ligações são explicadas no artigo Display LCD I2C.

O que acontece com a hora quando o Wi-Fi cai? RTC DS3231 de reserva

A pergunta mais comum é: se o Wi-Fi cair, a hora também se perde? Não. Depois que o ESP32 obtém a hora pelo NTP, continua contando com seu próprio temporizador; perder o Wi-Fi não para o relógio. Como esse temporizador usa o cristal de 40 MHz, ele apresenta um desvio de cerca de um ou dois segundos por dia, corrigido na próxima sincronização quando o Wi-Fi volta. O problema real é uma queda de energia ou uma reinicialização pelo botão EN: nesse caso, a hora do sistema volta a 1970, e sem Wi-Fi o ESP32 não tem como saber a hora.

SituaçãoA hora é mantida?O que fazer
O Wi-Fi cai por um tempoSim, o ESP32 continua contandoNada; o NTP corrige a hora quando o Wi-Fi volta
Deep sleepSim, o temporizador RTC continua contandoEm períodos longos de deep sleep, ressincronize com NTP em alguns despertares
Queda de energia ou botão ENNão, o relógio volta a 1970Adicione um módulo RTC com bateria de reserva, como o DS3231
Sem internet (campo, depósito)O NTP nunca chegaObtenha a hora apenas de um módulo RTC

Durante o deep sleep, a hora é contada por um oscilador RC menos preciso dentro do chip, e não pelo cristal principal. Esse oscilador é afetado pela temperatura, então, em períodos longos de deep sleep, o relógio pode desviar alguns segundos ou mais; em projetos alimentados por bateria que dormem por horas, é uma boa prática sincronizar com NTP a cada poucos despertares. Os detalhes estão no artigo Deep sleep no ESP32.

Módulo de relógio RTC
Módulo RTC com bateria de reserva: continua contando mesmo quando o ESP32 fica sem energia, permitindo obter a hora dele até que o Wi-Fi volte.

A melhor forma de ter um relógio que resista a quedas de energia é combinar os dois: na inicialização, obtenha imediatamente a hora do módulo DS3231, corrija-a com NTP assim que o Wi-Fi estiver ativo e atualize o DS3231 a cada sincronização NTP. No sketch abaixo, o DS3231 armazena UTC em vez da hora local; assim, a regra do fuso horário fica em um único lugar (a string TZ), e as mudanças de horário de verão nunca corrompem o RTC. Para detectar a sincronização NTP, um callback é registrado com sntp_set_time_sync_notification_cb(). Como esse callback roda dentro da tarefa de rede, nenhuma operação I2C é feita nele; ele apenas ativa uma flag, e loop() cuida da gravação no DS3231. O sketch usa a biblioteca RTClib.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <Wire.h>
3#include <RTClib.h>
4#include <sys/time.h>
5#include "esp_sntp.h"
6
7const char* WIFI_SSID = "AgAdi";
8const char* WIFI_PASS = "Sifre123";
9const char* TZ_INFO   = "<+03>-3";
10
11RTC_DS3231 rtc;
12volatile bool ntpGeldi = false;              // NTP eşitlemesi oldu mu? / did an NTP sync happen?
13
14void ntpCallback(struct timeval* tv) {       // her NTP eşitlemesinde çağrılır / called on every NTP sync
15  ntpGeldi = true;                           // I2C işini loop() yapsın / let loop() do the I2C work
16}
17
18void setup() {
19  Serial.begin(115200);
20  Wire.begin(21, 22);
21  setenv("TZ", TZ_INFO, 1);                  // NTP gelmese de saat dilimi geçerli / TZ valid even without NTP
22  tzset();
23
24  // 1) Sistem saatini önce DS3231'den kur (DS3231'de UTC saklanır) / set system time from the DS3231 (stores UTC)
25  if (rtc.begin() && !rtc.lostPower()) {
26    struct timeval tv = { (time_t)rtc.now().unixtime(), 0 };
27    settimeofday(&tv, nullptr);
28    Serial.println("Saat DS3231'den / time from DS3231");
29  }
30
31  // 2) Wi-Fi gelince NTP saati düzeltir; bağlantı sonra gelse de SNTP yeniden dener
32  //    NTP corrects the clock once Wi-Fi is up; SNTP keeps retrying if the link comes later
33  sntp_set_time_sync_notification_cb(ntpCallback);
34  WiFi.begin(WIFI_SSID, WIFI_PASS);
35  configTzTime(TZ_INFO, "pool.ntp.org", "time.cloudflare.com");
36}
37
38void loop() {
39  if (ntpGeldi) {
40    ntpGeldi = false;
41    rtc.adjust(DateTime((uint32_t)time(nullptr)));   // DS3231'e UTC yaz / write UTC to the DS3231
42    Serial.println("DS3231 NTP ile guncellendi / DS3231 updated from NTP");
43  }
44
45  struct tm t;
46  if (getLocalTime(&t, 0)) {
47    Serial.println(&t, "%d.%m.%Y %H:%M:%S");
48  } else {
49    Serial.println("Saat yok: NTP ve DS3231 bekleniyor / no time yet");
50  }
51  delay(1000);
52}
53
O DS1307 funciona com o mesmo código (escreva RTC_DS1307 e use isrunning no lugar de lostPower), mas pode desviar um ou dois minutos por mês. Graças ao cristal com compensação de temperatura, o DS3231 mantém o desvio em cerca de um minuto por ano. Para ver as diferenças entre os módulos e a armadilha do circuito de carregamento da bateria, consulte o artigo Módulo RTC com Arduino.

Executando uma tarefa em um horário definido todos os dias

O uso mais comum da hora NTP é uma tarefa agendada: ligar a bomba de irrigação por 10 minutos às 07:30, acender as luzes do jardim às 19:00, enviar uma leitura a cada hora cheia. O método é simples: ler a hora em loop() e iniciar a tarefa quando a hora e o minuto corresponderem ao horário desejado. O cuidado principal é impedir que a tarefa seja disparada centenas de vezes no mesmo minuto. Para isso, você armazena em uma variável o dia em que a tarefa rodou (tm_yday), para que ela nunca rode duas vezes no mesmo dia. O sketch abaixo liga o relé no GPIO 26 por 10 minutos todas as manhãs de segunda a sexta, às 07:30; como mede a duração com millis(), o código nunca bloqueia.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <time.h>
3
4const char* WIFI_SSID = "AgAdi";
5const char* WIFI_PASS = "Sifre123";
6const char* TZ_INFO   = "<+03>-3";
7
8const int ROLE_PIN = 26;                     // röle modülü IN / relay module IN
9const int GOREV_SAAT = 7;                    // her gün 07:30 / every day at 07:30
10const int GOREV_DAKIKA = 30;
11const unsigned long SURE_MS = 10UL * 60 * 1000;   // 10 dakika çalış / run for 10 minutes
12
13int sonGun = -1;                             // görevin çalıştığı son gün (tm_yday) / last day the task ran
14bool roleAcik = false;
15unsigned long acilisMs = 0;
16
17void setup() {
18  Serial.begin(115200);
19  pinMode(ROLE_PIN, OUTPUT);
20  digitalWrite(ROLE_PIN, LOW);
21  WiFi.begin(WIFI_SSID, WIFI_PASS);
22  while (WiFi.status() != WL_CONNECTED) delay(500);
23  configTzTime(TZ_INFO, "pool.ntp.org");
24}
25
26void loop() {
27  struct tm t;
28  if (getLocalTime(&t, 0)) {                 // saat geçerliyse / only when the time is valid
29    bool haftaici = (t.tm_wday >= 1 && t.tm_wday <= 5);   // Pzt-Cuma / Mon-Fri
30    if (haftaici && t.tm_hour == GOREV_SAAT && t.tm_min == GOREV_DAKIKA && t.tm_yday != sonGun) {
31      sonGun = t.tm_yday;                    // bugün bir kez / once per day
32      digitalWrite(ROLE_PIN, HIGH);
33      roleAcik = true;
34      acilisMs = millis();
35      Serial.println(&t, "%H:%M gorev basladi / task started");
36    }
37  }
38  if (roleAcik && millis() - acilisMs >= SURE_MS) {   // süre dolunca kapat / switch off after the period
39    digitalWrite(ROLE_PIN, LOW);
40    roleAcik = false;
41    Serial.println("Gorev bitti / task finished");
42  }
43  delay(200);
44}
45
  • Comparação pelos segundos: uma condição como t.tm_sec == 0 pula a tarefa se o ciclo perder aquele segundo (durante a reconexão do Wi-Fi ou um delay longo). Verificar o minuto e o dia é mais seguro.
  • Verificação de uma hora válida: não execute tarefas enquanto getLocalTime() retornar false, ou você tomará decisões erradas com base em 1970 logo após a inicialização.
  • Tarefas perdidas: se a energia cair às 07:29 e voltar às 07:35, a tarefa não rodará naquele dia. Se isso for importante, escreva a condição como “já passou das 07:30 e ainda não rodou hoje” e salve sonGun na memória flash com Preferences.
  • Várias tarefas: mantenha a hora, o minuto e a duração em um array e use uma variável separada para o último dia de execução de cada tarefa.
  • Cargas de alta corrente: acione uma bomba ou lâmpada por um módulo relé ou um MOSFET, nunca diretamente por um pino.
Os pinos GPIO do ESP32 fornecem 3,3 V e só conseguem fornecer algumas dezenas de miliampères. A entrada IN de um módulo relé geralmente é acionada com 3,3 V, mas algumas placas de relé de 5 V podem não comutar de forma confiável; nesse caso, use uma placa com acionamento em 3,3 V ou um circuito de acionamento com transistor. Ao trabalhar no lado da rede elétrica, nunca toque na fiação enquanto o circuito estiver ligado à tomada.

Alterando o intervalo de sincronização e monitorando o NTP

O intervalo padrão de sincronização de 3 horas atende à maioria dos projetos. Para sincronizar com mais frequência (por exemplo, ao ar livre, com grandes variações de temperatura) ou reduzir o tráfego, passe o novo intervalo em milissegundos para sntp_set_sync_interval(). Chame essa função antes de configTzTime(), pois o novo intervalo entra em vigor na próxima inicialização ou quando o intervalo atual termina. Pelas regras do protocolo, o intervalo não pode ser menor que 15 segundos. sntp_get_sync_status() retorna SNTP_SYNC_STATUS_COMPLETED uma vez após uma sincronização terminar e depois se redefine, permitindo detectar cada nova sincronização.

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <time.h>
3#include "esp_sntp.h"
4
5const char* TZ_INFO = "<+03>-3";
6
7void setup() {
8  Serial.begin(115200);
9  WiFi.begin("AgAdi", "Sifre123");
10  while (WiFi.status() != WL_CONNECTED) delay(500);
11
12  sntp_set_sync_interval(6UL * 60 * 60 * 1000);   // 6 saatte bir eşitle (en az 15 sn) / sync every 6 hours (min 15 s)
13  configTzTime(TZ_INFO, "pool.ntp.org");          // ayar sntp_init() öncesi verilir / set before sntp_init()
14}
15
16void loop() {
17  if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) {
18    Serial.println("Yeni NTP esitlemesi / new NTP sync");
19  }
20  delay(1000);
21}
22

Qual servidor NTP usar?

A escolha mais comum é pool.ntp.org: um conjunto de milhares de servidores mantidos por voluntários no mundo todo que fornece o endereço de um servidor próximo a você a cada solicitação. Os subdomínios de países (uk.pool.ntp.org, de.pool.ntp.org) aproximam ainda mais o servidor. configTzTime() aceita até três nomes de servidores; se o primeiro não responder, passa para o próximo.

ServidorCaracterísticaObservação
pool.ntp.org (endereço de servidor NTP)Conjunto de servidores voluntários, servidor mais próximoPrimeira escolha para uso geral
uk.pool.ntp.org / europe.pool.ntp.org (endereços de servidores NTP por país ou continente)Conjunto de servidores de um país ou continenteLatência um pouco menor
time.cloudflare.com (servidor NTP da Cloudflare)Infraestrutura de uma grande empresaBom como servidor de reserva
time.google.com (servidor NTP do Google)Distribui os segundos intercalares ao longo do tempo (leap smear)Não misture com outros servidores
Endereço IP do seu roteadorAlguns roteadores oferecem NTP localÚtil em uma rede local sem internet
Se você for distribuir seu próprio produto em centenas de dispositivos, é uma boa prática obter um conjunto de servidores em seu próprio nome (uma zona de fornecedor) em vez de usar pool.ntp.org, ou tornar aleatório o horário de inicialização dos dispositivos. Milhares de dispositivos acessando os servidores no mesmo segundo sobrecarregam os servidores voluntários.

NTP no Raspberry Pi Pico W: ntptime

No Raspberry Pi Pico W (e no Pico 2 W), o mesmo trabalho é feito com o módulo ntptime do MicroPython. ntptime.settime() consulta o servidor NTP e ajusta o RTC interno do Pico para UTC. O MicroPython não tem uma string de fuso horário como o ESP32; para a hora local, você soma sua diferença em segundos ao UTC (3 × 3600 para UTC+3 no exemplo). ntptime obtém a hora apenas uma vez e não ressincroniza automaticamente; em projetos que ficam ligados por muito tempo, chamar settime() uma ou duas vezes por dia elimina o desvio. O RTC do Pico W também é zerado quando falta energia, então a mesma reserva com DS3231 se aplica aqui. A conexão do Pico W ao Wi-Fi é explicada em detalhes no artigo Wi-Fi e servidor web no Pico W.

Python
1import network
2import ntptime
3import time
4
5SSID = "AgAdi"
6PASS = "Sifre123"
7UTC_OFFSET = 3 * 3600          # Türkiye UTC+3 / Turkey UTC+3
8# localtime(): 0 = Pazartesi / 0 = Monday
9GUNLER = ("Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi", "Pazar")
10
11wlan = network.WLAN(network.STA_IF)
12wlan.active(True)
13wlan.connect(SSID, PASS)
14while not wlan.isconnected():  # bağlanana kadar bekle / wait until connected
15    time.sleep(0.5)
16
17ntptime.host = "pool.ntp.org"
18for deneme in range(5):        # NTP bazen zaman aşımına uğrar / NTP may time out
19    try:
20        ntptime.settime()      # dahili RTC'yi UTC'ye ayarlar / sets the internal RTC to UTC
21        break
22    except OSError:
23        time.sleep(2)
24
25def yerel_saat():              # UTC + 3 saat / UTC + 3 hours
26    return time.localtime(time.time() + UTC_OFFSET)
27
28son_gun = -1
29while True:
30    yil, ay, gun, saat, dk, sn, hgun, ygun = yerel_saat()
31    print("{:02d}.{:02d}.{} {} {:02d}:{:02d}:{:02d}".format(gun, ay, yil, GUNLER[hgun], saat, dk, sn))
32    if saat == 7 and dk == 30 and ygun != son_gun:   # her gün 07:30'da bir kez / once a day at 07:30
33        son_gun = ygun
34        print("Gorev calisti / task ran")
35    time.sleep(1)
36

No MicroPython, time.localtime() retorna uma tupla com oito elementos: ano, mês (1–12), dia, hora, minuto, segundo, dia da semana e dia do ano. Ela difere da struct tm do ESP32 em dois pontos: o mês começa em 1 e a contagem do dia da semana usa 0 = segunda-feira. ntptime.settime() gera OSError quando o tempo limite da rede é atingido, por isso o sketch tem um laço de cinco tentativas.

Ideias de projetos de relógio NTP com ESP32

  • Relógio de mesa Wi-Fi: mostre a hora, a data e a temperatura ambiente em um único display OLED e reduza o brilho à noite.
  • Relógio gigante de LEDs: mostre a hora NTP como texto rolante em uma matriz de LEDs MAX7219.
  • Irrigação programada de plantas: verifique a umidade do solo todas as manhãs e acione uma minibomba de água por um tempo definido, se necessário.
  • Registro de dados com data e hora: grave leituras de sensores com datas ISO em um cartão SD.
  • Relógio com previsão do tempo: adicione ao lado da hora a previsão do tempo obtida da internet; o artigo sobre previsão do tempo com ESP32 continua a partir daqui.
  • Relatório matinal: todos os dias às 08:00, envie para seu celular a temperatura da casa e os movimentos detectados durante a noite pelo Telegram.

Erros comuns e soluções

SintomaCausa provávelSolução
A hora mostra 1970 ou nunca chegaO Wi-Fi não está conectado ou a porta NTP (UDP 123) está bloqueadaVerifique WiFi.status() primeiro; em redes de escolas e escritórios, tente outra rede ou um hotspot do celular
A hora difere exatamente pelo seu deslocamento em relação ao UTCNenhum fuso horário configurado, a hora é exibida em UTCconfigTzTime("<+03>-3", ...) ou a string do seu fuso
A hora difere pelo dobro do deslocamentoSinal invertido na string TZ ("UTC+3")Use "<+03>-3" ou "UTC-3" para UTC+3
O mês está uma unidade abaixo e o ano é 126tm_mon conta a partir de 0, tm_year a partir de 1900Use tm_mon + 1, tm_year + 1900 ou strftime
A tarefa dispara várias vezes no mesmo minutoFalta proteção contra repetiçãoArmazene o dia em que ela rodou (tm_yday) e verifique esse valor
O laço trava por 5 segundos a cada cicloSem uma hora válida, getLocalTime() espera 5.000 ms por padrãoUse getLocalTime(&t, 0) dentro de loop()
Símbolos estranhos no nome do dia no OLEDA fonte não tem letras acentuadasEscreva os nomes dos dias em ASCII simples ou mude para a biblioteca U8g2
Para isolar um problema, primeiro rode o primeiro sketch, que apenas imprime a hora no Monitor Serial. Se a hora estiver correta ali, o problema está no display, no RTC ou na lógica da tarefa; se também estiver errada ali, verifique o Wi-Fi e a configuração do fuso horário.

NTP no ESP32 é a forma mais barata e simples de adicionar a hora exata a um projeto: com poucas linhas de código, ele obtém a hora com precisão de segundos, cuida sozinho do fuso horário e do horário de verão e, com um DS3231, também resiste a quedas de energia. No app BlueGrays, você encontra projetos passo a passo com esquemas do circuito e código para relógio de tempo real via NTP no ESP32, relógio digital com RTC DS1307, display de previsão do tempo em OLED e registro de temperatura com data e hora.

Perguntas frequentes

O ESP32 consegue manter a hora sem internet?

Sim, depois de receber a hora pelo NTP: o ESP32 continua contando a hora do sistema com seu próprio temporizador, e perder o Wi-Fi não para o relógio. Porém, se faltar energia ou a placa reiniciar, a hora volta a 1970. Em locais sem internet, ou para um relógio que resista a quedas de energia, adicione um módulo RTC com bateria de reserva, como o DS3231.

Por que a hora NTP do meu ESP32 está com algumas horas de diferença?

Se nenhum fuso horário estiver configurado, o ESP32 mostra UTC, então o relógio fica com uma diferença igual ao seu deslocamento em relação ao UTC. Use configTzTime com a string POSIX correta, por exemplo "CET-1CEST,M3.5.0,M10.5.0/3" para a Europa Central ou "GMT0BST,M3.5.0/1,M10.5.0" para o Reino Unido. Se a diferença for o dobro do deslocamento, o sinal na sua string TZ está invertido.

Qual é a diferença entre configTime e configTzTime?

configTime recebe a diferença em relação ao UTC e um ajuste do horário de verão em segundos, mas não sabe quando o horário de verão começa e termina na sua região. configTzTime recebe uma string de fuso horário POSIX que inclui essas regras, então o ESP32 alterna automaticamente entre o horário de verão e o de inverno. Para qualquer região com horário de verão, configTzTime é a escolha certa.

Com que frequência o ESP32 sincroniza com NTP?

No core Arduino ESP32, o SNTP consulta o servidor novamente a cada 3 horas por padrão e mantém a hora com seu próprio temporizador entre as consultas. Você pode alterar o intervalo em milissegundos com sntp_set_sync_interval(), mas, pelas regras do protocolo, ele não pode ser menor que 15 segundos.

Preciso de um módulo RTC com o ESP32?

Não, se o dispositivo estiver sempre alimentado e tiver acesso ao Wi-Fi; o NTP fornece uma hora mais precisa. Se você precisar da hora correta após uma queda de energia, antes que a internet volte, ou se o dispositivo for funcionar em um local sem internet, adicione um módulo RTC com bateria de reserva, como o DS3231.

Como obter a hora NTP no Raspberry Pi Pico W?

No MicroPython, depois de conectar ao Wi-Fi, basta executar import ntptime e ntptime.settime(); isso ajusta o RTC interno do Pico para UTC. Para a hora local, some sua diferença em segundos, por exemplo time.localtime(time.time() + 3600) para UTC+1. ntptime não ressincroniza automaticamente, então chame settime() novamente uma ou duas vezes por dia.

Ver todos os projetos
Hora exata pela internet com ESP32 e NTP
#128Avançado
Premium

Hora exata pela internet com ESP32 e NTP

O ESP32 se conecta ao Wi-Fi e obtém a hora exata com precisão de milissegundos de servidores NTP (Network Time Protocol). Com a função configTzTime(), o fuso horário configurado (TRT, UTC+3 neste exemplo) é aplicado automaticamente, e as mudanças nos países que adotam horário de verão são gerenciadas automaticamente por uma string POSIX TZ. A hora é exibida tanto no Monitor Serial quanto em um LCD I2C opcional.

4.5(13)
21
Relógio digital com ESP32 e RTC DS1307 (I2C + LCD)
#120Intermediário
Premium

Relógio digital com ESP32 e RTC DS1307 (I2C + LCD)

Neste projeto, exibimos a hora e a data em um display LCD I2C 16x2 usando o módulo de relógio de tempo real (RTC) DS1307. O DS1307 é um RTC com bateria de reserva que mantém a hora mesmo quando o ESP32 é desligado. Os dois módulos compartilham o mesmo barramento I2C (SDA/SCL), permitindo aprender a controlar vários dispositivos com apenas dois fios de sinal. Você aprenderá na prática o protocolo de comunicação I2C, o funcionamento dos módulos RTC e a exibição de dados dinâmicos em um LCD.

4.6(26)
40
Conexão Wi-Fi STA com ESP32 e display LCD
#127Avançado

Conexão Wi-Fi STA com ESP32 e display LCD

Neste projeto, conectamos o ESP32 à rede Wi-Fi de casa no modo estação (STA), de forma segura e tolerante a falhas. Guardamos o SSID e a senha em um arquivo secrets.h separado para evitar que sejam expostos no Git, configuramos um mecanismo de reconexão automática e exibimos o nível de sinal RSSI e o endereço IP em um display LCD I2C. O LED integrado funciona como indicador de status. É um gerenciador de conexão pronto para uso em produção.

4.4(21)
34
ESP32 HTTPClient + API de clima (com display OLED SSD1306)
#134Avançado
Premium

ESP32 HTTPClient + API de clima (com display OLED SSD1306)

Neste projeto, usamos o ESP32 para fazer uma chamada REST à API de clima wttr.in pela internet com HTTPClient.h. Diferentemente da versão anterior com LCD I2C, aqui usamos um display OLED SSD1306 (128×64), que permite usar fontes grandes, ícones vetoriais e uma apresentação visual rica em uma única tela. Sete ícones vetoriais de condições do tempo (ensolarado, parcialmente nublado, nublado, chuvoso, com neve, com tempestade e com neblina) são desenhados durante a execução, sem arquivos bitmap.

4.3(10)
19
ESP32 HTTPClient + API de clima (com display LCD)
#133Avançado
Premium

ESP32 HTTPClient + API de clima (com display LCD)

Neste projeto, usamos o ESP32 para fazer uma chamada REST à API de clima wttr.in pela internet com HTTPClient.h. Interpretamos a resposta JSON com ArduinoJson v7 e mostramos cidade, temperatura, umidade, condições do tempo e vento em um display LCD I2C, em turco e inglês. O wttr.in é grátis e não exige chave de API — basta informar o nome de uma cidade para consultar o clima atual. Os dados se atualizam sozinhos a cada 30 minutos, e o LED dá um retorno visual diferente para cada tela.

4.7(13)
20
ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)
#135Avançado
Premium

ESP32 em sono profundo — Economia de bateria (OLED + contador na RAM RTC)

O modo de sono profundo do ESP32 reduz o consumo de corrente para ~10 µA ao desligar a CPU e a maioria dos periféricos, sendo essencial para dispositivos IoT alimentados por bateria. Neste projeto, usamos três fontes de despertar: um temporizador (15 segundos), um pino externo (EXT0 — botão no GPIO 33) e toque (T0/GPIO 4). As variáveis marcadas com RTC_DATA_ATTR são preservadas durante o sono profundo, mantendo os contadores de inicializações e de despertares por temporizador e botão.

4.3(10)
16
Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Avançado
Premium

Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Monte um registrador de temperatura com data e hora usando ESP32! Leia a temperatura e a umidade do DHT22 a cada minuto, obtenha a hora real do DS1307 RTC e grave os dados em CSV no cartão microSD. O OLED exibe a temperatura atual em letras grandes, a porcentagem de umidade, a data e a hora e o contador de registros. O arquivo /temp_log.csv no cartão SD pode ser aberto no Excel, Google Sheets ou pandas do Python para análise — ideal para monitorar estufas, acompanhar a temperatura de casa ou…

4.7(7)
12
Display LCD I2C com ESP32 (16x2)
#119Intermediário
Premium

Display LCD I2C com ESP32 (16x2)

Neste projeto, exibimos texto conectando um display LCD de 16x2 caracteres ao ESP32 pelo protocolo I2C. Com o I2C, controlamos o display usando apenas dois pinos de dados (SDA e SCL), dispensando o uso de seis ou mais pinos da conexão paralela tradicional. O código mostra uma mensagem fixa na primeira linha e um contador que aumenta a cada segundo na segunda linha. Na inicialização, também executamos um scanner I2C para mostrar o endereço do display no Monitor Serial (geralmente 0x27 ou 0x3F).

4.9(23)
40