Sunday, August 18, 2013

Lamp Sunset Timer

It seems like I have a lot of posts devoted to lamps... anyway, my latest one involves programming a Raspberry Pi to switch my living room lamp on approximately a half-hour before sunset. My previous design for switching my lamp involved using an old Pentium 3 that I have since replaced with a newer computer. The new computer doesn't have a parallel port, and isn't near the lamp any more, so I decided that the RPi's compact size suited itself to this project. Also, the old program turned the lamp on and off at the same time every day. This is problematic for two reasons: first, I could buy a simple timer to do this, and second, it required me to change the program's source code any time the lamp started turning on too late or early as the seasons changed.

Another goal for this project was to familiarize myself with Python. I am fairly well-versed in C (don't ask me about pointers though), but Python seems to be a tool that more and more people are using. The script I wrote uses a python module called "pyephem" to calculate the sunset time at my location (south Florida) every 30 seconds. Then it subtracts 30 minutes from this time, and if the current time matches the calculated sunset time, it turns the lamp on.

Another thing that I implemented in my Python script was random turn-off times. Importing a module called "random" allows me to call a function "random.randint". Giving it a range of values allows it to generate random numbers within this range when it is called. So "random.randint(0,59)" generates a random turn-off minute for my program, which turns the lamp off every night between 12:00 and 12:59. Hopefully this confuses anyone spying on my home!

It started out on the floor just to get things rolling. I spliced into an old extension cable.


This wasn't a permanent solution, obviously. I opened the lamp up to get at the internal wiring to tap off of the 120. From there I used an old cell phone charger to get power for the Pi and also spliced the 120 into the relay. The relay is tied to the control electronics which get their signal to turn the relay on or off from the RPi.


Since everything's under the lamp's table, it's not noticeable at all! The one change I need to make is getting a wireless card for the RPi so it doesn't need an ethernet connection. Apparently the OS version that's on the Pi doesn't have very good wireless support, but I hear they fixed it in a more recent release.


FYI, the other two cables have nothing to do with this project. Just ignore them!

Python script for this project:



# program calculates sunset each day and turns the lamp on 30 minutes before

# then the program calculates a random minute to turn the lamp off within an hour after midnight.



# REMEMBER TO USE 24-HOUR TIME FORMAT

# AND THAT PYEPHEM USES UTC/GMT AND NOT EST

import time
import datetime
import ephem
import random
import RPi.GPIO as GPIO 

GPIO.setmode(GPIO.BOARD) 
GPIO.setup(10, GPIO.OUT) 

# make sure "off_minutes" has a value
off_minutes = 1

while 1:
# figure out what time it is now
now = datetime.datetime.now()
now_hours = time.localtime(time.time())[3]
now_minutes = time.localtime(time.time())[4]
# provide the program with information about the current location:
# HS = Hobe Sound
HS=ephem.Observer()
HS.lat='27.0592'
HS.lon='-80.1367'
HS.date = now
sun = ephem.Sun() 
# figure out if it is daylight savings time or not:
# isdst will be 1 if DST is currently enforced, 0 otherwise
isdst = time.localtime().tm_isdst
# figure out when sunset is:
sunset_hours = HS.next_setting(sun).tuple()[3]
#sunset_hours will be in 24-hour GMT.
if isdst == 1: #add 20 to the time for DST
sunset_hours = sunset_hours + 20
else: #add 19 to the time for EST
sunset_hours = sunset_hours + 19
sunset_minutes = HS.next_setting(sun).tuple()[4]
# subtract 30 minutes from the time since it gets dark before actual sunset
sunset_minutes = sunset_minutes - 30
if sunset_minutes < 0:
sunset_hours = sunset_hours - 1
#sunset_mintues will be a negative number, so adding it to 60 will subtract it
sunset_minutes = 60 + sunset_minutes
# turn the light on if the hours and minutes match:
if now_hours == sunset_hours and now_minutes == sunset_minutes:
GPIO.output(10,True)
#also calculate a random time for the light to turn off
#this is in this "if" statement so it only calculates a random time once
#every 24 hours. 
off_minutes = random.randint(0,59)
# turn the light off at the randomly selected minute in the 00 (midnight-1:00 AM) hour
if now_hours == 0 and now_minutes == off_minutes:
GPIO.output(10,False)

# run once every 30 seconds:

time.sleep(30)

It also needs a script in /etc/init.d to tell the Pi to start this program at boot.


UPDATES!

I added a Staples Easy Button to the lamp since there was no way to turn it off or on except by SSHing to the Pi, and then running the "off" and "on" python programs manually. I took the Easy Button apart and soldered some wires to the button. The button is powered by two AA batteries, which is about 3 volts. I thought this would be enough for the Raspberry Pi's 3.3V logic, so I hooked the button's output up to a PNP transistor's base to watch for button presses. When a button is pressed the wire connected to the transistor's base goes low (3.0 V to 0 V) and the transistor turns on. The collector pin on the transistor is connected to one of the Pi's input pins, and when it sees the button was pressed it toggles the lamp.



I thought I was going to have to re-write my python sunset program to include watching for button presses. Fortunately I found that just running a second program using some of the same input/output pins as the sunset program doesn't interfere with its operation. 



import RPi.GPIO as GPIO
import time

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN)
GPIO.setup(10, GPIO.OUT)
GPIO.setup(8, GPIO.IN)

#program reads pin 11 to see if the lamp is on
#pin 11 is physically soldered to pin 10, the output pin
#which checks if the lamp is physically on or off

while 1:
        if ( GPIO.input(8) == True and GPIO.input(11) == False ):
                GPIO.output(10,True)
                time.sleep(2)
        if ( GPIO.input(8) == True and GPIO.input(11) == True ):
                GPIO.output(10,False)
                time.sleep(2)

Notice that I needed to physically bond pin 11 to pin 10. There's no way that I know of to monitor the state of an output pin like you can do by polling a microcontroller's registers. But there are plenty of input/output pins for me to waste one like this. The reason I needed to do this is because more than one program can change the state of the output pin, so the Easy Button program has to check to see if the lamp is on or off before deciding whether to turn it off or on.

No comments:

Post a Comment