Platform.ini

[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_dtr = 0
monitor_rts = 0
monitor_speed = 115200
lib_deps =
  bblanchon/ArduinoJson @ ^7.2.1
  256dpi/MQTT @ ^2.5.2
  ayushsharma82/ElegantOTA @ ^3.1.1
  majicdesigns/MD_MAX72XX @ ^3.5.1
  majicdesigns/MD_Parola @ ^3.7.2
  arduino-libraries/NTPClient@^3.2.1
  pfeerick/elapsedMillis @ ^1.0.6
  plageoj/UrlEncode @ ^1.0.1
build_flags=-DELEGANTOTA_USE_ASYNC_WEBSERVER=1  

Basic file structure:

/project-folder/
  ├── main.ino or main.cpp
  ├── mywifi.h
  ├── myota.h
  ├── myntp.h
  ├── mywebserver.h

mywifi.h

#ifndef MYWIFI_H
#define MYWIFI_H

#include <ESP8266WiFi.h> // Required for WiFi functionality

// WiFi credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* hostname = "HOSTNAME";
String IPaddr;


// Function to initialize WiFi
void wifiInit() {
  WiFi.hostname(hostname);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    debug(".");
  }
  IPaddr = WiFi.localIP().toString();
  debugln("\nConnected to WiFi");
  debug("IP address: ");
  debugln(IPaddr);
}

#endif

#endif

myota.h

#ifndef MYOTA_H
#define MYOTA_H

#include <ElegantOTA.h> // Required for OTA functionality
#include "mywebserver.h" // Include the web server module

// Function to initialize ElegantOTA
void otaInit() {
  ElegantOTA.begin(&server);
}

// Function to handle OTA updates in the loop
void otaLoop() {
  ElegantOTA.loop();
}

#endif

mywebserver.h

#ifndef MYWEBSERVER_H
#define MYWEBSERVER_H

#include <ESPAsyncWebServer.h> // Required for AsyncWebServer
#include <ESPAsyncTCP.h> // Required for ESPAsyncWebServer dependency
#include <ESP8266WiFi.h> // Required for WiFi functionality

// Declare and define Web Server objects
AsyncWebServer server(80);
AsyncEventSource events("/time");

// HTML content
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
  <title>ESP8266 Time Server</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body { font-family: Arial; text-align: center; margin-top: 50px; }
    .time { font-size: 48px; margin: 20px; }
    .ota-link { margin-top: 30px; display: block; }
  </style>
</head>
<body>
  <h1>ESP8266 Time Server</h1>
  <div class="time" id="time">--:--:--</div>
  <a href="/update" class="ota-link">OTA Update</a>
  
  <script>
    const eventSource = new EventSource('/time');
    eventSource.onmessage = function(e) {
      document.getElementById('time').innerHTML = e.data;
    };
  </script>
</body>
</html>
)rawliteral";

void handleRoot(AsyncWebServerRequest *request) {
    String message = "<html><body>"; 
    message += "<h1>ESP32-CAM Project: IoT Clock</h1>"; 
    message += "Hostname: " + String(hostname) + "<br>"; 
    message += "IP Address: " + IPaddr + "<br>"; 
    message += "ElegantOTA ready for <a href=\"/update\">firmware updates</a>.<br>"; 
    message += "</body></html>"; 
    request->send(200, "text/html", message);
  }

// Function to initialize the web server
void webServerInit() {
  // Route for root / web page
  server.on("/", HTTP_GET, handleRoot);
  server.on("/time", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });

  // Handle Server-Sent Events (SSE) for time updates
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      debugln("Client reconnected! Last message ID: %u\n", client->lastId());
    }
    client->send("hello!", NULL, millis(), 1000);
  });
  server.addHandler(&events);

  // Start server
  server.begin();
}

#endif

myntp.h

#ifndef MYNTP_H
#define MYNTP_H

#include <NTPClient.h> // Required for NTP functionality
#include <WiFiUdp.h> // Required for NTPClient dependency
#include <ESP8266WiFi.h> // Required for WiFi functionality

// Declare and define NTP objects
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.pagasa.dost.gov.ph", 28800, 60000); // Update every 60 seconds

// Function to initialize NTP
void ntpInit() {
  timeClient.begin();
  timeClient.update();
}

// Function to get the current time
String getCurrentTime() {
  timeClient.update();
  return timeClient.getFormattedTime();
}

#endif

main

#include "mywifi.h"
#include "myntp.h"
#include "mywebserver.h"
#include "myota.h"

void setup() {
  Serial.begin(115200);

  // Initialize WiFi
  wifiInit();

  // Initialize NTP
  ntpInit();

  // Initialize Web Server
  webServerInit();

  // Initialize OTA
  otaInit();
}

void loop() {
  static unsigned long lastUpdate = 0;

  // Send time update every second to connected clients
  if (millis() - lastUpdate >= 1000) {
    lastUpdate = millis();
    String currentTime = getCurrentTime();
    events.send(currentTime.c_str(), "time");
  }

  // Handle OTA updates
  otaLoop();
}