How to Build a Rocket Launch Alarm Clock with ESP32

Morning alarm with countdown animation, launch lights, and liftoff sound

ESP32Smart HomeBeginner40 minutes6 components

Updated

How to Build a Rocket Launch Alarm Clock with ESP32
For illustrative purposes only
On this page

What you'll build

In this guide you will build a rocket-launch alarm clock using an ESP32, a DS3231 real-time clock module, a WS2812B LED ring, a piezo buzzer, an OLED display, and a pair of buttons for setting the time and alarm. When the alarm triggers, instead of a boring beep you get a full 10-second launch countdown -- the OLED displays large descending numbers, the LED ring fills up segment by segment like a fuel gauge, and at zero the LEDs burst into a fiery orange-and-white liftoff animation while the buzzer blasts an ascending rocket roar. A snooze button gives you five more minutes of pre-launch hold, and a dismiss button cuts the engines immediately.

The DS3231 is the gold standard for battery-backed real-time clocks in maker projects. Its temperature-compensated crystal oscillator maintains accuracy to within a couple of minutes per year, and it keeps ticking through power outages thanks to a coin-cell backup. In this project you will learn how to communicate with the DS3231 over I2C, set and read time registers, configure its built-in alarm interrupt to wake the ESP32 from deep sleep, and synchronize the displayed time on the OLED. You will also implement a simple two-button time-setting interface with long-press detection for fast-scrolling through hours and minutes -- a useful UX pattern for any embedded device with limited inputs.

The finished alarm clock is a genuinely useful bedside device that makes waking up a little more entertaining. Because the DS3231's alarm interrupt drives a hardware pin, the ESP32 can spend the night in deep sleep drawing microamps until launch time, making the project practical for battery-powered operation. From here you could add Wi-Fi time synchronization via NTP to eliminate manual time setting, integrate a light sensor to auto-dim the display at night, or swap the buzzer for a small amplifier and speaker to play actual rocket launch audio. It is a beginner-friendly project that teaches RTC interfacing, low-power design, and sequenced multimedia output in one satisfying build.

Wiring diagram

Wiring diagram

Interactive wiring diagram

Components needed

ComponentTypeQtyBuy
DS3231 RTCsensor1€3.35
SSD1306 OLEDdisplay1€4.30
WS2812B LED Ringactuator1
Piezo Buzzeractuator1€4.75
Snooze / Confirm Buttonother1€8.30
Set Buttonother1€8.30

Prices and availability are indicative and may have been updated by the supplier. Schematik may earn a commission from purchases made through affiliate links.

Assembly

1

Wire the RTC and OLED on I2C

Connect DS3231 and SSD1306 OLED: VCC to 3.3V, GND to ground, SDA to GPIO21, SCL to GPIO22. Both share the I2C bus.

2

Connect the LED ring

Wire WS2812B 5V to USB 5V, GND to ESP32 GND, and DIN to GPIO4.

3

Add buzzer and buttons

Wire piezo buzzer signal to GPIO26 (other lead to GND). Connect snooze button between GPIO27 and GND, set button between GPIO14 and GND.

4

Upload and set alarm

Flash the sketch. Press SET to enter alarm mode, press SET again to advance by 5 minutes, then press SNOOZE to confirm. The RTC auto-sets from your compile time on first power-up.

Pin assignments

PinConnectionType
3V3rtc-1 VCCPOWER
GNDrtc-1 GNDGROUND
GPIO 21rtc-1 SDAI2C
GPIO 22rtc-1 SCLI2C
3V3launch-oled-1 VCCPOWER
GNDlaunch-oled-1 GNDGROUND
GPIO 21launch-oled-1 SDAI2C
GPIO 22launch-oled-1 SCLI2C
5Vlaunch-led-ring-1 5VPOWER
GNDlaunch-led-ring-1 GNDGROUND
GPIO 4launch-led-ring-1 DINDATA
GNDlaunch-buzzer-1 GNDGROUND
GPIO 26launch-buzzer-1 SIGPWM
GNDsnooze-button-1 GNDGROUND
GPIO 27snooze-button-1 SIGDIGITAL
GNDset-button-1 GNDGROUND
GPIO 14set-button-1 SIGDIGITAL

Code

#include <Wire.h>
#include <RTClib.h>
#include <FastLED.h>
#include <Adafruit_SSD1306.h>

#define SDA_PIN 21
#define SCL_PIN 22
#define LED_PIN 4
#define NUM_LEDS 16
#define BUZZER_PIN 26
#define BTN_SNOOZE 27
#define BTN_SET 14

Adafruit_SSD1306 display(128, 64, &Wire, -1);
RTC_DS3231 rtc;
CRGB leds[NUM_LEDS];

int alarmHour = 7;
int alarmMinute = 30;
bool alarmFiredToday = false;
bool settingMode = false;
unsigned long lastBtnMs = 0;

void runLaunchSequence() {
  for (int t = 10; t >= 0; t--) {
    int ledsLit = map(10 - t, 0, 10, 0, NUM_LEDS);
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    for (int i = 0; i < ledsLit; i++) {
      leds[i] = CHSV((10 - t) * 20, 255, 180);
    }
    FastLED.show();

    display.clearDisplay();
    display.setTextSize(3);
    display.setCursor(50, 20);
    display.printf("%d", t);
    display.display();
    display.setTextSize(1);

    tone(BUZZER_PIN, 700 + (10 - t) * 90, 140);
    delay(250);
  }

  for (int i = 0; i < 5; i++) {
    fill_solid(leds, NUM_LEDS, CRGB(255, 100 + random(80), 0));
    FastLED.show();
    tone(BUZZER_PIN, 1600 + i * 200, 80);
    delay(100);
    fill_solid(leds, NUM_LEDS, CRGB::White);
    FastLED.show();
    delay(60);
  }
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
  noTone(BUZZER_PIN);
}

void setup() {
  Serial.begin(115200);
  delay(100);

  Wire.begin(SDA_PIN, SCL_PIN);
  if (!rtc.begin()) Serial.println("RTC not found");
  if (rtc.lostPower()) {
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
  FastLED.setBrightness(120);

  pinMode(BTN_SNOOZE, INPUT_PULLUP);
  pinMode(BTN_SET, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);
  Serial.println("Rocket Alarm ready");
}

void loop() {
  DateTime now = rtc.now();
  unsigned long ms = millis();

  if (!digitalRead(BTN_SET) && ms - lastBtnMs > 200) {
    if (!settingMode) {
      settingMode = true;
    } else {
      alarmMinute += 5;
      if (alarmMinute >= 60) { alarmMinute = 0; alarmHour = (alarmHour + 1) % 24; }
    }
    tone(BUZZER_PIN, 1000, 30);
    lastBtnMs = ms;
  }

  if (!digitalRead(BTN_SNOOZE) && ms - lastBtnMs > 200) {
    if (settingMode) {
      settingMode = false;
      tone(BUZZER_PIN, 1500, 50);
    } else if (alarmFiredToday) {
      alarmFiredToday = false;
      tone(BUZZER_PIN, 550, 80);
    }
    lastBtnMs = ms;
  }

  if (now.hour() == alarmHour && now.minute() == alarmMinute &&
      !alarmFiredToday && !settingMode) {
    runLaunchSequence();
    alarmFiredToday = true;
  }

  if (now.hour() == 0 && now.minute() == 0) alarmFiredToday = false;

  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(2);
  display.printf(" %02d:%02d:%02d", now.hour(), now.minute(), now.second());
  display.setTextSize(1);
  display.setCursor(0, 24);
  display.printf("Alarm: %02d:%02d %s", alarmHour, alarmMinute,
    settingMode ? "[SET]" : alarmFiredToday ? "[DONE]" : "[ARM]");
  display.setCursor(0, 40);
  display.println("SET=adj  SNOOZE=save");
  display.setCursor(0, 52);
  display.printf("Temp: %.1f C", rtc.getTemperature());
  display.display();

  uint8_t glow = beatsin8(20, 10, 50);
  fill_solid(leds, NUM_LEDS, CRGB(0, 0, glow));
  FastLED.show();

  delay(100);
}

// Run this and build other cool things at schematik.io
Libraries: RTClib, FastLED, Adafruit SSD1306, Adafruit GFX Library