My payphone runs Linux now.

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:

Logical layout:


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: 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 -- 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 && ./'

(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.

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')
          print ('OPEN')
        last = state

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::gpio_set_pud (&Device::BCM2835::RPI_GPIO_P1_12,
    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.


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, 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.

Tags: , , , , , ,