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.
O 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
O 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.
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.