
In 2015, we threw a party called
Cyberdelia, celebrating the 20th anniversary of the movie
Hackers. It was epic: we built skate ramps and everything.
We did it again in
2016 and
2017,
2020!
Check out the photos of the parties:
2015,
2016,
2017 and
2020!
One of the props that I picked up to decorate DNA Lounge
at the first Cyberdelia in 2015 was an old payphone. It wasn't hooked up
for that first party, but just in time for the second party, I modified it to run Linux.
(It's still here, by the way. Come play with it in person the next time you are at DNA Lounge!)
When I was trying to decide what I wanted the phone to do,
"making phone calls" was obviously the least useful thing.
Nobody needs that: that's why payphones are extinct in the wild.
It's also why we no longer have
Internet kiosks.
So instead, when you pick up this phone, it "rings" and connects
you to a "voicemail" system. Press 1 to listen to our schedule of
upcoming events (the same message you hear when you call us at
415-626-1409); press 2 to listen to your saved messages; press
3 to record a message.
Here is the sordid tale of how I made a payphone run Linux.
I'm not so great at hardware hacks, and it shows. My bumbling
exists for your amusement.
Phase One: The Keypad
My first goal was to turn the telephone's keypad into a USB
keyboard. The plan: take a $5 USB numeric keypad; pull the
USB chip out of it; and hook that up to the phone's buttons.
Behind the buttons on the telephone keypad is the same sort of
design as exists inside most computer keyboards. The obvious way to
do this would be for there to be a switch for each key, attached to a
pin on the chip to tell it when the switch was pressed. But for a
keyboard, that would mean your chip would need 100+ inputs. So
instead, the way they work is, the keyboard is wired up in a grid:
there is a set of horizontal wires, and a set of vertical wires, and
pressing a key makes an electrical contact between one horizontal and
one vertical wire: so pressing 5 connects wires X and
B. The chip figures out which keys are pressed by polling
thousands of times a second: it squirts some electrons down W,
and if they come back on A, then 1 is pressed. Then it
squirts some electrons down X, and if they come back on
C, then 6 is pressed, and so on.
So for a simple keypad like this, it's a false economy, since this
chip needs 7 inputs instead of 12. But for a full-sized keyboard, you
reduce the number of inputs from about 100 to about 20. But, it
is what it is.
The logical electrical layout of the phone's keypad is exactly
what you'd expect: it's the drawing above. The logical layout of
the generic USB numeric keypad is a bit weirder.
Physical layout:
| |
| Tab | / | * | ← |
| 7 | 8 | 9 | - |
| 4 | 5 | 6 | + |
| 1 | 2 | 3 | Enter |
| 0 | 00 | . |
Logical layout:
| 11 | 4 | 3 | 2 | 1 |
| 10 | 1 | 2 | 3 | Enter | |
| 9 | 4 | 5 | 6 | 00 | ← |
| 8 | 7 | 8 | 9 | + | |
| 7 | | | - | | |
| 6 | Tab | / | * | | |
| 5 | | 0 | . | | |
It's like the Platonic ideal of a keyboard: you think you're
typing on the thing on the left, but that's just a shadow on the wall.
The real keyboard is the thing on the right.
The positive numbers line up properly, but there's no choice of
mappings here that will make the "*0#" row work. So, I
used the "T/*" row for that and I re-map the keys in software.
(The blank cells don't send any keys at all).
First I needed to get electrical connections out of the phone's
keypad. The key contacts are printed on one side of a two-sided
circuit board. I was able to use the "via" holes (where one side
of the board connects to the other) to connect in. That looked
like this:
That chip on the board is both the keyboard decoder and a DTMF
(touch-tone) generator: the output of this board is audio. So
that wasn't going to do me any good.
(Fun fact: the noises that telephone buttons make are chords of
two notes played simultaneously, and those notes are also
arranged in a grid just like the above, low frequencies vertically and
high frequencies horizontally. Other notes and chords did
other things on the old phone networks: one of the more
famous frequencies was 2600 Hz, and that's why both the Atari 2600
and the magazine are called that.
Fun fact two: Wozniak and Jobs funded Apple Computer by selling
blue boxes
that let you make phone calls for free.)
The next step was to connect those new wires to the inputs on
the USB keyboard chip. Easier said than done, because that keyboard
had a flat plastic membrane as its input: its socket accepted a
paper-thin piece of flexible plastic with contacts printed on it
that are 0.5mm wide and 1mm apart on center. I wasn't able to
find a breakout board that had a socket that small on it: I found
some blanks, but they required you to surface-mount the socket to
it (and I couldn't find the socket either). So I did it the hard
way: I manufactured a connector that would fit into that socket.
Yeah, those are sewing needles.
First: let me draw your attention to the plastic shim holding those
needles at 1mm spacing. Let me draw your attention to how hard it was
to manufacture that thing. Though I have 0.5mm drill bits, I had to
tape them to make them fit into the dremel at all without just
vanishing. And even though I have a drill-press stand for my dremel,
my first plan of drawing a pattern then lining up the drill and
dropping it was totally not working at all. So in the end, I just
eyeballed it: drop the drill; lift ever so slightly; give the
plastic the slighest kick with my fingernail; drop the drill again.
It only took me three tries to get it right!
Second: though those are the thinnest needles that I had in my
house at the time, they weren't thin enough to fit into the socket.
So I had to file them down. It was like I was making the world's
tiniest shivs. I turned the dremel sideways, put a sanding wheel on
it, and held the needles to it until they were flat and about 0.1mm
thick. I was doing this while wearing a 20x magnification monocle, so
all of this action was taking place with my face so close to the
spinny bit that I had to exercise great care to avoid sanding off the
tip of my nose.
When I described this to a friend he said, "So basically, you're
looked like some guy I have to find to send me on a side-quest in
Fallout." Pretty much.
Ok, remember that shim I was so proud of constructing? It didn't
really work. It was still too hard to get every pin to make contact
at the same time. So instead, I tore the top off of the jack and
jammed the pins into the underside of the contacts in it, then
used the tip of a needle to secure each with a drop of superglue.
Once everything was tested, I wrapped the whole mess of wires
inside some Sugru (this amazing
play doh-like stuff that dries into flexible rubber).
I did waste a few days trying to track down what I thought was
series of bad connections that actually turned out to be: to make some
keys work at all, you had to press really fucking hard.
I solved this problem by shimming with a thin piece of cardboard
between each key and the domes on the rubber membrane. I suspect this
phone would also have had that problem when it was still a phone.
Phase Two: The Handset
Fortunately, telephone speaker and microphone design has not
changed significantly since like, 1870 or something. Scoping out
the four wires coming from the telephone's handset was pretty
straightforward: one pair showed 150 Ω (that's the
speaker); one pair showed 200 kΩ (that's the mic).
If you're like me, and do these sorts of projects at 4am without
really planning ahead, you don't have, for example, a solderable TRRS
plug when you need one. So you take what's lying around, and tear
apart an old pre-made TRRS cable and solder a stacking header onto it.
What, soldering onto wires that are just slightly thicker than a
human hair? I'm getting used to it. Then a big old glob of epoxy
to finish it off.
Best iPhone headset ever.
Update, 2020: What I described above is wiring the handset's speaker
and microphone directly in to the pins on a TRRS jack and calling it a day.
As it turns out, that doesn't really work: it was noisy and unreliable.
The old-school telephone handset mics and modern mics (e.g., earbuds) are
substantially different electrically, so this fell into the category of "lucky
it worked at all, ever". The proper solution to this, which I did in my
2020 upgrade,
was to open up the handset and replace the old speaker and mic with their
modern equivalents. That also allowed me to include an audio amp to get
the volume up to nightclub levels.
Phase Three: The Computer
I got a
Raspberry Pi 2 B
to drive it, which is probably a more powerful computer than your
typical PC was at the time that this payphone was originally in
service.
The Pi has an audio output, but it's shit, and it's not powerful
enough to drive the speaker in this handset. But that doesn't matter
since the Pi does not have an audio input, meaning I needed a different
audio card anyway. I got a
Cirrus Logic,
which, conveniently enough, also has a TRRS headset input.
You know what this means, right? I voluntarily and of my
own free will recompiled my kernel in order to get my audio
card to work. I don't even know who I am any more. I don't even
know what year this is. "And you did this on your home phone?"
Because of course Raspbian doesn't come with the modules
pre-built. Of course it doesn't.
Anyway, after the usual jiggery-pokery, I had audio ouput via
aplay and audio input via arecord and all was right
with the world, except for the fact that I had hoped I would never
hear the word "ALSA" again in my life. (It's "Advanced", you know.
The "A" stands for "Advanced".)
So then I wrote some code: phonebooth.pl.
It sits there reading characters from the keyboard (the phone keypad)
and responding to them by playing MP3 files, or recording and saving
new ones. Most of the MP3 files are pre-recorded (generated by
the Makefile using the free online text-to-speech
service at voicerss.org
-- which doesn't do a fantastic job, but it's adequate). The infoline
is downloaded from the DNA Lounge web site every morning and re-converted
to an MP3 in the same way.
I launch it as a getty from /etc/inittab on console 0 so
that the machine boots up into the Perl script:
1:2345:respawn:su phonebooth -l -c 'cd ~jwz/src/phonebooth && ./phonebooth.pl'
(I write junk like this in Perl because I still just can't get
past Python's whitespace thing. I know, you kids today all think that
it's not a big deal, but I can't. I just can't.)
Phase Four: The Switch Hook
Now I just need to detect when the handset has been taken off hook
and hung up. This part should be easy, right? After all, the Pi 2
has about a zillion readable pins on it, and unlike the Arduino, they
even come with built-in software-configurable pull-up and pull-down
resistors, which saves a lot of soldering.
Well, no. Not so much. This next part took me days to figure out.
If you were me, you might assume that an audio card wouldn't
consume all forty pins on the Pi's GPIO header. Well, it
does... sorta...
The
manual is pretty unclear about whether any of the GPIO pins are
intended to be available for non-soundcard-related use, but in the
tables it does describe some of them as "EXP", which I interpreted
as "Expansion", which I further interpreted as "you can use these."
Also, the board has an "expansion header" (sometimes called a
"feature header") on it. It has 20 pins. The manual describes a
header that has 13 pins. Further, the 13 pins described in the manual
bear no relation to the first 13 pins, or any pins, on that 20 pin
header. What the hell, Cirrus.
Scoping out its continuity shows that the expansion header
corresponds to these pins on the Pi's header. This table is looking at
the board with the ethernet jack on the bottom left. The header is
not numbered and I won't hazard a guess as to which corner the
designers of this board thought of as "up".
| ? |
? |
? |
⏚ |
? |
? |
BCM 5 |
BCM 25 |
BCM 12 |
BCM 26 |
| 5v |
? |
? |
⏚ |
BCM 3 |
BCM 2 |
⏚ |
? |
BCM 6 |
BCM 16 |
So, just pick a pin, like BCM 26 let's say, and go to town, right?
Well, no. Not so much.
It turns out that if you try to use any of these pins, they
all behave as if they're "floating": you get random data, as if the
software was unable to attach a pull-up or pull-down resistor to them.
Or maybe it's not reading the pin at all but is reading something else
entirely. Who knows! The pins I tried worked properly when the
Cirrus card was not plugged in, but with it plugged in, they didn't
function.
So we're dead in the water, right? Time to spin the wheel and buy
a different audio card?
But I had an idea. Why is this card touching these pins at all?
Does it really need to? So I got out the needle-nose pliers
and very carefully bent one of the pins down, then plugged the audio
card back in, denying it access to that pin.
Pin 40: Audio card doesn't function. Bend it back and try again.
Pin 38: Audio card doesn't function. Bend it back and try again.
Pin 36: Audio card doesn't function. Bend it back and try again.
Pin 32: WE HAVE A WINNER.
Yup, I did that. I'm not proud. Or maybe I am. I can't tell.
I suppose it's possible that by doing this I have broken some
feature of this audio card, but since both playback and recording
work, it's not a feature that I use, so, so what!
Except the story didn't play out in quite so
straightforward a way as all that, because there were multiple
failures happening at the same time, as is my way, because I am
cursed.
It turned out that, on this Pi, with this version of Raspbian,
reading a pin state with Python worked great:
import RPi.GPIO as GPIO
import time
import sys
pin = 12
GPIO.setmode (GPIO.BCM)
GPIO.setup (pin, GPIO.IN, pull_up_down = GPIO.PUD_UP)
while True:
state = GPIO.input (pin)
if state != last:
if state == True:
print ('CLOSED')
else:
print ('OPEN')
last = state
sys.stdout.flush()
time.sleep(0.1)
But doing it from Perl didn't work at all. The pins
always acted like they were floating:
use Time::HiRes qw (sleep time);
use Device::BCM2835;
Device::BCM2835::init() || die;
Device::BCM2835::gpio_fsel (&Device::BCM2835::RPI_GPIO_P1_12,
&Device::BCM2835::BCM2835_GPIO_FSEL_INPT);
Device::BCM2835::gpio_set_pud (&Device::BCM2835::RPI_GPIO_P1_12,
&Device::BCM2835::BCM2835_GPIO_PUD_UP);
my $last = -1;
while (1) {
my $state = Device::BCM2835::gpio_lev (RPI_GPIO_P1_12);
if ($state != $last) {
print ($state ? "CLOSED\n" : "OPEN\n");
$state = $last;
}
sleep (0.1);
}
This was weird, because both of these libraries appear to be a thin
wrapper on top of the underlying bcm2835 library, which is written in
C. Ok, fine, let's do it in C:
#include <stdio.h>
#include <bcm2835.h>
#define PIN RPI_GPIO_P1_12
int main (int argc, char **argv) {
uint8_t last = ~0;
if (!bcm2835_init()) return 1;
bcm2835_gpio_fsel (PIN, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_set_pud (PIN, BCM2835_GPIO_PUD_UP);
while (1) {
uint8_t value = bcm2835_gpio_lev (PIN);
if (value != last)
printf (value ? "CLOSED\n" : "OPEN\n");
last = value;
delay (100);
}
return 0;
}
Oh hey, the C version didn't work either! What the hell was
Python doing under the covers that the Perl and C versions were not?
It turns out that they were using different pin numbering
schemes! Python (and all the documentation of anything ever) use
"BCP" numbers, but the C and Perl interfaces use Pi header pin numbers
instead. So changing the "12" to a "32" in the C code made it work.
Except!
Here's the next monumentally moronic thing that I discovered:
only root can read BPIO pins. And I don't mean "only users who have
read permission on /dev/mem", I mean literally "only root".
So either you have to run everything as root, or your Perl or Python
program has to call out to a C helper program that is setuid, just
to read the pins. (Said program being the one above, that simply loops, printing a line every time the pin state changes.)
(You might be thinking, "Well why not just run everything as root, then? So what?" If you are thinking that, then the Internet of Things, and by "Things" I mean "Moldovan Botnets", thanks you in advance for your complicity.)
Phase Five: Profit
I kind of wanted to make use of a few more pins, to do things like
detect when a coin was inserted (at least to have it say "Thank you,
and remember to tip your bartenders!") or to be able to ring the
physical bell, but after my pin-bending hijinks on the audio card, I
figured I had better quit while I was ahead. Maybe the next pin I
bend blows up the board.
And here it is, the complete "payphone", before being re-inserted
into its massive cast-iron chassis:
In conclusion, Hack the Planet.
Post Script: 2018
It has been two and a half years since I built and installed this
payphone, and I've watched many people interact with it over the years.
I am sad to report that it is... underappreciated. Nearly every night,
I've watched the following drama play out:
Someone says to their friend, "Oooh, neat! Take a picture of me!"
They pick up the handset, place it to their ear, and mug for the
camera.
The phone rings once, and the person PANICS and
slams down the handset and runs away.
They don't even stay on the line long enough to hear the menu
of options! Honestly, I don't get it. How can you be so incurious
as to not wonder what was going to happen next? With that kind of
attitude, how are you ever going to get sent on that side-quest?
People only record messages on it about once every few months,
and half of those sound to be from accidental button-mashing.
Even of those people who listen to the menu, I don't think most
of them even listen long enough to realize that pressing 4 will
lead to a deadpan robotic voice telling them a random "walks into
a bar" joke.
There are easter eggs, too. Forever undiscovered. Nobody even
tries to make it to a barrel roll.
Anyway, the fact that the payphone is appreciated almost exclusively
as a selfie-accessory and is barely acknowledged as an electronic toy has
sapped my enthusiasm for the other ideas I had for it, like rigging up
the physical bell, or the coin slot, or adding interactive lights or
a display of some kind.
It has amassed quite a nice collection of
stickers, though!
Planet, Hacking Thereof:
Regarding Cyberdelia, our Hackers party that was the
inspiration for this project: we'd love to continue throwing that party once a
year or so, but we had to skip it in 2018 because we were unable to find a
sponsor for it. The party has a lot of expensive overhead, and in 2017 we were
able to cover costs by making it be someone's official RSA / B-Sides after
party. That went great!
But in 2018, sadly, that company dropped out. Their lawyers told they that
they can't sponsor anything that involves alcohol, so their after-party had to
be held somewhere that you can't get a drink at all. (Yeah, I don't get it
either.)
If you love Hackers as much as we do and you'd like to
see Cyberdelia happen again, see if you can get your corporate
overlords to kick in some sponsorship money the next time you're in town for a
trade show or product release or something.
Eclectic events like this are what we specialize in at DNA Lounge, but
while such things make great art, they rarely make money. If you'd like to
help us keep doing this sort of thing, please join
the DNA Lounge Patreon!
Post Post Script: 2020:
We were able to bring back Cyberdelia one more time, in 2020 for the
25th
anniversary of Hackers! And it was
epic.
I largely rebuilt the payphone's innards for that. The basic design
remained the same, except that I re-built the handset, and used different
audio hardware. A few details are on my
2020 blog
post about it.
Also we have
Cyberdelia posters for sale!
Join our Patreon,
and hack the planet.
Post Post Post Script: March 261st, 2020:
The keypad had gotten a little flaky: stuck keys and such. I tried
cleaning and adjusting it with little luck, so I just ordered a new one
from payphone.com, and while I was at
it, I replaced the USB controller. Rather than jumping through the hoops
necessary to Frankenstein the microcontroller from an existing USB keypad onto
it, I built a new controller using a
Teensy 2.0.
It was trivial to
drive it
with a combination of the
Arduino USB Keyboard library and the
Adafruit keypad matrix decoder library.