Before diving in, give some thought to how you’ll be using this.
At the very least, this project uses a Teensy microcontroller and a display or two. The animation effects are all capable of running autonomously if need be, or there are options to have them manually controlled. The method of assembly is also open to interpretation.
If you’re making a spooky prop to sit in the window on Halloween, but plan to disassemble it afterward and use the parts in other projects, you can use a breadboard and jumper wires for quick assembly and re-use.
For something portable, like jewelry or a costume piece, soldering wires directly between components is vital — both for space savings and for durability.
If creating eyes for a puppet, you probably want manual controls for nearly everything, as that’s the very nature of puppetry.
For a costume, I think autonomous works better. Good cosplay is all body language…but when electronics are added, fantastic characters are spoiled when the performer is focused on modes and buttons. But hey, it’s up to you.
Give it some thought. I’ll wait!
Half and half: these eyes are neatly assembled in 3D-printed enclosures while the rest of the circuit is a messy breadboard. Once everything’s tested and working, the breadboard side will be replaced with a more permanent solution and fitted inside a Halloween prop. Whatever works for your needs!
Many configurations of this project are possible, depending on the features you’re after. Rather than a single complex wiring diagram, a few subassemblies are illustrated here…pick and choose to match your needs.
What’s shown here are schematic diagrams — they indicate where to connect wires, but not necessarily their exact actual layout or lengths when you build the thing. You’ll need to think how everything fits in your own setting. Using color-coded wires helps a lot here!
We’ll be referring to several pins by name or number, so here’s a pin map for the Teensy 3.1 or 3.2 microcontroller:
This is a simplified pinout to clarify things specifically for this guide. If you plan to add your own advanced bells and whistles, a more complete pinout map is available on the PJRC web site.
Ground connections are vital for distributing power throughout the circuit. In addition to the two GND pins labeled here, there are two copper pads on the back of the board — one near the center, and a second large one near the USB port (next to the GND pin).
The AGND pin provides a cleaner ground reference specifically for analog inputs — this is not a current-carrying pin for distributing power, do not connect the displays here.
Some of the pin numbers we’ll be referencing are negotiable…if you find that a different pin would make routing wires easier, there’s usually a setting in the code that can be made for it. Anything related to power or SPI is not negotiable…those wires must go to the pins stated.
If you have an opportunity to power everything from the Teensy’s USB port (running a USB cable to a power bank or wall charger), that’s easiest and reduces parts and steps.
The most compact, portable installations may optionally want a built-in Lithium-Polymer (LiPoly) battery. If your project uses one or two TFT LCDs, a 150 mAh battery may suffice (though a larger capacity will provide a longer run time). For one or two OLEDs, a 500 mAh battery is the minimum size.
To use the Teensy board with the Adafruit LiPoly Backpack (allowing USB charging), first two copper traces need to be cut: between the two pads next to the Teensy’s VUSB pin, and between the switch pads on the LiPoly Backpack (marked on back). Then add these three wires between the boards:
- LiPoly BAT to Teensy VIN/BAT+ (unmarked pin at corner)
- LiPoly G to Teensy GND
- LiPoly 5V to Teensy USB+ pin
Add power switch to pins on LiPoly backpack. These tactile on/off switches are my favorite!
If battery capacity is 500 mAh or larger, solder between the charge rate jumpers on the back of LiPoly backpack. Do not do this with small batteries!
When you cut the trace on the Teensy board, it won’t run from USB power until the LiPoly backpack is connected and switched on. This is normal. If a sketch won’t upload on a half-built project, this may be the reason why.
Power the display(s) from the BAT+ (corner) pin. If you're not using the LiPoly backpack (powering off a USB cable instead), that’s okay — by default this pin also connects to USB+.
You have a choice of using one or two displays, either OLED or LCD. But you can’t mix one of each — both must be the same type.
OLED displays have brighter colors and contrast. Downside is the price, and also that they flicker a little when captured on video. They also have a finite lifespan, albeit many thousands of hours.
TFT LCD displays are more affordable. Not as bright, but still a good effect. And they’re rock-steady on video.
If making two eyes, both displays need to connect to the same SPI MOSI and CLK pins on the Teensy, plus a few other wires. The “OC” or “TCS” pins are unique to each display, left or right.
For OLED displays, make the following connections from the display breakout board to the Teensy:
- SI to SPI MOSI
- CL to SPI CLK
- DC to Digital Pin 7
- R to Digital Pin 8
- OC to Digital Pin 9 (left eye) or 10 (right eye)
- + to BAT+ (if using LiPoly Backpack) or USB+
- G to GND
The remaining four pins are not connected.
For TFT LCD displays, use these connections:
- Vin to BAT+ (if LiPoly) or USB+
- Gnd to GND
- SCK to SPI CLK
- SI to SPI MOSI
- TCS to Digital Pin 9 (left eye) or 10 (right)
- RST to Digital Pin 8
- D/C to Digital Pin 7
“Left” and “right” eye in this case refer to the positions when looking at the eyes, not from their point of view. This nomenclature is used throughout this guide and in the software.
If you’re using them, the 3D-printed enclosures have a small notch that’s just wide enough for a 7-conductor ribbon cable to fit through. Space inside is really tight (I wanted them to fit inside a mask), so it’s necessary to route these wires to their pins very carefully so they lie as flat as possible on the back of the board, not all piled up. It’s delicate work that requires tweezers and patience.
If using a ribbon cable as shown above, write down your own legend that maps wire colors (or wire numbers if single-colored cable) to pin functions.
Because the cable colors are in a fixed order and there’s little space to reroute inside the case, certain wiring conventions used in electronics (such as using red wire for positive voltage and black for ground) no longer apply…you’re forced to take what you’re given. What’s more, with wires doubled back, the conductors along the ribbon don’t necessarily match the order along the display breakout header, it’s all jumbled now.
Do not use our photos for reference. Do not rely on the colors shown in any diagrams here. Write down the exact sequence for your cable and your routing, and use only that for reference, nothing else.
The wiring for the TFT was even more “creative,” with some wires a straight shot and others doubled back. Again, notice the wiring legend. Do what works for you.
If you’re not using the 3D-printed enclosures, everything is much simpler. You can just connect to the pins in-order, there’s no space constraint that must be met.
Wanting to test with a breadboard first, I made the ribbon cables extra long (about 8 inches) and soldered row pin headers on the end.
Later, for permanent installation, I'll cut the ribbon cables down to size and solder wires directly to the Teensy board.
This is totally optional…maybe you only want to build it once. Just try to keep the wire lengths to a minimum…high-speed SPI can be very finicky about this. Even 8 inches is pushing it.
The displays arrive with a plastic sheet on them. Keep this in place when soldering, but remove it once you’re done. This isn’t like a phone screen protector, just for shipping and soldering protection.
The display breakout boards include microSD slots, but those are not wired up in this project and the code doesn’t reference them at all. Advanced users who want this capability can connect the SPI MISO and card select pins (you’ll need to modify the enclosure slightly to accommodate the extra wires) and make the required changes in the code.
Any analog controls that are used should include connections to the 3.3V and AGND pins. Don’t use the other power pins or there will be…trouble.
XOUT and YOUT from a joystick can connect to Analog Pins A0 and A1.
The eyes move autonomously by default — settings in the code enable the joystick instead.
If you need to mount the joystick in a different orientation, there are also settings to invert each axis. Swap the X and Y pins in the code to use the joystick sideways.
To have the pupils contract or expand in response to light, connect a photocell and 10K resistor in series. The midpoint connects to Analog Pin A2.
Analog input for the pupils (either photocell or the dial below) are enabled in the code by default. You can comment out IRIS_PIN in the code to have this move autonomously.
For manual control of pupil dilation (instead of responding to light) a 10K potentiometer can be used. The center leg connects to Analog Pin A2 (same input as the photocell, just substituting a different analog control).
The eyes normally blink autonomously, but you can also add one or more buttons to make them blink (or even wink individually) on command.
For all buttons, connect one leg of each to GND, and the opposite leg to a digital pin:
- Digital Pin 0 is the left eye wink.
- Digital Pin 1 blinks both eyes.
- Digital Pin 2 winks the right eye.
If using our analog joystick breakout board, that stick includes a clicky button when you press down on it (on the SEL pin). This can optionally be used for manual blink control, or you can use a separate button for this (I find the joystick button a bit hamfisted).
Pro tip: a fully-equipped version of the circuit requires several ground connections, but there are only a few GND pins on the Teensy board. Two- or three-way splices are one option, but another way to provide extra ground connections is to repurpose unused I/O pins. Let’s suppose you want to make Digital Pin 4 a spare ground pin. Add the following code in the setup() function:
pinMode(4, OUTPUT); digitalWrite(4, LOW);
pinMode(4, OUTPUT); digitalWrite(4, LOW);
OUTPUT LOW makes the pin function as an ersatz ground connection. This works perfectly for button connections like the blink controls, but don’t use it for heavy loads (like powering the displays) — it won’t work and might even damage the Teensy.