ESP32-based LED clock with web configuration, weather integration, and auto-dimming.
Arduino code for my beautiful 3D printed LED clock in a retro 7 segment display style.
You can find more details about the project and all downloadable files (STL, 3MF, STEP) on Printables.com.
This is the easiest and fastest way to get the LED Clock up and running.
I currently don't provide a dedicated web flasher, but this may is comming in the future.
For now you can use this guide to flash an ESP32.
(After the first installation you can update the firmware from the Config Portal)
- Download the latest
bootloader.bin,partitions.binandLedClock_YYYY-MM-DD_HHmm.binfrom Releases - Connect the ESP to your computer over USB
- Open ESP Tool (Chrome or Edge browser)
- Set baudrate to 115'200, click "Connect" and select the port of the ESP (e.g. cu.usbserial-0001 on a Mac)
- Add the three files as follows:
- 0x1000:
bootloader.bin - 0x8000:
partitions.bin - 0x10000:
LedClock_YYYY-MM-DD_HHmm.bin
- 0x1000:
- Click "Program" and wait until flashing is finished (Hard resetting via RTS pin...)
- Connect to the WiFi SSID "LED-Clock-Config"
- If the SSID doesn't show up, try to restart it
- Usually the portal should show up automatically
- If not, open a browser and connect to 192.168.4.1
- Click "Configuration"
- Select your SSID and set the password
- You can set two different WiFi networks, which will try to connect to one of them.
Usually you will configure one network only.
- You can set two different WiFi networks, which will try to connect to one of them.
- Click "Save" to connect the LED Clock to your WiFI network
- With a device in the same network, you now should be able to connect to http://ledclock.local
- Configure the clock as needed using the Config Portal
To update a runnig LED Clock to the newest firmware version, you will only need to download the latest LedClock_YYYY-MM-DD_HHmm.bin from Releases
- Access the Config Portal: http://ledclock.local
- Click on "Update", select the downloaded file and click "Upadte Firmware"
I mostly use "ESP-WROOM-32 Devkit" as microcontroller for my projects (I may will move to ESP32-C5 in the future as this is the only ESP also supporting 5 GHz WiFi atm.)
If you buy one (e.g. from AliExpress) make sure you get the correct one. It must be the 30-pin version which usually has a yellow cpacitor (many other ESP32 should wok too, but I only test with this variant):

- API Reference - REST API endpoint documentation
- Architecture Overview - System components and data flow
- Code Review Report - Latest comprehensive analysis (Dec 30, 2025)
7-Segment-LED-Clock/
├── .github/
│ └── copilot-instructions.md # GitHub Copilot development guidelines
├── include/
│ ├── config.h # Default configuration values
│ ├── BrightnessControl.h # Auto-dimming system
│ ├── ConfigManager.h # Configuration persistence (LittleFS)
│ ├── ConfigStorage.h # WiFi credentials storage
│ ├── CronHelper.h # Cron expression parsing
│ ├── LED_Clock.h # LED display and character mapping
│ ├── Logger.h # Unified logging system
│ ├── schema.h # Web UI schema (embedded)
│ ├── version.h # Build version tracking
│ ├── Weather.h # Open-Meteo API integration
│ ├── WebConfig.h # Web configuration server
│ └── WiFi_Manager.h # WiFi and NTP configuration
├── src/
│ ├── main.cpp # Main program with TaskScheduler
│ ├── BrightnessControl.cpp # Auto-dimming implementation
│ ├── ConfigManager.cpp # Configuration management
│ ├── ConfigStorage.cpp # WiFi credentials handling
│ ├── CronHelper.cpp # Cron utilities
│ ├── LED_Clock.cpp # LED display implementation
│ ├── Logger.cpp # Logging implementation
│ ├── Weather.cpp # Weather API implementation (HTTPS)
│ ├── WebConfig.cpp # Web server and API endpoints
│ ├── web_html.h # Web UI HTML/CSS/JS (embedded)
│ └── WiFi_Manager.cpp # WiFi/NTP implementation
├── .gitignore
└── platformio.ini # PlatformIO configuration
- Board: ESP32-WROOM-32 (configured as
esp32dev) - LEDs: 58 WS2812 RGB LEDs
- 4 characters × 7 segments × 2 LEDs = 56 LEDs
- 2 LEDs for colon/second indicator
- Data Pin: GPIO 4 (configurable in
config.h)
- WiFi Manager: Auto-configuration portal with ESPAsync_WiFiManager
- Double Reset Detection: Press reset twice within 10 seconds to enter config portal
- NTP Time Sync: Automatic timezone-aware time synchronization
- 7-Segment Display: Custom character mapping for digits, letters, and symbols
- Color Modes:
- SOLID: Single color (e.g., Green, Blue, Red)
- PALETTE: Rainbow/animated color palettes with per-character blending
- Second Indicator: Blinking colon LEDs with configurable dimming
- Open-Meteo Weather Integration: Fetches temperature every hour via secure HTTPS connection (no API key required)
- Temperature Display: Shows temp for 5 seconds at :30 past each minute with color gradient
- Geolocation Support: Optional IP-based coordinate detection via ipapi.co for easy setup
- Task Scheduling: Replaced CronAlarms with TaskScheduler for reliability
- Web Configuration Interface: Runtime configuration via web browser at http://ledclock.local
- Live configuration editing with real-time validation
- API key masking for security
- OTA firmware updates via web interface
- Persistent storage with LittleFS
- RESTful API for programmatic control
- Auto-Brightness Dimming: Automatic brightness reduction during night hours with smooth fade transitions
- Configurable dim period (start/end times)
- Smooth brightness fading with adjustable duration
- Independent colon brightness control
- Install Visual Studio Code
- Install PlatformIO IDE extension
The file include/config.h contains default settings that are used on first boot. You can optionally edit these values before building, but all settings can be configured via the web interface after first boot.
Key defaults in config.h:
- WiFi portal password:
"ledclock" - LED brightness:
128(0-255) - Color mode:
1(PALETTE) - Location: Zurich, Switzerland coordinates
Note: Once you configure settings via the web interface, they are stored in LittleFS flash memory and persist across reboots. The web interface is the recommended way to configure your clock.
# Build the project
platformio run
# Upload to ESP32
platformio run --target upload
# Monitor serial output
platformio device monitor- Power on the ESP32
- Display shows "Load" then "ConF"
- Connect to WiFi network "LED-Clock-Config" (password from config.h)
- Browser opens to 192.168.4.1 (or open manually)
- Configure WiFi credentials and timezone
- Save and restart
- Press reset button twice within 10 seconds
- Config portal reopens for reconfiguration
After WiFi is configured, you can access the web interface for runtime configuration:
- Via mDNS (recommended): http://ledclock.local
- Via IP address: Check your router or serial monitor for the device's IP
The web interface provides:
- Real-time configuration editing with validation
- All settings from
config.hcan be modified - Firmware updates (OTA) without USB connection
- Device restart capability
- Configuration changes are saved to flash memory
- API key values are masked for security
Note: Some settings require a device restart to take effect (indicated in the web UI).
All libraries are automatically managed by PlatformIO:
| Library | Version | Purpose |
|---|---|---|
| FastLED | ^3.6.0 | WS2812 RGB LED control |
| ArduinoJson | ^6.21.4 | JSON parsing for weather API and config |
| TaskScheduler | ^3.7.0 | Periodic task execution |
| ESP_DoubleResetDetector | ^1.3.2 | Double reset detection |
| AsyncTCP | ^1.1.1 | Async TCP for ESP32 |
| ESPAsyncWebServer | latest | Async web server (from GitHub) |
| ESPAsyncDNSServer | latest | Async DNS server for captive portal |
| ESPAsync_WiFiManager | ^1.15.1 | WiFi configuration portal |
| ESP32Time | ^2.0.6 | RTC (Real-Time Clock) management |
portalPassword: Password for config portal (default: "ledclock")portalSsid: Config portal SSID (default: "LED-Clock-Config")
clockColorMode: 0 = SOLID, 1 = PALETTEclockColorSolid: Color for SOLID mode (e.g.,CRGB::Green)clockColorPaletteIndex: Palette for PALETTE mode (0=Rainbow, 1=Cloud, 2=Lava, 3=Ocean, 4=Forest)clockColorCharBlend: Per-character color offset (0-255)clockColorBlending: LINEARBLEND or NOBLENDclockSecIndicatorDiff: Second indicator dimming (0-255, 0=disabled)
locationLatitude: Latitude in decimal degrees (-90 to 90, e.g., "47.3769")locationLongitude: Longitude in decimal degrees (-180 to 180, e.g., "8.5417")locationUnits: "metric" (°C) or "imperial" (°F)weatherTempEnabled: 1 = show temperature, 0 = disabledweatherTempDisplayTime: Seconds to display temperature (default: 5)weatherTempMin: Minimum temperature for color gradient (default: -40)weatherTempMax: Maximum temperature for color gradient (default: 50)
LED_PIN: GPIO pin for LED data (default: 4)ledBrightness: Overall brightness (0-255, default: 128)
ledDimEnabled: Enable automatic dimming (0=disabled, 1=enabled, default: 1)ledDimBrightness: Brightness level during dim period (0-255, default: 64)ledDimStartTime: When to start dimming (format: "HH:MM", default: "22:00")ledDimEndTime: When to end dimming (format: "HH:MM", default: "06:00")ledDimFadeDuration: Fade transition time in seconds (default: 30)
HTTP_PORT: Config portal port (default: 80)NUM_WIFI_CREDENTIALS: Number of WiFi networks to store (default: 2)DRD_TIMEOUT: Double reset timeout in seconds (default: 10)weatherTempSchedule: When to show temp (default: "30 * * * * *" = :30 past each minute)clockUpdateSchedule: Clock update rate (default: "* * * * * *" = every second)weatherUpdateSchedule: Weather update rate (default: "0 5 * * * *" = 5 min past hour)
FORMAT_FILESYSTEM: Format LittleFS on boot (default: false)WIFI_RESET_SETTINGS: Reset all WiFi settings (default: false, debug only)DEBUG: Uncomment to enable serial debug output_ESPASYNC_WIFIMGR_LOGLEVEL_: WiFi manager log level (0-4)_ASYNC_HTTP_LOGLEVEL_: HTTP request log level (0-4)
The device exposes a RESTful API for configuration and management:
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Web configuration interface (HTML) |
/api/config |
GET | Get current configuration (JSON) |
/api/config |
POST | Update configuration (JSON body) |
/api/schema |
GET | Get configuration schema for web UI |
/api/version |
GET | Get firmware version and device info |
/api/geolocation |
GET | Detect approximate coordinates via IP address |
/api/restart |
POST | Restart the device |
/api/update |
POST | Upload firmware for OTA update (multipart/form-data) |
Get Current Configuration:
curl http://ledclock.local/api/configUpdate Configuration:
curl -X POST http://ledclock.local/api/config \
-H "Content-Type: application/json" \
-d '{"ledBrightness":200,"clockColorMode":1}'Get Device Information:
curl http://ledclock.local/api/versionRestart Device:
curl -X POST http://ledclock.local/api/restartOTA Firmware Update:
curl -X POST http://ledclock.local/api/update \
-F "[email protected]/build/esp32dev/firmware.bin"The 7-segment display supports:
- Digits: 0-9
- Hex: A-F
- Letters: H, L, N, O, P, R, S, U (upper and lowercase variants)
- Symbols: ° (degree), - (minus)
- Status Words: "Load", "ConF", "Conn", "Er##" (error codes)
| Code | Meaning | Troubleshooting |
|---|---|---|
| Er01 | WiFi initialization failed | Check WiFi credentials, try double reset |
| Er02 | Configuration load/save error | LittleFS filesystem issue, may need format |
| Er03 | Weather API failure | Check internet connection, verify coordinates |
| Er04 | Unknown temperature unit | Check Open-Meteo API response format |
| Er05 | WiFi disconnected | Auto-recovery in progress, check router |
| Task | Interval | Description |
|---|---|---|
| Update Clock | Every second | Display current time with blinking colon |
| Brightness Control | Every second | Calculate and apply brightness (dimming/fading) |
| Fetch Weather | Every hour at :05 | Get temperature from Open-Meteo API via HTTPS |
| Show Temperature | Every minute at :30 | Display temp for 5 seconds with color gradient |
- WiFi initialization failed
- Check serial monitor for details
- Try double reset to reconfigure WiFi
- Weather API request failed
- Check internet connection
- Verify latitude/longitude coordinates are set correctly
- Check serial monitor for detailed error messages
- Ensure ESP32 has sufficient free heap memory for HTTPS/TLS connections
- Open-Meteo returned an unexpected temperature unit
- This is rare; check serial monitor for API response details
- WiFi connection lost
- Auto-recovery is in progress (attempts reconnection every 5-60 seconds)
- Check your WiFi router is powered on and in range
- Serial monitor shows reconnection attempts and status
- After 30 seconds disconnected, performs full WiFi restart
- If persistent, check for WiFi interference or router issues
- Consider rebooting the ESP32 if recovery doesn't succeed after several minutes
- Check
weatherTempEnabled = 1in config.h or web interface - Verify coordinates are configured (latitude and longitude)
- Use web interface "Detect My Location" button for automatic setup
- Or manually enter coordinates from https://open-meteo.com/en/docs/geocoding-api
- Check serial monitor for API errors (HTTPS connection issues)
- Ensure WiFi is connected
- Weather updates occur at :05 past each hour (check timing)
- Verify device is connected to WiFi (check serial monitor)
- Try both http://ledclock.local and the IP address
- Check if mDNS is supported on your network (some corporate networks block it)
- Ensure device and your computer are on the same network
- Check that port 80 is not blocked by firewall
- Double reset detection: Press reset twice within 10 seconds
- First boot automatically enters config portal
- Check LED display shows "ConF"
- Verify
LED_PINmatches your wiring (default: GPIO 4) - Check LED power supply (5V, sufficient current for 58 LEDs)
- Increase brightness:
ledBrightness = 255
- NTP sync requires WiFi connection
- Check timezone configuration in config portal
- Serial monitor shows NTP sync status
- First sync may take 30-60 seconds
- Single blank line between functions/blocks
- No trailing whitespace
- Use
const char*over ArduinoStringwhere possible - Use ConfigManager for all runtime settings (avoid global variables)
- Follow logging conventions with Logger macros (LOG_INFO, LOG_ERROR, LOG_DEBUG)
- Default values in
config.hare only used on first boot; web interface settings persist in LittleFS
- Keep modular architecture (separate .h/.cpp files)
- Use TaskScheduler for periodic tasks
- Add new settings to Config struct in ConfigManager.h
- Update
config.hwith new default options - Update
schema.hfor web UI integration (embedded JSON schema) - Update this README with new features
- Test WiFi resilience and error handling
The project includes pre-commit hooks to ensure code quality:
# Install pre-commit (once)
pip install pre-commit
# Install hooks for this repository
pre-commit install
# Run manually on all files
pre-commit run --all-filesAutomatic checks:
- Remove trailing whitespace
- Ensure files end with newline
- Consistent line endings (LF)
- Validate JSON/YAML files
- Detect private keys
- Check that
config.hexists
Manual checks (run with pre-commit run --hook <id> --all-files):
clang-format: Format C/C++ code (requires clang-format installed)check-flash-usage: Warn if flash usage exceeds 95%
GNU General Public License v3.0
Copyright (c) 2021 Urs Weiss
- Prusa Printers: https://www.prusaprinters.org/prints/68013-7-segment-led-clock
- GitHub: https://github.com/ursweiss/7-Segment-LED-Clock
- Original Version: https://github.com/ursweiss/7-Segment-LED-Clock/releases/tag/v1.0.0
- PlatformIO: https://platformio.org/
- FastLED: https://github.com/FastLED/FastLED
- Open-Meteo: https://open-meteo.com/ (free weather API, no key required)
- ipapi.co: https://ipapi.co/ (IP-based geolocation service)
Original Arduino sketch by Urs Weiss (2021) Refactored to PlatformIO (2025)
Uses libraries by:
- Khoi Hoang (ESPAsync_WiFiManager, ESP_DoubleResetDetector)
- Daniel Garcia (FastLED)
- Benoit Blanchon (ArduinoJson)
- Anatoli Arkhipenko (TaskScheduler)
- Felix Biego (ESP32Time)