A digital alarm clock running on an ESP32 microcontroller, built as a coursework project for the Microprocessor and Embedded Systems course at FIT VUT. The whole thing - timekeeping, alarm logic, display rendering, and encoder input - is implemented from scratch in C++ on top of the Arduino framework, using PlatformIO as the build system.
Video, featuring a Slovak voiceover, showcasing the physical build and all functionality.
- Keeps time in software and shows it on a 128x64 SSD1306 OLED over SPI
- Fully configurable alarm - set the time, pick one of 3 melodies (Fast Beep, Slow Pulse, SOS Rhythm), enable/disable it, and configure how many times it retries if you snooze
- Snooze with a click, dismiss with a long-press while the alarm is firing
- Live melody preview when browsing through alarm sounds
- Everything navigated with a single rotary encoder - rotate to scroll, click to confirm, long-press to go back
The project uses an ESP32 D1 R32 board. Here's how everything is wired up:
| Component | GPIO |
|---|---|
| OLED SSD1306 (SPI) - SCLK | 18 |
| OLED SSD1306 (SPI) - MOSI | 23 |
| OLED SSD1306 (SPI) - CS | 5 |
| OLED SSD1306 (SPI) - DC | 27 |
| OLED SSD1306 (SPI) - RST | 17 |
| Rotary encoder - CLK | 16 |
| Rotary encoder - DT | 14 |
| Rotary encoder - SW | 19 |
| Buzzer | 26 |
| LED | 25 |
Each piece of hardware has its own module, and the UI lives separately in main.cpp as a simple state machine:
| Module | What it handles |
|---|---|
TimeManager |
Software clock, tick() on every loop iteration, second tracking |
AlarmManager |
All alarm logic, triggering, snooze, retries, melody selection |
Display |
Every screen: home clock view, menus, and all the edit screens |
Encoder |
Debounced rotary encoder with a small event queue and long-press |
Buzzer |
Plays timed on/off patterns on the buzzer pin |
LED |
Same pattern system as the buzzer, for visual feedback |
main.cpp |
The UI state machine that ties everything together |
The navigation flow looks like this:
Home
-> Menu
-> EditTime
-> EditAlarmTime
-> EditEnabled
-> EditMelody
-> EditAttempts
-> EditIntervalLong-press anywhere in the menus goes back. While the alarm is ringing, click snoozes and long-press stops it for good.
The whole interface is driven by a single rotary encoder. Rotate to move through a menu or change a value, click to confirm or advance to the next field, and long-press to cancel and go back. Every edit screen shows [L: CANCEL] and [C: SAVE] or [C: NEXT] at the bottom so you never have to guess.
| Home screen | Main menu |
|---|---|
![]() |
![]() |
| Current time up top, alarm time, retry config, and current state below | Rotate to scroll through settings, click to go in |
| Set time | Set alarm time |
|---|---|
![]() |
![]() |
| Hours, minutes, and seconds edited one field at a time; active field is underlined | Same as set time, but just hours and minutes |
| Alarm enable | Melody selection |
|---|---|
![]() |
![]() |
| One encoder turn to flip between ON and OFF, then click to save | The buzzer and LED preview the melody live as you scroll |
| Retry attempts | Retry interval |
|---|---|
![]() |
![]() |
| How many times the alarm fires again after a snooze before giving up | How many seconds to wait between retries |
You'll need PlatformIO - either the CLI or the VS Code extension works fine.
pio run # build
pio run --target upload # flash
pio device monitor # serial monitor at 115200 baudLibrary dependencies (Adafruit GFX and Adafruit SSD1306 >= 2.5.15) are declared in platformio.ini and fetched automatically.
esp32_budik/
platformio.ini # build config & dependencies
src/
main.cpp # UI state machine & entry point
AlarmManager.{h,cpp} # alarm logic
Buzzer.{h,cpp} # buzzer driver
Display.{h,cpp} # OLED rendering
Encoder.{h,cpp} # rotary encoder driver
LED.{h,cpp} # LED driver
TimeManager.{h,cpp} # software clock
doc/
doc.tex # project documentation (LaTeX)







