PurposeI like to get up early to have more time to work and design in a quiet place. (Well, I have a joke that lazy people get up earlier to have more time to do nothing). I noticed that in summer it's easy to wake up with the first sun rays, but in winter it's more difficult. And in order to optimize your sleep cycles you don't want to be woken up by a noisy alarm clock. So I've long wanted to build my own progressive lighting with a programmable clock.
Years ago I thought about building an alarm clock the old way, with a clock derived from the 50Hz on the mains, using an AVR microcontroller, with 7-segment digits. It would require an interface to set the clock up, etc.
Then when playing with my first ESP8266 I quickly managed to implement a remote controlled power plug and figured it wouldn't be hard to use this to make my alarm clock, and that it would simply retrieve the current time from a regular NTP server. It started to sound good.
I first thought about using an halogen lamp and a triac, but I didn't like the idea of letting such a fire igniter run by itself if I'm not at home, thus I started to experiment a lot with high power LEDs (1, 10, 50, 100W). High power LEDs are difficult to cool due to the high power density which requires a massive heat sink. My largest fan-less one is a 100W one that I limit to 45W. Also I figured that having such a huge power directly in your face is not the thing that makes you want to get up the most.
I then discovered LED strips. These ones are very convenient. The power is spread over a long length and thus a large surface. They don't need to be as powerful as compact LEDs because they already cover a long range, and they are easier to stare at.
Final choiceI opted for an ESP8285 made of a PSF-B85 board. It's extremely compact and has everything I needed, plus it's supported by the NodeMCU firmware :
For the display, instead of starting with 7-segment LED digits, I wanted to experiment with some I2C OLED displays which are also supported by the firmware. I found very cheap 128x32 graphics displays like this one. If I had to pick a new one though, I'd pick a larger one as this one is very small.
For the lights, I've opted for white LED strips. It is important not to pick the cheapest ones though. I discovered that some are not adhesive, and some expectedly adhesive ones have a very poor quality adhesive which quickly dries on your shelf and doesn't stick anymore when you unroll it. Also, some come with incorrectly soldered LEDs or even some with reverse polarity. I had to fix mine by hand, about 5% of the LEDs wouldn't turn on due to poor soldering. Preferably pick one from a vendor with a very good reputation, and stay away from misleading or confusing descriptions which often indicate lack of care for quality.
This time I needed to put this into an enclosure with buttons. I had some (very) old PVC enclosures made for MIDI adapters which were of the appropriate size, so I could start with this.
PWM driverControlling a LED for progressive lighting requires some PWM. The NodeMCU firmware running on the device supports emitting a PWM signal on one pin. But I needed to amplify this PWM signal to drive tens of Watts.
I restarted from the design I came up with for my high power tv-b-gone, and adapted it, resulting in this :
Planning for supporting high power, I changed the MOSFET for a larger one at the last minute. The diagram above shows an IRF7313 (dual FET) but I preferred to pick an IRLR3715 which stands higher currents pulses and better spreads heat into the PCB (supports 54A/71W vs 6.5A/2W).
ProgrammingThis time I didn't want to have to solder wires to reflash the device if things went wrong, so I decided to start with an easy solution. I implemented the adapter I designed here with a 6-pin connector so that I could simply attach an FTDI adapter to flash the board. In addition, among the two buttons, the first one will be used to switch to recovery mode during boot in case things go wrong. This was used quite a number of times during development :-)
Voltage regulationLike most ESP boards, this one requires a regulated 3V input. I preferred to use a switching DC-DC module instead of using a linear regulator, because the linear regulator would heat a lot by dissipating the difference between 12V and 3V.
PCBI had everything ready to start designing the PCB. Only add one decoupling capacitor, a power connector, another connector for the LED strip, and the two buttons. The DC-DC module will be external for simplicity. As usual I did it the old way with the pen since it remains the fastest method :
ConstructionTime to throw the PCB into the etching bath. I should document my alternating etcher by the way :
Ten minutes later, I picked it out of the bath, cleaned it and drilled it. The result is quite good :
Let's place the SMD component first. Below two versions, one raw, and another one with all the signals annotated :
Then place the PTH components and connectors :
Connecting to the PSF-B85 is not easy due to the 1.27mm pitch between the pins. The solution I turned to was to use some 1.27mm pitch floppy drive cables, allowing me to easily connect pins and still have long enough wires to go to the various places on the board. The result doesn't look pretty but it allows to move the board around to check signals and to replace it if needed :
ProgrammingI've reused the iot-core framework I developed on top of NodeMCU, and had to iterate several times using NodeMCU-build because my code ended up using lots of memory and I had to remove lots of unused modules to save memory.
Among the unexpected "innovations" there, I had to implement several programs depending on the displayed screen. NodeMCU can reclaim memory of an unused program so the final program is very similar to what you're used to do when using lots of UNIX commands at a shell. The program is like a shell which launches programs. Switching to another program is fast enough so you don't notice. All the code is available here. Feel free to duplicate it and use it.
I just had to connect my FTDI adapter to the purposely installed 6-pin connector and the programming was trivial :
One nice thing of keeping the voltage regulator external is that during development, the FTDI adapter is sufficient to power the board so you have total control over it. Keeping the leftmost button pressed during boot is enough to drop to the Lua interpreter shell (this is handled by the iot-core framework which refrains from loading the application in this case).
I implemented a few simple functions to draw large 7-segment digits on the OLED display. These ones are explained in the source code. The result is quite good (the differences in intensity are caused by the refreshing) :
The buttons are used like this :
- button 1 : switch between multiple light modes (on/off/rise/fall ...)
- button 2 : switch between multiple screens (big digits, status, alarm setting)
The lighting modes are very simple : by default, the output is in mode 0 which is off. Once in mode 1, the PWM output slowly rises from 0 to 100% over one minute, then it reaches mode 2 which is always on. Mode 3 falls from current value to 0. This one is convenient to leave the room after pressing the button. I had to change from linear to quadratic progression, because the eye doesn't react linearly to the LED intensity, and when used linearly you feel like there's little difference between half-lit and full-lit, so it was too fast at the beginning.
A completely useless feature I implemented was to indicate the day of week on the left of the hour and on the status screen between parenthesis. But I figured I never used it. I thought I would make the alarm programmable by day of week but there's no need for this, it's so easy to adjust twice a week that it's not worth the pain of making the configuration more complex :
Final assemblyThe PCB was placed inside the plastic enclosure, with some foam to plug the rear holes.
The finally assembled box looks like this :
For the LED strips, I've placed two 3-meters wide ones in parallel and connected them using some thin telephone wire. Since the ceiling is oblique in my room, I thought it was the perfect place to install it so that it doesn't send the light into the face while still casting it strong enough to wake up :
Results and lessons learnedIt has been running flawlessly for more than one year. In that regard the NodeMCU firmware is quite stable. I faced some stability issues with the first PSF-B85 module I used. The device would randomly reset several times a day and sometimes switch to the wrong baud rate. I spent a lot of time trying to debug this and improving power decoupling and pull-ups, to come to the conclusion that the device was faulty. I replaced it with another one and all issues disappeared.
The amount of RAM is too small for such applications to remain convenient to implement. What I like with NodeMCU is that you have a shell so you can debug and develop live without having to implement everything in your code, rebuild and upload. But it also means that files are compiled on the device before being processed. This takes a lot of RAM. The solution consists in saving them pre-compiled but it's not possible anymore once the application is loaded. It causes me issues twice a year when changing the daylight savings time. For this I have to connect over telnet, edit the configuration file, save it, reboot in safe mode by holding the button, recompile the config file, and reboot. It's quite of a pain.
I thought about re-implementing all of it in C using a different framework but it would take quite some time just to save half an hour twice a year... Not very tempting. The best long-term option would be to switch to the ESP32 which has way more RAM, but the NodeMCU kit is still in development for this device so I don't know if it works or not by now.
The OLED display is not great. First, the MCU doesn't have enough memory to draw on the screen at once, so it has to use pages and to draw the same image 4 times with a mask and only send one page at a time. Not only this is slow to compute, but it also requires quite some I2C bandwidth. Each screen change takes slightly less than half a second. It's annoying when changing the alarm time. The digits are small and very bright. At night it can be slightly blinding and possibly not always very readable for people who wear glasses. Larger red digits would be better in and less aggressive the end.
Also the OLED display wears quickly. The areas having the most segments lit are now fainter than the other ones.
The LED strip is too white ("cold" in LED vocabulary). It's a 6500K white. I hesitated with a 3200K that I already had and which I found too yellow. I think a good approach is to mix two of them. I'd need a bit more power as well. Right now the strips draw between 15 and 20W. I should go to 25-30W for a better effect
Another surprising point is that one LED is constantly lit despite the output showing zero volt. The reason is voltage leaking by capacitive coupling between the power supply and the wall! The lowest voltage LED is the one which turns on. During day it's not visible but at night it is. Installing a 1K resistor in parallel to the strip is enough to turn it off, but it would dissipate some power. I really don't care so I didn't fix it.
The LED still turns too fast from 0 to 100%, especially from 0 to, say, 10%. I think that making it more progressive over 10 minutes would be better. Ideally some experiments should be made with RGB lights such as WS2812B to better mimmick the sun's colors in the sky :
- start reddish for a few minutes
- progressively add the green component to make it yellow
- then add the blue component to turn white
I found it convenient to keep the light on for 30 minutes. It starts 20 minutes before the buzzer-based alarm, and almost always manages to wake me up so that I can stop the alarm before it rings. Thus it seems that with this amount of light, I need 20 minutes to detect the light (possibly finish a sleep cycle).
Interestingly such devices increase the probability that you wake up at the end of a cycle, and help you measure the length of your sleep cycles. You need to keep in mind to note the first hour you observe when waking up. I could measure mine to be multiples of 100 minutes (well more precisely 97 to 99 in fact), which is convenient to count since 300 minutes are 5 hours. I managed several times to wake up easily after 200 minutes (3h20). What I don't know yet is if in reality these are multiples of 50 minutes. I'm not yet sure about the effectiveness of 4h10 (250 minutes), since around this duration my few experiments have shown irregular results. Anyway I know it would not be enough for me over the long term.
Overall my sleep is much better and I'm much fresher in the morning. No more headaches when sleep is suddenly interrupted by the buzzer. I long thought I needed 6 hours but I feel way better with 5 hours with a soft wake up like this than with 6 hours using the buzzer previously. It once stopped working (poor wiring connection on the DC-DC board) and repairing it went very high on my priority list!
Ideally I should build a new and better version of this device, but I wouldn't value the improvements that much for the time required to make another one. So let's wait for it to die first :-)