FedPet - Use your Raspberry Pi to feed your pet

Everybody likes technology, even pets do!
We thought it would be great if you could setup a Raspberry Pi to feed a pet at certain times of day, and a controlled amount. Thanks to Tristan Linnell for the idea :)
We will use a PicoBorg to control a 6 wire stepper motor, which when driven will rotate a fixed amount for each step (the angle is specific to the stepper).
For your pets delight we present FedPet.py, a Python script which watches the time and rotates a stepper when scheduled
Here's the code, you can download the FedPet script file as text here
Save the text file on your pi as FedPet.py
Make the script executable usingchmod +x FedPet.py
and run usingsudo ./FedPet.py
You will probably want to change feedSchedule, lines 6-10, to your appropriate feed schedule and amounts.
#!/usr/bin/env python
# coding: Latin-1
# Schedule for pet feeding, list of tupples for feed times
#  formatted as ('HH:MM', c) where HH:MM is the time of day and c is the feed count (steps)
feedSchedule = [
                ('08:30', 25),
                ('14:00', 10),
                ('21:00', 25)
               ]
pollPeriod = 20                 # Number of seconds to wait between polls, should be < 60
holdWhenWaiting = False         # True to keep the stepper powered when not rotating, false to stop powering
# Import libary functions we need
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
import time
# Set which GPIO pins the drive outputs are connected to
DRIVE_1 = 4  # Black
DRIVE_2 = 18 # Green
DRIVE_3 = 8  # Red
DRIVE_4 = 7  # Blue
# Yellow and White are +ve centre taps
# Tell the system how to drive the stepper
sequence = [DRIVE_1, DRIVE_3, DRIVE_2, DRIVE_4] # Order for stepping (see data sheet for the stepper motor)
stepDelay = 0.002                               # Delay between steps
# Set all of the drive pins as output pins
GPIO.setup(DRIVE_1, GPIO.OUT)
GPIO.setup(DRIVE_2, GPIO.OUT)
GPIO.setup(DRIVE_3, GPIO.OUT)
GPIO.setup(DRIVE_4, GPIO.OUT)
# Function to set all drives off
def MotorOff():
    global step
    GPIO.output(DRIVE_1, GPIO.LOW)
    GPIO.output(DRIVE_2, GPIO.LOW)
    GPIO.output(DRIVE_3, GPIO.LOW)
    GPIO.output(DRIVE_4, GPIO.LOW)
    step = -1
# Function to perform a sequence of steps as fast as allowed
def MoveStep(count):
    global step
    # Choose direction based on sign (+/-)
    if count < 0:
        dir = -1
        count *= -1
    else:
        dir = 1
    # Loop through the steps
    while count > 0:
        # Set a starting position if this is the first move
        if step == -1:
            GPIO.output(DRIVE_4, GPIO.HIGH)
            step = 0
        else:
            step += dir
        # Wrap step when we reach the end of the sequence
        if step < 0:
            step = len(sequence) - 1
        elif step >= len(sequence):
            step = 0
        # For this step turn one of the drives off and another on
        if step < len(sequence):
            GPIO.output(sequence[step-2], GPIO.LOW)
            GPIO.output(sequence[step], GPIO.HIGH)
        time.sleep(stepDelay)
        count -= 1
# Function to save the stepper state and disable all drive
def MoveStandby():
    global savedState
    savedState = [
                  GPIO.input(DRIVE_1),
                  GPIO.input(DRIVE_2),
                  GPIO.input(DRIVE_3),
                  GPIO.input(DRIVE_4),
                 ]
    GPIO.output(DRIVE_1, GPIO.LOW)
    GPIO.output(DRIVE_2, GPIO.LOW)
    GPIO.output(DRIVE_3, GPIO.LOW)
    GPIO.output(DRIVE_4, GPIO.LOW)
# Function to resume the stepper state
def MoveResume():
    global savedState
    if savedState[0]:
        GPIO.output(DRIVE_1, GPIO.HIGH)
    else:
        GPIO.output(DRIVE_1, GPIO.LOW)
    if savedState[1]:
        GPIO.output(DRIVE_2, GPIO.HIGH)
    else:
        GPIO.output(DRIVE_2, GPIO.LOW)
    if savedState[2]:
        GPIO.output(DRIVE_3, GPIO.HIGH)
    else:
        GPIO.output(DRIVE_3, GPIO.LOW)
    if savedState[3]:
        GPIO.output(DRIVE_4, GPIO.HIGH)
    else:
        GPIO.output(DRIVE_4, GPIO.LOW)
# Make a list of fed times to avoid double feedings
fedAlready = []
for feedTime in feedSchedule:
    fedAlready.append([feedTime[0], feedTime[1], False])
    print 'At %s feed %d' % (feedTime[0], feedTime[1])
hasResetAtMidnight = False
try:
    # Start by turning all drives off
    MotorOff()
    raw_input('You can now turn on the power, press ENTER to continue')
    # Move to initial position, then either hold or let go as needed
    MoveStep(1)
    if not holdWhenWaiting:
        MoveStandby()
    # Loop forever
    while True:
        # Get the current time formatted as HH:MM
        now = time.strftime('%H:%M')
        # See if we have reached midnight (reset fed list)
        if now == '00:00':
            # Reset all times to unfed if not done already
            if not hasResetAtMidnight:
                for feedTime in fedAlready:
                    feedTime[2] = False
                hasResetAtMidnight = True
        elif now == '00:01':
            # Past midnight, rest latch as we will reset all feeds tomorrow
            hasResetAtMidnight = False
        # Check to see if any feed time matches the current time
        for feedTime in fedAlready:
            if feedTime[0] == now:
                # Check if this feed has beed done, if not feed
                if not feedTime[2]:
                    print '%s feeding %d' % (feedTime[0], feedTime[1])
                    if not holdWhenWaiting:
                        MoveResume()
                    MoveStep(feedTime[1])
                    if not holdWhenWaiting:
                        MoveStandby()
                    feedTime[2] = True
        # Wait for pollPeriod
        time.sleep(pollPeriod)
except KeyboardInterrupt:
    # CTRL+C exit, turn off the drives and release the GPIO pins
    print 'Terminated'
    MotorOff()
    raw_input('Turn the power off now, press ENTER to continue')
    GPIO.cleanup()
      
