Relógio com NodeMCU ESP8266 usando NTP

A imagem parece a mesma do post anterior sobre o NodeMCU ESP8266 com display Oled, mas o assunto aqui é outro: vamos ver como você pode criar um relógio com NodeMCU e Display Oled I2C, atualizando a hora através da internet via protocolo NTP sem precisar de nenhum hardware adicional.

Relógio com NodeMCU ESP8266 e display oled usando NTP

módulo NodeMCU ESP8266 vai se conectar ao seu roteador wifi e efetuar uma conexão com o servidor NTP (um “servidor de horário”), atualizando em tempo real as informações no display. Você pode usar o mesmo princípio para criar outros projetos com o NodeMCU (ou outros módulos ESP8266), que envolvam a utilização de horários específicos, controle de tempo, etc.

O que é o NTP – Network Time Protocol

NTP – Network Time Protocol – é um protocolo criado especialmente para sincronizar relógios com servidores na internet, usando UDP na porta 123. Uma estrutura de servidores NTP é dividida em camadas, ou estratos, numerados de 0 a 16. Essa estrutura basicamente tem como referência uma fonte de horário (um GPS ou um relógio atômico, por exemplo), e distribui essa informação para todos os computadores ou dispositivos que se conectam à ela.

Imagem: xmodulo.com

E por que o NTP é importante? Os computadores e dispositivos, de certa maneira, tendem a ter um relógio não confiável: podem atrasar ou adiantar por características de hardware, ou ter o seu horário alterado acidentalmente pelo usuário ou até mesmo por vírus e outros programas. Em uma época em que tudo deve estar conectado e sincronizado, é de extrema importância garantir a integridade dos dados armazenados, seja para rodar um backup em um horário pré-estabelecido, gravar corretamente um log de acessos em um servidor ou até mesmo por questões de auditoria de dados.

Circuito relógio com NodeMCU e Display Oled

O nosso relógio com NodeMCU e Display Oled vai usar exatamente o mesmo circuito do post anterior. Vamos apenas alterar o programa para que este busque o horário atualizado no servidor NTP. Conecte então o seu display oled I2C no NodeMCU ESP8266 seguindo o esquema abaixo:

Relembrando que a comunicação I2C será feita pelo pino D1 para o SDA e o D2 para o SCL, e a alimentação do display será de 3.3V.

Programa relógio com NodeMCU usando NTP

O programa abaixo foi baseado no programa original contido no guia A Beginner´s Guide to the ESP8266.  Ele usa basicamente as bibliotecas do ESP8266, mais a biblioteca SSD1306Wire.h mencionada no post anterior para configuração do display Oled.

Adicionei as funções de comunicação com o display e também os comandos para mostrar o horário no display (linhas 102 à 117). Antes de carregar o programa, você deve configurar as redes wifi disponíveis para o NodeMCU nas linhas 123 a 125, e pode também incluir mais redes, se necessário.

//Programa: Relogio com NodeMCU ESP8266 e display Oled - NTP
//Referencia: A Beginner's Guide to the ESP8266 - Pieter P.
//Adaptacoes e alteracoes: Arduino e Cia

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <SSD1306Wire.h>

//Cria uma instancia da classe ESP8266WiFiMulti, chamada 'wifiMulti'
ESP8266WiFiMulti wifiMulti;

//Cria uma instancia da classe WifiUDP para enviar e receber dados
WiFiUDP UDP;

IPAddress timeServerIP;
//Define o servidor NTP utilizado
const char* NTPServerName = "b.ntp.br";

//Time stamp do NTP se encontra nos primeiros 48 bytes da mensagem
const int NTP_PACKET_SIZE = 48;

//Buffer para armazenar os pacotes transmitidos e recebidos
byte NTPBuffer[NTP_PACKET_SIZE];

//Pinos do NodeMCU - Interface I2C: SDA => D1, SCL => D2

//Inicializa o display Oled
SSD1306Wire  display(0x3c, D1, D2);

void setup()
{
  Serial.begin(115200);
  delay(10);
  Serial.println("rn");

  //Inicia a comunicacao com os hothospts configurados
  startWiFi();
  startUDP();

  if (!WiFi.hostByName(NTPServerName, timeServerIP))
  {
    //Obtem o endereco IP do servidor NTP
    Serial.println("DNS lookup failed. Rebooting.");
    Serial.flush();
    ESP.reset();
  }
  Serial.print("IP do servidor NTP:t");
  Serial.println(timeServerIP);

  Serial.println("rnEnviando requisicao NTP...");
  sendNTPpacket(timeServerIP);

  //Inicializacao do display
  display.init();
  display.flipScreenVertically();
}

//Requisita horario do servidor NTP a cada minuto
unsigned long intervalNTP = 60000;
unsigned long prevNTP = 0;
unsigned long lastNTPResponse = millis();
uint32_t timeUNIX = 0;

unsigned long prevActualTime = 0;

void loop()
{
  unsigned long currentMillis = millis();

  if (currentMillis - prevNTP > intervalNTP)
  {
    //Verificar se passou um minuto da ultima requisicao
    prevNTP = currentMillis;
    Serial.println("rnEnviando requisicao NTP ...");
    sendNTPpacket(timeServerIP);
  }

  uint32_t time = getTime();
  if (time)
  {
    timeUNIX = time - 10800;
    Serial.print("Resposta NTP:t");
    Serial.println(timeUNIX);
    lastNTPResponse = currentMillis;
  } else if ((currentMillis - lastNTPResponse) > 3600000) {
    Serial.println("Mais de 1 hora desde a ultima resposta NTP. Reiniciando.");
    Serial.flush();
    ESP.reset();
  }

  uint32_t actualTime = timeUNIX + (currentMillis - lastNTPResponse) / 1000;
  if (actualTime != prevActualTime && timeUNIX != 0)
  {
    //Verifica se passou um segundo desde a ultima impressao de valores no serial monitor
    prevActualTime = actualTime;
    Serial.printf("rUTC time:t%d:%d:%d   ", getHours(actualTime), getMinutes(actualTime), getSeconds(actualTime));
    Serial.println();
  }

  //Mostrando a hora no display
  //Apaga o display
  display.clear();
  display.setTextAlignment(TEXT_ALIGN_CENTER);
  //Seleciona a fonte
  display.setFont(ArialMT_Plain_10);
  //Mostra o titulo na parte superior do display
  display.drawString(63, 10, "NTP Clock ESP8266");
  //Mostra o horario atualizado
  display.setFont(ArialMT_Plain_24);
  display.drawString(29, 29, String(getHours(actualTime)));
  display.drawString(45, 28, ":");
  display.drawString(62, 29, String(getMinutes(actualTime)));
  display.drawString(78, 28, ":");
  display.drawString(95, 29, String(getSeconds(actualTime)));
  display.display();
}

void startWiFi()
{
  //Coloque aqui as redes wifi necessarias
  wifiMulti.addAP("NOME_DA_REDE_WIFI_1", "SENHA_DA_REDE_WIFI_1");
  wifiMulti.addAP("NOME_DA_REDE_WIFI_2", "SENHA_DA_REDE_WIFI_2");
  wifiMulti.addAP("NOME_DA_REDE_WIFI_3", "SENHA_DA_REDE_WIFI_3");

  Serial.println("Conectando");
  while (wifiMulti.run() != WL_CONNECTED)
  {
    //Aguarda a conexao da rede wifi
    delay(250);
    Serial.print('.');
  }
  Serial.println("rn");
  Serial.print("Conectado a rede ");
  Serial.println(WiFi.SSID());
  Serial.print("Endereco IP:t");
  Serial.print(WiFi.localIP());
  Serial.println("rn");
}

void startUDP()
{
  Serial.println("Iniciando UDP");
  //Inicializa UDP na porta 23
  UDP.begin(123);
  Serial.print("Porta local:t");
  Serial.println(UDP.localPort());
  Serial.println();
}

uint32_t getTime()
{
  if (UDP.parsePacket() == 0)
  {
    return 0;
  }
  UDP.read(NTPBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
  //Combina os 4 bytes do timestamp em um numero de 32 bits
  uint32_t NTPTime = (NTPBuffer[40] << 24) | (NTPBuffer[41] << 16) | (NTPBuffer[42] << 8) | NTPBuffer[43];
  //Converte o horario NTP para UNIX timestamp
  //Unix time comeca em 1 de Jan de 1970. Sao 2208988800 segundos no horario NTP:
  const uint32_t seventyYears = 2208988800UL;
  //Subtrai setenta anos do tempo
  uint32_t UNIXTime = NTPTime - seventyYears;
  return UNIXTime;
}

void sendNTPpacket(IPAddress& address)
{
  //Seta todos os bytes do buffer como 0
  memset(NTPBuffer, 0, NTP_PACKET_SIZE);
  //Inicializa os valores necessarios para formar a requisicao NTP
  NTPBuffer[0] = 0b11100011;   // LI, Version, Mode
  //Envia um pacote requisitando o timestamp
  UDP.beginPacket(address, 123);
  UDP.write(NTPBuffer, NTP_PACKET_SIZE);
  UDP.endPacket();
}

inline int getSeconds(uint32_t UNIXTime) 
{
  return UNIXTime % 60;
}

inline int getMinutes(uint32_t UNIXTime) 
{
  return UNIXTime / 60 % 60;
}

inline int getHours(uint32_t UNIXTime) 
{
  return UNIXTime / 3600 % 24;
}

Carregado o programa, o NodeMCU vai se conectar na rede wifi disponível, abrir a conexão UDP e efetuar a requisição NTP. Todo o processo pode ser acompanhado pelo serial monitor:

Após a conexão o horário será mostrado no display e atualizado à cada segundo.

Experimente também usar bibliotecas prontas para conexão NTP, como a NtpClientLib, e também veja opções de uso do protocolo NTP com Arduino, que você pode usar por exemplo com conexões ethernet ou shields wifi.

4.5/5 - (6 votos)

Related posts

GSM Shield SIM900: Acione portas do Arduino por SMS e realize chamadas telefônicas

by Arduino e Cia
9 anos ago

Comunicação Wifi entre módulos ESP8266 ESP-01

by Arduino e Cia
3 anos ago

Cotação de Bitcoin com ESP32 e display e-Paper

by Arduino e Cia
4 anos ago
Sair da versão mobile