Adafruit Spooky Eyes On Raspberry Pi

[Emily] and I were first exposed to Adafruit’s “Spooky Eyes” in the context of their HalloWing given out to all Superconference 2018 attendees. We think it looks like a lot of fun and thought it would be nice to make it available on other hardware platforms. We looked under the hood to see how it has been packed tightly for low power microcontrollers, but as a result of its simplicity it was a fairly simple task to translate encoded Spooky Eyes data into PNG image files. This would make the image data more easily usable on less constrained hardware like a Raspberry Pi.

But as it turned out, Adafruit was way ahead of us. They already offer Spooky Eyes running on a Raspbbery Pi! I thought I had looked for this earlier but if so I had missed it.

Adafruit Raspberry Pi Eyes

Reading the details of that tutorial, a few interesting items of note:

  • Just like the HalloWing, there’s provision for light reactivity. However, a HalloWing has an onboard light sensor but a Raspberry Pi does not. The user would have to install a photocell.
  • By default the eyes move randomly, but there’s also provision for a joystick to steer their gaze direction. Again this is an analog joystick the user would have to install.
  • By default the eyes blink at random intervals, but there’s also the option to add buttons to trigger eyelid blinks.
  • Even though the product image shows a Raspberry Pi zero, the documentation says A Raspberry Pi 3 or Pi 2 is highly recommended. The code will run on a Pi Zero or other single-core Raspberry Pi boards, but performance lags greatly.
  • It is designed for Adafruit’s little display units, but the code could just as happily render to a Raspberry Pi’s standard HDMI output. Bypassing all the Adafruit hardware is possible as described in the Using Just the Software section.

Browsing through the source code repository, I see it uses quite a few Raspberry Pi specific code libraries. In addition code dealing interfacing the Adafruit display units, there’s also GPIO code to handle the joysticks and buttons above. And lastly, rendering is handled by the pi3d library to take advantage of a Raspberry Pi’s GPU. If I wanted to make Spooky Eyes run on, say, my Linux laptop, all those pieces of code would require modification.

Using LibPNG To Encode Spooky Eye Data

Sclera array and bitmap

Emily and I thought it would be cool to have the Spooky Eye visualization running on platforms in addition to Teensy and Adafruit SAMD boards. The first target: a Raspberry Pi zero. Reading through the project documentation and source code gave us a good idea how the data is encoded, but the best test is always to make use of that data and see if it turns out as I expected.

This would be a new coding experiment, so a new Github repository was created. I added the header files for various eyeballs then I started looking for ways to use that data. Since the header files are in C, it made sense to look for a C library to do something. I decided to output data to PNG bitmap files. Verifying the output looks correct would be as simple as opening the bitmap in an image viewer.

The canonical reference library for PNG image compression is libpng. Since I expect my use to be fairly mainstream, I skipped over the official documentation that covers all the corner cases a full application would need to consider. In the spirit of a quick hack prototype, I looked for sample code to modify. I found one by Dr. Andrew Greensted that looked simple and amenable to this project.

I fired up my Ubuntu 18.04 WSL and installed gcc and libpng-dev as per instructions. The sample failed to compile at first with this error:

/tmp/ccT3r4xP.o: In function `writeImage':
makePNG.c:(.text+0x36f): undefined reference to `setRGB'
collect2: error: ld returned 1 exit status

Since there were a lot of references to this sample code, I thought this wouldn’t be a new problem. A web search on “makePNG undefined reference to setRGB” sent me to this page on Stackoverflow, which indicated there was a problem with use of C keyword inline. There were two options to get around this problem: either remove inline or use the -Ofast compiler option to bypass some standards compliance. I chose to remove inline.

That was enough to get baseline sample code up and running, and modification begins. The first act is to #include "defaultEye.h" and see if that even compiles… it did not.

In file included from makePNG.c:20:0:
defaultEye.h:4:7: error: unknown type name ‘uint16_t’

Again this was a fairly straightforward fix to #include <stdint.h> which takes care of defining standard integer type uint16_t.

Once everything compiled, the makePNG sample code for generating a fractal was removed, as was the code to translate the fractal’s floating point value into color. The image data was replaced with data from Spooky Eye header files. If all works well, I should have a PNG bitmap. The first few efforts generated odd-looking images because there were bugs in my code to covert Spooky Eyes image array, encoded in 16-bit RGB565 format, to be written out in 24-bit RGB888 format. Once my bitwise manipulation errors were fixed, I had my eyeballs!

Looking Under The Hood Of Adafruit Spooky Eyes

Sclera array and bitmap

Adafruit’s Hallowing was easily the most memorable part of the 2018 Superconference attendee gift bag. Having a little moving blinking eye looking around is far more interesting than a blinking LED. It is so cool, in fact, that Emily has ambition to put the same visual effect on other devices.

Since the Hallowing was one of the headline platforms that supported CircuitPython, the original hope was that it would be very easy to translate to a Raspberry Pi. Sadly, it turns out “Spooky Eyes” is actually a sketch created using Arduino IDE for a Teensy board that also ran on the Hallowing.

As I found out in my own Nyan cat project for Superconference 2018 badge, modern image compression algorithms are a tough fit for small micro controllers. And just as I translated an animated GIF into raw byte data for my project, Spooky Eyes represented their image data in the form of raw bytes in a header file.

Adafruit always has excellent documentation, so of course there’s a page describing what these bytes represent and where they came from for the purposes of letting people create their own eye bitmaps. Apparently this project came from this forum thread. I was a little grumpy the Adafruit page said “from the Github repository” without providing a link, but the forum thread pointed here for the Python script tablegen.py.

There was a chance the source bitmaps would be on Github as well, but I had no luck finding them. They certainly weren’t in the same repository as tablegen.py or the Arduino sketches I examined. Still, the data is there, we just need to figure out what format would be most useful for putting the eye on another project.

As a first step, I’ll try to extract and translate them into a more familiar lossless bitmap format. Something that can be directly usable by more powerful devices like a Raspberry Pi. A successful translation would confirm I understand the eyeball data format correctly, which would be good to know for any future projects that might want to encode that data into different formats as needed for other devices.