Teleflix - The One True Single Button Interface

About a month ago, I was on the phone with my dad, discussing the upcoming summer hackday at Netflix. I had no idea what to work on, as I was recounting to him the previous few hacks. The team had done full motion video on an original NES, which began a tech arms race of hacks going back through history; at the next event, the Bogdan Collective matched our 80s NES hardware but had gone even further with a 1960s era CRT television, including working channel / volume dials remapped to allow left-right-up-down movement in the Netflix UI. The ante was high. Netflix had even made an official polished version of Bogdan's hack, which was now permanently on display in the building D cafeteria.

"Dad, there's not much I can do with a video streaming app before the 60s. What, a first gen TV? Just wheeling it on stage would be an effort, assuming we can even find one!"

"Guy, there was plenty of communications tech going on before the television."

"Like what? The telegraph?!"

Little did I know, my fate was already sealed. I did a quick search online and found a stainless steel telegraph key on eBay. Dad chided me for even thinking of using a utilitarian stainless steel telegraph key for Victorian era communications.

"If you really want to go Victorian, you need to use period materials. It should be brass."

Inception

After more fruitless hunting on eBay, I was gaming with Alex when we both got distracted in the middle of a dungeon, and I discovered that hackday was on his mind too.

"So I was thinking about hackday..."

"Oh boy."

"...and I know you don't like it, but I think you're underestimating the value of doing a carhack."

"I told you, no carhacks."

Another player got annoyed. "Can you two knock it off and kill the damn boss?"

"Oops, sorry."

We finished the level off, and then got down to the serious work.

"So I've got an idea, if you and Carenina are interested. How about we go a bit steampunk on this one."

"What? Like goggles and nixie tubes?"

"Exactly."

"Go on..."

"So I was thinking how we could really get Bogdan good."

"Uh oh."

"Apple has tried it. Google has tried it. But they both failed to deliver on the real one true single button interface that existed since time immemorial."

"Uh oh."

"The telegraph."

"A TELEGRAPH!?!?!"

"So I was hunting around on eBay, and I noticed this beautiful brass vintage AT&T telegraph key, right off the line, pulled from service back when they were actually American Telephone--and TELEGRAPH!!"

"YES! I'm in."

A few weeks later we told Carenina that we knew what the next hack would be. I pulled a box from my desk and slowly opened it to reveal the 1920s era telegraph key. “Noooo really?!” she said with a glint in her eye. . And just like that, the team was back together.

Implementation - Pre-Work

After a bit of discussion, we landed on what seemed like a simple idea - clean up the old AT&T telegraph key, do some electronics, hook it up to a Raspberry Pi, decode the tapped Morse code, have the Pi present itself as a USB Keyboard, and fire the decoded letters at a retail PS4 running a custom version of the Netflix UI adapted to work with the true one button interface. In block diagram, it looked something like this:

Teleflix Block Diagram

To bound the problem, I decided to investigate how hard it was to hook a regular clean microswitch up to a Raspberry Pi Zero W I had lying around. The guides out on the web were easy to read and straightforward. After a brief survey of the ones covering Morse, I decided I wasn't really enamored with any of their debouncing or processing code. Instead, I wrote a simple blocking python gpiozero.Button.wait_for_press() loop and did some rough conversions of keydown time to dots and dashes (or "dit" (".") and "dah" ("-"), as I would learn later that they were called).

For those not familiar with Morse code, it is a way of encoding letters into short pulses (dits) and long pulses (dahs). Letters that are more common in English (such as "e", "t", or "a") are encoded with short sequences, while less common letters (such as "q" or "x") are encoded with longer sequences. For building a keyboard, a dit is a short pulse of sound / light / etc, while a dah is a longer pulse, equal to 3 times the length of a dit. A space is given by waiting 7 times the length of a dit. The length of a dit is not fixed, but is commonly started at 50-100ms, and usually much shorter for experts.

International Morse Code Table

The table of Morse, from the Wikipedia

Once I had proved that the blocking version of the decoder worked, I needed to see how to make the Pi behave like a keyboard. I was using a stock version of Raspbian, and was a little worried about how hard it would be to hack the kernel around the USB gadget interface. Thanks to the helpful Adafruit guide about configuring the Pi to show up as a network adapter by using the dwc2 overlay, that looked straightforward to implement as well. Sadly, it turned out to be quite a bit harder to get the Pi to act like a keyboard rather than a network adapter due to the confusion in the libcomposite vs. classic gadget-hid drivers, but a little leg work and plenty of google searches later, I found the hardpass-pi project, and worked off of their notes on how to generate USB keyboard (called "USB HID") scancodes.

To add a little fun, Alex, Carenina, and I scoured the internet ordering various steampunk accoutrement such as, goggles, pocket watches, period correct hats, etc. We were prepared to await the day of the event when Cirino, Motion, and Wolfe Heavy Industries would need to work their magic.

I won the bid on a 1920s original AT&T brass telegraph key. After searching around for the best chemical cleaner I could find, I learned that the key (pun intentional) to removing tarnish from brass involved taking the device apart, washing it with soap and water, then fully immersing it in... ketchup. Despite my surprise, it turns out that soaking brass in ketchup works wonderfully.

There was one other problem - I needed to learn Morse code, but how hard could that be?

The Day of the Hack

A few weeks later, it was time to get serious. Alex dug in on building a custom version of the Netflix TV user interface based on the version of our app that runs on the PS4, while Carenina sat down to fix some of the more egregious soldering and woodworking mess that I had created for the Pi and on the wood I'd roughly cut out on which to mount the telegraph. I had snapped the microSD card that had our OS image after trying to push it into the pi slot with a little too much force, so I began with building a new Raspbian image.

On the UI side, Alex covered over the normal home screen with a black and white curly box motif reminiscent of old style silent movies. The interface needed to be dead simple and without even the basic buttons required for the Netflix app. He moved the title up to the top, and built a large search box which was the default focus target. After a bit of iteration, it started to look like this:

Mock Teleflix UI

Teleflix Alphanumeric Only Morse UI

I dropped my earlier code prototypes to create a non-blocking timer based version of the key decoder based on the gpiozero python module. After a little bit of timing practice, I realized that I needed about 100ms for a short keypress without bouncing the contacts too much, and a safety buffer before calling the keypress long enough for a dah. A professional could tap well above 5 words per minute and would prefer shorter dits and dahs, but at that point, my Morse was anything but professional; besides, we had only two minutes on stage, and I fully intended to use as much of that time as I could to correctly tap before optimizing for a lot of words.

After looking at the complexity of some of the C based GPIO open source code, I decided to just keep the loop tight in python with as few statements and function calls as I could manage. After the dust settles, we will clean the code up, and post it online for your perusal. Unfortunately, we had a much bigger problem in front of us - the contacts of the switch weren't meeting correctly, and the simple debounce code in the gpiozero module couldn't deal with the crazy voltage spikes coming off of the key when I would slap it down trying to enter a Morse sequence.

Oscilloscope capture of Switch Bounce

Bouncy Switch, from the Wikipedia

Carenina built a relatively straightforward analog RC debouncer on a prototype board based on a 100 microfarad capacitor, which reduced some of the bounce, but we were still getting a lot of spurious input from the key. With less than 6 hours left on the clock (if we wanted to get any sleep before the presentation), I went to the bible on software debouncing, Ganssle's guide. I wasted an hour or so trying to figure out why DebounceSwitch2 wasn't working with the base python numeric types before cutting my losses and switching to the simpler (but code heavier) DebounceSwitch1 program.

Bullseye, Ganssle delivered!

The dits and the dahs finally began to show up as proper dots and dashes on the terminal. All that left was to write the decoder tree!

Not wanting to trip over any licensing issues, I wrote down a text file of the Morse standard from memory (with only a couple mistakes), while Alex checked it. A few python and vim transforms later, we had a JSON file describing the standard. We spent a few minutes talking out possible algorithmic implementations to parse a stream of Morse symbols until we settled on the following:

  1. Build a binary tree with an empty start node. Go left on a dah, right on a dit.
  2. At each point, the decoder can either be on an invalid sequence, a valid sequence, or a completed sequence (a valid sequence where there's no other possible options for further input).
  3. Check the state:
    1. If the sequence is invalid, notify the main loop, and reset the decoder.
    2. If the sequence is completed or just valid, let the main loop know.
    3. Wait for the main loop to resolve the current character.
  4. The main loop could immediately resolve if the decoder was in the completed state, or it needed to set a timeout to wait for the next character; if that timer lapsed, then resolve the current character. With a single button, time is an important dimension.

The three of us paired (tripled?) the python code, and after some painful debugging, hit a working solution. Unless you whacked the key way out of alignment or accidentally closed the circuit with the swinging switch, the program could now properly interpret the input sequences, once you got used to its timing.

The decoder did need one more touch - Morse doesn't have a notion of navigating around a television interface, and we needed a way to jump from the top search area (where we typed our search query) down to the search suggestions (shown as boxart images). Alex made the UI assign four letters to the four possible search options, labeled A, B, C, and D, and dimmed the search box to show the focus change, but I still needed a way to bounce between the search and the suggestions without burning any letters.

Prosigns to the rescue!

Standard Morse is really just the basic letters and numbers that most beginners will need. Experts added a number of additional code combinations to deal with the more mundane details of sending addresses for telegraphs, such as punctuation, symbols, and flow control ("message done" / "erase message, error"). I learned two more symbols, one for period ("." - not a dit in the dit-dah sense, coded as ".-.-.-") and the other for comma ("," as a pause, coded as "--..--"). Alex mapped these to "focus change" (period having the "done" meaning on a sentence), and to backspace/delete (because comma is all else I knew). We planned to use better prosigns for this, but as the clock ticked closer and closer to midnight, we decided to scale back our reach goals, not the least of which was to get an edison bulb to flash out the movie subtitles.

Game Time

Early in the morning, we had the good fortune of speaking with some of our PR experts, Marlee Tart and Lexi Nisita. They introduced us to a reporter from Variety magazine, who was on site writing about the Netflix hackday. Alex and Carenina made some interesting remarks, while I sang the "Hackin' Dirty" theme song from our Nesflix hackday video. The rest of the day passed in a blur before we were wheeling our TV rack around the ramp and heading towards the stage for a quick HDMI check between the PS4 and the projector.

The hacks began in a torrent of awesome. I was most excited about looking at a repurposed, and reverse engineered, vending machine that allowed you to sign up for Netflix using cash and get a working account, aimed at parts of the world less oriented around credit cards, but the breadth of hacks was definitely impressive.

Hackday Morning

Early morning tweaks before the masses arrive

When Michael Spiegelman called us on stage, I was barely paying attention to where I was. Alex and Carenina had stepped up to the presentation challenge and absolutely rocked the audience with high Victorian oratory and I locked my eyes on a reflection of the gigantic projected Teleflix-on-PS4 UI behind me (always face the crowd!) and began tapping.

The room became distant, and the spotlights washed out the intent faces from the audience. I held my breath and lost everything in the room but the blinking i-beam staring back at me. Focus, you know this. What if the USB keyboard emulation driver doesn't detect correctly? Damn that race condition on setting the USB port up in time - DAMMIT, DID I PLUG THE PI IN BEFORE LETTING THE BOOT SCRIPT RUN?!?!!?

Not constructive. FOCUS!

DAAAAAAAAH DIT.

I ran low on oxygen.

Was that long enough on the DIT? Did the switch even close? DID I ALREADY RUIN THE HACK ON THE FIRST LETTER?!?!?!

The i-beam laughed at me insultingly, and I swore under my breath.

...until it moved.

"N"

IT DECODED!!!!!

Don't lose the sequence. If I delay too long, it'll insert a space and the search will be fooled!

DIT DAAAAH.

"A"

Keep going. You can do this.

DIT DAAAAAH (dammit, I never hold that long enough! Over accentuate!) DIT

I'd fouled that letter up right in front of the Variety reporter. Dammit, I can't even type a common character, why did I think I could ever do this?

"R"

YES! Steady. STEADY!

DAAAAH DIT DAAAAAAAAH DIT.

Watch those contacts, I always slide on that dah.

"C"

Almost there. Don't blow it, Alex and Carenina worked too hard. Become the code, you are not human any longer.

DAAAAAAAH DAAAAAAH DAAAAAAHH

Hold the dash. Not too short. You always get too fast on the last one.

"O"

Just one more. You're almost there, the search is already showing the suggestion. Don't look, you are the telegraph. Think brass thoughts. Just bring it home, finish the cry for help in the SOS.

DIT DIT DIT.

Alex was carrying on with abandon, a truly masterful performance. "---to which everyone in this room owes their jobs, the very BIRTH of communications, when---"

"YES," I shouted, completely forgetting myself. The cursed i-beam surrendered the final "S" and in 1,000 point bold font the word "NARCOS" screamed in all of its uppercase glory at the crowd.

The speech stopped. The crowd stared. The moment drew out. Idiot, you ruined his speech! "Sorry," I muttered.

The cheers were thunderous as the crowd exploded.

A Word of Thanks

As always, true greatness comes from the greatest people - Carenina and Alex, you're an inspiration, and when you can't run, you crawl, and when you can't do that anymore... well, you know the rest.

I would also like to thank both George and Bobby who helped with some of the crafting details when we were missing the right parts. Also to the team that reverse engineered the vending machine, Tony, Micah, Ryan, and Tyler, and were very generous in lending us some tools and screws when we realized we were underprovisioned.

On the software and hardware side, I'd also like to thank the Hardpass project for information on the USB HID scancodes and composite registration, to the Raspbian maintainers who did a fantastic job making an accessible and modifiable OS image, and to Adafruit who provided the helpful tutorials.

Cirino, Wolfe, and Motion Heavy Industries

Until next hackday, see you later!

Articles: