Skip to content

DuyHai33/esp32-digital-clock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ESP32 Digital Clock & IoT Environmental Monitor

A personal hardware + firmware project built from scratch — custom PCB design, cooperative non-blocking firmware, MQTT telemetry, local web dashboard, OTA updates, and Telegram push alerts.

PlatformIO Framework MCU License


Board Demo

What it does

  • Displays NTP-synced time (UTC+7) and live temperature/humidity on a 16×2 I²C LCD
  • Publishes sensor data to Adafruit IO via MQTT every 30 seconds
  • Serves a local web dashboard (Chart.js, 30-min history, editable thresholds)
  • Sends Telegram push notifications when temperature, humidity, or battery crosses a threshold
  • Supports OTA firmware updates over WiFi — no USB cable needed after first flash
  • Enters deep sleep on keypad command to save power; wakes, publishes one reading, sleeps again

Hardware

Designed entirely from scratch: schematic and single-layer PCB layout in Proteus, then fabricated and hand-soldered.

Components: ESP32 DevKit v1 · DHT11 · 4×4 Keypad · 16×2 LCD (I²C) · Custom PCB rev1

Block Diagram

Block Diagram

Schematic

Schematic

PCB Layout

PCB Layout

Pin Map

Block Signal ESP32 GPIO
LCD 1602 (I²C) SDA / SCL 21 / 22
DHT11 DATA 5
Keypad 4×4 Rows 32, 33, 25, 26
Keypad 4×4 Cols 13, 12, 14, 27
Battery ADC VBAT (via divider) 34
Power 5 V VIN jack

GPIO 12 and GPIO 5 are ESP32 strapping pins — noted as rev1 limitation, will be remapped in rev2.


Firmware Architecture

The firmware is a cooperative, non-blocking schedulerloop() ticks each service's update() in turn. Every update() returns in microseconds; timing is handled with millis(). No delay() anywhere in the steady-state loop.

loop()
 ├── timeService.update()    // WiFi state machine + NTP sync
 ├── battery.update()        // ADC voltage read every 30 s
 ├── sensor.update()         // throttled DHT11 read
 ├── display.update()        // LCD mode switch + redraw
 ├── telemetry.update()      // MQTT state machine + publish
 ├── webDash.update()        // HTTP handleClient()
 ├── ota.update()            // ArduinoOTA handle()
 ├── alerter.update()        // threshold check + Telegram task
 └── handleInput()           // keypad events

Modules

Module Responsibility
TimeService WiFi state machine (CONNECTING → CONNECTED → FAILED → retry), NTP
EnvironmentSensor Throttled DHT11 reads, cached values, validity flag
DisplayManager LCD render, auto CLOCK ↔ ENVIRONMENT cycling, manual override
TelemetryService MQTT state machine, JSON publish, LWT online/offline, command subscription
WebDashboard HTTP server, Chart.js UI, NVS-persisted thresholds, alert log
AlertNotifier Threshold monitoring, Telegram HTTPS via background FreeRTOS task
OTAService ArduinoOTA wrapper, mDNS hostname, LCD progress during flash
BatteryMonitor ADC voltage divider, LiPo percentage estimation
KeypadInput 4×4 matrix scanning

Key design decisions

Non-blocking MQTT reconnectTelemetryService runs a state machine (WAITING_FOR_WIFI → CONNECTING → CONNECTED → FAILED) that mirrors the pattern in TimeService, so MQTT drops and WiFi drops are handled independently without stalling the loop.

Last Will & Testament — the MQTT broker auto-publishes offline to the status feed if the device disappears without a clean disconnect, giving the dashboard accurate presence info.

Telegram in a FreeRTOS task — HTTPS to Telegram can take 1–3 seconds. AlertNotifier spawns a background task so the main loop never blocks. The task frees itself on completion.

NVS persistence — alert thresholds edited in the web dashboard survive power cycles via the ESP32's Non-Volatile Storage.


Getting Started

Prerequisites

  • VS Code + PlatformIO IDE
  • ESP32 DevKit v1 and a USB data cable (required for first flash only)
  • WiFi network · Adafruit IO free account · Telegram bot (optional)

1 — Clone and configure credentials

git clone https://github.com/DuyHai33/esp32-digital-clock.git
cd esp32-digital-clock

cp include/secrets.example.h include/secrets.h

Fill in include/secrets.h:

#define WIFI_SSID          "your-wifi"
#define WIFI_PASSWORD      "your-password"
#define MQTT_USERNAME      "adafruit-io-username"
#define MQTT_PASSWORD      "adafruit-io-key"
#define OTA_PASSWORD       "choose-a-password"
#define TELEGRAM_BOT_TOKEN ""   // leave empty to disable
#define TELEGRAM_CHAT_ID   ""

2 — First flash (USB)

pio run -e esp32doit-devkit-v1 --target upload
pio device monitor        # 115200 baud

Look for OTA ready — hostname=esp32-clock in the log before trying OTA.

3 — OTA updates (WiFi)

cp platformio.local.ini.example platformio.local.ini
# Edit platformio.local.ini — set --auth= to match OTA_PASSWORD in secrets.h

pio run -e esp32-ota --target upload

Usage

Keypad

Key Action
A Show clock
B Show temperature / humidity
C Enter deep sleep (wakes every 5 min, publishes one reading)
D Cancel deep sleep mode

Auto-cycle when idle: clock for 30 s → environment for 2 min, repeating.

Web Dashboard

Open http://esp32-clock.local in a browser on the same network.

Dashboard

Dashboard Settings

  • Dashboard — live cards (temp, humidity, battery, WiFi RSSI) + 30-min Chart.js history
  • Settings — edit alert thresholds, saved instantly to flash
  • Alerts — timestamped log of every threshold breach

Telegram Alerts

When a threshold is breached, a push notification is sent instantly via Telegram bot.

Telegram Alert

Alert Message

MQTT (Adafruit IO feeds)

Feed Direction Value
temperature Publish °C, every 30 s
humidity Publish %, every 30 s
battery Publish %, every 30 s
status Publish (LWT) online / offline
command Subscribe {"cmd":"set_mode","value":"clock"}

Project Structure

esp32-digital-clock/
├── include/
│   ├── config.h                  # Pin map, timing, MQTT broker & topics
│   ├── secrets.example.h         # Credential template (safe to commit)
│   ├── secrets.h                 # Actual credentials (gitignored)
│   └── Logger.h                  # LOGI / LOGW / LOGE macros
├── src/
│   ├── main.cpp
│   ├── TimeService.{h,cpp}
│   ├── EnvironmentSensor.{h,cpp}
│   ├── DisplayManager.{h,cpp}
│   ├── TelemetryService.{h,cpp}
│   ├── WebDashboard.{h,cpp}
│   ├── AlertNotifier.{h,cpp}
│   ├── OTAService.{h,cpp}
│   ├── BatteryMonitor.{h,cpp}
│   └── KeypadInput.{h,cpp}
├── docs/
│   ├── images/                   # Screenshots and photos
│   └── hardware/                 # Proteus schematic & PCB layout files
├── platformio.ini
├── platformio.local.ini.example
├── .gitignore
├── LICENSE
└── README.md

Known Limitations (rev1)

Limitation Planned fix (rev2)
GPIO 12 & 5 are strapping pins Remap to non-strapping GPIOs
DHT11 accuracy ±2 °C / ±5 % RH Upgrade to DHT22 or BME280
Single-layer PCB, limited routing Two-layer design
I²C pull-ups may exceed 3.3 V if LCD is 5 V Add level shifter

Roadmap

  • Light sensor — BH1750 on shared I²C bus
  • Air quality — PMS5003 dust sensor on UART2 (GPIO 16/17)
  • NVS-persisted user config (no web form required after reboot)
  • Rev2 PCB

License

MIT — see LICENSE.


Nguyen Duy Hai · Embedded Software / IoT · github.com/DuyHai33 · nguyenduyhaivt0303@gmail.com

About

ESP32 IoT device — NTP clock, DHT11 sensing, MQTT telemetry, web dashboard, OTA updates & Telegram alerts on custom PCB

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors