The WebRTC example provides a comprehensive real-time clock interface for your ESP32 projects. Designed for ESP32 educational platform with built-in RTC capabilities, enhanced time management features, and seamless integration with the educational ecosystem. You can display real-time clock information, synchronize time from web browser to Arduino, and monitor time differences through an intuitive web interface.
Features
Real-time Clock Display: Shows current ESP32 RTC time with automatic updates
Device Time Comparison: Display web browser/device time alongside ESP32 time
One-click Time Synchronization: Sync ESP32 RTC with web browser time instantly
Visual Time Difference Indicator: Shows time drift between devices in minutes
Two-line Time Format: 12-hour AM/PM format with full date display
Modern Gradient UI: Card-style layout with responsive design
WebSocket Communication: Real-time bidirectional updates without page refresh
Timezone-aware Synchronization: Uses local device time for accurate sync
Connection Status Monitoring: Visual indicators for WebSocket connection state
Automatic Time Requests: Requests current ESP32 time on page load
Disclosure: Some of the links in this section are Amazon affiliate links, meaning we may earn a commission at no additional cost to you if you make a purchase through them. Additionally, some links direct you to products from our own brand, DIYables .
Wiring Diagram
This image is created using Fritzing. Click to enlarge image
Connect the ESP32 board to your computer using a USB cable.
Launch the Arduino IDE on your computer.
Select the appropriate ESP32 board (e.g. ESP32 Dev Module) and COM port.
Navigate to the Libraries icon on the left bar of the Arduino IDE.
Search "DIYables ESP32 WebApps", then find the DIYables ESP32 WebApps Library by DIYables
Click Install button to install the library.
You will be asked for installing some other library dependencies
Click Install All button to install all library dependencies.
Search “RTClib”, then find the RTC library by Adafruit
Click Install button to install RTC library.
You may be asked to install dependencies for the library
Install all dependencies for the library by clicking on Install All button.
On Arduino IDE, Go to File Examples DIYables ESP32 WebApps WebRTC example, or copy the above code and paste it to the editor of Arduino IDE
/* * DIYables WebApp Library - Web RTC Example * * This example demonstrates the Web RTC feature: * - Real-time clock display for both ESP32 and client device * - One-click time synchronization from web browser to ESP32 * - Hardware RTC integration for persistent timekeeping * - Visual time difference monitoring * * Hardware Required: * - ESP32 development board * - DS3231 RTC module (connected via I2C) * * Required Libraries: * - RTClib library (install via Library Manager) * * Setup: * 1. Connect DS3231 RTC module to ESP32 I2C pins (SDA/SCL) * 2. Install RTClib library in Arduino IDE * 3. Update WiFi credentials below * 4. Upload the sketch to your ESP32 * 5. Open Serial Monitor to see the IP address * 6. Navigate to http://[IP_ADDRESS]/web-rtc */#include <DIYables_ESP32_Platform.h>#include <DIYablesWebApps.h>#include <RTClib.h>// RTC objectRTC_DS3231 rtc;char daysOfWeek[7][12] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};// WiFi credentials - UPDATE THESE WITH YOUR NETWORKconstchar WIFI_SSID[] = "YOUR_WIFI_SSID";constchar WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";// Create WebApp server and page instancesESP32ServerFactory serverFactory;DIYablesWebAppServerwebAppsServer(serverFactory, 80, 81);DIYablesHomePage homePage;DIYablesWebRTCPage webRTCPage;voidsetup() {Serial.begin(9600);delay(1000);Serial.println("DIYables ESP32 WebApp - Web RTC Example");// Initialize RTCif (!rtc.begin()) {Serial.println("RTC module is NOT found");Serial.flush();while (1); }// Check if RTC lost power and if so, set the timeif (rtc.lostPower()) {Serial.println("RTC lost power, setting time!");// When time needs to be set on a new device, or after a power loss, the// following line sets the RTC to the date & time this sketch was compiled rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));// This line sets the RTC with an explicit date & time, for example to set// January 21, 2021 at 3am you would call:// rtc.adjust(DateTime(2021, 1, 21, 3, 0, 0)); }// Print initial RTC timeDateTime initialTime = rtc.now();Serial.print("Initial RTC Time: ");Serial.print(initialTime.year(), DEC);Serial.print("/");Serial.print(initialTime.month(), DEC);Serial.print("/");Serial.print(initialTime.day(), DEC);Serial.print(" (");Serial.print(daysOfWeek[initialTime.dayOfTheWeek()]);Serial.print(") ");if (initialTime.hour() < 10) Serial.print("0");Serial.print(initialTime.hour(), DEC);Serial.print(":");if (initialTime.minute() < 10) Serial.print("0");Serial.print(initialTime.minute(), DEC);Serial.print(":");if (initialTime.second() < 10) Serial.print("0");Serial.print(initialTime.second(), DEC);Serial.println();// Add pages to serverwebAppsServer.addApp(&homePage);webAppsServer.addApp(&webRTCPage);// Optional: Add 404 page for better user experiencewebAppsServer.setNotFoundPage(DIYablesNotFoundPage());// Set callback for time sync from web webRTCPage.onTimeSyncFromWeb(onTimeSyncReceived);// Set callback for time request from web webRTCPage.onTimeRequestToWeb(onTimeRequested);// Start the WebApp serverif (!webAppsServer.begin(WIFI_SSID, WIFI_PASSWORD)) {while (1) {Serial.println("Failed to connect to WiFi");delay(1000); } }}voidloop() {// Handle web serverwebAppsServer.loop();// Send current time to web clients and print to Serial every 1 secondstaticunsignedlong lastUpdate = 0;if (millis() - lastUpdate >= 1000) { lastUpdate = millis();// Get current RTC timeDateTime currentTime = rtc.now();// Send time to web clients in human readable format webRTCPage.sendTimeToWeb(currentTime.year(), currentTime.month(), currentTime.day(), currentTime.hour(), currentTime.minute(), currentTime.second());// Print time to Serial MonitorSerial.print("RTC Time: ");Serial.print(currentTime.year(), DEC);Serial.print("/");Serial.print(currentTime.month(), DEC);Serial.print("/");Serial.print(currentTime.day(), DEC);Serial.print(" (");Serial.print(daysOfWeek[currentTime.dayOfTheWeek()]);Serial.print(") ");if (currentTime.hour() < 10) Serial.print("0");Serial.print(currentTime.hour(), DEC);Serial.print(":");if (currentTime.minute() < 10) Serial.print("0");Serial.print(currentTime.minute(), DEC);Serial.print(":");if (currentTime.second() < 10) Serial.print("0");Serial.print(currentTime.second(), DEC);Serial.println(); }delay(10);}// Callback function called when web client sends time sync commandvoid onTimeSyncReceived(unsignedlong unixTimestamp) {Serial.print("Time sync received: ");Serial.println(unixTimestamp);// Convert Unix timestamp to DateTime and set RTC timeDateTime newTime(unixTimestamp); rtc.adjust(newTime);Serial.println("ESP32 RTC synchronized!");}// Callback function called when web client requests current ESP32 timevoid onTimeRequested() {// Get current RTC time and send to web in human readable formatDateTime currentTime = rtc.now(); webRTCPage.sendTimeToWeb(currentTime.year(), currentTime.month(), currentTime.day(), currentTime.hour(), currentTime.minute(), currentTime.second());}
Configure WiFi credentials in the code by updating these lines:
Click Upload button on Arduino IDE to upload the code to Arduino.
Open the Serial Monitor on Arduino IDE
Wait for the connection to WiFi and the prints of WiFi information on Serial Monitor.
Check out the result on Serial Monitor. It looks like the below
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
DIYables ESP32 WebApp - Web RTC Example
Initial RTC Time: 2025/8/28 (Thursday) 12:00:08
INFO: Added app /
INFO: Added app /web-rtc
DIYables WebApp Library
Platform: ESP32
Network connected!
IP address: 192.168.0.2
HTTP server started on port 80
Configuring WebSocket server callbacks...
WebSocket server started on port 81
WebSocket URL: ws://192.168.0.2:81
WebSocket server started on port 81
==========================================
DIYables WebApp Ready!
==========================================
📱 Web Interface: http://192.168.0.2
🔗 WebSocket: ws://192.168.0.2:81
📋 Available Applications:
🏠 Home Page: http://192.168.0.2/
🕐 Web RTC: http://192.168.0.2/web-rtc
==========================================
Ln 11, Col 1
ESP32 Dev Module on COM15
2
If you do not see anything, reboot ESP32 board.
Using the Web Interface
Open a web browser on your computer or mobile device connected to the same WiFi network
Type the IP address shown in the Serial Monitor to the web browser
Example: http://192.168.1.100
You will see the home page like below image:
Click to the Web RTC link, you will see the Web RTC app's UI like the below:
Or you can also access the page directly by IP address followed by /web-rtc. For example: http://192.168.1.100/web-rtc
You will see the Web RTC interface showing:
Arduino Time: Current time from the Arduino's RTC
Your Device Time: Current time from your web browser/device
Time Difference: Difference between the two times in minutes
Sync ESP32 Time Button: Click to synchronize ESP32 time with your device
Time Synchronization
Click the "Sync ESP32 Time" button to synchronize the Arduino's RTC with your device's local time
The synchronization process:
Gets your device's current local time (not UTC)
Adjusts for timezone offset to ensure accurate local time sync
Sends timestamp to Arduino via WebSocket
Arduino updates its RTC with the received time
Web interface updates to show the synchronized time
After synchronization, the time difference should be minimal (usually 0-1 minutes)
The ESP32 will maintain accurate time even after the web interface is closed
Code Explanation
Key Components
#include <DIYablesWebApps.h>#include <RTClib.h>// Initialize RTC object and web serverRTC_DS3231 rtc;DIYablesWebAppServer server;DIYablesWebRTCPage rtcPage;// Days of week array for displaychar daysOfWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"};
Setup Function
voidsetup() {Serial.begin(9600);// Initialize DS3231 RTC moduleif (!rtc.begin()) {Serial.println("RTC module is NOT found");Serial.flush();while (1); }// Check if RTC lost power and set time if neededif (rtc.lostPower()) {Serial.println("RTC lost power, setting time!"); rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); }// Setup WiFi connection server.setupWiFi(WIFI_SSID, WIFI_PASSWORD);// Add RTC page to server server.addWebApp(rtcPage);// Set up callback functions rtcPage.onTimeSyncFromWeb(onTimeSyncReceived); rtcPage.onTimeRequestToWeb(onTimeRequested);// Start the server server.begin();}
Callback Functions
Time Synchronization Callback:
// Called when web interface sends time sync commandvoid onTimeSyncReceived(unsignedlong unixTimestamp) {Serial.print("Time sync received: ");Serial.println(unixTimestamp);// Convert Unix timestamp to DateTime and set RTC timeDateTime newTime(unixTimestamp); rtc.adjust(newTime);Serial.println("ESP32 RTC synchronized!");}
Time Request Callback:
// Called when web interface requests current ESP32 timevoid onTimeRequested() {DateTime currentTime = rtc.now();// Send current time to web interface rtcPage.sendTimeToWeb( currentTime.year(), currentTime.month(), currentTime.day(), currentTime.hour(), currentTime.minute(), currentTime.second() );}
Main Loop
voidloop() { server.handleClient();// Send current time to web clients every 1 secondstaticunsignedlong lastUpdate = 0;if (millis() - lastUpdate >= 1000) { lastUpdate = millis();DateTime currentTime = rtc.now();// Send time to web clients rtcPage.sendTimeToWeb(currentTime.year(), currentTime.month(), currentTime.day(), currentTime.hour(), currentTime.minute(), currentTime.second());// Print time to Serial MonitorSerial.print("RTC Time: ");Serial.print(currentTime.year(), DEC);Serial.print("/");Serial.print(currentTime.month(), DEC);Serial.print("/");Serial.print(currentTime.day(), DEC);Serial.print(" (");Serial.print(daysOfWeek[currentTime.dayOfTheWeek()]);Serial.print(") ");if (currentTime.hour() < 10) Serial.print("0");Serial.print(currentTime.hour(), DEC);Serial.print(":");if (currentTime.minute() < 10) Serial.print("0");Serial.print(currentTime.minute(), DEC);Serial.print(":");if (currentTime.second() < 10) Serial.print("0");Serial.print(currentTime.second(), DEC);Serial.println(); }delay(10);}
API Methods
DIYablesWebRTCPage Class Methods
onTimeSyncFromWeb(callback)
Sets the callback function to handle time synchronization from web browser
void checkScheduledActions() {DateTime currentTime = rtc.now();// Turn on LED every day at 6:00 AMif (currentTime.hour() == 6 && currentTime.minute() == 0 && currentTime.second() == 0) {digitalWrite(LED_BUILTIN, HIGH);Serial.print("Morning LED activated! Time: ");Serial.print(daysOfWeek[currentTime.dayOfTheWeek()]);Serial.print(" ");Serial.print(currentTime.hour());Serial.print(":");Serial.println(currentTime.minute()); }// Turn off LED every day at 10:00 PM if (currentTime.hour() == 22 && currentTime.minute() == 0 && currentTime.second() == 0) {digitalWrite(LED_BUILTIN, LOW);Serial.println("Evening LED deactivated!"); }}
Multiple Web Apps Integration
// Combine WebRTC with other web appsserver.addWebApp(rtcPage); // Real-time clockserver.addWebApp(monitorPage); // Serial monitor with timestampsserver.addWebApp(plotterPage); // Data plotting with time axis
Applications and Use Cases
Educational Projects
Time Management Learning: Teach students about RTC, timekeeping, and synchronization
IoT Time Concepts: Demonstrate network time synchronization in IoT systems
Data Logging Projects: Add timestamps to sensor readings and experiments
Scheduling Systems: Create time-based automation and control systems
Real-World Applications
Home Automation: Schedule lights, irrigation, or other devices
Data Acquisition: Timestamp sensor readings for analysis
Event Logging: Record when specific events occur with accurate timing
Remote Monitoring: Check device status and last update times remotely
STEM Education Benefits
Time Zone Concepts: Understand local time vs. UTC and timezone handling
Network Communication: Learn WebSocket communication and real-time updates
Hardware Integration: Combine web interfaces with hardware RTC functionality
Problem Solving: Debug timing issues and synchronization problems
Technical Specifications
Memory Usage
Flash Memory: ~8KB for WebRTC functionality
SRAM: ~2KB during operation
WebSocket Buffer: ~1KB for message handling
Performance Characteristics
Update Frequency: Real-time updates via WebSocket
Sync Accuracy: Typically within 1-2 seconds
Network Overhead: ~50 bytes per time update message
Response Time: <100ms for time sync operations
Compatibility
ESP32 boards: ESP32, ESP32 Web Apps
Browsers: All modern browsers with WebSocket support
Devices: Desktop, tablet, and mobile devices
Networks: WiFi networks with internet access
Video Tutorial
The below video demo uses the below code:
/* * This ESP32 code is created by esp32io.com * * This ESP32 code is released in the public domain * * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/diyables-esp32-web-apps-web-rtc *//** * @file SmartPillReminder_ESP32.ino * @brief IoT Medication Reminder System using ESP32 and an external I2C DS3231 RTC module. * * This firmware integrates an asynchronous Web Server for RTC synchronization, * a MAX7219 8x32 LED Matrix driven via SPI, and a discrete GPIO active buzzer peripheral. * Memory safety is enforced via static char buffer allocation to prevent runtime heap corruption. * * Hardware Connections: * - DS3231 SDA -> ESP32 GPIO 21 (Default I2C SDA pin) * - DS3231 SCL -> ESP32 GPIO 22 (Default I2C SCL pin) * - MAX7219 VCC -> 5V * - MAX7219 GND -> GND * - MAX7219 DIN -> ESP32 MOSI (Default GPIO 23) * - MAX7219 CLK -> ESP32 SCK (Default GPIO 18) * - MAX7219 CS -> GPIO 25 (Configured SPI Chip Select pin) * - Active Buzzer -> GPIO 15 (Configured Digital Output pin) */#include <DIYables_ESP32_Platform.h>#include <DIYablesWebApps.h>#include <RTClib.h>#include <MD_Parola.h>#include <MD_MAX72xx.h>// --- PERIPHERAL INSTANTIATION ---RTC_DS3231 rtc;// --- NETWORK CONFIGURATION ---constchar WIFI_SSID[] = "Nhuan 3";constchar WIFI_PASSWORD[] = "15671108";// --- WEB SERVER ROUTING ENGINE ---ESP32ServerFactory serverFactory;DIYablesWebAppServerwebAppsServer(serverFactory, 80, 81);DIYablesHomePage homePage;DIYablesWebRTCPage webRTCPage;// --- DISPLAY SUBSYSTEM CONFIGURATION ---#define HARDWARE_TYPE MD_MAX72XX::FC16_HW// Hardware register layout variant for physical modules#define MAX_DEVICES 4 // Number of cascaded 8x8 matrix blocks#define CS_PIN 25 // SPI Chip Select active-low hardware pinMD_Parola ledMatrix = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);// --- ALARM SUBSYSTEM ARCHITECTURE ---constint BUZZER_PIN = 15;bool isAlarming = false; // State flag to prevent redundant re-initialization of display bufferstypedefstruct {inthour;intminute;} AlarmTime;// Scheduled alarm arrays for validationconstint TOTAL_ALARMS = 3;AlarmTime medicationAlarms[TOTAL_ALARMS] = { {9, 9}, // Slot 1 {9, 11}, // Slot 2 {9, 12} // Slot 3};// Fixed-size static allocation to prevent dangling pointers during stack deallocationchar AlertMsg[50] = "";char daysOfWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};// --- CALLBACK FUNCTION PROTOTYPES ---void onTimeSyncReceived(unsignedlong unixTimestamp);void onTimeRequested();/** * @brief Hardware initialization routine. */voidsetup() {Serial.begin(115200); // Updated to match default ESP32 bootloader speed to eliminate garbage textdelay(1000);// Configure GPIO peripheralspinMode(BUZZER_PIN, OUTPUT);digitalWrite(BUZZER_PIN, LOW);// Initialize SPI-driven LED Matrix ledMatrix.begin(); ledMatrix.setIntensity(8); // Brightness optimization register value to prevent camera sensor overexposure ledMatrix.displayClear();// Set initial boot token into display queue ledMatrix.displayText("Medicine Reminder System READY", PA_LEFT, 60, 2000, PA_SCROLL_LEFT, PA_SCROLL_LEFT);Serial.println("[SYS] Starting firmware initialization pipeline...");// Initialize I2C clock communication busif (!rtc.begin()) {Serial.println("[ERR] Fatal: DS3231 hardware module not detected on I2C bus.");Serial.flush();while (1); }// Verify internal register state; apply fallback parameters if compile parameters mismatchif (rtc.lostPower()) {Serial.println("[RTC] Power down event detected. Aligning internal clock parameters to compile time."); rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); }// Construct Web App HTTP server routing layerswebAppsServer.addApp(&homePage);webAppsServer.addApp(&webRTCPage);webAppsServer.setNotFoundPage(DIYablesNotFoundPage());// Attach synchronization service event hooks webRTCPage.onTimeSyncFromWeb(onTimeSyncReceived); webRTCPage.onTimeRequestToWeb(onTimeRequested);// Initialize network link layer socket connectionif (!webAppsServer.begin(WIFI_SSID, WIFI_PASSWORD)) {while (1) {Serial.println("[NET] Fatal: Network connection timed out.");delay(1000); } }Serial.print("[NET] Infrastructure active. Resource path accessible via: http://");Serial.println(WiFi.localIP());}/** * @brief Infinite loop processing task pipelines. */voidloop() {// Service inbound network sockets and data frame handshakeswebAppsServer.loop();// Non-blocking shift engine: Calculates next step pixel translations on every passif (ledMatrix.displayAnimate()) { ledMatrix.displayReset(); }// Time-sliced task execution block running at a strict 1000ms polling intervalstaticunsignedlong lastUpdate = 0;if (millis() - lastUpdate >= 1000) { lastUpdate = millis();// Query immediate system states from the DS3231 tracking registersDateTime currentTime = rtc.now();int currentH = currentTime.hour();int currentM = currentTime.minute();int currentS = currentTime.second();// Output status parameters to local tracking streamSerial.print("[LOG] Time Status: ");Serial.print(currentH); Serial.print(":");Serial.print(currentM); Serial.print(":");Serial.println(currentS);// SEQUENTIAL SEARCH SCAN ACROSS ACTIVE ALARM VECTORSfor (int i = 0; i < TOTAL_ALARMS; i++) {if (currentH == medicationAlarms[i].hour && currentM == medicationAlarms[i].minute && currentS == 0) {// Conditional block protecting display memory boundaries from re-entrant override bugsif (!isAlarming) { isAlarming = true;// Print safe formatted characters straight to locked char index slots// %02d ensures leading zeros are preserved flawlessly (e.g. [23:43]) sprintf(AlertMsg, "TAKE MEDICATION ! [%02d:%02d] ", currentH, currentM); ledMatrix.displayClear(); // Link structural char reference space to the active render queue ledMatrix.displayText(AlertMsg, PA_LEFT, 60, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);Serial.print("[ALARM] Trigger event confirmed at slot ID: ");Serial.println(i + 1); } } }// Auto-timeout block: Clears active alert metrics after 20 elapsed secondsif (currentS >= 20 && isAlarming) { isAlarming = false; ledMatrix.displayClear(); ledMatrix.displayText("Medicine Reminder System With WEB RTC", PA_LEFT, 60, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);Serial.println("[ALARM] Trigger window expired. Display registers reset to default state."); }// Synchronize network clock states with browser clients (Executed at bottom to shield SPI lines) webRTCPage.sendTimeToWeb(currentTime.year(), currentTime.month(), currentTime.day(), currentH, currentM, currentS); }// Asynchronous Active Buzzer Duty Cycle Control (50% duty cycle, 1Hz rate square-wave generator)if (isAlarming) {staticunsignedlong lastBuzzerToggle = 0;if (millis() - lastBuzzerToggle >= 500) { lastBuzzerToggle = millis();digitalWrite(BUZZER_PIN, !digitalRead(BUZZER_PIN)); // Invert state logic } } else {digitalWrite(BUZZER_PIN, LOW); // Enforce noise suppression when system state is idle }delay(10); // System cycle stabilizer trashing delay}/** * @brief Processes epoch update queries coming down from external browser UI sync commands. * @param unixTimestamp Incoming epoch time tracking token. */void onTimeSyncReceived(unsignedlong unixTimestamp) {Serial.print("[NET] Inbound synchronization frame detected: ");Serial.println(unixTimestamp);DateTime newTime(unixTimestamp); rtc.adjust(newTime); // Write updated parameters straight to external RTC register cellsSerial.println("[RTC] External DS3231 synchronization routine complete.");}/** * @brief Processes outbound interface frame payload requests from polling web hosts. */void onTimeRequested() {DateTime currentTime = rtc.now(); webRTCPage.sendTimeToWeb(currentTime.year(), currentTime.month(), currentTime.day(), currentTime.hour(), currentTime.minute(), currentTime.second());}
Summary
The WebRTC example demonstrates how to:
Create a web-based real-time clock interface
Synchronize ESP32 RTC with web browser time
Display time information in user-friendly format
Monitor time differences and connection status
Integrate time functionality with other web applications
Build educational IoT projects with time management features
This example is perfect for projects requiring accurate timekeeping, data logging with timestamps, scheduled automation, and educational demonstrations of time synchronization concepts in IoT systems.
Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!