Category: Raspberry Pi

Automatic Cat Laser Pointer Toy Using the Raspberry Pi

Automatic Cat Laser Pointer Toy Using the Raspberry Pi

Living in the city has shown me just how hard it can be to find an affordable apartment with a reasonable amount of space. Having two cats in such a small place doesn’t really give them much room to roam or pseudo hunt. Inspired by this cat tower, I decided to build an automatic cat laser pointer toy using my Raspberry Pi. The cats had one condition though, that it be a shark with a frikin’ laser beam attached to its heads (sound warning!).

If you’ve come across my automatic cat feeder post you might be thinking I’m automating myself out of the equation. Fear not, for we are still their primary source of entertainment, as they are ours.

Goal

The goal may be obvious at this point, but I like being explicit: to build an automatic cat laser pointer toy that is configurable, and can be triggered by a button.

Bonus Goal: configure and trigger this remotely. We’ll focus on the simpler solution first, and optionally over-engineer the project later on.

Prerequisites

You should have a configured Raspberry Pi ready to go. I used my Raspberry Pi 3B (40 GPIO pins), a 32gb MicroSD card (you could certainly go smaller) and the Raspbian distro (highly recommended). This guide was pretty straightforward and got me up and running in no time. 

The Parts

As with my automatic cat feeder post, I decided to break this down into four distinct parts:

  • Here in part one we’re introducing the project and what you’ll need to get it done.
  • In part two we’ll assemble all the pieces of the cat laser pointer toy.
  • Part three will focus on writing the python code, which will be responsible for moving the servo motors and turning on the laser beam at the push of a button.
  • Part four will cover the bonus goal of configuring and triggering remotely [optional].

Things You’ll Need for the Automatic Cat Laser Pointer Toy

  • Pan/tilt camera mount with 2x servos for controlling the movement of the laser.
  • 1x Laser diode to take over the world with.
  • 6x male-to-female, 6x any-to-female jumper wires to connect everything to the GPIO pins. What I mean by “any” here is that “it doesn’t matter“, because we’ll be cutting off that end of the wire and soldering it anyway.
  • 1x NPN Transistor to logically control the Pi’s 3v3 power rail through a GPIO pin, and toggle the laser on/ off. You only need one of these. eBay may have better options in terms of quantity, or hey, even stop at a physical store like Radioshack!
  • SHARKS! because it really is hilarious.
  • 5v 2.5A power supply to power the Pi, servos and lasers. The servos and laser draw a good amount of current, so we need to make sure the power supply is capable of supporting it.
  • Raspberry Pi Case to house the Pi and wiring. This isn’t strictly necessary, but it made for a convenient way to mount the laser and control the wiring.
  • 1x Push Button to manually engage the laser. I got mine in a starter kit, but you can buy them separately if you’d like.
  • Velcro to mount the shark (or laser if you opted out of adding a shark, which saddens me) to the pan/tilt mount. I used velcro here so I can conveniently point the laser in the opposite direction, which provides an entirely different available range for the pan/tilt mount.
  • Electrical tape to wrap the soldered wires.

The Tools

  • [Optional] Power drill to make holes in the Raspberry Pi case to fasten the pan/tilt mount to it. If I did this project over, I would have used velcro as my fastener as it’s much simpler and less intrusive. Your call.
  • Hot glue gun to glue the laser to the shark. Again, any other adhesive would suffice if you don’t already have a hot glue gun.
  • Soldering iron to solder some of the wiring together.
  • Wire stripper for when we get to stripping and soldering a few of the wires.
  • Tiny philips screwdriver to assemble the pan/tilt mount.

Conclusion and Next Steps

At this point we covered the basics of what it is we’re trying to build here in part one, and what you’ll need to accomplish it. Head over to part two where we’ll be focusing on assembling all the pieces.

Ahead of the game? Nice! Part three has the Python code, and part four sets up a way to remotely control the whole thing.

Assemble the Raspberry Pi Cat Laser Pointer Toy

Assemble the Raspberry Pi Cat Laser Pointer Toy

In part one we discussed what exactly we’re trying to build, and what’s needed to begin assembling our cat laser pointer toy. Here in part two we’re going to take all the pieces we’ve gathered and assemble them into one beautiful creation.

The goal of this part?

  • Assemble the pan/tilt mount and servo motors.
  • Solder the laser diode to the NPN transistor, and attach it to the shark(!!!).
  • Solder the physical push button.
  • Connect all the pieces together

If words like solder and transistor are a bit intimidating, don’t let them be! It’s really not that difficult, promise.

Later on in part three we’ll cover how to write code to fire the laser in a pseudo-random fashion at the touch of a button. As a bonus, part four will take this one more over-engineered step and demonstrate how to trigger the laser remotely as well as on a schedule, should you be so inclined.

Assemble the Pan Tilt Mount and Servo Motors

The pan/tilt mount provides a convenient way for us to use our servo motors as a means to navigate the cartesian plane. We will be using one of the servos as the x-axis, and the other as the y-axis.

Our servo motors can rotate from 0 to 180 degrees, no more no less. In order to provide a full range of motion for our laser contraption after we assemble all the pieces, we should first ensure the starting point of each servo is where we want it.

The servo traveling along the x axis will be responsible for moving the laser left and right. In order to accomplish this we should have a center position once mounted of 90 degrees, which allows it to turn left to 0 degrees, and right to 180 degrees.

The servo traveling along the y axis will handle moving the laser up and down. For this, we’ll want the starting position to be 0 degrees, which will aim the laser straight out once mounted, while 90 degrees will aim straight down and 180 degrees will, well, aim the laser backwards. We won’t have much use for anything over 90 degrees on the y axis, but it’s good to note.

Calibrating the X and Y Servos

The first step to calibrating our servos is to designate one as the x axis and the other as the y axis. I wrapped a piece of electrical tape around the x axis wires for quick identification, which helped avoid confusing the two later on.

Now that we’ve identified the servos, it’s time to write some code to set them in the correct starting positions before we actually attach them to the pan/tilt mount. If you’re using the same servos as me, they have powerground and control wires.

Connect three of your male-to-female jumper cables to your servo pins. Now, using the GPIO diagram below as reference, connect the female connectors to the Pi’s GPIO headers as follows:

GPIO Pinout Diagram

X Servo Y Servo
  • power connects to header pin 2 (5v power)
  • ground connects to header pin 6 (Ground)
  • control connects to header pin 7 (GPIO4)
  • power connects to header pin 4 (5v power)
  • ground connects to header pin 9 (Ground)
  • control connects to header pin 11 (GPIO17)

The servos are now connected and ready to be calibrated. Nice work!

Writing Code to Calibrate the Servos

Before getting started, let’s update our Pi to the latest packages and install the GPIO library. Run the below commands in the terminal:

Note: if this is the first time you’re running these commands it may take a bit to complete, so hang tight.

sudo apt-get update 
sudo apt-get upgrade 
sudo apt-get install rpi.gpio

We’re all set, it’s time to create the calibration script. Create a new file called Calibrate.py with the following code in it:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO_X_SERVO = 4
GPIO_Y_SERVO = 17

if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    
    GPIO.setup(GPIO_X_SERVO, GPIO.OUT)
    GPIO.setup(GPIO_Y_SERVO, GPIO.OUT)

    print "calibrating..."
    try:
        x_servo = GPIO.PWM(GPIO_X_SERVO, 50)
        y_servo = GPIO.PWM(GPIO_Y_SERVO, 50)
        
        x_servo.start(7.5)  # X Servo: 7.5 is 90 degrees
        y_servo.start(2.5)  # Y Servo: 2.5 is 0 degrees

        time.sleep(1) # give the servos a chance to move
        
    finally:
        GPIO.cleanup()

The above script is is pretty minimal: each time it runs the x servo will be set to its center at 90 degrees, while the y servo gets set to its starting point of 0 degrees.

You need only run this once.

In the terminal, navigate to the directory that has this newly created python script in it. Once there, type chmod +x Calibrate.py and press enter to make the script an executable, then type ./Calibrate.py. If there were no issues in your script, you should have heard the motors spin to the desired positions.

Excellent! We now have two servos pointing in the correct starting positions. You can unplug the servo wires from the GPIO pins, it’s time to attach them to the pan/tilt mount.

Attach the Servos to the Pan Tilt Mount

Attaching the servos to the pan/tilt mount went fairly smooth with the exception of one rather important part: the two white plastic servo attachments, despite coming with the pan/tilt mount, didn’t fit perfectly in their designated spots. You may not have this issue, but I ended up using scissors and a box cutter to pare them back a bit where necessary, which is apparent in the below image:

Pan Tilt and Servo Motor Pieces Ready to Assemble

When piecing this together it’s important to keep in mind that the servos are calibrated right now to their starting positions, but we haven’t attached the white plastic arms yet. When it comes time to attach them, the servo should be positioned in a way that when the plastic arm is attached, it’s aligned as follows, without the need to manually rotate the motor:

I demonstrate this a bit further a couple images down. As for the assembly itself, just in case you’re having trouble piecing it together here’s a few screenshots of the progress as I went.

X and Y Servos Attached
X (bottom) and Y (top) servo’s connected to their respective pieces
Y Servo mounted and X Servo in Place
Final wall set in place, and the Y servo’s plastic arm attached and screwed in.

The previous image showcases a couple of important points:

  • I consider the front of this pan/tilt mount to be the side where the wires leave the servo motors. This allows sufficient movement when the mount is on a flat surface. Alternatively, the back in this case (opposite side) could be used when your intention is to have the mount on its side (e.g. on a wall, or fridge).
  • The top of the mount where the y axis servo is attached was positioned as flat as can be before screwing in the servo arm. If you recall, we want the y axis to have a starting position of 0 degrees. If you can imagine a laser sitting on top of the mount in this position, aiming the direction of where the wires are coming out, it would be shooting straight. Tilting the mount to 90 degrees would cause the laser to aim straight down, and so on. 
Pan Tilt Mount Assembled
X servo’s plastic arm connected to the base plate, and jumper cables set to extend the servo wires.

I decided to use similarly colored jumper cables as the built-in servo wires to help reduce the amount of confusion while connecting them later. Red is power, brown is ground and orange is control.

Now that we have motors calibrated and attached to the mount, it’s time to setup the laser.

Prepare the Laser Diode

The laser diode has two wires: power and ground. It does not have a third wire for control. The GPIO pins on your Raspberry Pi output 3.3v when turned on but don’t provide enough current to make the laser glow to a brightness worth chasing. The 3V3 regulated pin (e.g. pin 1 on the Pi) provides the necessary current, but cannot be logically toggled on and off by our code. On one hand we have control but insufficient current, and on the other hand we have sufficient current without control. How do we get around this?

Enter the NPN transistor. We’ll leverage this little device as a way to introduce control into the equation. The NPN transistor has three connections: the base, which is what our control wire will connect to, the collector and the emitter. The transistor is used as an electronic switch, which logically connects the collector with the emitter once electricity hits the base.

With that said, we’re going to attach the laser diode to the shark (please tell me you bought the shark!), then we’ll solder the laser and some additional wiring to the NPN transistor.

Using a hot glue gun (or some other form of adhesive), attach the laser diode to the sharks head, like so:

Once the glue dries, take three (3) wires that have at least one female connector and snip off the opposite end of the female connector. Then, strip about an inch of this newly snipped end from its casing to expose the wiring inside. You should be left with three wires having one female end and one stripped end. These three wires will be used to connect the laser/ transistor to the Raspberry Pi’s GPIO pins.

Take one more wire, snip off both ends and strip about an inch of casing from either side. This wire will be used as an extension between the laser diode and the transistor to provide some needed slack.

The screenshot below demonstrates the three female wires and one extension wire soldered to the transistor as follows:

  • The green wire is soldered to the transistor’s emitter, and will eventually connect to the Pi’s ground GPIO.
  • The purple wire is soldered directly to the laser diode’s power wire, and will provide power to the laser. 
  • The blue wire (not the one connected to the laser diode) is connected to the transistors base (middle connection), and will be used as our control.
  •  The laser diode’s ground wire is connected to an extension wire (yellow below), which is then connected to the transistor. Again, this is to provide additional slack so the wire can reach the GPIO pins once mounted.

I wrapped the soldered parts with electrical tape to give them a bit of protection.

Now that everything is soldered together, let’s give it a test drive! Using the above image, wire coloring and previously shown GPIO diagram as reference, connect the laser to the Raspberry Pi as follows:

  • Purple connects pin 1 (3V3 Power)
  • Blue connects to pin 13 (GPIO27)
  • Green connects to pin 25 (Ground)

With the connections in place, create a new file called LaserTestDrive.py and paste the following code snippet inside:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO_LASER = 27

if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_LASER, GPIO.OUT)

    print 'blinking laser...'
    try:  
        # Test turning the laser on and off
        time.sleep(1)
        GPIO.output(GPIO_LASER, 1)
        time.sleep(1)
        GPIO.output(GPIO_LASER, 0)
        time.sleep(1)
        GPIO.output(GPIO_LASER, 1)
        time.sleep(1)
        GPIO.output(GPIO_LASER, 0)

    finally:
        GPIO.output(GPIO_LASER, 0)
        GPIO.cleanup()

In the terminal, navigate to the folder that contains the above script and type chmod +x LaserTestDrive.py to make it an executable. Now, type ./LaserTestDrive.py to kick off the script. If done correctly, you should see the laser flash multiple times. Boom baby!

We now have a shark with a freakin’ laser beam attached to it’s head. Dr. Evil would be proud. It’s time to set ourselves up with a physical push button so, later on, we can actually engage the cat laser pointer toy at the press of this button.

Setup a Physical Push Button

The physical push button I used came as part of a starter kit, but you can purchase just the buttons if the kit isn’t your thing. Take two (2) female-to-any wires from your stash, snip off the non-female end, then strip about an inch of casing to expose the wires. If you’re using the same button as me, flip it over and you’ll see two parallel plastic lines running the length of the button, inline with the metal connectors. Twist and solder your wires to the buttons metal connectors, one wire per plastic line. Here’s how mine came out:

With the soldering in place, use the previously mentioned GPIO diagram as a reference and connect the wires as follows:

  • Black connects to pin 39 (Ground)
  • White connects to pin 37 (GPIO26)

Now that everything’s wired in, let’s test our little button out. Create a new file called Button.py with the following code in it:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

GPIO_BUTTON = 26
    
if __name__ == '__main__':
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    print 'Press the button 3 times to exit...'

    press_count = 0
    while press_count < 3:
        # Test pressing of the button!
        button_pressed = GPIO.input(GPIO_BUTTON) == False
        if(button_pressed):
            print 'Button Pressed!'
            press_count += 1

            # pause to avoid the button press being picked up multiple times
            time.sleep(0.5)
    
    GPIO.cleanup()

In the terminal, navigate to the directory where this new script lives and type chmod +x Button.py to make it executable. Afterwards, run the script by typing ./Button.py. Once running, pressing the button should print Button Pressed! in the terminal. Press the button three total times to have the script exit.

Fantastic work! You’ve accomplished a lot so far. Now it’s time to put all those disparate pieces together.

Connecting All the Pieces

At this point we prepared all the individual pieces and ran some tests to ensure they’re functioning as we expect. Now it’s time to put it all together. If you’re using the same Pi case as me, open it up and place the Pi inside, but leave the top piece detached. The top of the case has a rectangular opening which exposes the GPIO section of the Pi, we’ll use this to fish some wires through. With the Pi in place, let’s get to assembling:

We’ll start off by wiring up the pan/tilt mount. Fish the servo wires through the rectangular opening in the top of the Pi case, then connect them to the GPIO’s as follows:

X Servo Y Servo
  • power connects to header pin 2 (5v power)
  • ground connects to header pin 6 (Ground)
  • control connects to header pin 7 (GPIO4)
  • power connects to header pin 4 (5v power)
  • ground connects to header pin 9 (Ground)
  • control connects to header pin 11 (GPIO17)

 

Servos Connected to Pi

Next up is attaching the shark and laser to the pan/tilt mount. I used velcro which allowed for a less permanent solution should I decide I want to turn the shark around. As was mentioned before, the servos have a finite range of motion. Right now we’re treating the front of our mount as the side where the wires exit the servos. Deciding to treat that as the back of our mount would allow for an entirely different range of motion.

I used a hacksaw to trim away some of the plastic on top of the pan/tilt mount prior to mounting the shark. This allowed for less interference and better positioning, but is not strictly necessary.

Shark Laser Mounted

Once the shark is mounted, fish the wires and NPN transistor through that same rectangular opening in the Pi case, then connect them as follows:

  • Purple connects pin 1 (3V3 Power)
  • Blue connects to pin 13 (GPIO27)
  • Green connects to pin 25 (Ground)

The mount and shark laser are primed and ready, time for the push button. I fished these wires through a different hole on the case to avoid overcrowding the GPIO opening. Fish them through wherever works, then connect as follows:

  • Black connects to pin 39 (Ground)
  • White connects to pin 37 (GPIO26)

Push Button Attached

I didn’t use any adhesive or other trickery to keep the button in place. The wiring and positioning of the button actually made it naturally stationary, enough for my needs anyway.

Finally, it’s time to attach the pan/tilt mount to the case itself. For this I went the overly complicated route of drilling out holes in the case to screw the base of the mount into. A much simpler and less permanent solution would be to use velcro. Velcro is plenty strong enough to support the weight of our little contraption.

Cat Toy Laser Assembly Complete

Congratulations, you now have a super evil cat laser pointer toy assembled and ready for action. Now we just need to give it life in the form of python code.

Conclusion and Next Steps

Part one set the goals and outline. Now with part two wrapped up we have a fully assembled cat toy laser. I’m heading to part three where we’ll write the python code to make all these parts function together at the push of a button.

Later in Part Four I’ll be covering how to trigger our cat laser pointer toy remotely as well as on a schedule. First, let’s figure out how to trigger it by pushing that button we just installed.

 

Trigger the Automatic Cat Laser Pointer Toy Remotely

Trigger the Automatic Cat Laser Pointer Toy Remotely

You made it here to part four because pushing that button isn’t enough. No, you want to trigger this cat laser pointer toy remotely, or even on a schedule. Well you’re in luck, because I too wanted more ways to do the same thing.

Part one got us on our way, part two was all about building the laser contraption, and part three brought it life. That means you have a fully functional cat laser pointer toy capable of being triggered at the push of a button.

What are we going to be doing here exactly?

  • Set up your Gmail account with 2-step authentication for a secure way to interact with it through code.
  • Introduce a modified version of the GmailWrapper.py script from my automatic cat feeder series to scan for an email with a given subject and, when found, trigger the cat laser pointer toy.
  • Use IFTTT to provide support for remote triggering and scheduling.

Enough said. It’s go time.

Preparing Your Gmail Account with 2-step Authentication

We’re going to be using email as the trigger to engage the cat laser pointer toy. Before that’s possible we’ll need a way to securely log into our Gmail account from code. You could store your credentials in plain text within the code we write, but I strongly discourage that. Instead, make use of Gmail’s two-step authentication and app passwords

I created a new Gmail account for this purpose.

  1. Log into your Gmail account
  2. Navigate to the Sign-in and security page
  3. Under the Signing in to Google section, click the 2-Step Verification menu, then follow the instructions to enable 2-Step Verification
  4. Back in the Sign-in and security page right under the 2-Step Verification button you’ll see App passwords
  5. Generate a password
    1. Note: this password will be 16 digits with spaces separating every 4 digits (e.g. xxxx xxxx xxxx xxxx). Please be sure to include the spaces!
  6. You can only view a generated password once, so copy it to the side for now.

The password you generated can be used to log into your Gmail account. We don’t need it right this second, but we’ll be using it to scan for an email that demands we initiate the laser sequence!

Writing Code to Read A Gmail Account

Now that we have a Gmail account ready to rock, let’s write some code to interrogate it. We’re going to be using the IMAPClient python library for this purpose, and we’ll wrap the calls to this client in our own Python class.

Install IMAPClient now from the terminal: sudo pip install imapclient

Create the GmailWrapper.py Script

Now let’s create our Gmail wrapper class: create a new file called GmailWrapper.py with the following code:

#!/usr/bin/env python
 
from imapclient import IMAPClient, SEEN
 
SEEN_FLAG = 'SEEN'
UNSEEN_FLAG = 'UNSEEN'
 
class GmailWrapper:
    def __init__(self, host, userName, password):
        #   force the user to pass along username and password to log in as 
        self.host = host
        self.userName = userName
        self.password = password
        
        self.login()
 
    def login(self):
        print('Logging in as ' + self.userName)
        server = IMAPClient(self.host, use_uid=True, ssl=True)
        server.login(self.userName, self.password)
        self.server = server
 
    #   The IMAPClient search returns a list of Id's that match the given criteria.
    #   An Id in this case identifies a specific email
    def getIdsBySubject(self, subject, unreadOnly=True, folder='INBOX'):
        #   search within the specified folder, e.g. Inbox
        self.setFolder(folder)  
 
        #   build the search criteria (e.g. unread emails with the given subject)
        self.searchCriteria = [UNSEEN_FLAG, 'SUBJECT', subject]
 
        if(unreadOnly == False):
            #   force the search to include "read" emails too
            self.searchCriteria.append(SEEN_FLAG)
 
        #   conduct the search and return the resulting Ids
        return self.server.search(self.searchCriteria)

    def getIdsByGmailSearch(self, search, folder='INBOX'):
        # powerful search enabled by Gmail. Examples: `in:unread, subject: <subject>`
        self.setFolder(folder)
        return self.server.gmail_search(search)
    
    def getFirstSubject(self, mailIds, folder='INBOX'):
        self.setFolder(folder)
        
        data = self.server.fetch(mailIds, ['ENVELOPE'])
        for msgId, data in data.items():
            envelope = data[b'ENVELOPE']
            return envelope.subject
            
        return None
 
    def markAsRead(self, mailIds, folder='INBOX'):
        self.setFolder(folder)
        self.server.set_flags(mailIds, [SEEN])
 
    def setFolder(self, folder):
        self.server.select_folder(folder)

Verifying the GmailWrapper.py Class Works

Let’s do a test: our script is going to log into the Gmail account, search for email with a specific subject, retrieve that subject, then mark the email as read. Before running the code, send yourself an email with the subject begin laser ignition (the search is case-insensitive, by the way).

We’re going to use the Python interpreter to run our tests. In your terminal make sure you’re in the same directory as the GmailWrapper.py script we just created, then:

# press enter after each line for the interpreter to engage
 
# invoke the interpreter
python
 
# import our wrapper class for reference
from GmailWrapper import GmailWrapper
 
# create an instance of the class, which will also log us in
# the <password> should be the 2-step auth App Password, or your regular password
gmailWrapper = GmailWrapper('imap.gmail.com', '<your gmail username>', '<password>')
 
# search for any unread emails with the subject 'begin laser ignition', and return their Ids
ids = gmailWrapper.getIdsByGmailSearch('begin laser ignition')
 
# have the interpreter print the ids variable so you know you've got something
ids

# grab the full subject of the first id returned
subject = gmailWrapper.getFirstSubject(ids)

# have the interpreter print the subject variable to see what you've got
subject
 
# we successfully found and read our email subject, now lets mark the email as read
gmailWrapper.markAsRead(ids)
 
# exit the interpreter
quit()

If everything went as planned your email should now be marked as read. Pretty neat! 

LaserWrapper, Meet GmailWrapper: Putting the Two Together

As we know by now, the LaserWrapper.py script is constantly watching the physical button and waiting for it to be pressed. We’re going to modify it a bit so it also watches the Gmail account and waits for an email with the correct subject to come through. In the event it finds this email we want it to trigger the cat laser pointer toy.

Alright, time for some modifications. Open your LaserWrapper.py script and replace its contents with the following:

#!/usr/bin/env python
 
from Laser import Laser
from GmailWatcher import GmailWrapper
import json
import datetime
import RPi.GPIO as GPIO
import time
from imaplib import IMAP4

HOSTNAME = 'imap.gmail.com'
USERNAME = 'your username'
PASSWORD = 'your password'
 
# seconds to wait before searching Gmail
CHECK_DELAY = 30
 
# seconds to wait before logging into gmail. if we don't wait, we run the risk of trying to 
# log in before the Pi had a chance to connect to wifi.
GMAIL_CONNECT_DELAY = 20

# minutes to wait before reconnecting our Gmail instance.
GMAIL_RECONNECT_DELAY = 60
GPIO_BUTTON = 26
 
FIRE_LASER_SUBJECT = 'begin laser ignition'
DISMANTLE_LASER_SUBJECT = 'dismantle laser'
 
engage = False
gmailWrapper = None
laser = Laser()
 
start_time = datetime.datetime.now()
run_time = 0
last_gmail_check_time = datetime.datetime.now()
last_gmail_connect_attempt = datetime.datetime.now()
last_reconnect_attempt = datetime.datetime.now()
 
default_configuration = '{"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}'
 
def initiateLaserSequence():
    global gmailWrapper
    global engage
    
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
    stop = False
    run = True
    
    # wire up a button press event handler to avoid missing a button click while the loop below
    # is busy processing something else.
    GPIO.add_event_detect(GPIO_BUTTON, GPIO.FALLING, callback=__button_handler, bouncetime=200)
    
    last_gmail_check = datetime.datetime.now().time()
    print 'running, press CTRL+C to exit...'
    try:
        while run:
            try:
                __check_gmail_connection()
                
                # avoid pinging gmail too frequently so they don't lock us out.
                if(__should_check_gmail(CHECK_DELAY)):
                    print 'Checking Gmail!'
                    stop = __should_stop_firing()
                    
                    ids = __get_email_ids_with_subject(FIRE_LASER_SUBJECT)
                    if(len(ids) > 0):
                        print 'Email found, initiating laser sequence!'
                        engage = True
            
                        laser.stop()
            
                        # grab any config options from the email and begin
                        __calibrate_laser(__get_configuration(ids))
                        gmailWrapper.markAsRead(ids)
                
                if(stop):
                    engage = False
                    stop = False
                    laser.stop()
                
                if(engage):
                    if(__run_time_elapsed()):
                        stop = True
                    else:
                        laser.fire()
                else:
                    # sleep here to lessen the CPU impact of our infinite loop
                    # while we're not busy shooting the laser. Without this, the CPU would
                    # be eaten up and potentially lock the Pi.
                    time.sleep(1)
                
            except IMAP4.abort, e:
                # Gmail will expire your session after a while, so when that happens we
                # need to reconect. Setting None here will trigger reconnect on the
                # next loop.
                gmailWrapper = None
                print 'IMAP4.abort exception: {0}'.format(str(e))
            except Exception, e:
                # swallowing exceptions isn't cool, but here we provide an opportunity to
                # print the exception to an output log, should crontab be configured this way
                # for debugging.
                print 'Unhandled exception: {0}'.format(str(e))
            except KeyboardInterrupt:
                run = False
                print 'KeyboardInterrupt: user quit the script.'
                break
    finally:
        print 'Exiting program'
        laser.stop()
        GPIO.cleanup()
        
def __button_handler(channel):
    global engage
    
    print 'Button pressed! '.format(str(channel))
    
    if(engage):
        print 'Already firing the laser, button press ignored'
    else:
        print 'Initiating Firing Sequence!'
        # only start a new firing sequence if we're not already in the middle of one.
        engage = True
        __calibrate_laser(None)
 
def __check_gmail_connection():
    if(gmailWrapper is None):
        __connect_gmail()
    
def __connect_gmail():
    global gmailWrapper
    global last_gmail_connect_attempt
    
    now = datetime.datetime.now()
    next_connect_time = (last_gmail_connect_attempt + datetime.timedelta(seconds=GMAIL_CONNECT_DELAY)).time()
    if(now.time() > next_connect_time):
        print '__connect_gmail: Attempting to login to Gmail'
        try:
            last_gmail_connect_attempt = now
            gmailWrapper = GmailWrapper(HOSTNAME, USERNAME, PASSWORD)
        except Exception, e:
            print '__connect_gmail: Gmail failed during login, will retry automatically.'.format(str(e))

def __should_check_gmail(delay):
    global last_gmail_check_time
    
    if(gmailWrapper is None):
        # we haven't yet successfully connected to Gmail, so exit
        return
    
    now = datetime.datetime.now()
    next_check_time = (last_gmail_check_time + datetime.timedelta(seconds=delay)).time()
    
    if(now.time() > next_check_time):
        last_gmail_check_time = now
        return True
    
    return False
 
def __run_time_elapsed():
    now = datetime.datetime.now()
    end_time = (start_time + datetime.timedelta(seconds=run_time)).time()
    
    if(now.time() > end_time):
        return True
    
    return False
 
def __calibrate_laser(configuration):
    global start_time
    global run_time
    
    if(configuration is None):
        # no user defined config, so we'll go with the defaults
        configuration = json.loads(default_configuration)
    
    print "starting laser with config: {0}".format(configuration)
    
    start_time = datetime.datetime.now()
    
    run_time = configuration.get('run_time')
    min_movement = configuration.get('min_movement')
    x_max = configuration.get('x_max')
    x_min = configuration.get('x_min')
    y_max = configuration.get('y_max')
    y_min = configuration.get('y_min')
 
    laser.calibrate_laser(min_movement, x_min, x_max, y_min, y_max)
        
def __get_email_ids_with_subject(subject):
    return gmailWrapper.getIdsByGmailSearch('in:unread subject:{0}'.format(subject))
    
def __get_configuration(emailIds):
    subject = gmailWrapper.getFirstSubject(emailIds)
    config_start_index = subject.find('{')
    
    # no config found in subject, so return nothing
    if(config_start_index == -1):
        return None
    
    # grab the substring from opening { to the end
    return json.loads(subject[config_start_index:None])
 
def __should_stop_firing():
    ids = __get_email_ids_with_subject(DISMANTLE_LASER_SUBJECT)
    return len(ids) > 0

if __name__ == '__main__':
    initiateLaserSequence()

The above script is almost ready for use, we just need to make a few tweaks:

  • Replace lines 12 and 13 with your Gmail username (that’s your email without @gmail), and the app password you generated.
  • Replace lines 26 and 27 with the email subjects that you want to use to start or stop the laser sequence. That’s right, the ability to remotely stop the laser is baked in as well, just in case it’s running a bit longer than desired.

Once these changes are in place, give it a test drive. Run the script and press the physical button to ensure we didn’t break anything there. If all looks good, send yourself an email with the subject you chose (I used begin laser ignition). After about 30 seconds the laser should have began firing.

Subject Matters: Override the Default Configuration

At this point we’re able to trigger the cat laser pointer toy remotely by sending an email. This is pretty powerful stuff. If you recall from the previous posts, we built in the ability to configure the cat laser pointer toy in the form of a JSON string. What if we want to set the run time, or the minimum amount of movement, remotely?

You’re in luck! The script we just wrote above is smart enough to do that. If you send an email with just the trigger key in the subject (e.g. begin laser ignition), then it will use the default_configuration variable on line 39 of the script. However, you can override that by adding your own configuration into the subject itself.

Here’s a sample email subject to showcase what I mean:

begin laser ignition {"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}

The script we wrote will parse this more complex subject and use the configuration settings you provide here over the default ones.

Scheduling an Email to be Sent Regularly

We have the code ready to rock. Sending an email on demand will cause the cat laser pointer toy to start firing, but what about firing it on a schedule? That’s where we’ll make use of IFTTT. IFTTT stands for if this then that and allows you to connect two disparate systems based on what they call a “recipe” (trigger and action). For our purpose, we need the clock to trigger an email to be sent (action).

Here’s what to do:

  1. Setup an account if you haven’t already (free, free, free).
  2. Use IFTTT website or download the app to your phone and log in.
  3. In the My Applets section, add a new applet.
  4. You’ll see a screen saying if +this then that: click the +this button, then select the Date & Time service.
  5. Select the Every day at trigger, and select the time you’d like the cat laser pointer toy to activate, then hit next
  6. Now you’ll see +that, click it and find the Gmail service. You’ll need to connect the service to your Gmail account. Once finished, set the action to Send yourself an email with the subject  Begin Laser Ignition.
    1. Don’t forget, you have the option of adding configuration to the subject, too.
  7. Hit next, then Finish

There you have it, every day at the time you specified an email will be sent to your account. If you had any issues setting up the IFTTT recipe, check out this post for a really nice and in-depth walk-through.

Having fun? Here’s Other Ways to Trigger

Alexa

Alexa (and the Echo Dot) integrates nicely with IFTTT. In the IFTTT app, create a new recipe with the trigger (+this) connecting to Alexa. You’ll need to connect the service like you did for Gmail. Once connected, select the option to Say a specific phrase and enter a phrase like cat laser. Once the Alexa side is setup, set the action (+that) to send an email, like we did in the previous section.

Hands free laser initiation at the ready, just say: Alexa, trigger the cat laser.

The DO Button App

Created by the IFTTT team, the DO Button app and accompanying widget provides a straightforward way to trigger the action. You! You’re the trigger. You setup a recipe, same as before, except you’ll notice there’s no +this. You are +this. You open the app and click the button, it then triggers an email which triggers the cat laser. This app can also be configured to show on your iPhone or Androids home screen, so triggering the laser is even easier.

Conclusion

I hope you enjoyed this project as much as I did. It was a blast seeing a shark with a laser attached to its head causing mayhem throughout my apartment. I think the cats enjoy it too, almost as much as I do.

As always, a little shoutout to the previous sections in case you need to get there:

  • Part one where we found out what we were building and what it would take.
  • Part two which helped guide us to creating a machine of chaos.
  • Part three where our inner mad scientist gave life to this machine in the form of code.
  • And of course, the bonus part, part four where we couldn’t live without a remote way to control our cat laser pointer toy.

I encourage you to leave feedback in the comments below. If you’re stuck, reach out! And of course, thank you for reading.

Write Code to Control the Raspberry Pi Cat Laser Pointer Toy

Write Code to Control the Raspberry Pi Cat Laser Pointer Toy

At this point you’ve covered part one and part two (nice job!), or you didn’t and just happen to stumble upon part three of this guide (it’s good to have you). Here in part three we’ll be focusing primarily on writing the necessary python code to make our super evil laser contraption fully functional at the push of a button. This could technically be your last part of the series (it’s sad, but it had to end sometime). If you’re hankering for more like I was, part four will cover the bonus feature of triggering the cat laser pointer toy remotely and on a schedule.

With that said, let’s dig in!

Create the Scripts to Automate the Cat Laser Pointer Toy

We’re going to create two scripts:

  • Laser.py, which will contain the code responsible for configuring our laser and servo motors, and moving the laser from one position to another.
  • LaserWrapper.py, which as the name suggests will wrap the Laser.py functionality, and will primarily be responsible for deciding when the cat laser pointer toy should be enabled as well as which settings it should use during runtime.

Create a new file called Laser.py with the following code:

#!/usr/bin/env python

import time
import RPi.GPIO as GPIO
import random

DEFAULT_RUN_TIME = 90
DEFAULT_MIN_MOVEMENT = 10
DEFAULT_X_MIN_POSITION = 40
DEFAULT_X_MAX_POSITION = 120
DEFAULT_Y_MIN_POSITION = 20
DEFAULT_Y_MAX_POSITION = 60

# define which GPIO pins to use for the servos and laser
GPIO_X_SERVO = 4
GPIO_Y_SERVO = 17
GPIO_LASER = 27

class Laser:
    def __init__(self):
        GPIO.setmode(GPIO.BCM)
        
        self.x_servo = None
        self.y_servo = None

        GPIO.setup(GPIO_X_SERVO, GPIO.OUT)
        GPIO.setup(GPIO_Y_SERVO, GPIO.OUT)
        GPIO.setup(GPIO_LASER, GPIO.OUT)

    def calibrate_laser(self, min_movement, x_min, x_max, y_min, y_max):
        # set config variables, using the defaults if one wasn't provided
        self.min_movement = DEFAULT_MIN_MOVEMENT if min_movement is None else min_movement
        self.x_min = DEFAULT_X_MIN_POSITION if x_min is None else x_min
        self.x_max = DEFAULT_X_MAX_POSITION if x_max is None else x_max
        self.y_min = DEFAULT_Y_MIN_POSITION if y_min is None else y_min
        self.y_max = DEFAULT_Y_MAX_POSITION if y_max is None else y_max
        
        # start at the center of our square/ rectangle.
        self.x_position = x_min + (x_max - x_min) / 2
        self.y_position = y_min + (y_max - y_min) / 2
        
        # turn on the laser and configure the servos
        GPIO.output(GPIO_LASER, 1)
        self.x_servo = GPIO.PWM(GPIO_X_SERVO, 50)
        self.y_servo = GPIO.PWM(GPIO_Y_SERVO, 50)
        
        # start the servo which initializes it, and positions them center on the cartesian plane
        self.x_servo.start(self.__get_position(self.x_position))
        self.y_servo.start(self.__get_position(self.y_position))

        # give the servo a chance to position itself
        time.sleep(1)

    def fire(self):
        self.movement_time = self.__get_movement_time()
        print "Movement time: {0}".format(self.movement_time)
        print "Current position: X: {0}, Y: {1}".format(self.x_position, self.y_position)

        # how many steps (how long) should we take to get from old to new position
        self.x_incrementer = self.__get_position_incrementer(self.x_position, self.x_min, self.x_max)
        self.y_incrementer = self.__get_position_incrementer(self.y_position, self.y_min, self.y_max)

        for index in range(self.movement_time):
            print "In For, X Position: {0}, Y Position: {1}".format(self.x_position, self.y_position)
            self.x_position += self.x_incrementer
            self.y_position += self.y_incrementer

            self.__set_servo_position(self.x_servo, self.x_position)
            self.__set_servo_position(self.y_servo, self.y_position)

            time.sleep(0.02)

        # leave the laser still so the cat has a chance to catch up
        time.sleep(self.__get_movement_delay())

    def stop(self):
        # always cleanup after ourselves
        print ("\nTidying up")
        if(self.x_servo is not None):
            self.x_servo.stop()
        
        if(self.y_servo is not None):
            self.y_servo.stop()
            
        GPIO.output(GPIO_LASER, 0)
        
    def __set_servo_position(self, servo, position):
        servo.ChangeDutyCycle(self.__get_position(position))

    def __get_position(self, angle):
        return (angle / 18.0) + 2.5

    def __get_position_incrementer(self, position, min, max):
        # randomly pick new position, leaving a buffer +- the min values for adjustment later
        newPosition = random.randint(min + self.min_movement, max - self.min_movement)
        print "New position: {0}".format(newPosition)

        # bump up the new position if we didn't move more than our minimum requirement
        if((newPosition > position) and (abs(newPosition - position) < self.min_movement)):
            newPosition += self.min_movement
        elif((newPosition < position) and (abs(newPosition - position) < self.min_movement)):
            newPosition -= self.min_movement

        # return the number of steps, or incrementer, we should take to get to the new position
        # this is a convenient way to slow the movement down, rather than seeing very rapid movements
        # from point A to point B
        return float((newPosition - position) / self.movement_time)

    def __get_movement_delay(self):
        return random.uniform(0, 1)

    def __get_movement_time(self):
        return random.randint(10, 40)

This code is pretty self contained. The only reason you’d need to adjust anything above is if you decided to use different GPIO pins than I described in the previous sections. If so, you’ll need to set them accordingly (lines 15 – 17).

One script down, that was easy! Create another file called LaserWrapper.py with the following code:

#!/usr/bin/env python

from Laser import Laser
import json
import datetime
import RPi.GPIO as GPIO
import time

GPIO_BUTTON = 26

laser = Laser()
start_time = datetime.datetime.now()
run_time = 0
engage = False

default_configuration = '{"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}'

def initiateLaserSequence():
    global engage
    # setup the push button GPIO pins
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    run = True
    
    # wire up a button press event handler to avoid missing a button click while the loop below
    # is busy processing something else.
    GPIO.add_event_detect(GPIO_BUTTON, GPIO.FALLING, callback=__button_handler, bouncetime=200)
    
    print "running, press CTRL+C to exit..."
    try:
        while run:
            try:
                if(engage):
                    if(__run_time_elapsed()):
                        # we ran out of time for this run, shutdown the laser
                        engage = False
                        laser.stop()
                    else:
                        laser.fire()
                else:
                    # sleep here to lessen the CPU impact of our infinite loop
                    # while we're not busy shooting the laser. Without this, the CPU would
                    # be eaten up and potentially lock the Pi.
                    time.sleep(1)
                        
            except Exception, e:
                # swallowing exceptions isn't cool, but here we provide an opportunity to
                # print the exception to an output log, should crontab be configured this way
                # for debugging.
                print 'Unhandled exception: {0}'.format(str(e))
            except KeyboardInterrupt:
                run = False
                print 'KeyboardInterrupt: user quit the script.'
                break
    finally:
        print 'Exiting program'
        laser.stop()
        GPIO.cleanup()

def __button_handler(channel):
    global engage
    
    print 'Button pressed! '.format(str(channel))
    
    if(engage):
        print 'Already firing the laser, button press ignored'
    else:
        print 'Initiating Firing Sequence!'
        # only start a new firing sequence if we're not already in the middle of one.
        engage = True
        __calibrate_laser(None)

def __run_time_elapsed():
    # figure out if the laser has ran its course, and should be stopped.
    now = datetime.datetime.now()
    end_time = (start_time + datetime.timedelta(seconds=run_time)).time()
    
    if(now.time() > end_time):
        return True
    
    return False

def __calibrate_laser(configuration):
    global start_time
    global run_time
    
    if(configuration is None):
        # no user defined config, so we'll go with the defaults
        configuration = json.loads(default_configuration)
    
    print "starting laser with config: {0}".format(configuration)
    
    start_time = datetime.datetime.now()
    
    run_time = configuration.get('run_time')
    min_movement = configuration.get('min_movement')
    x_max = configuration.get('x_max')
    x_min = configuration.get('x_min')
    y_max = configuration.get('y_max')
    y_min = configuration.get('y_min')
 
    laser.calibrate_laser(min_movement, x_min, x_max, y_min, y_max)

if __name__ == '__main__':
    initiateLaserSequence()

As mentioned previously, this script defines a few settings which tell the Laser.py script information about how it should move. This script is also responsible for triggering the movement, as it’s continuously looking for the press of that physical button we wired up. 

The default settings are defined as a JSON string on line 16, and are stored in the default_configuration variable. I chose to use JSON instead of individual variables to prepare the script for future changes introduced in part four, where we trigger the laser remotely. 

Here’s a bit of documentation around what exactly these configuration variables mean:

PropertyDescription
run_timeHow long, in seconds, the cat laser pointer toy should operate each time the button is pressed.
min_movementThe minimum amount of movement that needs to occur when the laser moves from point A to point B on both the x and y axis.
x_minThe minimum position, in degrees, the laser is allowed to move to along the x axis.
Must be greater than or equal to 0 degrees.
x_maxThe maximum position, in degrees, the laser is allowed to move to along the x axis.
Must be less than or equal to 180 degrees. 
y_minThe minimum position, in degrees, the laser is allowed to move to along the y axis
Must be greater than or equal to 0 degrees.
y_maxThe maximum position, in degrees, the laser is allowed to move to along the y axis.
Must be less than or equal to 180 degrees. 

 

To really drive home the point, let’s dissect the default_configuration variable in the above script:

default_configuration = '{"run_time": 30, "min_movement": 12, "x_min": 0, "x_max": 90, "y_min": 0, "y_max": 22}'

With these settings, the cat laser pointer toy will:

  • Run for 30 seconds when the physical button is pressed, stopping after that time has elapsed.
  • Move at least 12 degrees along both the x axis (left/ right) and y axis (up/ down).
  • Move within the bounds of 0 degrees (minimum) and 90 degrees (maximum) along the x axis. Remember, our servo’s can move between 0 and 180 degrees if we wanted. Here we can restrict that range to our needs.
  • Move within the bounds of 0 degrees (minimum) and 22 degrees (maximum) along the y axis.

The x and y min/ max variables are nothing more than a way for us to define a square or rectangular shape for the laser to move within. I adjusted these settings until they fit the space where I have the cat laser pointer toy set up. You’ll likely need to tailor them to your own play space.

Now that we have all the code in place we can finally test this puppy out, hell yeah!

Begin Laser Ignition!

Frau Farbissina said it best, it’s time we begin laser ignition. Open up the terminal and navigate to the directory where you created the Laser.py and LaserWrapper.py scripts, then type the following commands:

chmod +x Laser.py
chmod +x LaserWrapper.py

Our scripts are now executable. The LaserWrapper.py script is the one we want to kick off, as it’s responsible for configuring and running Laser.py internally. Run the script by typing ./LaserWrapper.py. If all went well you should see an error-free terminal, which means the script is running and awaiting your command.

Press the button already!

If everything’s wired up correctly and the code is in place, the laser should have started moving. Congratulations, you’ve built a fully functional world dominating cat laser pointer toy!

If nothing happened or you had errors while executing the script: take a step back and double-check all your GPIO connections from the previous section. Did you use the same GPIO’s as me? If you did, great, that’s probably not the problem, but if you didn’t then you’ll need to adjust the GPIO variables in the scripts to point to the ones you chose. If the pins are in place and still no luck, don’t be discouraged! Reach out in the comments or contact me directly and we can take a crack at it together.

Using Cron to Automatically Run our Script

Our cat laser pointer toy has a push button to trigger the movement of the laser, but that button is no good to us if our LaserWrapper.py script isn’t constantly running. We need this script to run on a regular basis, which means starting it when the Raspberry Pi boots up. For that, we can use cron.

Open a terminal and type crontab -e to open the cron job editor in nano. Add the following line to the end of this file:

@reboot sudo python /path/to/your/script/LaserWrapper.py

It may seem obvious, but make sure you replace /path/to/your/script above with the actual path to your script. Move your cursor to the end of the newly added line (after .../LaserWrapper.py) and press Enter to create a new line under it. A known quirk with cron is that it requires the command to be followed by a new line, else it won’t run our script.

Since we’re in nano, save and exit by pressing CTRL+X, then Y, then Enter. This script will now be executed anytime the Pi reboots. Give it a go: reboot your Pi and give it time to load up. Once loaded, try triggering the cat laser pointer toy without manually running the script first.  

Conclusion and Next Steps

You did it! We now have the awesome power of an automatic cat laser pointer toy. I hope you have a cat to use it on (I won’t judge if you don’t).

Part one got us started, while part two really made the project interesting once we had a fully assembled contraption. Here in part three we were able to bring life to our laser with a little help from our friend python. 

Not satisfied with stopping here? Too lazy to physically push the button on the laser? Me too. If you haven’t had enough yet I’ll be over in part four over-engineering this project with some remote capabilities.

I want to reiterate that if you’re stuck, that’s ok! Don’t be discouraged. This project caught me up a few times, it’s all part of the process. Just reach out!

If you won’t be joining me for part four, then I want to thank you for taking the time to read through this series; I hope you found it most excellent. If you have any feedback, good or bad, I implore you to reach out in the comments.

Mounting the Raspberry Pi and Servo Motor to a Dry Food Dispenser

Mounting the Raspberry Pi and Servo Motor to a Dry Food Dispenser

You made it to the end! (or you happened to stumble upon this part of the guide). Looking back, part’s one, two and three weren’t so bad now that you’ve got the hang of things. Here in part four we’re going to mount the Raspberry Pi and servo motor onto the food dispenser.

The goals of this part are:

  • Connect the servo motor to the knob of the dispenser.
  • Cut the threaded rod down to size.
  • Mount the servo to the dispenser.
  • Bind the PVC fitting to the dispenser so the food has a path to follow.
  • Mount the Raspberry Pi to the dispenser.

I chose to connect everything to the dispenser and nothing to the stand. This offers a bit of flexibility: with everything relying on the dispenser the stand can be interchanged. Before we get started, power down the Raspberry Pi: sudo shutdown -h

Connecting the Parts

We’ll start by connecting the servo motor to the knob of the dispenser. I originally did this by using tape which actually worked fine for a while. However, over time the tape began to lose its grip on the knob, preventing it from turning as effectively. The tape has been replaced with zip ties which have been working flawlessly:

  • Take the knob of the food dispenser out of the dispenser.
  • To give the zip ties a flat surface to latch onto, I used a hacksaw to square off the rounded edges of the knob.
  • Wrap and connect a zip tie around the knob and servo, tightening it in place. Repeat this process until the servo is securely fastened. Use the image below as a guide to how I went about doing this.
  • Insert your creation back into the food dispenser.

Servo Attached to Knob

This is great! It looks complete, but what you’ll notice if you tried spinning the motor is that it won’t have anything to grasp onto, so it will spin with the knob. We need to fasten the motor to the dispenser. If you look at the top and bottom of the motor you’ll notice two sets of screw holes. The set that’s furthest away from the center of the dispenser knob is the one we’ll be dealing with. Using the image above as reference, that’s the set of screw holes on the top of the motor.

The screw holes are slightly smaller than the threaded rod, so we’ll need to do a tiny bit of surgery to get the rods to fit:

  • Using your box cutter, carefully cut away a very little piece of the plastic at the smallest point of the screw hole.
  • Try pushing the threaded rod into the screw hole. Did it fit? Then move on down! Otherwise repeat the previous steps until it fits snug.

Now we’ll cut the rod to the appropriate length, drill a couple holes into the dispenser and fish the threaded rods through. With the motor/ knob inserted into the dispenser (like the image above):

  • Measure the distance from the screw hole to the dispenser, then add about an inch or two.
  • Using your hacksaw, cut two pieces of threaded rod to the measured length.
  • Place the two pieces of threaded rod into the screw holes of the motor and push them flush against the wall of the dispenser.
  • Make a tiny mark on the dispenser where the threaded rods touch it. This will identify where we need holes for the rods to fit through.
  • Make an attempt to spin the wheel of the dispenser manually by rotating the knob. If any part of the knob hits the rod you’ll need to trim the knob’s plastic down a bit with your hacksaw.
  • Remove the motor/ knob, and the white rubber wheel from inside the dispenser.
  • Stop: obligatory (but very important) warning to put on your safety goggles, please.
  • Drill two holes slightly larger than the threaded rods into the dispenser where you made your markings.
  • Insert the threaded rods through the holes and fasten them down with a couple of hex nuts.

Did you wind up with this?

Threaded Rod in Dispenser

At this point mounting the motor is pretty straightforward. Before we bring it back into the picture let’s get the PVC fitting attached to the hole of the dispenser. We need two holes opposite one another at the mouth of the dispenser, and two more holes at the top of the PVC fitting.

  • Place the mouth of the PVC fitting flush against the mouth of the dispenser.
  • Use a pen to mark slightly below the lip of the PVC fitting closest to the dispenser. Make another mark directly above that on the dispenser itself. Make the same markings on opposite sides of the PVC fitting and dispenser.
  • Using a drill bit large enough for a zip tie to squeeze through, drill holes where you made the marks.
  • Using zip ties, bind the PVC fitting to the mouth of the dispenser.

The result should look something like this:

PVC Fitting Connected to Dispenser

Now that the threaded rods are in place, add back the motor/ knob. Position the threaded rod through the screw holes of the motor and tighten with a hex nut. You probably won’t have enough space to add two hex nuts. That’s alright, I didn’t either:

Motor Attached to Dispenser

Fantastic, we’re mostly there. We just need to mount the Raspberry Pi to the dispenser and we’re done! I mounted my Raspberry Pi as center to the dispenser as I could, but it doesn’t really matter:

  • Place the Raspberry Pi against the body of the dispenser where you’d like to mount it.
  • Make two markings on the dispenser, one on either side of the Raspberry Pi.
  • Drill two holes into the dispenser where you marked it.
  • Using a zip tie, mount the Raspberry Pi to the dispenser through those two holes.
Pi Mounted to Dispenser
Pi Mounted to Dispenser

Congratulations, you did it! An automated food dispenser is now at your command. 

Conclusion

The Raspberry Pi is an incredible little machine with endless possibilities. I sincerely hope this series of posts was as helpful to you as it was me. It was a long journey, but now you’re prepared to tackle any new DIY Raspberry Pi projects that may come your way.

I encourage you to leave comments letting me know what you think. If anything was unclear I’d appreciate hearing about it.

  • In part one we laid out the plan.
  • Part two we set ourselves up with the power of reading emails.
  • In part three we talked all about controlling servo motors.
  • And here in part four we put it all together to make an impressive new toy.

If you enjoyed this series of posts, please be sure to check out my series on building an automatic cat laser pointer toy!

Controlling a Servo Motor with Gmail and Raspberry Pi

Controlling a Servo Motor with Gmail and Raspberry Pi

You covered part one and part two (good work!), or maybe you didn’t and you found yourself here anyway (welcome). Part three is focused on the servo motor, primarily making it spin. Later in part four we’ll wrap things up by connecting it to the dispenser.

As always, let’s start with what it is we’re trying to accomplish here:

  • Connect the servo motor to the Raspberry Pi’s GPIO header pins
  • Write some code to make that motor functional
  • Tie in the GmailWrapper.py script we wrote in part two so the motor spins when the right email lands in your inbox

Here we go!

Connecting the Servo Motor to the Raspberry Pi

If you’re using the same servo motor as me, it has power, ground and control wires. If you decided to use the standard non-continuous servo motor (which is a viable alternative since we won’t be performing a full revolution), then: power is red, ground is black and white is control.

Connect your male-to-female jumper cables to the servo. Now, using the GPIO diagram below as reference, connect the wires to the Pi’s GPIO headers as follows:

Note: this diagram is for Raspberry Pi’s with 40 pin GPIO, but the below works with 26 pin GPIO as well.

  • power connects to header pin 2 (5v power)
  • ground (black wire for standard servo) connects to header pin 6 (Ground)
  • control (white for standard servo) connects to header pin 12 (GPIO18)

GPIO Pinout Diagram

Here’s what mine looks like (don’t mind the zip tie):

Servo Connecting to Pi
Servo Connecting to Pi’s GPIO

 

Writing Code to Make the Servo Motor Spin

Before getting started, let’s update our Pi to the latest packages and install the GPIO library. Run this in the terminal:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install rpi.gpio

We’re all set. In the terminal, create a new python script using nano: sudo nano CatFeeder.py

Within the CatFeeder.py script, add the following code:

#!/usr/bin/env python

import RPi.GPIO as GPIO
import time

def feed():
    # let the GPIO library know where we've connected our servo to the Pi
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)

    try:
        servo = GPIO.PWM(18, 50)
        servo.start(12.5)

        # spin left, right, then left again rather than in a continuous circle
        # to prevent the food from jamming the servo
        for index in range(0, 3):
            dutyCycle = 2.5 if (index % 2 == 0) else 12.5
            servo.ChangeDutyCycle(dutyCycle)
            # adjust the sleep time to have the servo spin longer or shorter in that direction
            time.sleep(0.8) 
    finally:
        # always cleanup after ourselves
        servo.stop()
        GPIO.cleanup()

if __name__ == '__main__':
    # kick off the feeding process (move the servo)
    feed()

Save and exit the nano editor: CTRL+X, then Y, then Enter. Moment of truth, time to make that servo move! Let’s drop back into the Python interpreter, in the terminal:

# remember, hit enter after each line to have the interpreter... interpret
python
import CatFeeder
CatFeeder.feed()

I hope that went smooth for you, because I just got really excited. Welcome to physical computing! You moved a physical object with code, hell yeah.

GmailWrapper, Meet CatFeeder: Putting it all Together

Alright, so we’ve created a way to read emails, and we’ve created a way to move a servo motor. Now we need to combine the two so the servo motor moves when we read emails.

Open CatFeeder.py in nano and add the highlighted lines of code: sudo nano CatFeeder.py

#!/usr/bin/env python

from GmailWrapper import GmailWrapper

import RPi.GPIO as GPIO
import time

HOSTNAME = 'imap.gmail.com'
USERNAME = '<your gmail username>'
PASSWORD = '<your app password or regular gmail password>'

def feedByGmail():
    gmailWrapper = GmailWrapper(HOSTNAME, USERNAME, PASSWORD)
    ids = gmailWrapper.getIdsBySubject('feed cats')
    if(len(ids) > 0):
        try:
            feed()
            gmailWrapper.markAsRead(ids)
        except:
            print("Failed to feed cats, they're starvingggg")

def feed():
    # let the library know where we've connected our servo to the Pi
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)

    try:
        servo = GPIO.PWM(18, 50)
        servo.start(12.5)

        # spin left, right, then left again rather than in a continuous circle
        # to prevent the food from jamming the servo
        for index in range(0, 3):
            dutyCycle = 2.5 if (index % 2 == 0) else 12.5
            servo.ChangeDutyCycle(dutyCycle)
            time.sleep(0.8)
    finally:
        # always cleanup after ourselves
        servo.stop()
        GPIO.cleanup()

if __name__ == '__main__':
    # kick off the feeding process (move the servo)
    # we now use our new feedByGmail method to handle the feeding
    feedByGmail()

Save and exit the nano editor: CTRL+X, then Y, then Enter. As always, let’s give it a test. Send yourself an email with the subject feed cats. Drop into the python interpreter once the email arrives:

python
import CatFeeder
CatFeeder.feedByGmail()

Did it work? It did? Excellent! If it didn’t, don’t be discouraged! Python’s support community is huge, you’ll find the answer. Plus, you can always leave a comment below.

Scheduling a Cron Job to Run Our Script Regularly

We have code to move our servo motor when the correct email lands in our inbox, but it currently only runs when we physically tell it to. Let’s fix that by setting up a cron job to run our script every 60 seconds.

In order for the cron job to execute our script, we need to make our script executable. This is done by issuing the following command in the terminal: sudo chmod +x CatFeeder.py

Now we’ll add the cron job. In the terminal: crontab -e. Add the following after the very last commented line (comments start with #):

* * * * * python /path/to/your/script/CatFeeder.py

As Gabe called out in the comments, adding python after the last asterisk (*) in the line above will force the python interpreter to be used while running our script. We’re in the nano editor, so save and exit: CTRL+XYEnter. Easy as Pi (sorry). A job is now running every 60 seconds with the sole task of looking for emails and feeding cats. Don’t believe me? Give it a shot, send yourself an email with the subject feed cats.

A nicety of cron jobs is they’ll continue running automatically if your Pi ever restarts.

Scheduling an Email to be Sent Regularly

We have the code, we have the cron job. Sending an email on demand will cause the servo motor to spin, but what about spinning it on a schedule? That’s where we’ll make use of IFTTT. IFTTT stands for if this then that and allows you to connect two disparate systems based on what they call a “recipe” (trigger and action). For our purpose, we need the clock to trigger an email to be sent (action). Here’s what to do:

  1. Setup an account if you haven’t already
  2. Use IFTTT website or download the app to your phone and log in
  3. In the My Applets section, add a new applet
  4. You’ll see a screen saying if +this then that: click the +this button, then select the Date & Time service.
  5. Select the Every day at trigger, and select the time you’d like the cat feeder to activate, then hit next
  6. Now you’ll see +that, click it and find the Gmail service. You’ll need to connect the service to your Gmail account. Once finished, set the action to Send yourself an email with the subject Feed cats.
  7. Hit next, then Finish

There you have it, every day at the time you specified an email will be sent to your account. If you had any issues setting up the IFTTT recipe, check out this post for a really nice and in-depth walk-through.

Having fun? Here’s Other Ways to Send an Email

Alexa

Alexa integrates nicely with IFTTT. In the IFTTT app, create a new recipe with the trigger (+this) connecting to Alexa. You’ll need to connect the service like you did for Gmail. Once connected, set the action (+that) to send an email, like we did in the previous section.

Hands free feeding at your ready: Alexa, trigger the cat feeder.

The DO Button App

Created by the IFTTT team, the DO Button app and accompanying widget provides a straightforward way to trigger the action. You! You’re the trigger. You setup a recipe, same as before, except you’ll notice there’s no +this. You are the +this. You open the app and click the button, it then triggers an email. This app can also be configured to show on your iPhone or Androids home screen, so triggering the email is even easier.

Conclusion and Next Steps

Part one has goals and items covered, part two has Gmail automation down, and part three provided the spinning of the motor. A lot has been done so far, we’re nearly there. Up for part four? That’s where I’m headed to connect the Pi and motor to the dispenser.

Reading Gmail with Raspberry Pi and IMAPClient

Reading Gmail with Raspberry Pi and IMAPClient

In part one we covered the basics of what we’re trying to build, and what you’ll need to get started. Here in part two we’ll be playing with some python code to interact with our Gmail account using IMAPClient. Later on, part three is focused on the servo motor, while part four connects it all to the food dispenser.

The goal of this part?

  • Setup your Gmail Account with 2-step authentication for a secure way to interact with it through code. 
  • Write some code to interrogate your inbox for an email and mark it as read when found using the IMAPClient library.

Let’s get to it!

Preparing Your Gmail Account with 2-step Authentication

This is not strictly necessary, but a good security practice. If you don’t mind your credentials being in plain text in your code, feel free to skip ahead.

Since we’re going to be using email as the trigger, we’ll need a way to securely log into our Gmail account from code. You could store your credentials in plain text within the code we write, but I strongly discourage that. Instead, make use of Gmail’s two-step authentication and app passwords

I created a new Gmail account for this purpose.

  1. Log into your gmail account
  2. Navigate to the Sign-in and security page
  3. Under the Signing in to Google section, click the 2-Step Verification menu, then follow the instructions to enable 2-Step Verification
  4. Back in the Sign-in and security page right under the 2-Step Verification button you’ll see App passwords
  5. Generate a password
    1. Note: this password will be 16 digits with spaces separating every 4 digits (e.g. xxxx xxxx xxxx xxxx). Please be sure to include the spaces!

The password you generated can be used to log into your Gmail account. We don’t need it right this second, but we’ll be using it to scan for an email that demands we feed the cats!

Writing Code to Read Your Gmail

Now that we have a Gmail account ready to rock, let’s write some code to interrogate it. We’re going to be using the IMAPClient python library for this purpose, and we’ll wrap the calls to this client in our own Python class.

Install IMAPClient now from the terminal: sudo pip install imapclient

Create the GmailWrapper.py Script

Now let’s create our Gmail wrapper class: nano GmailWrapper.py. Within the GmailWrapper.py script, add the following code:

#!/usr/bin/env python

from imapclient import IMAPClient, SEEN

SEEN_FLAG = 'SEEN'
UNSEEN_FLAG = 'UNSEEN'

class GmailWrapper:
    def __init__(self, host, userName, password):
        #   force the user to pass along username and password to log in as 
        self.host = host
        self.userName = userName
        self.password = password
        self.login()

    def login(self):
        print('Logging in as ' + self.userName)
        server = IMAPClient(self.host, use_uid=True, ssl=True)
        server.login(self.userName, self.password)
        self.server = server

    #   The IMAPClient search returns a list of Id's that match the given criteria.
    #   An Id in this case identifies a specific email
    def getIdsBySubject(self, subject, unreadOnly=True, folder='INBOX'):
        #   search within the specified folder, e.g. Inbox
        self.setFolder(folder)  

        #   build the search criteria (e.g. unread emails with the given subject)
        self.searchCriteria = [UNSEEN_FLAG, 'SUBJECT', subject]

        if(unreadOnly == False):
            #   force the search to include "read" emails too
            self.searchCriteria.append(SEEN_FLAG)

        #   conduct the search and return the resulting Ids
        return self.server.search(self.searchCriteria)

    def markAsRead(self, mailIds, folder='INBOX'):
        self.setFolder(folder)
        self.server.set_flags(mailIds, [SEEN])

    def setFolder(self, folder):
        self.server.select_folder(folder)

Save and exit the nano editor: CTRL+Xthen Y, then Enter.

Verifying the GmailWrapper.py Class Works

Let’s do a test: our script is going to log into the Gmail account, search for email with a specific subject, then mark it as read. Before running the code, send yourself an email with the subject feed cats (the search is case-insensitive, by the way).

We’re going to use the Python interpreter to run our tests. In your terminal make sure you’re in the same directory as the GmailWrapper.py class we just created, then:

# press enter after each line for the interpreter to engage

# invoke the interpreter
python

# import our wrapper class for reference
from GmailWrapper import GmailWrapper

# create an instance of the class, which will also log us in
# the <password> should be the 2-step auth App Password, or your regular password
gmailWrapper = GmailWrapper('imap.gmail.com', '<your gmail username>', '<password>')

# search for any unread emails with the subject 'feed cats', and return their Ids
ids = gmailWrapper.getIdsBySubject('feed cats')

# have the interpreter print the ids variable so you know you've got something
ids

# now lets mark the email as read
gmailWrapper.markAsRead(ids)

# exit the interpreter
quit()

If everything went as planned your email should now be marked as read. Pretty neat!

Conclusion and Next Steps

Part one set the goals and outline. Now with part two wrapped up we have the ability to discern whether or not an email exists. With this power we can trigger certain events, such as spinning a servo motor. That’s exactly what we’re going to do in part three, I’ll meet you there.

Later on in part four we’ll be attaching the Pi and servo motor to the dispenser, but before we make anything permanent let’s get that servo spinning.

Automatic Cat Feeder Using the Raspberry Pi

Automatic Cat Feeder Using the Raspberry Pi

After many failed attempts at changing their feeding schedule, our cats are starving by 3:00 AM. What this equates to is many meows until we’re awake. I remembered a post I saw on David Bryan’s blog about his Raspberry Pi controlled automatic cat feeder. Inspired by David, I decided to take a crack at building my own. Here’s what I came up with, and what I’ll be covering in this post.

Goal

An automated way to feed the cats both on demand as well as on a schedule.

This cat feeder will be triggered by email. More specifically, Gmail. That’s right, we’re going to set up a Gmail account that our Raspberry Pi will monitor. When an email arrives with the expected subject, the cat feeder will let loose its goods.

I chose this route to avoid overly complicating the project. Feeding the cats on demand is as easy as sending an email, while feeding them on a schedule is as easy as creating an IFTTT applet. So long as the cats don’t figure out the email account and secret subject, this should be a viable solution.

If you find this series of posts interesting, you might also enjoy the automatic cat laser pointer toy series!

Prerequisites

You should have a configured Raspberry Pi ready to go. I used my original Raspberry Pi Model B (26 GPIO pins), an 8gb SD card and the Raspbian distro (highly recommended). This guide was pretty straightforward and got me up and running in no time.

The Parts

I decided to break this post down into distinct parts for readability:

  • Here in part one we’re introducing what we’re building and the things you’ll need to get it done
  • Part two will cover writing code to interrogate your Gmail account for an email with the subject feed cats
  • Meanwhile, part three is going to focus on connecting your servo motor to the Raspberry Pi and writing code to make it spin
  • And lastly, part four will cover my approach at mounting the Pi and servo motor to the dispenser

The Things You’ll Need for the Automatic Cat Feeder

  • Cereal/ Dry Food Dispenser to hold the cat food. The stand this dispenser came with didn’t have sufficient space to mount the motor on the back end. I wanted the motor to be hidden as much as possible, so this drove me to build my own stand for the dispenser.
  • Servo motor to spin the dispenser. I used this continuous rotation servo motor, though the standard non-continuous servo motor will work just the same, as we won’t be doing a full revolution. 
  • 5V 2A USB Power Adapter to power the Pi and motor. The motor draws a good amount of current, so we need to make sure the power supply is capable of supporting it. I ended up using my iPad charger.
  • 3x Male-to Female jumper cables to connect the motor to the Pi’s GPIO pins.
  • WiFi Dongle, unless you’ll be hard wiring or have a later Raspberry Pi model that comes with the WiFi built in (good on you).
  • I have two cats, so I used a PVC fitting that will split the load into two bowls. Ideally, a 2 inch PVC Double 90 Degree Elbow. They don’t sell them in stores around me, so I settled on the double wye and plugged the center hole.
  • Zip ties, or some other cheap and easy fastener.
  • 1x #8-32 Threaded Rod to fasten the base of the servo motor to the dispenser, keeping it from spinning.
  • 4x #8-32 Hex Nut to tighten the rod in place.

The Tools

  • Power drill if you plan on attaching the Pi and PVC pipe directly to the dispenser.
  • Box cutter if you’re using the threaded rods as a fastener, as they’re slightly too large to fit in the servo motor mounting holes.
  • Hacksaw if you’re going to use the threaded rod from above. It’s a bit too long so we’ll need to shorten it. 

[Optional] Building a Custom Stand for the Dispenser

The stand that came with the dispenser didn’t have enough space behind it for the servo motor to be mounted properly. I wasn’t keen on the idea of the servo being up front, especially knowing my cats with wires, so I went the route of building my own.

I chose to build this using copper piping (had to match our boho theme). It didn’t take long to find something on Pinterest that (mostly) fit our needs. This plant holder was re-purposed to be a dry food dispenser stand. The small square in the middle was sized to fit the base of the dispenser firmly, then copper piping was built around it.

I’m not going to go into the specifics of how this was constructed, but here’s a list of additional parts anyway:

Conclusion and Next Steps

So far we talked about what you’ll need to get started. Head over to part two where we cover writing a python script to read your Gmail account using the IMAPClient library.

Ahead of the game? Part three covers rotating the servo motor, and part four is all about connecting the Pi and motor to the dispenser.