Wake up with a sunrise lamp that knows the time

The ESP32 gets the exact time from the internet and slowly fades an RGB LED from deep red to warm white before your alarm time, like a sunrise in your room.
  • Level:Intermediate
  • Duration: 50 min
  • Ages 11+
  • Platform: ESP32

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Wake up with a sunrise lamp that knows the time – EEMENTOR STEM™ lesson

Waking up in the dark is hard, because our body clock uses light to know when it is morning. A sunrise lamp starts glowing a dim red some minutes before your alarm and slowly turns into bright, warm light, just like the sky at dawn. In this lesson the ESP32 gets the exact time from internet time servers, so you never have to set a clock, and the lamp follows your alarm time on school or work days. A button turns it off for the rest of the day.

What you'll learn

  • How the ESP32 gets the exact time from the internet (NTP)
  • How to work with time zones and summer time
  • How to fade smoothly through colours
  • Why brightness steps should grow as the light gets brighter

What you need

  • ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
  • RGB LED and 3 resistors, 220 Ω (ESP32 IoT Starter Kit)
  • 1 push button (ESP32 IoT Starter Kit)
  • A 2.4 GHz Wi-Fi network
  • The printed parts from the 3D printing box below (lamp dome), or the cardboard version described there
  • Breadboard and jumper wires (ESP32 IoT Starter Kit)

Wiring

Breadboard wiring diagram: Wake up with a sunrise lamp that knows the time
Breadboard view. Check each connection against the wiring table below.
From To
ESP32 GPIO 25 220 Ω resistor, then the RGB LED's red leg
ESP32 GPIO 26 220 Ω resistor, then the green leg
ESP32 GPIO 33 220 Ω resistor, then the blue leg
RGB LED longest leg (common) GND
Push button, one leg ESP32 GPIO 4
Push button, the diagonally opposite leg GND

The sketch

Replace YOUR_WIFI_NAME and YOUR_WIFI_PASSWORD with your own network details (2.4 GHz only).

Set WAKE_HOUR and WAKE_MINUTE to your alarm time and TIME_ZONE to your time zone (the one in the sketch is for Estonia and Finland; search for "POSIX time zone" plus your city). Everything else is built in.

#include <WiFi.h>
#include <time.h>

const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* TIME_ZONE = "EET-2EEST,M3.5.0/3,M10.5.0/4";

const int WAKE_HOUR = 7;
const int WAKE_MINUTE = 0;
const int SUNRISE_MINUTES = 20;          // start this many minutes before the alarm
const int STAY_ON_MINUTES = 30;          // full light after the alarm time
const bool WEEKDAYS_ONLY = true;

const int RED_PIN = 25, GREEN_PIN = 26, BLUE_PIN = 33;
const int BUTTON_PIN = 4;
int dayOff = -1;                         // day of the year the button switched it off

void colour(int r, int g, int b) {
  analogWrite(RED_PIN, r);
  analogWrite(GREEN_PIN, g);
  analogWrite(BLUE_PIN, b);
}

// p = 0 (start of sunrise) ... 1 (alarm time): deep red -> orange -> warm white
void sunrise(float p) {
  float level = p * p;                   // our eyes need bigger steps when it is brighter
  int r = 255 * level;
  int g = 255 * level * constrain(p * 1.3 - 0.2, 0.0, 0.75);
  int b = 255 * level * constrain(p - 0.6, 0.0, 0.4);
  colour(max(r, p > 0 ? 2 : 0), g, b);
}

void setup() {
  Serial.begin(115200);
  pinMode(RED_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" connected");
  configTzTime(TIME_ZONE, "pool.ntp.org", "time.google.com");
}

void loop() {
  struct tm now;
  if (!getLocalTime(&now)) {
    Serial.println("Waiting for the time...");
    delay(1000);
    return;
  }

  if (digitalRead(BUTTON_PIN) == LOW) {        // off for the rest of today
    dayOff = now.tm_yday;
    colour(0, 0, 0);
    Serial.println("Lamp off for today");
    delay(300);
  }

  bool workday = now.tm_wday >= 1 && now.tm_wday <= 5;
  float minutes = now.tm_hour * 60 + now.tm_min + now.tm_sec / 60.0;
  float wake = WAKE_HOUR * 60 + WAKE_MINUTE;
  float start = wake - SUNRISE_MINUTES;
  bool active = (workday || !WEEKDAYS_ONLY) && now.tm_yday != dayOff;

  if (active && minutes >= start && minutes < wake) {
    sunrise((minutes - start) / SUNRISE_MINUTES);
  } else if (active && minutes >= wake && minutes < wake + STAY_ON_MINUTES) {
    sunrise(1.0);
  } else {
    colour(0, 0, 0);
  }

  static int lastPrinted = -1;
  if (now.tm_min != lastPrinted) {
    lastPrinted = now.tm_min;
    Serial.printf("%02d:%02d, alarm %02d:%02d\n", now.tm_hour, now.tm_min, WAKE_HOUR, WAKE_MINUTE);
  }
  delay(200);
}

Test it now

You don't want to wait until tomorrow morning: set WAKE_HOUR and WAKE_MINUTE to 5 minutes from now, SUNRISE_MINUTES to 3 and WEEKDAYS_ONLY to false, and upload. Watch the lamp start red and turn warm white.

How it works

configTzTime() asks internet time servers for the exact time using NTP, the Network Time Protocol that keeps computers and phones in step all over the world. The time zone text tells the ESP32 how far your clock is from world time (UTC) and when summer time starts and ends, so the lamp is right all year without changes.

The sunrise works on a value p that grows from 0 to 1. Brightness is p × p rather than p: our eyes notice a step from 1 to 2 much more than a step from 200 to 201, so a squared curve looks like a smooth, even sunrise. Green and blue are added later than red, which moves the colour from deep red through orange to warm white.

Try this next

  • Add a web page to set the alarm time, like the internet alarm clock.
  • Add a sunset mode that fades slowly to dark at bedtime.
  • Play a gentle tune on the passive buzzer at the alarm time.

Troubleshooting

  • "Waiting for the time..." forever: the ESP32 is on Wi-Fi but can't reach the time servers; check the network has internet access.
  • The lamp starts an hour early or late: the time zone is wrong. Check the time printed in the Serial Monitor.
  • The colours look wrong: swap the wires on GPIO 25, 26 and 33.
  • It never connects to Wi-Fi: check the network name and password (both are case-sensitive) and make sure the network is 2.4 GHz.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the kit