Skull Eyes Project

A couple of years ago I picked up two plastic skull decorations in the post-halloween sales. Once I got them home, it occurred to me that they could become an interesting project. Adding some lights to their eyes with some fun effects was the plan. It has taken me a while to get time to do this, but I finally pulled all the parts together and modified the basic plastic skull with some LED eyes.

The parts, in addition to the skull of course, are as follows:

The circuit is very simple:

Power comes from the USB, which also acts as the data connection to the computer for programming the board.

Circuit Python

As mentioned in an earlier post, this board comes pre-installed with Circuit Python, which makes it very simple to code up the effects we needed for this relatively simple project. We only need a single output pin from the board to drive the data line for the LEDs. They connect to each other to form a chain of 14 pixels. Each pixel has 16 million colors to choose from, expressed in standard RGB values.

Each eye has 7 pixels. Pixel 0 is the center one, the 1 through 6 are around it. Since we chained them together, Pixels 0 and 7 are the centers of our eyes and 1-6 and 8-13 are the rings. The plan for the eyes is to have a two LED chaser ring in first in orange, then purple and finally green, interspersed with random flickering red from all 7 LEDs.

Using the Jupyter Notebook connected to Circuit Python made it simple to test colors and effects. Then, when it was complete, I turned it into a simple Python script which is copied to the ItsyBitsy (which looks like a USB drive to the host computer). Name the file code.py and when the board sees it, it will run it.

Setting up the Board

Setup for our simple circuit is easy. Make sure the NeoPixel Python library is copied in to the libs folder and then this will get the board setup:

import time
import board
import neopixel
import random

OFF = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
PURPLE = (180, 0, 255)
ORANGE = (255, 75, 0)

pixels = neopixel.NeoPixel(board.A1, 14, brightness=0.3, auto_write=False)
pixels.fill(OFF)
pixels.show()

This also sets up some variables for the colors we’re going to be using.

Chaser

The sequence starts with LEDs 1 and 2 on each eye lit and then loops around extinguishing the first and then lighting the next. The initial setup is handled by this code:

def setupChase(colour):
    pixels.fill(OFF)
    pixels[1] = colour
    pixels[2] = colour
    pixels[8] = colour
    pixels[9] = colour
    pixels.show()

The actual chaser by this:

def chase(colour, loops=2):
    for loop in range(loops):
        for x in range(1,7):
            time.sleep(0.1)
            # Turn x off, x+2 on (wrapping)
            pixels[x] = OFF
            pixels[x+7] = OFF
            if x <=4:
                nxt = x+2
            else:
                nxt = x-4 # 5 => 1, 6 => 2
            pixels[nxt] = colour
            pixels[nxt+7] = colour
            pixels.show()

Red Flashing

The red flashing is meant to be flickery, so there is a random element to the timing. The code for this is as follows:

def redFlash(limit=1.0):
    t = 0.0
    mx = limit / 8.0
    while (True):
        pixels.fill(RED)
        pixels.show()
        delay = (random.random() * mx)
        t += delay
        time.sleep(delay)
        pixels.fill(OFF)
        pixels.show()
        delay = (random.random() * mx)
        time.sleep(delay)
        t += delay
        if t > limit:
            break;

Putting It All Together

For testing, I ran this in a loop that just went a few times. Once ready for standalone use though this sits in a while true loop as we want it to just run forever.

col = 0
colours = [ ORANGE, PURPLE, GREEN ]

while True:
    setupChase(colours[col])
    chase(colours[col], loops=5)
    redFlash()
    col = (col + 1) % len(colours)