Last week at DerbyCon 5.0 the CircleCityCon folks had a booth with a challenge, the Challenge of Tiamat’s Eye.

The challenge consisted of the small chest pictured above containing an eye made up of blinking red LEDs. Every 30 seconds the pattern would reset. The content organizers hinted that we would need to record the eye at 60fps in order to capture all of the information we needed. We ended up using a coffee creamer cup as a diffuser over the LEDs to make the difference in the pixels clearer. This resulted in the following recording. Note: we recorded 30 seconds at 60fps, which resulted in a 60 second 30fps recording.

Next we extracted each frame from the video with the following command: (ffmpeg would have worked as well too)
avconv -i VID_20150926_175956.mp4 -r 30 frames/%5d.jpg

This resulted in the following 1800 frames.

frames

After viewing the image thumbnails we observed that the shortest amount of time the LED was either on or off was 3 frames. This means that every 3 frames represents a single bit. Since on some frames the LED was half-light we decided to sample every 3rd frame starting in the middle of each bit to get the best representation.

#! /usr/bin/env python

import os
from PIL import Image
import sys

def main():
    i = 0
    # because os.walk is not sorted, and I'm lazy
    # ls frames/* > frames.list
    for l in open("frames.list"):
        i+=1
        if i % 3 == 0:
            im = Image.open(l.strip())
            # sample pixel location
            r,g,b = im.getpixel((1357,657))
            if (r>250):
                sys.stdout.write('1')
            else:
                sys.stdout.write('0')
    print ""

if __name__ == "__main__":
    main()

The python script above reads every 3rd frame and prints out a 0 or 1 depending on the LED state. This gave us the following binary string.

0101010101111111111001100110100110100110011100101001110011100111010010011101001001101111100110110010011011111001100011100110000110011101001001100101100111010010011010001001100101100110100010011011111001110101100111001110011001011001101111100110011010011000101001100101100110000110011100101001101010100110010110011100111001110100100110010110011100101001110111100110100110011011101001110011100110000110011001101001110010100110010110011001011001100011100011001110011101001001101001100110001110011010111001100101100111010000000000000000000000000000000000000000000

Our first guess was to convert it to ASCII, however that did not return anything intelligible. Another vague hint provided got us thinking it was 10 bit serial data. A quick search on Google led us to Asynchronous Serial Communication, which after removing the first 20 bits for the header, tells us that there is 8 data bits padded by a start and stop bit. Splitting the binary into 10 bit lines and then removing the first and last bit provided us with the following binary.

01100110
01101001
01110010
01110011
01110100
01110100
01101111
01101100
01101111
01100011
01100001
01110100
01100101
01110100
01101000
01100101
01101000
01101111
01110101
01110011
01100101
01101111
01100110
...

Converting this to ASCII returns the following text.

firsttolocatethehouseofbearjesterwinsafreec3ticket

After adding a few spaces says:
first to locate the house of bear jester wins a free c3 ticket

which was the magic phrase to say to the house of bear to solve the challenge and win CircleCityCon tickets.

The puzzle was fun and simple enough to solve in a few hours allowing us to enjoy the rest of DerbyCon. I would like to end by thanking CircleCityCon for creating the challenge and my teammates @SID_tracker and @ryatesm for assisting me in solving the challenge.

All of the data used can be found in This Gist.