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 using
chmod +x FedPet.py
and run using
sudo ./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()
Subscribe to Comments for &quot;FedPet - Use your Raspberry Pi to feed your pet&quot;