Introduction: Wake-up Ceiling Light

I like the idea of a wake-up light, simulating sunrise in the morning to wake up in a gentle fashion. There are many commercial products for this, usually having the form factor of a stand-alone bedside lamp. I thought the entire concept would actually work better if it were integrated into our bedroom's main ceiling lighting, so I decided to build that.

I started with the following requirements:

  1. The wake-up light should increase light intensity automatically from zero to full during a configurable period (for instance, starting at 6:30am ending up with full intensity at 7:00am).
  2. Normal functionality of the ceiling light (switching it on and off with the wall switch) must be retained; the wake-up functionality should just be an add-on to that.
  3. I wanted the solution to be integrated in the lamp; no additional visible boxes or cables.
  4. The solution must be stand-alone in the sense that it doesn't rely on any already deployed domotics system (for the simple reason that I don't have one).

Requirement (2) implies that the solution must be able to keep track of time and alarm settings with power-off. I've chosen NTP server synchronization for time sync and EEPROM storage for alarm settings. Requirements (1) and (3) imply that there must be some wireless way of configuring the alarm. I've chosen an HTML5 mobile app served by the lamp to implement this.

Step 1: Choosing the Light Fixture

This project starts with a regular ceiling lamp to modify. It has to be a LED-based lamp with a 12 Volt power supply module and LED unit inside. This set-up allows you to hook up your own electronics between the power supply and the LED unit. Of course, the fixture needs to have sufficient physical spare room for our electronics as well - about 8.0 x 5.0 x 3.0 cm (or 3.1 x 2.0 x 1.2 inch).

The super friendly guys at my local Hubo hardware store let me open up boxes to find a suitable one and I ended up with an Eglo LED GIRON 13493. This is a 24 LED, 12 Watt model. The picture shows this lamp in its unmodified form, with the cap removed. If you want to build this project with a bigger lamp, that's absolutely possible - the circuitry we'll use can handle much larger currents.

Costs for this particular ceiling lamp were €40 (about US$ 44).

Step 2: Other Parts and Equipment

I used the following electronics components in this project:

  • An LM2596 DC/DC step-down voltage regulator module (not just the LM2596 IC).
  • An ESP-01 module.
  • An LM358 opamp.
  • An STP65NF06 power MOSFET.
  • 7 Resistors: 5x 2k2, 1x 100k, 1x 220R
  • 2 Capacitors: 1x 100n, 1x 47n
  • 1 Diode: 1N4148

The MOSFET type is not critical. It just needs to be an N-Channel power MOSFET in TO-220 package with low on resistance - many other types will do.

The following connectors etc. are used to build the circuit:

  • 2 PCB screw terminals with 2 pins each (2.54mm / 0.1'') (for power supply and LED connections)
  • 1 male PCB header, 3 pins.(for optional serial port debug connection)
  • 1 female PCB header, 2 rows of 4 pins (we will plug the ESP-01 module into this)
  • A piece of prototype PCB with copper strips connecting 3 holes, like this.
  • A couple of pieces of mounting wire, preferably in a few different colors (for use on the PCB).
  • A piece of twin wire cable (for use between the power supply and our PCB).
  • 4 spacers/screws/bolts to firmly mount the PCB inside the lamp.
  • And of course some solder.

Total costs for the components vary a lot with the supplier. I think I paid about €15 (about US$ 16.50) for everything, but it should be possible to get a better deal through DX or AliExpress.

You'll need the following tools and equipment to build this:

  • Common tools like screwdrivers, a wire cutter and a wire stripper.
  • A soldering iron.
  • A drill with bits to drill in PCB and metal - I used a 3.0mm (0.12'') bit.
  • A voltage/multi meter.
  • A PC/Laptop with the Arduino IDE installed and the ability to program an ESP-01 module. This can be achieved by a regular USB-to-UART module in combination with some breadboard circuitry to put the ESP-01 in programming mode, or (which I think is more convenient) with an ESP-01 programming board (as pictured).
  • Although it's not strictly required, having an oscilloscope will come in handy if you need to debug the circuit.

Step 3: The Circuitry

Microcontroller core

The heart of the circuit is the ESP8266-based ESP-01 wifi microcontroller module. The basic idea is to program this thing to keep time, serve a webpage, trigger an alarm and control a dimmer, by sending a PWM signal to a MOSFET switch.

The ESP-01 board is cheap, small, and has an easy-to-use 2.54mm / 0.1'' pitch connector. The disadvantage is that you only get 2 general purpose input/output (GPIO) pins: GPIO0 and GPIO2. Also, both of these need to be pulled high initially for proper microcontroller start-up, and they are driven by the boot loader for a short while after start-up (as can be seen in the two attached scope screenshots). These things will lead to noticeable flicker if we would drive the lamp directly from either of those pins.

Looking at the scope measurements again, we can see that even during boot, there is no time when GPIO0 and GPIO2 are driven low simultaneously. We'll exploit this by constructing the circuit in such a way that the lamp will only turn on when both GPIO0 and GPIO2 are driven low - effectively putting a NOR gate between the GPIO0/2 and the MOSFET. GPIO0 will be used for (inverted) PWM, and GPIO2 will be an 'enable' pin. This set-up gives us full control to avoid flicker.

Opamp and MOSFET

The outputs of GPIO0 and GPIO2 are fed into the inverting input of the opamp via R4 and R5. The non-inverting input of the opamp gets a reference voltage of about 0.5V created by R6/D1. This sets up the desired NOR function. By feeding the opamp with 12V, it also provides the necessary boost from the 3.3V logic of the ESP-01 to the 10V+ required to fully drive the MOSFET. We drive the MOSFET from the opamp output through R7. While this certainly doesn't make an ideal MOSFET driver, it is absolutely good (fast) enough for this application. When building up the circuit on a breadboard I noticed some voltage spikes and ringing, probably related to the self inductance of the wires between the MOSFET and the LED unit. I (crudely but effectively) suppressed this by putting C2 over source and drain.

Power supply

Part of our circuit needs about 12V, and the other part needs about 3.3V, and we have an existing 12V power supply to get this from. We're effectively 'stealing' power from an existing design, hoping that the amount of power we draw doesn't exceed the safety margins in the existing lamp - which means we have to keep power usage pretty low.

According to this research, we need to assume a 215mA current draw from the ESP-01. Using a linear regulator (like an LD1117) is not a good option. It would mean that we would also draw the 215mA from the original power supply rated at 1A - so 215mA would be pretty significant. Also, the regulator would dissipate about 1.9W and thus need a heat sink. Therefore, I used a switching power supply module instead. This particular LM2596-based module can be adjusted to the desired 3.3V by adjusting a trimpot. Assuming a 85% conversion efficiency, it would draw 70mA from the original power supply and dissipate 125mW - which is much more acceptable.

Step 4: The PCB

I built up everything on a piece of prototype PCB board. I used the type that has copper strips connecting 3 holes as a matter of personal preference. I started by drilling 4 holes in the corners to be able to mount the board later, then soldered the main active components and socket for the ESP-01, then everything else.

Included are both a diagram of this PCB and photographs with and without the ESP-01 module plugged in. These together should provide all the needed info.

To connect the PCB, I used screw terminals for the power supply and LED unit connections, and a male print header to expose the serial port of the ESP-01. The latter is not required, but highly recommended to do debugging if necessary.

Step 5: The Software

If you want to use the software as-is without any modifications and don't care how it's made, the procedure is simple: download and unpack the attached '', open in the Arduino IDE. Modify the settings in 'configuration.h'. It needs your Wifi SSID and password, IP address information, the name of an NTP server (you can probably leave this unchanged), and timezone information - your standard offset from UTC in minutes as well as any daylight saving time rules.

After making the appropriate changes, simply upload the sketch to the ESP-01.

If you want to make some changes to the software, or simply are interested in how it works, some more detailed information is included below.

Overall architecture

My initial version of this software was in plain C and grew organically from experimenting. At some point, it worked (kinda), but it was a total mess. I then decided to refactor and switch to C++, creating a single class for each separate function (consisting of a .h file defining the class and a .cpp file containing the implementation). The classes get instantiated in the main module. Of course, most classes are dependent on others. The main module injects those dependencies through the constructors of the depending classes.

The following classes are present:

  • Configuration: provides an interface to all configuration data, both hardcoded (mentioned above) as well as data stored in EEPROM (like the alarm settings). All other modules (except Dimmer) depend on Configuration.
  • Dimmer: adjusts light intensity using PWM.
  • Wificlient: establishes the connection to a Wifi access point.
  • NTPClient: gets universal time from a time server.
  • LocalClock: converts universal time to local time, taking into account DST rules if any.
  • Alarm: uses Dimmer, LocalClock and Configuration to perform the basic function of switching on the light gradually during a set time period.
  • Webserver: serves a mobile-friendly web page to enable alarm configuration and manual control.
  • Serialhost: allows serial communication for debugging purposes.

More detailed information is available within the source files. The Dimmer and Webserver modules are special in the sense that they partly use generated code. The '' file contains an Eclipse Java project that does the generation. We'll zoom into this below.


The point of the wake-up light is to let the light intensity increase gradually as perceived by a human. The PWM mechanism gives us linear control over the light intensity, but human perception is more or less logarithmic (the Weber-Fechner law). If we wouldn't compensate for that and just let the PWM duty cycle increase from 0 to 100% in say 1000 steps, the result would be that in the early part of the wake-up cycle, light intensity would increase too fast with noticeable jumps, reaching a high level early, and then rise very slowly from high to full in the remainder of the cycle. That's obviously not what we want. We need to change the light intensity exponentially rather than linearly in time, to give the perception of a linear increase in time.

Also, the ESP8266 PWM is not perfect. It has some jitter which is noticeable in very small duty cycles and causes noticeable flicker in the lamp. So the light can be fully off, or at some minimum stable value, but we should avoid being in between. This problem becomes less severe if the PWM frequency is low - which is why I've chosen just 100Hz.

The file '' calculates a function from a required step in perceived light intensity (on a 0 to 1800 scale) and a PWM value (on a 0 to 20000 scale). The '' program calculates this function and writes it to a .h file to be included in the Arduino project. The Dimmer code in the Arduino simply does a look up on this generated table.


I wanted to control the project with my smartphone. As I have much more experience writing web-apps than native mobile apps, I decided to take the mobile-friendly web-app approach.

The web-app is a single-page HTML5/jQuery application that sends an Ajax POST request when the user presses a button (and also retrieves status updates periodically). The page instructs the browser to get jQuery from the jsDelivr CDN, all JavaScript and CSS code is included in the HTML, as well as the images using Data URLs. As a result, loading the page entails just a single GET request to the ESP-01, keeping everything simple and fast.

By including the 'mobile-web-app-capable' and 'apple-mobile-web-app-capable' tags and a shortcut icon link, smartphones can add this webpage to the home screen. After that, it can be started in the same way as an app (it will be started in a full screen browser), largely hiding the fact that it is not a native app.

Having to upload a sketch to the ESP-01 every time when testing small changes to the web page is not a nice development cycle. Therefore, I've developed this in a Java web project, with '' simulating the response of the ESP-01 to the Ajax POST requests. The '' converts the web projects index.html to the 'webserver_homepage.h' include file which contains the web page as a string constant.

Step 6: Putting Everything Together

With the PCB built and the software uploaded to the ESP-01, it's time to put everything together.

To mount the PCB in the fixture, of course you need to drill some holes in it. I removed the power supply and LED unit prior to doing that, to avoid the drilling vibrations damaging them. After that, the PCB can be mounted using the spacers. I removed the original connecting wires from the power supply, and used them to connect to the PCB instead. I used a piece of twin wire to connect the PCB to the power supply. After this, the lamp could be mounted on the ceiling!

When switching on the lamp with the wall switch, the light should go fully on as usual, but with a short delay (much less than a second, but noticeable). You should now be able to point your mobile browser to the IP address of the lamp, and see the web page. With the 'Add to Home screen' option, you can add an icon to your home screen to start this as a true app (tested this on Android but should also work on iOS).

You can now configure the wake-up period and enable it. When you want to go to sleep, don't switch off the wall switch as the whole unit will lose power of course. Instead, press the 'off' button in the app. You will be gently woken up at the configured time the next morning.

Arduino All The Things! Contest

Participated in the
Arduino All The Things! Contest