Jan 11, 2021

Building a Better Mousetrap

Recently, my daughter (age 6) was into building “mousetraps” out of shoe boxes. These were more or less comfortable cardboard mouse houses complete with beds, rooms, everything a mouse could want or need and not actually be trapped. That got me wondering, how could I involve her and her older sister (age 8) in making a real mousetrap and get them exposed to some STEM concepts? Enter the Raspberry Pi! If you are not familiar with what a Raspberry Pi is, it is a small, inexpensive, single board computer complete with inputs and outputs and has fueled a booming interest in the makers space. They are used in all sorts of projects ranging from robotics to video security systems.

I scrounged up an old Rasperry Pi Model 2 that I had used for some other project and read up on different sensors and types of motors. I decided an IR break beam sensor and a stepper motor were the simplest things to make this work. We found some scrap pieces of wood and built a basic box and frame that will make up the trap itself and hold the various parts.

They helped measure, cut, drill, screw, and nail the frame together. We discussed the best place to put the sensor and how we could use the stepper motor to close the door. Our initial design for the door involved a slot in the door and an arm on the motor with a thin piece of metal screwed on the end that held the door open. This worked, but it wouldn’t be easy to reset the door as we’d have to somehow actuate the arm and hold the door open at the same time. Our second design for the door had the motor arm connected to a string and the piece of metal wedged in the slot holding the door open. We set it up to have the arm move in one direction, pulling out the wedge, then reset itself back to where it started. This worked ok, but the wedge was finicky to get it to hold the door open and the motor wasn’t always strong enough to pull it out – the wedge would get caught sometimes. The third design is the one we stuck with. We stayed with the string, but instead of a wedge, we used a nail held in place by another piece of wood. The arm actuates, pulls the nail out of the slot in the door, then the arm moves back to the initial position. This proved to be stable and easy to use and reset. Iteration at it’s best!

In between when we were designing and building the frame, I took on the task of figuring out how to interact with the sensor and the motor using the Raspberry Pi. Thankfully, many people have blazed the trail ahead of me. The main language used on the Raspberry Pi is Python, but many of the top languages are able to interact with the Raspberry Pi. I went with Python as everything is setup right out of the box.

The break beam sensor works like most sensors. You code up the program to look for changes in voltage:

# setup the break beam pin
GPIO.setup(BEAM_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

This line of code says “on the pin that the break beam sensor is wired to, check for input voltage to decrease to 0”. Then in the main loop, you set it up to wait for the change:

GPIO.wait_for_edge(BEAM_PIN, GPIO.FALLING)

The program will wait there until a falling edge (decreasing voltage) is detected. At that point the next line of code is executed. There are two other ways to respond to sensor changes that may be better in other use cases. Read about them here.

Actuating the stepper motor was a little more tricky. Stepper motors work by activating different internal parts in a sequence to make it move either forward or in reverse. After defining that matrix of what to activate in each step, I couldn’t figure out why my motor wasn’t rotating. It turns out, it was, but in very small, nearly undetectable amounts. When I set it up to loop many times, with a small wait time between steps to give it a chance to move, I could finally see it rotate. At that point it was just a matter of trial and error to see how far different amounts of iterations of the loop made the motor rotate.

The final piece of the puzzle was to make the program startup when the Raspberry Pi started up. Like most things in the Linux world, there are several ways to do this. I chose to use the simple method of putting the command in rc.local.

# edit the file with
# sudo vi /etc/rc.local
# then put this line above "exit 0":
sudo python /home/pi/projects/mousetrap/mousetrap.py

Here is the complete program listing:

import RPi.GPIO as GPIO
import time

# setup some constants
GPIO.setmode(GPIO.BCM)
BEAM_PIN = 23
pin_1 = 13
pin_2 = 21
pin_3 = 27
pin_4 = 4

motor_operating = False

# setup the motor pins
GPIO.setup(pin_1, GPIO.OUT)
GPIO.setup(pin_2, GPIO.OUT)
GPIO.setup(pin_3, GPIO.OUT)
GPIO.setup(pin_4, GPIO.OUT)

GPIO.output(pin_1, GPIO.LOW)
GPIO.output(pin_2, GPIO.LOW)
GPIO.output(pin_3, GPIO.LOW)
GPIO.output(pin_4, GPIO.LOW)

# these steps are what actuates the
# parts of the stepper motors that
# make it move.
steps = [
        [1, 0, 0, 1],
        [1, 0, 0, 0],
        [1, 1, 0, 0],
        [0, 1, 0, 0],
        [0, 1, 1, 0],
        [0, 0, 1, 0],
        [0, 0, 1, 1],
        [0, 0, 0, 1]
    ]

def set_step(w1, w2, w3, w4):
    GPIO.output(pin_1, w1)
    GPIO.output(pin_2, w2)
    GPIO.output(pin_3, w3)
    GPIO.output(pin_4, w4)
    
def actuate_motor(motor_operating):
    # don't do anything if motor is already operating
    if motor_operating == True:
        return
    else:
        motor_operating = True
    # move motor one way through many sets of steps
    for i in range(0, 100):
        for step in reversed(steps):
            set_step(step[0], step[1], step[2], step[3])
            time.sleep(.003)
    set_step(0, 0, 0, 0)
    
    # move the motor back to where it started
    for i in range(0, 100):
        for step in steps:
            set_step(step[0], step[1], step[2], step[3])
            time.sleep(.003)
    set_step(0, 0, 0, 0)
    
    motor_operating = False

# setup the break beam pin
GPIO.setup(BEAM_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# This is the "main" program. Wait for an "edge"
# in the break beam sensor. The "edge" is when
# voltage changes indicating a break in the beam.
while (True):
    try:
        print("Waiting for edge")
        GPIO.wait_for_edge(BEAM_PIN, GPIO.FALLING)
        actuate_motor(motor_operating)
    except KeyboardInterrupt:
        print("Received keyboard interrupt")
        GPIO.cleanup()

Here are some pictures of the mousetrap in a mostly complete state. I still need to add the mesh around it to allow me to keep the mouse in the trap until I can release it into the wild.

Mousetrap from the side ready to catch a mouse.
Mousetrap from the top showing the stepper motor and the stepper motor controller.
Mousetrap “catching” a mouse.

A couple of improvements we talked about is having a sensor to detect when the door is closed so that the motor doesn’t actuate when the captured mouse inevitably moves over the sensor again and again. Also, adding a sensor to indicate when the arm is at a “home” position in case the cheap stepper motor doesn’t actually move the same amount forward and backward.

This was a fun project to hopefully get the next generation interested in technology. The cool part is it combined physically building something with wood and tools with an abstract concept like software development. Plus, I got to do something with my kids that we all enjoyed working on!

About the Author

Brendon Anderson profile.

Brendon Anderson

Sr. Consultant

Brendon has over 15 years of software development experience at organizations large and small.  He craves learning new technologies and techniques and lives in and understands large enterprise application environments with complex software and hardware architectures.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Why we started using JSON Schema in our internal Helm charts
Helm 3 supports validating the provided chart values against a JSON Schema. While it may be quicker to get started in your chart development without a schema, we found it valuable for a number of […]
Rewriting files in Google Cloud Storage
Rewriting Files in GCP Note: even though this code is in Python, this should be the same idea in JavaScript, Go, etc. I wrote the following to copy a file from one Google Cloud Storage […]
Interpreting Spatial Data in the Age of COVID-19
As 2020 has come to an end, many are eager to leave the mess of COVID-19 behind with the new year and gain a fresh start. Unfortunately, new cases are still soaring across the United […]
ARM Wrestling Its Way Into Mainstream Software Development
Nearly all smart phones have been running ARM-based processors for years. They provide superior power for the amount of power consumed, and thus extend battery life. With Apple’s recent release of the Apple Silicon M1 […]