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
Feature Flags in Terraform
Feature flagging any code can be useful to developers but many don’t know how to or even that you can do it in Terraform. Some benefits of Feature Flagging your code You can enable different […]
Infrastructure as Code – The Wrong Way
You are probably familiar with the term “infrastructure as code”. It’s a great concept, and it’s gaining steam in the industry. Unfortunately, just as we had a lot to learn about how to write clean […]
Snowflake CI/CD using Jenkins and Schemachange
CI/CD and Management of Data Warehouses can be a serious challenge. In this blog you will learn how to setup CI/CD for Snowflake using Schemachange, Github, and Jenkins. For access to the code check out […]
How to get your pull requests approved more quickly
TL;DR The fewer reviews necessary, the quicker your PR gets approved. Code reviews serve an essential function on any software codebase. Done right, they help ensure correctness, reliability, and maintainability of code. On many teams, […]