Natural Wakeup
Aiden Magee
Front view of the device with the power on and the wakeup time not set.
This project serves as an assistive wakeup device. On the side, there are buttons and dials to enter the desired wakeup time. Thirty minutes before that time is reached, this device will flood my room with light, starting dim and brightening over the thirty-minute period to simulate a sunrise.
Rear view (with the back panel detached). Almost all of the components were taped down. The Real Time Clock module is in the top right, the LCD is to its left, and below that is the LED. The LED requires a large heatsink, as seen in the center, and a high-current LED driver, which is vertical and on top of the heatsink (if you look closely you can see its copper coil).
All of the components were soldered to a small protoboard for robustness.
Right side view of the clock interface. The red button in the middle and the toggle switch below are surface mounted using their threads and accompanying nuts.
In this video, I show the operation of the device. When the clock starts up, there will be no wakeup time. When I flip the toggle switch on the bottom middle, the device enters its "time setting state". The potentiometer is used to skim through the times and the red button is used to cycle through the time scale (hours, minutes, or seconds). Once the desired time is inputted, I flip back down the toggle switch to lock it in. Once the wakeup sequence begins and the LED is on, a simple press of the red button will turn off the LED and reset the wakeup time.
Process
I began with just an Arduino, a Real Time Clock module, and an LCD display. I hardcoded in values for the wakeup time.
I next added the LED, its high-current LED driver, and its heatsink.
Laser cutting the housing out of acrylic.
Beginning assembly. Managing all the wires and individual components while ensuring that they didn't get unplugged from the Arduino was a struggle.
The LED in particular presented a challenge. Given that I wanted a light bright enough to simulate a sunrise, I needed to choose a high-power LED. The high-power demands necessitate different components, such as a high-current motor driver to ensure the current is stable and great enough to produce the desired brightness. In addition, the LED needed thermal paste and a heatsink to prevent it from burning out.
In the future, I will think more about what material I make my housing out of. At the time of construction, I thought the clear acrylic was cool as it would allow me to see the inner workings of my device. While that was achieved, I probably should have thought twice about whether this would have been a pure novelty or whether it would take away from the aesthetics of my final product. Given my lack of forethought into how I wanted to fasten down my components, I chose to strap down many of them with electrical tape. As a result, the final circuity wasn't exactly the prettiest, and an opaque housing would have disguised this.
Discussion
While I was proud of my final product, I wasn’t fully satisfied with it and I recognize that I have a lot of room to improve in the domain of physical computing.
Most of the critiques I received pertained to my light, such as, “(The) Light is so bright” and “Consider the color temperature for the light (this looks like blue light) and warmer could be nicer.” While I don’t agree that my light is too bright, I think the issue lies in the fact the LED produces a very concentrated stream of light. As another piece of feedback noted, “(an) LED is a raw emitter… Maybe add something to diffuse the light or filter to make the (direct) brightness more tolerable.” Given that I want a light bright enough to flood my room with light, I believe that my light is the right brightness. I just need to modulate the stream of light which is inherently very concentrated, and as mentioned above I believe that a diffuser would achieve that. Regarding my light temperature, I do agree that the LED is cooler than I would have hoped despite being listed as a “warm” LED.
I am quite happy with how my project turned out given that about halfway through I thought that I had a real challenge in choosing an LED. I feared that the LEDs I had on hand wouldn’t be bright enough, and I delved deep into the world of high-power LEDs, and even considered using a conventional lightbulb with a complex dimming circuit. Fortunately, I felt that the LEDs I had access to were bright enough so this additional complication was avoided.
I believe the biggest flaw with my project involves my use of electrical tape to fasten down my LED and heatsink. As Professor Zach pointed out, my heatsink may not function properly given that I have covered a lot with tape. While this hasn’t presented any immediate issues, I am almost certain that I will be shortening the lifespan of my LED and project as a whole as a result.
I learned quite a bit about my limitations during the project, namely my poor fabrication skills. Given my experience as an engineer, for example in electronics labs, I have always strove for functionality over aesthetics. This often meant producing large and clunky circuits on breadboards that were good enough to give me the desired reading. What I now know is that design and aesthetics go hand in hand with functionality when you are trying to produce a product for repeated use. Going forward I will think much more carefully beforehand about my design rather than trying to throw everything in a box. Forethought will prevent small mistakes waiting to happen such as components getting shorted or unplugged or such as my heatsink which is covered in electrical tape.
I do believe that I will revisit this project. I hope to make a more compact version that features a diffuse light and other features such as a built-in speaker which will which will take my product from a device that functions in conjunction with my phone alarm clock to a standalone alarm clock. I would likely construct my device out of an opaque material such as wood and carefully design my box with designated spots for certain components and screw holes so that I can properly fasten them down. I think that by taking these measures, I will not only produce a device that is more aesthetically pleasing, but I believe that it will function better and last longer.
Technical information
/**
Natural Wakeup code by Aiden Magee
Code functions to operate a wakeup device. It uses a button, switch, and dial (potentiometer) to allow
users to set a wakeup time. "wakeup_duration" seconds before the wakeup time is reached, the LED will
start emitting light. The brightness of the light will increase until its max, which will occur at the
set wakeup time. Once the device is in "waking up" state, the press of the red button on the side will
reset the device to be in idle state with no wakeup time.
pin mappings: POWER_PIN = 4;
SWITCH_PIN = 6;
BUTTON_PIN = 5;
LED_PIN = 3;
DIAL_PIN = A0;
Credit to "DS3231_Serial_Easy"
Copyright (C)2015 Rinky-Dink Electronics, Henning Karlsen. All right reserved
web: http://www.RinkyDinkElectronics.com/
**/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DS3231.h>
#define POTENTIOMETER_MAX 950
const int POWER_PIN = 4;
const int SWITCH_PIN = 6;
const int BUTTON_PIN = 5;
const int LED_PIN = 3;
const int DIAL_PIN = A0;
int prevButtonState = 0;
int buttonState = 0;
int switchState = 0;
// in sec
const float wakeup_duration = 60.0*1.0;
String wakeup_time = "N/A";
enum State {IDLE, WAKING, SETTING_TIME};
enum Timeframe {HOUR, MIN, SEC};
State state = IDLE;
Timeframe currently_setting = HOUR;
String led_start = "N/A";
float wakeup_start;
float wakeup_end;
// Initialize clock module
DS3231 rtc(SDA, SCL);
// Initialize lcd module
LiquidCrystal_I2C lcd(0x27,20,4);
String get_wakeup_time(){
unsigned int firstColon = wakeup_time.indexOf(":");
unsigned int secondColon = wakeup_time.lastIndexOf(":");
String hour = String(wakeup_time.charAt(firstColon-2)) + String(wakeup_time.charAt(firstColon-1));
String min = String(wakeup_time.charAt(firstColon+1)) + String(wakeup_time.charAt(firstColon+2));
String sec = String(wakeup_time.charAt(secondColon+1)) + String(wakeup_time.charAt(secondColon+2));
int time[3];
time[0] = hour.toInt();
time[1] = min.toInt();
time[2] = sec.toInt();
switch(currently_setting){
case HOUR:{
time[0] = map(analogRead(DIAL_PIN), 0, POTENTIOMETER_MAX, 1, 23);
break;
}
default:{
time[currently_setting] = map(analogRead(DIAL_PIN), 0, POTENTIOMETER_MAX, 0, 59);
}
}
time[0] = time[0] % 24;
time[1] = time[1] % 60;
time[2] = time[2] % 60;
String led_start = String(time[0]) + ":" + String(time[1]) + ":" + String(time[2]);
Serial.println("led start: " + led_start);
return led_start;
}
String produce_time_string(String hr, String min, String sec){
// we need to pad all single digit times
if (hr.length() == 1){
hr = "0" + hr;
}
if (min.length() == 1){
min = "0" + min;
}
if (sec.length() == 1){
sec = "0" + sec;
}
return hr + ":" + min + ":" + sec;
}
// This function takes in the string of wakeup time in the form HH:MM:SS and return an array of floats [hour, min, sec]
String find_led_start(String wakeup_time){
unsigned int firstColon = wakeup_time.indexOf(":");
unsigned int secondColon = wakeup_time.lastIndexOf(":");
String hour = String(wakeup_time.charAt(firstColon-2)) + String(wakeup_time.charAt(firstColon-1));
String min = String(wakeup_time.charAt(firstColon+1)) + String(wakeup_time.charAt(firstColon+2));
String sec = String(wakeup_time.charAt(secondColon+1)) + String(wakeup_time.charAt(secondColon+2));
int time[3];
time[0] = hour.toInt();
time[1] = min.toInt();
time[2] = sec.toInt();
// if mins < wakeup duration (in mins), decrement the hour
if (time[1] < wakeup_duration/60.0){
time[0] = (time[0]-1) % 24;
}
time[1] = (time[1] - (int)(wakeup_duration/60.0)) % 60;
time[0] = time[0] % 24;
time[1] = time[1] % 60;
time[2] = time[2] % 60;
hour = String(time[0]);
min = String(time[1]);
sec = String(time[2]);
String result = produce_time_string(hour,min,sec);
return result;
}
void setup()
{
// Initialize objects
lcd.init();
lcd.backlight();
rtc.begin();
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(POWER_PIN, OUTPUT);
pinMode(SWITCH_PIN, INPUT);
pinMode(DIAL_PIN, INPUT);
digitalWrite(POWER_PIN, HIGH);
// The following lines can be uncommented to set the date and time
//rtc.setDOW(MONDAY); // Set Day-of-Week to SUNDAY
//rtc.setTime(12, 5, 10); // Set the time to 12:00:00 (24hr format)
//rtc.setDate(19, 2, 2024); // Set the date to January 1st, 2014
}
void print_clock(String time)
{
lcd.clear();
lcd.home();
String row1 = "It is " + time;
//Serial.println(time);
String row2 = "Wakeup " + wakeup_time;
lcd.print(row1);
lcd.setCursor(0,1);
lcd.print(row2);
}
void loop(){
// if lever switch flipped
String curr_time = String(rtc.getTimeStr());
prevButtonState = buttonState;
buttonState = digitalRead(BUTTON_PIN);
switchState = digitalRead(SWITCH_PIN);
switch(state){
case IDLE:{
Serial.println("idle");
if (switchState){
state = SETTING_TIME;
break;
}
if (curr_time == led_start){
state = WAKING;
wakeup_start = millis()/(1000.0);
wakeup_end = wakeup_start + wakeup_duration;
break;
}
}
case SETTING_TIME:{
if (!switchState){
state = IDLE;
break;
}
// button pressed
if (prevButtonState == 0 && buttonState == 1){
currently_setting = (currently_setting+1)%3;
}
wakeup_time = get_wakeup_time();
led_start = find_led_start(wakeup_time);
delay(250);
break;
}
case WAKING:{
Serial.println("waking");
int brightness = map(millis()/1000.0, wakeup_start, wakeup_end, 0, 255);
//Serial.println("LED brightness : " + brightness);
analogWrite(LED_PIN, brightness);
// if button pressed state = idle, wakeup time and all its tangent variable reset to base case
if (prevButtonState == 0 && buttonState == 1){
wakeup_time = "N/A";
led_start = "N/A";
state = IDLE;
analogWrite(LED_PIN, 0);
}
break;
}
default:
Serial.println("Error: unrecognized state " + state);
}
print_clock(curr_time);
delay(250);
}