At the beginning of the semester, Chloe had an idea for an alarm clock that wakes people up through lights rather than sounds. I then bought an alarm clock, sunrise alarm clock, that has a similar functionality, and it has been great. Then I thought, well instead of using artificial lights, why can’t we use natural lights? (Of course natural light would not work if you are waking up in the middle of the night).
I then came up with this idea of a smart curtain that is basically a reverse sunrise alarm clock. It serves as an “alarm clock” as it opens and lets lights in at the time you set. I also added a natural light mode: curtains are closed when the sun is down, and the curtain is opened when the sun is up. Houses/apartments in China are much closer to one another than ones in the US, so you have to close your curtains at night. But I rely on the lights to help me wake up in the morning, so ever since I came back home in China, my mom has been yelling at me to close my curtains completely every other night.
I don’t have access to a linear stepper motor, nor a time module. So I used a servo motor to show the actions of opening and closing the curtains, and hardcoded the current time.
In addition, I used a potentiometer to adjust time, and I kept on getting connection issues with the potentiometer to have consistent readings. I used a push button to change mode, a photo cell to detect day/night(in practice, this photo cell needs to be placed such that only outdoor lightings are sensed but not indoor lightings), and an LCD to display mode and time.
This smart curtain can be improved in some ways. In alarm mode, the curtain could be slowly opening instead of opening at once, to fake the effect of sunrise. Like the sunrise alarm clock that can be controlled by smart phones, smart curtain should do that too and maybe even integrate with a traditional alarm system, i.e. slowly opens up at curtain and then alarm rings.
Code:
#include <LiquidCrystal.h> #include <Servo.h> LiquidCrystal lcd = LiquidCrystal(7,6,5,4,3,2); Servo myservo; const int POTPIN = A1; const int BUTTONPIN = 13; const int PHOTOPIN = A0; const int SERVOPIN = 9; const int PHOTOTHRESH = 300; bool isAlarm = true; bool isOpen = false; int clockTime = 0; // used for demo purpose long long lastLCDTime = 0; long long LCDinterval = 100; long long lastDebounceTime = 0; long long debounceDelay = 50; void openCurtain() { for (int pos = 89; pos > 0; pos -= 1) { // in steps of 1 degree myservo.write(pos); delay(15); } isOpen = true; } void closeCurtain() { for (int pos = 0; pos < 90; pos += 1) { // in steps of 1 degree myservo.write(pos); delay(15); } isOpen = false; } void updateLCD(long long currTime, int h, int m, bool isAlarm) { if (currTime - lastLCDTime > LCDinterval) { lcd.clear(); lcd.setCursor(2, 0); String mode = isAlarm ? "alarm" : "natural"; lcd.setCursor(2, 1); lastLCDTime = currTime; } } void setup() { lcd.begin(16, 2); analogWrite(A5, 40); myservo.attach(SERVOPIN); myservo.write(0); pinMode(BUTTONPIN, INPUT_PULLUP); Serial.begin(9600); } void loop() { // read button long long currTime = millis(); if (currTime-lastDebounceTime>debounceDelay) { if (digitalRead(BUTTONPIN) == 0) { isAlarm = !isAlarm; } lastDebounceTime = currTime; } // read potentiometer int potVal = analogRead(POTPIN); int potTime = potVal/(1023.0/(24.0*60.0)); int h = potTime/60; int m = potTime%60; // read photo resistor int photoVal = analogRead(PHOTOPIN); updateLCD(currTime, h, m, isAlarm); if ((!isAlarm) && (!isOpen) && (photoVal>PHOTOTHRESH)) { openCurtain(); } else if (isAlarm && (!isOpen) && (potTime==clockTime)) { openCurtain(); } else if (isOpen && (photoVal<PHOTOTHRESH)) { closeCurtain(); } }