For when you're feeling just a little old school

Overview

Some dozen years ago, I very nearly failed a 200-level Electrical Engineering course. More than fifteen years ago was the last time I did much programming. In FORTRAN. This is my guide to making a Raspberry Pi-powered interactive Jack-o'-Lantern from my completely novice perspective.

Why a Raspberry Pi? Well, it's all the rage right now, and, while an Arduino is probably a better choice here, a Pi was what I had. And we all know what they say about what to do when all you have is a hammer. Also, I've been working on learning Python lately, so using the Pi was a good way to put what I've been learning to use.

I'll walk through building the circuits, writing the code, and putting it all together into a pumpkin before we admire the final result.

Jack Skellington, the Pumpkin King

Build Some Circuits

I built everything onto two breadboards. The breakout board connects to the GPIO headers through the Adafruit Pi Cobbler Kit and the remote board is inside the pumpkin and holds the lights and motion sensor. The remote board is tethered by a piece of CAT-5e cable with the ends cut off. By using two boards this way, I can keep the Raspberry Pi and main board inside and run the cable out the window to the pumpkin on the porch.

The Raspberry Pi provides 3.3V and 5V power. The 5V pin is connected to the bottom rail to power the PIR. Each LED is connected to a GPIO pin that provides 3.3V when switched on.

Setting up to test the PIR
Components used:

Some of the specifics on the parts list were driven by what I had on hand. I have four circuits for LEDs on pins 22, 23, 24 and 25. For a brighter red glow, I put two red LEDs together on one circuit. They are wired in parallel so each sees 3.3V for maximum brightness. Each individual LED also has a current limiting resistor.

The PIR wiring is very simple. It has 3 connections: ground, +5V and signal. My signal wire connects to pin 17 and the whole PIR is on a 12 inch lead so I can position it for best coverage. When the PIR senses motion, it sends out 3.3V on the signal wire for a little over a second. The Raspberry Pi interprets that as True. To test using the PIR to trigger an action, I connected the PIR to pin 17, an LED to pin 22, and ran this script. All it does is listen for the PIR on pin 17. When a voltage is detected, it prints a message to the terminal and turns on the light. The while True: statement will cause this loop to run forever. To exit this quick test, wait until the LED is off and hit Ctrl-C. In the picture, only the resistor in the middle is part of the circuit. The other two were sitting in the board for something else and I just forgot to take them off before taking the picture.

import RPi.GPIO as GPIO, time

GPIO.setmode(GPIO.BCM)      # Set up
GPIO.setup(22, GPIO.OUT)
GPIO.setup(17, GPIO.IN)

GPIO.output(22, True)       # Turn the light on to confirm it's working
time.sleep(5)
GPIO.output(22, False)
time.sleep(5)

while True:
    if GPIO.input(17):        # Listen for the PIR on pin 17
        print "Motion Sensor"
        GPIO.output(22, True)
        time.sleep(10)
        GPIO.output(22, False)

A quick note about the GPIO pins on the Raspberry Pi: They are digital, which means that they are all or nothing, high or low, on or off, True or False. The correlation between what is happening in the code and physically, on the Pi, is that True equals +3.3V on the GPIO pin and False equals no voltage on the pin.

Pumpkin Pi on the bench

Write Some Code

The code is in Python and posted on GitHub. You'll need to make sure you have the RPi.GPIO library installed and up to date. The first thing to do is import some modules and set up the board and motion sensor.

import RPi.GPIO as GPIO, random, signal, sys, time, bigletters

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
motionsensor = 17
GPIO.setup(motionsensor, GPIO.IN)
The remote board

The bigletters module is something I wrote that prints short messages to the terminal using ASCII art characters.

Next is the LED class.

class LED:
    '''Access the LEDs with some simple methods.'''
    def __init__(self, pin):
        self.pin = pin
        leds.append(self)
        GPIO.setup(self.pin, GPIO.OUT)
    def on(self):
        GPIO.output(self.pin, True)
    def off(self):
        GPIO.output(self.pin, False)

This allows me to create an instance of an LED with a descriptive name and pin number and forget about it.

RedLED = LED(23)

Now, to turn on the red LED, just call RedLED.on(). The object also adds itself to a running list of all instances when it is created, to make it easy to perform an action on all of the LEDs attached to the system.

def pumpkin_pi_on():
    for i in range(len(leds)):
        leds[i].on()

I defined a handful of lighting patterns using some for: loops to keep the code small. To make the program pause, use time.sleep(t) where t is the length of the delay in seconds. Each sequence lasts around two or three seconds.

def flash_1():
    for i in range(6):
        pumpkin_pi_on()
        time.sleep(.25)
        pumpkin_pi_off()
        time.sleep(.25)

The main program flow involves a few branching for loops that string together a relatively small number of animations to make it look more random. Every iteration of the main loop checks if the allotted running time is up, then checks the motion sensor before randomly choosing a blink pattern.

def pumpkin_pi(stopTime):
    while stopTime > time.time():
        if GPIO.input(motionsensor):
            motion_sequence()
        else:
            random.choice(flash_patterns)()
            pumpkin_pi_off()
    pumpkin_pi_quit()

The while loop will execute the code inside the loop as long as the condition is True For randomly branching to different functions, I found this piece of code fascinating:

list_of_functions=[func_1, func_2, func_3]
random.choice(list_of_functions)()

So, given a list of function names, random.choice() will pick one, pass it the arguments () and execute it. This condenses a teetering tower of if: / elif: statements down to two lines.

That's the bulk of the code. There is a main() function to get things moving and some window dressing for the terminal. There is also a signal handler to catch Ctrl+C from the keyboard and trigger a nice shutdown. To get things running from the command line, make sure it is executable:

chmod +x pumpkinPi.py

And run it as root with a duration argument to tell it how long you want the pumpkin to run. The GPIO library requires root to get to the GPIO pins on the Raspberry Pi. To run for one hour:

sudo ./pumpkinPi.py 01:00

The command line has two options: -v for verbose, where every subordinate function announces itself to the console when it runs. Debug mode is activated with -d and allows you to manually run sequences.

Make a Pumpkin

I chose a medium sized pumpkin, something small enough that the LEDs can adequately light it. Carve it up and add holes for the Cat-5 tether cable and the PIR wires. I used this chart to keep track of the wires from the GPIO through the terminal block. Using Cat 5e cable was convenient for packaging eight wires together, but added complexity to track what goes where. Terminal 1 is the one at the top of the picture.

The breakout board and terminal block
CircuitGPIOWire ColorTerminalCat 5 Color
Red LED23Green1Green
Amber LED22Orange2Green/Stripe
White1 LED24White3Orange
White2 LED25Blue4Orange/Stripe
PIR Signal17Yellow5Brown
+5V--Red7Purple
GND--Black8Brown/Stripe
The remote board in place

I hadn't really thought about what I was going to actually carve until I was looking at the hollowed-out pumpkin, knife in hand. Jack Skellington crossed my mind, so I looked up some pictures, sketched it out with a dry erase marker, and cut away. I set the remote board inside of a short drinking glass to insulate it from pumpkin goop. I think it turned out pretty good. I am quite pleased with how well the LEDs light up the jack-o'-lantern. They are performing far better than I had hoped. I particularly like how the red LEDs fill the volume of the pumpkin. The two big white LEDs produce an extra bright blowout, and the amber provides a touch of warmth.

I'll call this Version 1.0 of Pumpkin Pi. Next year, I might revisit this to scale up the LED array, use RG-45 jacks to connect the boards, figure out some more advanced sequences and add a better interface.

The Result