View this document in German (Deutsch)
Panelclock is a flexible and modular project for an ESP32-based clock running on a HUB75 RGB LED matrix panel. It is much more than just a clock; it's a versatile information display that can be extended with a variety of modules.
The system is designed for 24/7 operation, featuring a sophisticated web interface for configuration, Over-the-Air (OTA) update capabilities, and robust memory management that relies on PSRAM.
The heart of the project is a modular architecture, orchestrated by the central Application class. Each unit of information that can be displayed on the panel (like the time, calendar, sports results) is encapsulated as a standalone DrawableModule.
- Application (
Application.cpp/.hpp): The conductor of the entire project. It initializes all managers and modules and controls the main loop. - PanelManager (
PanelManager.cpp/.hpp): The display controller. It manages the list of active modules, cycles through them in a predefined rotation, and is responsible for rendering the final image to the LED panel. - DrawableModule (
DrawableModule.hpp): A virtual base class (interface) implemented by all display modules. It defines the fundamental functions a module must have, such asdraw(),logicTick(), andgetDisplayDuration(). - WebClientModule (
WebClientModule.cpp/.hpp): A central, asynchronous manager for all HTTP requests. Modules register their required web resources here, and the WebClient efficiently handles fetching and caching the data in the background. - WebServer & Configuration (
webconfig.cpp/.hpp,WebServerManager.cpp/.hpp): The project hosts a comprehensive web interface that allows for full configuration of all modules without recompiling. Settings are stored on the device's LittleFS. - BackupManager (
BackupManager.cpp/.hpp): Manages complete system backups including all configurations, module data, and certificates. Supports automatic daily backups, manual backup creation, download/upload via web interface, and emergency recovery in Access Point mode. See BACKUP_SYSTEM.md for details.
- Modular Display: Easily extendable with new modules. The displayed information rotates automatically.
- Web Configuration: An integrated web server allows for convenient configuration of Wi-Fi, timezone, module settings, and more via a browser.
- OTA Updates: The firmware can be updated "Over-the-Air" through the web interface.
- Backup & Restore: Complete system backups including all configurations, module data, and certificates. Automatic daily backups with web-based download/upload and emergency recovery in Access Point mode. See BACKUP_SYSTEM.md for details.
- Efficient Resource Management: Heavy use of PSRAM for web data, JSON parsing, player lists, and string operations to conserve the ESP32's scarce internal memory. All modules use PsramString and PsramVector to minimize heap fragmentation.
- Asynchronous Data Fetching: All external data is loaded in the background without blocking the display or other operations.
- Presence Detection: The
MwaveSensorModulecan automatically turn off the display when no one is present and reactivate it, saving power. - Robust Time Conversion: A custom
GeneralTimeConverterensures reliable conversion from UTC to local time, including correct Daylight Saving Time (DST) rules.
The Panelclock system consists of a series of specialized modules:
- ClockModule: A simple yet elegant digital clock. The core module.
- CalendarModule: Displays events from one or more iCal sources (.ics). Supports complex recurrence rules thanks to the
RRuleParser.
- DartsRankingModule: Displays the "Order of Merit" and "Pro Tour" rankings of the darts world. Tracked players can be highlighted, and for live tournaments, the player's current round is shown.
- CuriousHolidaysModule: Fetches and displays curious holidays for the current and next day from
kuriose-feiertage.de. - TankerkoenigModule: Retrieves current fuel prices for configured gas stations via the Tankerkönig API.
- FritzboxModule: Connects to an AVM FRITZ!Box to display a call list of recent missed, received, or placed calls.
- WeatherModule: Shows weather information and forecasts from OpenWeatherMap.
- ThemeParkModule: Displays wait times for attractions from selected theme parks. Data is fetched via the Wartezeiten.APP API. For each park, the name, crowd level, and highest wait times are displayed.
- MwaveSensorModule: Controls an RCWL-0516 microwave motion sensor to detect human presence in the room and toggle the display.
- ConnectionManager: Manages the Wi-Fi connection and ensures automatic reconnection.
- OtaManager: Provides the functionality for Over-the-Air updates.
The modular design makes it easy to extend the Panelclock's functionality. Follow these steps to create your own DrawableModule:
-
Create Header and Source Files:
- Create
YourNewModule.hppandYourNewModule.cpp. - In the
.hppfile, includeDrawableModule.hppand have your new class inherit from it.#include "DrawableModule.hpp" class YourNewModule : public DrawableModule { public: YourNewModule(/* Dependencies like u8g2, canvas, etc. */); // Implement the virtual functions from DrawableModule void draw() override; void logicTick() override; unsigned long getDisplayDuration() override; bool isEnabled() override; const char* getModuleName() const override { return "YourNewModule"; } // ... other optional functions like `tick()` or `resetPaging()` ... };
- Create
-
Instantiate the Module in the Application:
- Open
Application.hppand declare a pointer to your new module. - Open
Application.cpp. In thesetup()method, create a new instance of your module usingnew.
- Open
-
Register the Module with the PanelManager:
- In
Application::setup(), add your new module to thepanelManager's list:panelManager.addModule(yourNewModule.get());
- In
-
Add Configuration in the Web Interface:
- Define Structure: In
webconfig.hpp, extend thestruct WebConfigwith the configuration parameters for your module (e.g., abool enabledswitch, API keys, etc.). - Create HTML Page: In
WebPages.hpp, add a newPROGMEMstring that contains the HTML form for your module's settings. Use placeholders (%placeholder%) that will be replaced later. - Implement Web Handler: In
WebHandlers.cpp, create a new handler function (e.g.,handleYourModuleConfig()) that serves the configuration page and saves the form input. - Register Server Route: In
WebServerManager::begin(), register a new route for your handler (e.g.,/config/yourmodule).
- Define Structure: In
-
Data Fetching (if necessary):
- If your module needs data from the internet, use the
WebClientModule. - Register the required URL in your module's
setConfigmethod usingwebClient->registerResource(...). - Implement the
queueData()method to trigger the data fetch and theprocessData()method to process the loaded data.
- If your module needs data from the internet, use the
- Controller: ESP32 with PSRAM (e.g., a WROVER module).
- Display: HUB75-compatible RGB LED matrix panel.
- Libraries:
ESP32-HUB75-LED-MATRIX-PANEL-DMA-Displayfor controlling the panel.Adafruit_GFX_LibraryandU8g2_for_Adafruit_GFXfor text and graphics rendering.ArduinoJsonfor processing API responses.NTPClientfor time synchronization.- As well as standard ESP32 libraries (
WiFi,HTTPClient, etc.).
The project is structured as an Arduino sketch.
- Partition Scheme: A custom
partitions.csvis used to provide enough space for the large application, OTA updates, and the LittleFS filesystem for web files. - PSRAM: PSRAM must be enabled in the Arduino IDE's board settings.
After compiling with the Arduino IDE, several .bin files are generated in the sketch folder:
- Complete Image: Contains bootloader, partition table, and application
- Flash Address: Always flash at
0x0000 - Use Cases:
- ✅ Initial installation on a new/erased ESP32
- ✅ Flashing with
esptool.pyor other flash tools - ✅ When experiencing boot problems after flashing
- Example (esptool.py):
esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 write_flash 0x0000 panelclock.merged.bin
- Application Only: Contains only the compiled application, no bootloader
- Flash Address: Flash at
0x10000(seepartitions.csv) - Use Cases:
- ✅ OTA updates via the web interface
- ✅ Updates with Arduino IDE (automatically uses correct address)
⚠️ Not suitable for initial installation (bootloader missing)
panelclock.ino.bootloader.bin- Bootloader onlypanelclock.ino.partitions.bin- Partition table only- These are typically not flashed individually
- Select the correct port and ESP32 board type
- Click "Upload" - the IDE automatically uses the correct addresses
- The IDE automatically flashes bootloader, partition table, and application
- Open the Panelclock's web interface
- Navigate to the update page
- Upload the
panelclock.binfile - The device will automatically restart after the update
For initial installation with panelclock.merged.bin:
esptool.py --chip esp32 --port COM3 --baud 921600 write_flash 0x0000 panelclock.merged.binFor update with panelclock.bin (only if bootloader is already present):
esptool.py --chip esp32 --port COM3 --baud 921600 write_flash 0x10000 panelclock.binThis README was generated based on a source code analysis and has been manually extended.