Connect a Wyse Terminal to a Raspberry Pi

I love old terminals. I love amber and green phosphors. It just sort of feels like doing real computing. Unfortunately these days, you don’t see a lot of terminals in day-to-day life, but they are available for sale on the second-hand market.

If you’re lucky to snag an old Wyse terminal, don’t get intimidated! They are fairly easy to connect to modern computers, even Raspberry Pis.

Below I will show two ways to connect a terminal to a Raspberry Pi 3: through the UART or via USB adapter.

A Wyse terminal displaying cowsay.

A Wyse terminal displaying cowsay.

Before You Begin

So you have a terminal and a Raspberry Pi, what next? I’ll first assume that you have a power cord for the terminal and all the standard supplies for the Raspberry Pi (power cord, microSD card, etc.) and otherwise have the Raspberry Pi booting an operating system like Raspbian. I’ll also assume that you have a keyboard for the terminal so you can use it… as a terminal. Note that most (if not all) Wyse terminals appear to use RJ-9 connectors for their keyboards, and you likely cannot adapt another keyboard connector to use on the terminal. Even if you are somehow able to “hijack” the session between your Raspberry Pi and terminal to get something on the screen without having to input from the terminal itself, you will probably have to go through some configuration on the terminal itself (and that requires the keyboard).

Configuring the Terminal

Before we hook the terminal up to a Raspberry Pi, we need to check the terminal’s configuration to make sure it can effectively communicate with the host. With only the power cord and keyboard plugged into the terminal, you should be able to turn it on and wait a few seconds for it to warm up. When the display loads, hold down [Shift]+[Setup]. Depending on your keyboard, you may not have a [Setup} key, but try the key(s) at the top right corner of the keyboard. You should be presented with a configuration menu.

Press the [F2] key to go to General settings and use your arrow keys to navigate to the Personality setting. Use the [Space] key to cycle through personalities until you find VT 100.

General settings.

General settings.

Now, press the [F5] key to get to the Ports settings. Use the arrow keys and [Space] key in a similar manor to set MDM Baud Rate to 9600, MDM Data/Parity to 8/NONE, MDM Stop Bits to 1, and finally Host Port to MODEM PORT.

Ports settings.

Ports settings.

These settings should work, fairly well, but I am in no way saying that these will give you the fastest connection. You may want to go through all of the settings menus to see if there is anything else you would like to configure like cursor blink and stuff like that.

Save the settings.

Save the settings.

When you are done, press the [F12] key to get to the Exit screen and use the [Space] button to toggle the Save option to YES. Finally, press [F12] again to save the settings.

Now, turn off the terminal so we can work on getting the Raspberry Pi ready.

Using a USB Serial Adapter

Perhaps the easiest way to connect your terminal to your Raspberry Pi is through the use of a USB to serial adapter. I ordered the inexpensive Sabrent USB 2.0 to Serial (9-Pin) DB-9 RS-232 Converter Cable from Amazon which gets the job done.

The USB Serial adapter plugged into the Pi.

The USB Serial adapter plugged into the Pi.

After you plug the adapter into your Raspberry Pi, you will still need to configure the Pi to allow console incoming console connections from the device.

First, check to make sure your Pi recognizes the adapter:

$ ls /dev/*USB*

Now, we can leverage getty to manage the adapter and provide a console to the connecting terminal:

$ sudo systemctl enable serial-getty@ttyUSB0.service

This is really all there is to it, and this will start automatically on system boot. If you are using a different type of terminal (like a VT100), the default serial-getty service may not work for you and you will have to modify serial-getty@.service. However, the service seems to work just fine for me by default using the stock ExecStart string:

$ cat /lib/systemd/system/serial-getty@.service | grep 'ExecStart'
ExecStart=-/sbin/agetty -o '-p -- \\u' --keep-baud 115200,38400,9600 %I $TERM

Using a USB adapter sets up the Raspberry Pi as a DTE (Data Terminal Equipment) while the terminal itself is hard-wired to be a DTE. Because of this, we will need to use a null modem adapter between the two devices to allow them to communicate. As you may have also noticed, the USB adapter provides a DB9 connector while the terminal has only DB25 ports. We will need to get a cable to connect the two together, and you can easily purchase an all-in-one cable with db9/db25 conversion and null modem adapter built in like the 6ft Null Modem DB9F/DB25F Molded Cable from Monoprice. Any other combination of DB9 cable, null modem, and DB9/DB25 converter should also do the trick.

DB9/DB25 null modem cable.

DB9/DB25 null modem cable.

Connecting the the Pi to the terminal is as easy as connecting the DB9 end of the cable into the USB adapter and the DB25 end into the Modem port on the terminal (the one on the left, not the printer port). Now, you can start up the terminal. If you are not immediately presented with a login prompt, press a key on the keyboard and the screen should spring to life.

Use the Modem port on the back of the terminal.

Use the Modem port on the back of the terminal.

Using the UART

For whatever reason, I like to keep my USB ports free and like to connect a terminal to my Raspberry Pi through the UART pins. While the Raspberry Pi provides TTL at 3.3v, the terminal can only speak RS232 at 12v. To get around this, we need a little convertor board that sits between the Raspberry Pi and terminal. After a little research, I purchased the NulSom Inc. Ultra Compact RS232 to TTL Converter with Male DB9 from Amazon to serve this purpose. From the light reading I did, converters with a MAX3232 chip on them outperform older MAX232 chips. You can probably get away with a cheaper converter based on the MAX232 (though I haven’t tested it personally) but the newer MAX3232 chips are now being used in convertors that sell for a similar price. As long as your convertor can handle 3.3v, it should be safe to use with the Raspberry Pi.

This converter that I bought requires you to solder your own header pins, which are relatively easy to get and easy to install. Cheap ones from Amazon should work fine, and while you only need four pins you will likely need to buy a pack with a lot more. To connect between the soldered pins on the convertor and your Raspberry Pi, you will also need four female-to-female jumper wires like these from Amazon.

The UART/RS232 convertor with pins soldered.

The UART/RS232 convertor with pins soldered.

Now we need to configure the Raspberry Pi to enable UART, this is as simple as making sure enable_uart=1 is present in /boot/config.txt:

$ cat /boot/config.txt
# For more options and information see
# Some settings may impact device functionality. See link above for details

If it isn’t already there, add it and sudo reboot the Pi.

Next, we can check if there is already a getty service running for UART to allow console connections. On the Raspberry Pi 3, the standard /dev/ttyAMA0 device is now used for Bluetooth which is a departure from previous models where it was used for communication via GPIO pins. We do however have a mini UART device (/dev/ttyS0) available which is generally seen as less-featured but perfectly fine for console connections.

When I checked my Pi, there was already a getty running for the mini UART:

$ sudo systemctl status serial-getty@ttyS0.service
● serial-getty@ttyS0.service - Serial Getty on ttyS0
   Loaded: loaded (/lib/systemd/system/serial-getty@.service; enabled-runtime; vendor preset: enabled)
   Active: active (running) since Thu 2020-07-30 00:17:17 EDT; 13h ago
     Docs: man:agetty(8)
 Main PID: 542 (agetty)
    Tasks: 1 (limit: 2200)
   Memory: 140.0K
   CGroup: /system.slice/system-serial\x2dgetty.slice/serial-getty@ttyS0.service
           └─542 /sbin/agetty -o -p -- \u --keep-baud 115200,38400,9600 ttyS0 vt220

Jul 30 00:17:17 raspberrypi systemd[1]: Started Serial Getty on ttyS0.

If you get a message that your service is disabled, it is easy to enable and start:

$ sudo systemctl enable serial-getty@ttyS0.service
$ sudo systemctl start serial-getty@ttyS0.service

This will not only start getty to monitor connections via mini UART, but also automatically launch this service every time the Raspberry Pi is rebooted. As I mentioned in the USB adapter section of this post, if you are using a different type of terminal besides Wyse, you may have to update the serial-getty service to get things working properly.

Your convertor should have four pins on it: GND, VCC, RX, and TX. Now, with the Pi off, use the jumper wires to connect the 3.3v pin on the Raspberry Pi to the VCC pin on the convertor, and also connect up the GNDs. Then, connect the Pi’s TX pin to the convertor’s RX pin and the Pi’s RX pin to the convertor’s TX pin.

     Edge of pi (furthest from you)                                                     
   L           GND TX  RX                                                               
   E            |   |   |                                                               
   F +---------------------------------------------------------------------------------+
   T |  x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x  |
     |  x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x  |
   E +--^------------------------------------------------------------------------------+
   D    |                                                                              |
   G   3.3V (VCC)                                                                       
     Body of Pi (closest to you)                                                        

Raspberry Pi 3 pinout, via

The convertor connected to the RPi.

The convertor connected to the RPi.

Again, Using this convertor sets up the Raspberry Pi as a DTE (Data Terminal Equipment) while the terminal itself is hard-wired to be a DTE. Because of this, we will need to use a null modem adapter between the two devices to allow them to communicate. As you may have also noticed, the convertor provides a DB9 connector while the terminal has only DB25 ports. We will need to get a cable to connect the two together, and you can easily purchase an all-in-one cable with db9/db25 conversion and null modem adapter built in like the 6ft Null Modem DB9F/DB25F Molded Cable from Monoprice. Any other combination of DB9 cable, null modem, and DB9/DB25 converter should also do the trick.

DB9/DB25 null modem cable.

DB9/DB25 null modem cable.

Connecting the the Pi to the terminal is as easy as connecting the DB9 end of the cable into the convertor and the DB25 end into the Modem port on the terminal (the one on the left, not the printer port).

Use the Modem port on the back of the terminal.

Use the Modem port on the back of the terminal.

Now you can power on the Pi and give it a few seconds to boot up. Next, you can start up the terminal. If you are not immediately presented with a login prompt, press a key on the keyboard and the screen should spring to life.



Corebooting the ThinkPad X230 with Skulls

I had been looking for an inexpensive-but-capable laptop that I could take to conventions/meetups in place of my much larger (and heavier) Clevo laptop that usually took up way too much space in my bag. I’m not editing videos or gaming at these places (or otherwise anymore, really), and the most I’m probably doing is taking notes, browsing the web, presenting a slideshow, or running some lightweight console-based software. I certainly don’t need a high-end machine.

Additionally, I wanted a machine I didn’t have to worry about as much. My Clevo is an expensive workhorse, and I can have quite a bit of data sitting on it at any given time. It wouldn’t be the end of the world if the machine was damaged, but it would be a nuisance. With an inexpensive machine, I’m less bothered about someone stealing it or having it damaged when I’m out and about.

I decided to take a look at older ThinkPad models, as they are something of the golden standard in terms of durable, portable workstations these days. I decided to go a bit older and initially looked at X200 models as I knew they were inexpensive and reasonably upgradable, but strangely they seem to have taken a price hike over the past few months where they cost 2-3 times as much as they did last year. I decided to settle on the slightly newer X230 model that featured better specifications when compared to the X200, but was about the same price on the second-hand market. I was able to secure a unit with an Intel i5-3230m@2.6GHz and 8 GB of RAM for somewhere between $100-$150.

ThinkPads are generally Linux-friendly, but before dealing with a fresh operating system install, I ordered an SSD (a Samsung SSD 860 EVO 250GB) to replace the stock hard disk. Installation of the SSD took about two minutes, and then I went ahead and and installed Debian to the drive with full disk encryption and Xfce as a lightweight desktop environment to reduce the potential of sluggishness.

The Majestic ThinkPad X230

The majestic ThinkPad X230.

Getting WiFi to Work

I performed the installation with the help of a wired LAN connection because the X230’s WiFi card uses non-free firmware. After the OS installation was finished up and I could log into the system, this was easily rectified. Make sure you add non-free to each line in /etc/apt/sources.list:

# cat /etc/apt/sources.list

deb buster main non-free
deb-src buster main non-free

deb buster/updates main non-free
deb-src buster/updates main non-free

# buster-updates, previously known as 'volatile'
deb buster-updates main non-free
deb-src buster-updates main non-free

Then, (as root or using sudo) install firmware-iwlwifi:

# apt update
# apt install -y firmware-iwlwifi

What is Coreboot?

Part of my interest when originally pursuing an X200 was the possibility of installing the Libreboot firmware, a free (as in speech) BIOS/UEFI replacement that doesn’t have any binary blobs or backdoors. I’m a freedom-loving computer user with a penchant for privacy, but unfortunately Libreboot isn’t available for the X230.

Luckily, I learned about coreboot, which happens to be Libreboot’s main upstream provider. coreboot still relies on some binary blobs, but aims to be an open-source project that boasts speed and security through alternative firmware.

I decided I’d give it a try using Skulls, a project that creates pre-built coreboot images that are easy to flash. coreboot isn’t something that can just be installed via the host operating system, you have to physically open the machine up and flash 1-2 chips soldered to the machine’s motherboard. Luckily this can be done with some inexpensive hardware that I will outline below.

Before You Flash

While you cannot flash the firmware in the host operating system, you can use it do perform a check to make sure the existing firmware is in a state that CAN be flashed.

On your host operating system, run the following to install dmidecode and print out relevant system information:

$ sudo apt-get install dmidecode
$ git clone
$ cd skulls/x230
$ sudo ./

If all goes well, you won’t get a message that says “The installed original BIOS is very old.” which would indicate that you probably want to perform an update to the existing Lenovo BIOS before proceeding with coreboot. If you do get this message, the Skulls guide has outlined some steps you can take to perform this update.

Necessary Hardware

There are a few different ways to install coreboot onto a ThinkPad x230 as outlined in the Skulls documentation, but I will run through my setup below as I know it works.

One thing I had to buy was a Pomona SOIC9 Test Clip 5250 (though I have heard clones from AliExpress work pretty well). This will actually clip onto the chips on the motherboard so you don’t need to desolder them. You will connect this clip to a flashing device via some female-to-female jumper wires like these before attaching to a chip, but we’ll get to that part soon.

My main flashing device was a Raspberry Pi 3B (with power cord, microSD card, and Ethernet connection). I installed the latest Raspbian on it, booted it up, and then prepared it for flashing with a few simple steps.

First, we will need to make sure UART is enabled, so drop into a root shell and modify /boot/config.txt to be sure the following options are set:


Next, modify /etc/modules to make sure the following modules will be loaded:


Now we will install flashrom and get the latest Skulls release ready for flashing (check here for fresh release links to wget):

# apt-get install -y flashrom
# wget
# tar -xf skulls-x230-*.tar.xz

Finally, shut down the Pi:

# shutdown -h now

At this point the Raspberry Pi should be off, so unplug its power cord before proceeding to the next step.

Opening the X230

The X230 is surprisingly easy to open up, requiring the removal of just a handful of screws. Make sure that the X230 is disconnected from its charger, and then flip it over so the bottom of the laptop faces up. Now, remove the battery and get your crosshead screwdriver ready.

Remove the following seven screws and put them in a safe place.

Screw removal guide

Screw removal guide via

Next, flip the laptop back over and open it up (as though you were going to use it). The screws taken out will allow the keyboard and palm rest to come out, so we will take them out in that order.

Gently slide the keyboard toward the screen (you may want to get your fingernail under the bottom edge) and flip it up against the screen being careful of the ribbon cable attaching it to the motherboard. It isn’t necessary to disconnect the keyboard as it can be angled out of the way as-is. Then, pop out the palm rest and again gently move it up near where they keyboard had been sitting (again minding the connector cable).

You should notice a black film on the motherboard where the palm rest was. Peel up the bottom left corner of this film to reveal two chips, and tape the corner of the film down so it is out of the way as shown below.

The two chips exposed!

The two chips exposed!

Connecting the X230 and RPi

Now it is time to wire the clip to the Raspberry Pi 3. Below are two diagrams: one for the chips and one for the Raspberry Pi header. Making sure you keep track of your clip’s orientation, connect the pins from the clip to the pins on the header as indicated. Note: If you are using a different model of Raspberry Pi, your pinout might be different so be sure to verify the chip pinout against a pinout from your Pi’s model.

     Edge of pi (furthest from you)
   L           GND TX  RX                           CS
   E            |   |   |                           |
   F +---------------------------------------------------------------------------------+
   T |  x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x  |
     |  x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x  |
   E +----------------------------------^---^---^---^-------------------------------^--+
   D                                    |   |   |   |                               |
   G                                   3.3V MOSIMISO|                              GND
   E                                 (VCC)         CLK
     Body of Pi (closest to you)

Raspberry Pi 3 pinout, via


Now we can start the flashing process! Flashing the bottom chip is optional, but does have two main benefits: it allows you to do subsequent coreboot flashes through software (without needing to disassemble the machine) and it also disables functionality of Intel’s Management Engine which can be a security concern otherwise. I opted to flash the bottom chip, so after I ensured that my clip was wired to the Raspberry Pi properly, I attached my clip to the bottom chip and gave it a wiggle to ensure a snug connection.

Next, I powered up the Raspberry Pi and logged into it so I could navigate to the Skulls release I downloaded earlier:

# cd skulls-x230-*

Now we perform the flash on the bottom chip (and create a backup) using one command below. This can take around 10 minutes, so be patient (and make sure you don’t touch anything!):

# ./ -m -k bottom.bak
Skulls for the X230

Please select the hardware you use:
1) Raspberry Pi
2) CH341A
3) Exit
Please select the hardware flasher: 1
Ok. Run this on a Rasperry Pi.
trying to detect the chip...
Detected MX25L6406E/MX25L6408E.
make: Entering directory '/root/skulls-x230-0.1.9/util/ifdtool'
gcc -O2 -g -Wall -Wextra -Wmissing-prototypes -Werror -I../commonlib/include -c -o ifdtool.o ifdtool.c
gcc -o ifdtool ifdtool.o
make: Leaving directory '/root/skulls-x230-0.1.9/util/ifdtool'
Intel ME will be cleaned.
The flash ROM will be unlocked.
Start reading 2 times. Please be patient...
flashrom  on Linux 4.19.118-v7+ (armv7l)
flashrom is free software, get the source code at

Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L6406E/MX25L6408E" (8192 kB, SPI) on linux_spi.
Reading flash... done.
flashrom  on Linux 4.19.118-v7+ (armv7l)
flashrom is free software, get the source code at

Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L6406E/MX25L6408E" (8192 kB, SPI) on linux_spi.
Reading flash... done.
current image saved as bottom.bak
connection ok
start unlocking ...
Full image detected
The ME/TXE region goes from 0x3000 to 0x500000
Found FPT header at 0x3010
Found 23 partition(s)
Found FTPR header: FTPR partition spans from 0x180000 to 0x24a000
ME/TXE firmware version
Public key match: Intel ME, firmware versions 7.x.x.x, 8.x.x.x
The AltMeDisable bit is NOT SET
Reading partitions list...
 ???? (0x000003c0 - 0x000000400, 0x00000040 total bytes): removed
 FOVD (0x00000400 - 0x000001000, 0x00000c00 total bytes): removed
 MDES (0x00001000 - 0x000002000, 0x00001000 total bytes): removed
 FCRS (0x00002000 - 0x000003000, 0x00001000 total bytes): removed
 EFFS (0x00003000 - 0x0000df000, 0x000dc000 total bytes): removed
 BIAL (NVRAM partition, no data, 0x0000add0 total bytes): nothing to remove
 BIEL (NVRAM partition, no data, 0x00003000 total bytes): nothing to remove
 BIIS (NVRAM partition, no data, 0x00036000 total bytes): nothing to remove
 NVCL (NVRAM partition, no data, 0x00010511 total bytes): nothing to remove
 NVCM (NVRAM partition, no data, 0x0000493f total bytes): nothing to remove
 NVCP (NVRAM partition, no data, 0x0000a553 total bytes): nothing to remove
 NVJC (NVRAM partition, no data, 0x00004000 total bytes): nothing to remove
 NVKR (NVRAM partition, no data, 0x0001257d total bytes): nothing to remove
 NVOS (NVRAM partition, no data, 0x00034af5 total bytes): nothing to remove
 NVSH (NVRAM partition, no data, 0x00007609 total bytes): nothing to remove
 NVTD (NVRAM partition, no data, 0x00001eac total bytes): nothing to remove
 PLDM (NVRAM partition, no data, 0x0000a000 total bytes): nothing to remove
 GLUT (0x000df000 - 0x0000e3000, 0x00004000 total bytes): removed
 LOCL (0x000e3000 - 0x0000e7000, 0x00004000 total bytes): removed
 WCOD (0x000e7000 - 0x000140000, 0x00059000 total bytes): removed
 MDMV (0x00140000 - 0x000180000, 0x00040000 total bytes): removed
 FTPR (0x00180000 - 0x00024a000, 0x000ca000 total bytes): NOT removed
 NFTP (0x0024a000 - 0x0004a4000, 0x0025a000 total bytes): removed
Removing partition entries in FPT...
Removing EFFS presence flag...
Correcting checksum (0x7b)...
Reading FTPR modules list...
 UPDATE           (LZMA   , 0x1cc508 - 0x1cc6c6       ): removed
 ROMP             (Huffman, fragmented data, ~2 KiB   ): NOT removed, essential
 BUP              (Huffman, fragmented data, ~56 KiB  ): NOT removed, essential
 KERNEL           (Huffman, fragmented data, ~135 KiB ): removed
 POLICY           (Huffman, fragmented data, ~91 KiB  ): removed
 HOSTCOMM         (LZMA   , 0x1cc6c6 - 0x1d343f       ): removed
 RSA              (LZMA   , 0x1d343f - 0x1d872a       ): removed
 CLS              (LZMA   , 0x1d872a - 0x1ddec0       ): removed
 TDT              (LZMA   , 0x1ddec0 - 0x1e45be       ): removed
 FTCS             (Huffman, fragmented data, ~18 KiB  ): removed
 ClsPriv          (LZMA   , 0x1e45be - 0x1e499f       ): removed
 SESSMGR          (LZMA   , 0x1e499f - 0x1f32cb       ): removed
The ME minimum size should be 1667072 bytes (0x197000 bytes)
The ME region can be reduced up to:
 00003000:00199fff me
Setting the AltMeDisable bit in PCHSTRP10 to disable Intel ME...
Removing ME/TXE R/W access to the other flash regions...
Checking the FTPR RSA signature... VALID
Done! Good luck!
File /tmp/tmp.ob4vOGD0XY/work.rom is 8388608 bytes
Writing new image to /tmp/tmp.ob4vOGD0XY/
ifdtool and me_cleaner ok
make: Entering directory '/root/skulls-x230-0.1.9/util/ifdtool'
rm -f ifdtool *.o *~ .dependencies
make: Leaving directory '/root/skulls-x230-0.1.9/util/ifdtool'
start writing...
flashrom  on Linux 4.19.118-v7+ (armv7l)
flashrom is free software, get the source code at

Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L6406E/MX25L6408E" (8192 kB, SPI) on linux_spi.
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.

After it completes, we can move on to the top chip. I like to power down the Raspberry Pi before disconnecting/connecting the clip, so I will do that now by issuing a shutdown -h now. I don’t know if this is necessary, but feels safer than potentially applying power to the wrong pins of the chip if I connect the clip incorrectly.

With the top chip connected, we can switch the Raspberry Pi on and get back into our Skulls working directory. Here, there is a choice between which firmware image you want to flash, as one includes the proprietary VGA BIOS and the other is completely free software. There are pros and cons for each choice so I recommend you do your research here before selecting which image is right for you, I decided to use the image with proprietary code.

Now it’s time to flash the top chip, again also creating a backup:

# cd skulls-x230-*
# ls *top.rom
# ./ -k top.bak
1) ./x230_coreboot_seabios_free_4bd6927388_top.rom
2) ./x230_coreboot_seabios_4bd6927388_top.rom
3) Quit
Please select a file to flash or start with the -i option to use a different one: 2
Please select the hardware you use:
1) Raspberry Pi
2) CH341A
3) Quit
Please select the hardware flasher: 1
trying to detect the chip...
Detected MX25L3206E/MX25L3208E.
verifying SPI connection by reading 2 times. please wait.
flashrom  on Linux 4.19.118-v7+ (armv7l)
flashrom is free software, get the source code at

Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L3206E/MX25L3208E" (4096 kB, SPI) on linux_spi.
Reading flash... done.
flashrom  on Linux 4.19.118-v7+ (armv7l)
flashrom is free software, get the source code at

Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L3206E/MX25L3208E" (4096 kB, SPI) on linux_spi.
Reading flash... done.
current image saved as top.bak
connection ok. flashing x230_coreboot_seabios_4bd6927388_top.rom
flashrom  on Linux 4.19.118-v7+ (armv7l)
flashrom is free software, get the source code at

Using clock_gettime for delay loops (clk_id: 1, resolution: 1ns).
Found Macronix flash chip "MX25L3206E/MX25L3208E" (4096 kB, SPI) on linux_spi.
Reading old flash chip contents... done.
Erasing and writing flash chip... Erase/write done.
Verifying flash... VERIFIED.
Flashing in progress!

Flashing in progress!

That’s it, you are now all flashed! You can reassemble your X230 and boot it up to make sure it all work. If you used the image with proprietary VGA BIOS, you will also get a new splash screen.

Skulls is installed!

Skulls is installed!


Now that coreboot is installed, you have a more free system that not only increases your security and privacy, but also does some nifty things like remove the WiFi card whitelist so you can use a more modern and capable wireless card for increased performance.

In the future, I will take a look at small upgrades and modifications I can continue to do to the X230.

In the meantime, make sure you keep your chip backup files in a safe place!



NODE Vol 02

A little late to share this news here, but the second issue of the NODE zine is out now for free download and physical order. I once again served as editor and a writer on this volume.

NODE Vol 02

From the site:

We’ve taken all the feedback we received on Volume 1, and created an 180 page handbook for the future. This issue is packed with hardware projects like the OpenBook, The Reform 2 laptop, Librerouter, as well as staples like the new Mini Server, and Zero Terminal handheld computer.

There are tonnes of in-depth articles and interviews covering some of the leading P2P projects out there, including IPFS, Iris, Cabal, Secure Scuttlebutt, and lots more. Returning is an everexpanding Open Source Directory, and a new Meshnet Atlas.

See you all for Vol 03!


How to Join NPSTN, a VoIP Network for Telephone Collectors

NPSTN (standing for NoveltyPSTN or NostalgicPSTN, see is a hobbyist VoIP-based phone network for phone collectors and phreaks. While similar to the popular C*NET (or Collector’s Network,, NPSTN is considered more phreak-friendly than C*NET (dig out your blue box, seriously), but also has a handful of other differences like using conventional 7-digit telephone numbers, and offering both payphone trunks and MF trunks. Both networks are home to people looking to hook up vintage telephone hardware like payphones, teletypes, and even electromechanical switches, but NPSTN seemed more interesting to me and I decided to pursue that first.

The network functions fairly simply, allowing for each operator to run Asterisk phone switch software on a machine of their choosing to create their own Central Office. To avoid number collisions, operators register directly with NPSTN to reserve their own office code and get a “thousands-block” of numbers (such as 123-4XXX, where the operator chooses the first four digits). When an operator (or anyone connected to the operator’s PBX) places a call to a phone number outside of their office code, it is routed over the NPSTN network using the IAX2 protocol to get to the proper office (another machine running Asterisk for the office code dialed) where it is handed to the proper local extension.

While this is an imperfect metaphor, the NPSTN network mirrors a lot of the functionality behind how the traditional telephone network used to operate, albeit with modern hardware and software. Note that NPSTN does not let you dial out to the traditional PSTN by default; you won’t be able to call your friend’s cell phone or anything like that unless you configure your own egress. Think of NPSTN like a darknet for telephony where people connect interesting hardware to experiment with and allow others on the network to use.

If you want to read more about NPSTN, they have thorough documentation available here, Below, I’ve created something of a quick-start guide for joining the network and getting running with minimal effort.

Joining NPSTN

The first step you will need to take to join the network is to register with NPSTN and reserve an office code. Simply fill out the form here,, which should be fairly straight-forward. Two fields that may not seem completely obvious are Protocol and IAX2/SIP Username, where you should be safe to use IAX2 and npstn respectively. Something that might throw you through a loop is the IP Address or FQDN (fully qualified domain name) field for your machine running Asterisk. While many people on NPSTN choose to host their exchange on a VPS or dedicated server in a datacenter somewhere with a static IP address, I host mine on a Raspberry Pi at home. Due to this, my machine will be accessible via a dynamic IP address that could change periodically so I have a script to update a FQDN whenever my IP address changes and I use this FQDN with NPSTN. If you don’t own a domain, you can accomplish the same task with a dynamic DNS service like FreeDNS,

Installing and Configuring Asterisk

After submitting your request, you will receive a UCP (User Control Panel) Key, so until that happens we can get a machine running Asterisk set up. As I mentioned earlier, I run my exchange off of an older Raspberry Pi–you really don’t need a powerful or new machine to run Asterisk. For the below steps, I am going to assume you are running a Debian-based system.

First, let’s get a root shell:

$ sudo -i

Now let’s install ntp and set out timezone. Note that this is the timezone of where the machine is, not necessarily the timezone of where you are.

# apt install -y ntp
# timedatectl set-timezone America/New_York

Next, we will download and install asterisk. NPSTN recommends version 13, which is still supported by Asterisk.

# cd /tmp
# wget
# tar -xzf asterisk-13-current.tar.gz
# cd asterisk-13.*/
# ./configure
# make && make install
# make samples

Now that Asterisk is installed and set up with some basic configuration files, we will go ahead and replace necessary configuration and audio files using NPSTN’s boilerplate samples:

# cd /etc/asterisk/
# rm iax.conf
# rm sip.conf
# rm musiconhold.conf
# rm extensions.conf
# wget
# wget
# wget
# wget
# wget
# cd /var/lib/asterisk/sounds/en/
# mkdir custom
# cd custom
# mkdir signal
# cd signal
# wget
# wget
# wget
# wget
# wget
# cd /var/lib/asterisk/moh/
# mkdir ringback
# cd ringback
# wget

Pretty easy so far!

Configuring the Dialplan

Now we will edit extensions.conf with info specific for the your office. You’re going to need a unique CLLI for your switch, which generally acts as a name. To be compliant with NPSTN standards, these names begin with NPSTN and are followed by 4-8 more characters of location information or function. Because my switch is the first in the Southeastern Pennsylvania region, I used NPSTNSEPA01 as my CLLI. After you settle on a CLLI, you will also need to have the UCP Key sent to you after registering before proceeding. Below is my extensions.conf configuration for the 892-7 thousands-block. You should be able to compare this to the boilerplate configuration and read the comments to figure out hot to apply your own switch details. You’ll likely only need to make small modifications to the first 50 lines or so!

;;;;;;;;; NPSTN NA 201905011 - Boilerplate Template for New NPSTN Nodes
#include verification.conf ; this includes /etc/asterisk/verification.conf in the main Asterisk dialplan (loaded via extensions.conf)

[globals] ; Global variables that persist across all calls are defined here
; "Core" variables that MUST be defined on ALL NPSTN nodes
clli=NPSTNSEPA01 ; the unique CLLI of your node
zipcode=19086 ; your ZIP code (00000 for nodes outside the U.S.A.)
maindisa=8927111 ; number to your primary DISA (if you don't have one, then any number that comes into your switch from NPSTN)
allowdisathru=YES ; See to learn more about what this variable does
allowpstnthru=YES ; See to learn more about what this variable does
npstnkey=REDACTED ; your NPSTN UCP auth key (dial the business office at 116 or (407) 564-4141 if you need one)

; NOTE: NPSTN has a "billing" functionality that is purely for fun. The idea is to show you all the calls you make on NPSTN and what they would have cost back in the day. The bill is for fun, and you are not actually charged anything. NPSTN is and always will be 100% free.

; "Add-on" variables that are used only for convenience purposes on this node - in this case to define the path to audio files
dialtone=custom/signal/dialtone ; File path to audio file
busy=custom/signal/busy ; File path to audio file
reorder=custom/signal/reorder ; File path to audio file
ccad=custom/signal/ccad ; File path to audio file
acbn=custom/signal/acbn ; File path to audio file

; Number range assignments assignments
OC1=892 ; your office code's NNX (NOT NNX-X, whether or not you have all its thousands blocks) - create OC2, OC3, etc. if you have multiple blocks
                ; This sample implementation assumes the thousand blocks 555-1 and 555-9 have been assigned (adjust accordingly based on YOUR assignment)
                ; The individual extensions pre-created in [local] all adhere to the NPSTN numbering standards

[local] ; Define all local extensions here, except ATA lines which are automatically covered by the [ata] fallthrough
;exten => ${OC1}7XXX,1,GoSub(SIPxNumToPeer,s,1(${EXTEN}))
;       same => n,GoToIf($["${GOSUB_RETVAL}"="000"]?intercept:ata)
;       same => n(ata),GoSub(ata,s,1(${GOSUB_RETVAL})) ; Dial SIP peer if it exists
;       same => n,Hangup()
;       same => n(intercept),GoSub(ccad,s,1) ; Intercept message if SIP peer does not exist
;       same => n,Hangup()
exten => _${OC1}7XXX,1,GoSub(ata,s,1(${EXTEN})) ; See if any other numbers in this number block are possibly ATAs
        same => n,Hangup()
; exten => _${OC1}9XXX,1,GoTo(ata,${EXTEN},1) ; See if any other numbers in this number block are possibly ATAs
exten => ${OC1}7111,1,GoTo(dt,s,1) ; DISA access
exten => ${OC1}7701,1,SayAlpha(${clli}) ; "Switch verification"
        same => n,Hangup()
exten => ${OC1}7731,1,GoTo(echotest,s,1) ; Echo test
exten => ${OC1}7732,1,Answer() ; Calls that may last a while that won't get answered by a real person are best answered right away
        same => n,Wait(7200) ; Silent termination
        same => n,Hangup()
exten => ${OC1}7758,1,SayDigits(${CALLERID(num)}) ; a primitive ANAC
        same => n,Hangup()
exten => ${OC1}7760,1,GoSub(mwtone,s,1) ; Milliwatt test tone
        same => n,Hangup()
exten => ${OC1}7970,1,GoSub(busy,s,1) ; Always busy
        same => n,Hangup()
exten => ${OC1}7771,1,GoSub(reorder,s,1) ; Always reorder
        same => n,Hangup()
exten => ${OC1}7790,1,Answer() ; Calls that may last a while that won't get answered by a real person are best answered right away
        same => n,Dial(Local/ring@ringout,,m(ringback)) ; Number that always rings "forever"
;exten = _X.,1,Verbose(1, "User ${CALLERID(num)} dialed an invalid number.")
;       same =  n,Playback(pbx-invalid)
;       same = n,Hangup()

exten => ring,1,Wait(7200)
        same => n,Hangup()

[private] ; Any extensions you want to be able to dial but want to keep off-limits to outside callers
exten => 9876,1,Answer()
        same => n,Hangup()

;;; Any additional contexts you need
[echotest] ; Echo test
exten => s,1,Answer()
        same => n,Echo()
        same => n,Hangup()

[mwtone] ; Milliwatt test tone
exten => s,1,SET(VOLUME(TX)=8)
        same => n,Wait(0.5)
        same => n,PlayTones(1004/1000)
        same => n,Wait(600)
        same => n,SET(VOLUME(TX)=1)
        same => n,Return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; You should not need to touch anything below here ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[from-npstn] ; Incoming calls from NPSTN
exten => _X!,1,GoSub(npstn-verify,${EXTEN},1)
        same => n,GotoIf($["${clidverif}"="11"]?:accept)
        same => n,GotoIf($[${GROUP_COUNT(npstnspam@cvs)}<5]?:reject)
        same => n(accept),GoTo(external-users,${EXTEN},1)
        same => n(reject),Hangup()

[external-users] ; External calls from various places (NPSTN, possibly C*NET, PSTN, and other networks, arrive here)
include => local ; Include the contents of the [local] context

include => private ; Note: accessible only by *direct* dialing from ATA lines (not via DISA)
include => local
include => longdistance ; If the number dialed is not local, go "out" to NPSTN

[from-internal] ; Incoming calls from local SIP devices
exten => 8,1,GoTo(dt,s,1) ; Use extension "8" for ATA off-hook auto-dial for immediate NPSTN city dialtone (could likewise do 9 for PSTN, 7 for C*NET, etc.)
exten => _X!,1,Set(CHANNEL(hangup_handler_push)=npstnbilling,${EXTEN},1(${STRFTIME(${EPOCH},,%s)},${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)}))
        same => n,GoTo(internal-users,${EXTEN},1)

[SIPxNumToPeer] ; NPSTNNA 20191130 ; Returns SIP peer of a number or 000 if peer/number does not exist
exten => s,1,Set(num=${ARG1})
        same => n,GotoIf($["${num}"=""]?dne)
        same => n,Set(scriptexists=${STAT(e,/etc/asterisk/scripts/})
        same => n,GotoIf($["${scriptexists}"="1"]?use)
        same => n(download),SYSTEM(wget -P /etc/asterisk/scripts/)
        same => n(perm),SYSTEM(sudo chmod 777 /etc/asterisk/scripts/
        same => n(use),Set(peer=${SHELL(/etc/asterisk/scripts/ "${num}")})
        same => n,GotoIf($["${peer}"=""]?dne)
        same => n,Return(${peer})
        same => n(dne),Return(000)

[ata] ; Automatically dials the SIP peer by name (ARG1 = SIP peer name)
exten => s,1,Dial(SIP/${ARG1},,m(ringback)g)
        same => n,GotoIf($["${DIALSTATUS}"="CHANUNAVAIL"]?reorder,1)
        same => n,GotoIf($["${DIALSTATUS}"="BUSY"]?busy,1)
        same => n,GotoIf($["${DIALSTATUS}"="CONGESTION"]?reorder,1)
        same => n,GotoIf($["${DIALSTATUS}"="NOANSWER"]?reorder,1)
        same => n,Return()
exten => busy,1,GoSub(busy,s,1)
        same => n,Return()
exten => reorder,1,GoSub(reorder,s,1)
        same => n,Return()
exten => ccad,1,GoSub(ccad,s,1)
        same => n,Return()
exten => acbn,1,GoSub(allcktsbusy,s,1)
        same => n,Return()

[busy] ; Busy signal (~60ipm)
exten => s,1,Playback(${busy},noanswer)
        ;same => n,GoTo(1) ; to loop the busy signal, uncomment this line
        same => n,Return()

[reorder] ; Reorder signal (~120ipm)
exten => s,1,Playback(${reorder},noanswer)
        ;same => n,GoTo(1) ; to loop the reorder signal, uncomment this line
        same => n,Return()

[ccad] ; Cannot Be Completed As Dialed Intercept
exten => s,1,Playback(${ccad},noanswer)
        same => n,Return()

[allcktsbusy] ; All Circuits Busy Intercept
exten => s,1,Playback(${acbn},noanswer)
        same => n,Return()

[dt] ; NPSTN dialtone
exten => s,1,Answer()
        same => n(begin),Set(num=)
        same => n,Read(num1,${dialtone},1)
        same => n,Set(num=${num}${num1})
        same => n,GotoIf($["${num}"=""]?permsig)
        same => n,GotoIf($["${num}"="0"]?done)
        same => n,Read(num2,,1)
        same => n,Set(num=${num}${num2})
        same => n,Read(num3,,1)
        same => n,Set(num=${num}${num3})
        same => n,GotoIf($["${num1}${num2}"="11"]?done)
        same => n,GotoIf($["${num2}${num3}"="11"]?done)
        same => n,GotoIf($["${num1}"="1"]?ld8)
        same => n,GotoIf($["${num}"="660"]?done)
        same => n,GotoIf($["${num}"="958"]?done)
        same => n,GotoIf($["${num}"="959"]?done)
        same => n,Read(station,,4)
        same => n,Set(num=${num}${station})
        same => n,GoTo(done)
        same => n(ld8),Read(station,,5)
        same => n,Set(num=${num}${station})
        same => n(done),Set(__ticketed=0)
        same => n,Set(CHANNEL(hangup_handler_push)=npstnbilling,${num},1(${STRFTIME(${EPOCH},,%s)},${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)}))
        same => n,GoTo(dtdest,${num},1)
        same => n(permsig),GoTo(dtdest,permsig,1)
exten => h,1,GotoIf($["${ticketed}"="0"]?:done)
        same => n,GoSub(npstnbilling,${dtnum},1(${ss},${tt}))
        same => n(done),Hangup()

[dtdest] ; Routes calls from [dt]
exten => permsig,1,GoSub(dialnpstn,start,1(,disable,${zipcode},${maindisa}))
        same => n,Hangup()
include => local
include => dtnpstn

[longdistance] ; Outgoing NPSTN access for internal users
exten => _X!,1,GoTo(dtnpstn,${EXTEN},1)

exten => _X!,1,Set(originalclid=${CALLERID(all)}) ; Any other calls should be sent "out" to NPSTN
        same => n,GoSub(npstn-out-verify,${EXTEN},1)
        same => n,GotoIf($["${GOSUB_RETVAL}"="0"]?npstn-out-blocked,s,1)
        same => n,GoSub(dialnpstn,start,1(${EXTEN},disable,${zipcode},${maindisa}))
        same => n,Set(CALLERID(all)=${originalclid})
        same => n,Hangup()

[dialnpstn] ; NPSTN 20190215 NA ; Sends a call to NPSTN
exten => start,1,Set(num=${ARG1})
        same => n,GotoIf($[${LEN("${ARG4}")}>2]?clidoverride)
        same => n,Set(lookup=${SHELL(curl "${ARG1}&cid=${CALLERID(num)}&sntandem=${ARG2}&zipcode=${ARG3}")})
        same => n,GoTo(verify)
        same => n(clidoverride),Set(lookup=${SHELL(curl "${ARG1}&cid=${ARG4}&sntandem=${ARG2}&zipcode=${ARG3}")})
        same => n(verify),GoSub(lookupchan,s,1(${lookup})) ; verifies lookup for extra security
        same => n,GotoIf($["${GOSUB_RETVAL}"="0"]?npstn-BLOCKED,1)
        same => n,NoOp(NPSTN ROUTE TO: ${lookup})
        same => n,Dial(${lookup},,g)
        same => n,GoTo(npstn-${DIALSTATUS},1)
exten => npstn-ANSWER,1,Return()
exten => npstn-BUSY,1,GoSub(busy,s,1)
        same => n,Return()
exten => npstn-CONGESTION,1,GoSub(allcktsbusy,s,1)
        same => n,Return()
exten => npstn-CHANUNAVAIL,1,GoSub(reorder,s,1)
        same => n,Return()
exten => npstn-NOANSWER,1,GoSub(allcktsbusy,s,1)
        same => n,Return()
exten => npstn-BLOCKED,1,GoSub(ccad,s,1)
        same => n,Return()
exten => _npstn-.,1,Return()

[npstnbilling] ; NPSTN NA 20190504 NPSTN Toll Ticketing; EXTEN= number dialed, ARG1= start time of call in seconds, ARG2= datetime that the call started
exten => _X!,1,GoToIf($["${ARG1}"=""]?done)
        same => n,Set(endtime=${STRFTIME(${EPOCH},,%s)})
        same => n,Set(duration=$[${endtime} -${ARG1}]) ; can't use ${CDR(billsec)}
        same => n,GoToIf($["${ARG3}"=""]?:special)
        same => n,Set(type=direct)
        same => n,GoTo(request)
        same => n(special),Set(type=${ARG3})
        same => n(request),Set(request=${SHELL(curl "${FILTER(0-9A-Za-z,${CALLERID(num)})}&callee=${FILTER(0-9,${EXTEN})}&type=${type}&duration=${duration}&key=${npstnkey}&clli=${clli}&year=${ARG2:0:4}&month=${ARG2:5:2}&day=${ARG2:8:2}&hr=${ARG2:11:2}&min=${ARG2:14:2}&sec=${ARG2:17:2}")})
        same => n(done),Set(__ticketed=1)
        same => n,Return()

Configuring SIP Peers

Now we also want to set up a local phone or two to interact with our switch and NPSTN as a whole. I connect vintage physical phones to my Asterisk machine though SIP-enabled ATA (analog telephone adapter) devices that essentially act as intermediaries between an old phone and the Asterisk switch. The most simple ATA will have both an RJ-11 port to connect the phone, and an RJ-45 port to plug the device into your LAN. When connected, the ATA can be configured (usually through a web interface) with credentials for Asterisk so the phone can take and place calls.

As an alternative option, there are SIP clients for many operating systems that will serve the same purpose and allow you to make calls through and into your switch through your computer or mobile device.

Before you connect a SIP client or an ATA to the switch, you need to modify the sip.conf configuration file to expect connections from your device(s). Here is an example of my sip.conf with two peers: 8927181 and 8927189:

bindport=16555   ;set it to a UDP port and never tell anyone what it is! Do not forward the port in your router
;do not use port 5600 for external SIP connections; change your port to something non-standard i.e. bindport=39145 pick a port between 16383 and 65535 and never tell anyone what port you are using! Do not forward that UDP port in your router and ensure that UDP port 5600 is not forwarded in your router either. Indeed you should not need RTP UDP ports (usually 10000-20000) forwarded either.
allowguest=no ;keep intruders out
alwaysauthreject=yes    ;make life difficult for scanners trying to find a way into your dialplan
nat=force_rport,comedia ;should make nat more secure
tos_sip=cs3      ; Sets TOS for SIP packets.
tos_audio=ef     ; Sets TOS for RTP audio packets.
threewaycalling = yes
transfer = yes

[ATAs](!) ; template for all ATA logins
type = peer
host = dynamic
qualify = yes
insecure = port,invite
canreinvite = no ; don't allow RTP voice traffic to bypass Asterisk
relaxdtmf = yes
progressinband = yes

[8927181](ATAs) ; SIP login for user 8927181
defaultuser = 8927181
secret = yourpasswordhere
authid = 8927181
callerid = "Mide Dank" <8927181> ; change the CNAM and caller ID of your line here
context = from-internal ; context in the dialplan in which this user originates a call

[8927189](ATAs) ; SIP login for user 8927189
defaultuser = 8927189
secret = yourpasswordhere
authid = 8927189
callerid = "Mide Dank" <8927189> ; change the CNAM and caller ID of your line here
context = from-internal ; context in the dialplan in which this user originates a call

For the 8927181 number, I am using a Gradstream HT704 device with mostly default settings. Below, I am configuring Profile 1 on the device to connect to my PBX at As you can see, I set the fields for Primary SIP Server and Outbound Proxy to use this address. I think the only other thing I did on this page was change Enable Pulse Dialing to Yes. When finished, click on the Apply button at the bottom of the page.

The Profile 1 configuration on the Grandstream

The Profile 1 configuration on the Grandstream.

Next, I went to the FXS Ports tab to enter the SIP credentials. This is fairly basic, and you just need to make sure that the credentials match what they are in Asterisk’s sip.conf while also selecting Profile 1 to use what we just set up (and don’t forget to check the option for Enable Port). Again, Apply the settings, but then also Reboot so they can take effect.

Configuring the FXS Ports on the Grandstream.

Configuring the FXS Ports on the Grandstream.

For the 8927189 number, I set this up with the Android application Zoiper. All I needed to do here was add an account to the application with the corresponding SIP credentials from sip.conf and server address ( again), and everything was set up in just as few clicks.

Zoiper configuration

Zoiper configuration.

Start on System Boot

Now let’s create a systemd unit file so that Asterisk will start up when the system does. First we create a unit file:

# nano /etc/systemd/system/asterisk.service

Paste the following and save with ctrl-x:

Description=Asterisk PBX and telephony daemon

ExecStart=/usr/sbin/asterisk -f -C /etc/asterisk/asterisk.conf
ExecStop=/usr/sbin/asterisk -rx 'core stop now'
ExecReload=/usr/bin/asterisk -rx 'core reload'


Now we can enable and start asterisk:

# systemctl daemon-reload
# systemctl enable asterisk
# systemctl start asterisk

That’s it! If all goes well your PBX should spring to life.

Explore Your Switch and NPSTN

Now that everything is configured, you can start testing your switch. The first test you’ll probably want to do is to dial into your DISA (Direct Inward System Access) which gives you access directly into your switch. This is typically your NNN-N111 extension (so mine would be 892-7111). If all goes well you should hear a different dial tone. You can also dial several different test numbers on your switch by consulting extensions.conf to see what is set up. Here’s the relevant excerpt from my configuration above that you already (hopefully) modified for your own block:

exten => ${OC1}7111,1,GoTo(dt,s,1) ; DISA access
exten => ${OC1}7701,1,SayAlpha(${clli}) ; "Switch verification"
        same => n,Hangup()
exten => ${OC1}7731,1,GoTo(echotest,s,1) ; Echo test
exten => ${OC1}7732,1,Answer() ; Calls that may last a while that won't get answered by a real person are best answered right away
        same => n,Wait(7200) ; Silent termination
        same => n,Hangup()
exten => ${OC1}7758,1,SayDigits(${CALLERID(num)}) ; a primitive ANAC
        same => n,Hangup()
exten => ${OC1}7760,1,GoSub(mwtone,s,1) ; Milliwatt test tone
        same => n,Hangup()
exten => ${OC1}7970,1,GoSub(busy,s,1) ; Always busy
        same => n,Hangup()
exten => ${OC1}7771,1,GoSub(reorder,s,1) ; Always reorder
        same => n,Hangup()
exten => ${OC1}7790,1,Answer() ; Calls that may last a while that won't get answered by a real person are best answered right away
        same => n,Dial(Local/ring@ringout,,m(ringback)) ; Number that always rings "forever"

To find numbers on NPSTN that you can access, take a look at the directory and see if there is anything that interests you, There are literally thousands of operational numbers on the network!


Hopefully by this point you have a working NPSTN node that you can grow with extensions of your own. As you add more extensions to the network, make sure you use your UCP Key to log into the control panel ( and update the directory!

In the future, I’ll go into detail about hooking your PBX into your residential phone line so you can make outgoing calls and have incoming calls come into your PBX directly.

In the meantime, check out some of my older articles about setting up a PBX with FreePBX,

Special thanks to Naveen Albert who was essential in helping me get my system running properly, and the members of the PhreakNet chat!


The Best of 2019

Here is my fifth installment of the best things I’ve found, learned, read, etc. over the past year. These things are listed in no particular order, and may not necessarily be new.

See the 2018 post here!
See the 2017 post here!
See the 2016 post here!
See the 2015 post here!

This annual “Best Of” series is inspired by @fogus and his blog, Send More Paramedics.

Favorite Blog Posts Read

I end up reading a lot of articles over the course of the year, and cannot possibly remember all of them. Here is a good selection of that ones that I can recall:

Articles I’ve Written for Other Publications

I’ve continued to write outside of my own site in 2019, but all of this material has gone into NODE VOL 01. Below is a list of articles included in that volume. All articles are freely available by downloading the zine.

  • “Editor’s Letter” – An introduction to the zine.
  • “Dat Project 101” – Overview of the Dat protocol, including an explanation of underlying technologies and usage.
  • “So God Made a Farmer” – A look at farmers as hackers and tinkerers, highlighting issues the idea of ownership and involvement in the Right to Repair movement.
  • “Chattervox Tutorial” – Shows how to use Chattervox, an application that allows cryptographically secure chat over amateur radio.
  • “Ricochet IM” – Explanation and usage of Ricochet IM, secure chat over the Tor network.
  • “Bulletin Board Systems Today” – A list of currently-running bulletin board systems you can access today.
  • “Mesh Networking Basics” – Introduction to mesh networking technologies and examples of current mesh groups.
  • “MNT Reform: Open Source DIY Laptop” – Showcase of the MNT Reform laptop.
  • “Deciphering the Lexicon” – Definitions of common terms for the decentralized world.

Number of Books Read

A disappointing number this year 🙁


Still Need to Read

Dream Machines, Literary Machines, Design Patterns, 10 PRINT CHR$(205.5+RND(1)); : GOTO 10

Favorite Music Discovered

John Maus

Favorite Television Shows

Mr. Robot (2015)

Favorite Podcasts

Reply All, 99% Invisible, My Brother, My Brother and Me, Darknet Diaries

Favorite YouTube Subscriptions

Johnson’s Gee-rage, Vinsauce: The Full Sauce, GameGrumps, DIY Solar Power with Will Prowse, Techmoan

Programming Languages Used for Work/Personal

Java, JavaScript, TypeScript, Python, Bash, Batch, PowerShell.

Life Events of 2019

Life Changing Technologies Discovered

  • Othernet – Low-cost satellite datacasting.
  • DreamPi – Present-day online play for the Dreamcast using the ubiquitous dial-up adapter interfaced with a Raspberry Pi.
  • Plex – While I had previously heard of Plex, I got to use and run it for the first time this year. A big step up from Kodi and Samba shares.
  • Anderson Powerpole Connectors – The best power connectors for DC electrical power.
  • iFlash – Aftermarket SD card adapters for various models of the iPod. Perfect for replacing againg hard disks with more abundant, more reliable, less expensive storage.

Favorite Subreddits

I’ve taken a lot of steps away from Reddit this year and only reduced what I check down to a handful of subreddits.

/r/amateurradio, /r/retrobattlestations

Conferences Attended

Unfortunately, BarCamp Philly 2019, Pumpcon 2019, and Radical Networks 2019 were all booked for the same weekend this year. I hope to make it to Pumpcon in 2020!

Completed in 2019

Plans for 2020

  • Write for stuff I’ve written for already (NODE, Lunchmeat, Neon Dystopia, 2600).
  • Write for new stuff (Do you have a publication that needs writers?), maybe Paged Out!
  • Participate in more public server projects (ntp pool, etc.).
  • Do more work with packet radio.
  • Get a fully-working PBX on NPSTN and connect my vintage phones.
  • Restore a vintage payphone I already own.
  • Restore a vintage Ms. Pac-Man cocktail cabinet I already own.
  • Play with LoRa.
  • Build a blue box.
  • Start a dial-up ISP?

See you in 2020!


Belated News: NODE VOL 01 and Presenting at Radical Networks 2019

I was quite busy in 2019 and worked on two big things that were never shared here. Both were fairly large undertakings for me, and I figured it was good to make note of them here.


I served as the editor for the premier issue of the NODE zine, a really cool publication to come out of NODE (which you may likely recognize from its video offering). Production of the first issue took many months, and while I had worked previously writing content for NODE, editing proved to be a new and different animal. That said, I also ended up writing a handful of articles for the issues, and am currently working towards production of a second! The zine is licensed under Creative Commons, and is available freely to download via the Dat network. A physical version was released but is currently sold out.

NODE VOL 01 is an 150 page zine for the NODE community. Volume 1 is packed with features on P2P projects, such as Dat, Beaker Browser, Ricochet IM, Aether, and more. There are many tutorials showing projects like the new NODE Mini Server, how to 3D print long range wifi antennas, how to chat via packet radio, and how to do things like Libreboot the Thinkpad X200. There’s also a handy open source directory at the back, along with lots more.

Radical Networks 2019 — BGP: The Internet’s Fragile Beast

In October of 2019, I had the pleasure of presenting at the Radical Networks conference in New York City. My talk was on Border Gateway Protocol, the sort of invisible glue that holds the Internet together. The talk is available for download in odp format as well as pdf!

BGP (Border Gateway Protocol) manages how all of our packets are routed across the Internet. It is one of the most powerful and important protocols currently deployed on the ‘net, but it is also incredibly fragile. Devised as a quick fix 30 years ago (without concern for security), BGP is constantly blamed in the news as Internet outages occur worldwide due to misconfigurations by multinational telecommunications conglomerates or hijackings by government actors.

This talk will demystify the misunderstood protocol that is BGP, and explain how entities exchange giant flows of data across the Internet, highlight past misuses, and consider what we may be able to expect in the future.

See you all in 2020!


Installing FreeBSD 12 on the Atomic Pi

By the end of April, Digital Loggers released the Atomic Pi, an x86-64-based single board computer, for sale on their website. Many speculate that these boards were bought wholesale from another company’s liquidation, and their strange assortment of navigation and audio features may indicate that they were originally built for automotive use. That said, they boast decent specifications for the inexpensive ($35) price especially compared to other x86/x86-64 boards like the Jaguarboard and UDOO:

- SoC – Intel Atom x5-Z8350 quad core processor @ up to 1.92GHz with Intel HD graphics
- System Memory – 2GB DDR3L-1600
- Storage – 16GB eMMC flash, slot for SD expansion up to 256GB
- Video Output – HDMI port
- Connectivity
    - Gigabit Ethernet via Realtek RTL8111G transceiver
    - Dual band 802.11b/g/n/ac WiFi 5 via RT5572 with IPX connectors on board
    - Bluetooth 4.0 via CSR8510
- USB – USB 3.0 and USB 2.0 ports
- Sensor – 9-axis inertial navigation sensor with compass (BNO055)
- Expansion – JST style connectors and a 26-pin header for power & GPIO
- Debugging – TTL serial debug and expansion serial ports up to 3.6Mbps
- Misc – Real time clock & battery
- Power Supply – 5V/4A recommended. 4-15 watts typ. power consumption
- Dimensions – 130 x 100 x 50 mm

(Specs via

I’ve been eyeing a board like this for some time in order to run a BBS on low-cost, small-footprint hardware without having to emulate x86 architecture on top of ARM via some other SoC. That said, gigabit ethernet and onboard wireless (with an antenna connector, even!) could make this board viable for homebrew mesh-networking applications in the future.

Atomic Pi overview.

Getting Started

Upon receiving the board, the first hurdle you must face is powering it. The board itself has no power jack, whether it be a barrel plug or micro USB female. Some bundles include a breakout board that supports a barrel-type connector, which can also be purchased separately from resellers. However, with one of those you will also need a DC adapter with a 5.5mm/2.1mm plug that can pump out ~3 amps.

I happened to already have a 5-volt power supply (similar to this one) that went unused for a past project, so I chopped up a PC power cord to feed it AC. I then cut two dupont jumper wires in half (down the middle), stripped away some insulation, and screwed two halves into each of the two 5-volt pos/neg terminals on the supply (cut side on the terminals). To connect the power supply to the board, the two positive wires can be plugged into any two of pins 3, 5, 7, 9, 11 or 13 for +5 while the two negative wires can be plugged into any two of the pins 2,4,6,8,10,12 or 14 for GND.


At this point the board should power up and boot into its default operating system, which should help you determine if the hardware is performing properly. However, we want to install something different.

Installing FreeBSD

So first, we need to prepare some installation media. I had bad luck booting FreeBSD from an external DVD drive, so I recommend preparing either a microSD card or a flash drive. Due to the Atomic Pi only having one USB port, you will need to use a USB hub if you decide to use a flash drive so you can connect the drive along with a keyboard.

Insert your drive/card into a separate machine and download the FreeBSD-12.0-RELEASE-amd64-memstick.img image file from here. Then, write the image to your device following the instructions under section Writing an Image File to USB in the guide here.

Now, insert your device into the Atomic Pi and power-cycle it. While it is restarting, repeatedly press the Esc key to boot the machine into the UEFI. Then, cycle through to the Boot tab and change the boot order, putting your device before the eMMC.

Make sure your device is first, while Android-IA (the eMMC) is second.

Then, press F4 to save and exit, which will restart the system again. This time, the FreeBSD installer should boot up. On the initial installer screen selection option 3. Escape to loader prompt.

You will now have a limited terminal to configure some configuration options. Enter the following to disable UART (otherwise the install will freeze) and then start bsdinstall (thanks to u/ArchiKola via Reddit for this):

set hint.uart.0.disabled="1"
set hint.uart.1.disabled="1"

Now, you can go about the install process as normal.

Once finished, elect for the option to boot into the live environment to perform configuration before rebooting system. We need to apply those same UART changes or the new installation won’t boot properly. To do this, edit the loader configuration:

# vi /boot/loader.conf

Then, add the following two lines to the bottom of the file:


Everything’s done! We can now remove the installation media and reboot:


FreeBSD 12.0!

Happy hacking!


The Best of 2018

Here is my fourth installment of the best things I’ve found, learned, read, etc. over the past year. These things are listed in no particular order, and may not necessarily be new.

See the 2017 post here!
See the 2016 post here!
See the 2015 post here!

This annual “Best Of” series is inspired by @fogus and his blog, Send More Paramedics.

Favorite Blog Posts Read

I end up reading a lot of articles over the course of the year, and cannot possibly remember all of them. Here is a good selection of that ones that I can recall:

Articles I’ve Written for Other Publications

I’ve continued to write for a few different places outside of my own site. Here is a complete list for 2018:

Favorite Technical Books Read

Favorite Non-Technical Books Read

  • The Philip K. Dick Reader (1997) – My second time reading any Dick, this collection is amazing, lengthy, and inexpensive to pick up. You’ll be up all night reading this and surprised how sci-fi from the ’50s is so relevant today.

Number of Books Read

Back up to a good amount of books read this year!


Still Need to Read

Dream Machines, Literary Machines, Design Patterns, 10 PRINT CHR$(205.5+RND(1)); : GOTO 10

Favorite Music Discovered

Favorite Television Shows

Black Mirror (2011)

Favorite Podcasts

Reply All99% InvisibleMy Brother, My Brother and Me, Darknet Diaries

Programming Languages Used for Work/Personal

Java, JavaScript, Python,Objective-C, Swift.

Life Events of 2018

  • Visited Ireland.
  • Got my Amateur Radio Technician license.

Life Changing Technologies Discovered

  • Baofeng radios – Inexpensive amateur radios. You can get a decent handheld for about $30
  • MuckRock – A 3rd party service for submitting information requests.
  • TIL Wiki – A wiki idea for doing small today-i-learned posts as a way of note taking. 

Favorite Subreddits

/r/amateurradio, /r/vintageaudio

Conferences Attended

Completed in 2018

Plans for 2019

  • Write for stuff I’ve written for already (NODE, Lunchmeat, Neon Dystopia2600).
  • Write for new stuff (Do you have a publication that needs writers?).
  • Participate in more public server projects (ntp pool, etc.).
  • Launch a BBS?
  • Continue work for Philly Mesh.
  • Continue rebooting Raunchy Taco IRC (Have one server and a certificate authority configured).

See you in 2019!


The Brain Mutator For Higher Primates — A bOING bOING Retrospective

This article was originally written for and published at Neon Dystopia on June 13th, 2018 It has been posted here for safe keeping.

In 1988, Mark Frauenfelder and his wife Carla Sinclair started a small zine out of their apartment in Sherman Oaks, California. This wasn’t a full-time job for Frauenfelder, who studied mechanical engineering in school and worked professionally designing hard disk drives during the day. The drudgery of his work got to him, and he desperately needed a creative outlet, and that outlet would become bOING bOING.

Frauenfelder was fascinated by self-produced magazines of the time like Screamsheet and Reality Hackers, which in many ways acted as a precursor to amateur websites and blogs that permeate the Internet today. Zines were a bit different than the magazines you may pick up in a corporate bookstore. They were rough, uncensored, and often handmade by a group of amateurs having fun. Maybe you’d find some on a table at a trendy coffeehouse, or maybe the employee bathroom at work, but more often than not you would have to seek them out by mailing cash to the creators and hoping they sent something back. This wasn’t the first foray into publishing for Mark, who had created two issues of a mini-comic called Toilet Devil, and a one-issue zine titled Important Science Journal some time earlier. This new zine would be different. It would be for cool things, cyberpunk, wacky stuff, high weirdness, and anything downright crazy the husband and wife duo found interesting.

Carla Sinclair and Mark Frauenfelder.

Frauenfelder, an avid punk rock fan, enjoyed music by acts like The Ramones and The Clash throughout his youth. When asked what he liked about punk music during a 2011 interview, Mark responded, “It was the DIY aspect of the punk culture. You didn’t need to have expensive equipment or a record contract. I also liked the primitive sound. It’s hard to say, but as soon as I heard it, I loved it.” In many ways, the new zine would mirror punk culture and the DIY aesthetic: it wasn’t perfect, there wasn’t any backing or stability— it was raw and unfiltered and noisy and fun.

As a child, Frauenfelder was drawn to computers and comics, which eventually inspired his love of all things geeky and his fascination with alternative media. He first learned about zines from the Winter 1987 issue of Whole Earth Review titled “Signal” (co-edited by Kevin Kelly, who would later be among the founders of Wired magazine) in which an article explained the concept of zines and even mentioned a zine directory (which was actually a zine itself!) titled Factsheet Five. Frauenfelder ordered a copy and immediately sent away for as many zines as he could.

The first issue was layed out before the pair needed to move to Boulder, Colorado in 1989. Carla took on the role of editor, which she would retain for the run of the magazine, while Mark settled into the co-editor/publisher position. Packed full of cyberpunk sci-fi, underground comix, and mind-altering media, Carla xerox’d about 100 issues of the 36-page zine, and began to distribute it. The first issue was a trip: there was an interview with futurist Robert Anton Wilson, a comic about taking LSD, and a libertarian-cyberpunk manifesto titled “Crossbows to Cryptography: Techno-Thwarting the State!”, amongst others. bOING bOING, the World’s Greatest Neurozine, was born!

bOING bOING issue 1 (1989) cover. Read the whole issue here.

Stop right there. I can already see the candy-colored cogs in your brain cranking away trying to understand the text you just sucked off the page. bOING bOING… as in Boing Boing… as in, the popular group blog that arguably pioneered blogging as a concept in the early days of the Internet. Few people know that Boing Boing started its life as a humble zine, printed on dead-tree paper— not electronic bits ethereally whirling around the ‘net. Boing Boing may now be a staple of the Internet for those interested in science fiction, futurism, technology, and left-wing politics, but 30 years ago, it was a brand new zine called (and stylized as) bOING bOING.

bOING bOING stayed in Colorado for several issues and were hitting their stride as distribution ramped up. They refined their manic, madcap, eclectic style to become the premier net rag, full of punk attitude and sassy style. While the first issue of the magazine featured content from a handful of technoid misfits, the contributions were soon creeping in from all over. Back before the Internet, zines had to rely on a combination of luck and word-of-mouth to be successful. You could distribute copies of your zine to your friends, send them to other zines you like in the hopes that they’d review it, or trade them with others to spread far and wide. If it was any good, you’d have insatiable, bug-eyed mutants clamoring for more. If it was bad, it would fizzle out, and be all but lost to time. Early contributors for bOING bOING included science fiction authors like Paul Di Filippo and Rudy Rucker, as well as cyberculture writers and zine editors like Going Gaga helm Gareth Branwyn and FringeWare Review wizards Paco Nathan and Jon Lebkowsky. Within the zine microcosm, bOING bOING was a hit!

Mark Frauenfelder pasting paper together to assemble copies of bOING bOING issue 2 (1990). Read the whole issue here.

It is important to understand just how much cyberpunk influence bOING bOING was amassing in this early period of publication. Just three years before bOING bOING’s first issue, Bruce Sterling edited the acclaimed Mirrorshades: The Cyberpunk Anthology (1986), featuring short stories by prominent, front-wave authors in the cyberpunk subgenre. bOING bOING would go on to feature articles by authors from this anthology such as Rudy Rucker, Marc Laidlaw, Paul Di Filippo, John Shirley, and even Sterling himself. Others such as William Gibson and Lewis Shiner would ultimately be interviewed. Di Filippo in particular would even use bOING bOING Second issue as a launching point to share his ideas on a half-serious new subgenre he was developing called “ribofunk,” a blend of “ribosome” and “funk” (a direct response to “cyberpunk”), that acted as a prototype for what we would later come to call “biopunk.”

Around the time that Mirrorshades was hitting hardback, before bOING bOING launched, Mark and Carla would run into R.U. Sirius and Queen Mu selling the poster-sized second issue of their High Frontiers zine (a psychedelic counter-culture zine which would eventually morph into the cyberpunk Mondo 2000 a few years later) at a Timothy Leary show in San Francisco. Frauenfelder vividly describes the duo by stating “RU was this grinning hobbit-looking character with a floppy hat with a Andy Warhol button on it. Queen Mu was a very delicate blond woman with Stevie Nicks clothes and granny glasses and she [had] a permanent blissful smile and didn’t say much.” After buying a copy of their zine, Mark and Carla would attend High Frontiers Monthly Forum events in Berkeley thrown by R. U. and Mu, eventually meeting like-minded cyberpunks and tuned-on mutants such as author Rudy Rucker and future Mondo 2000 art director Bart Nagel. The friendship between the group grew, with both Rucker and Sirius eventually writing for bOING bOING.

R.U. Sirius and Timothy Leary.

bOING bOING covered culture in a no—holds-barred way. No topic seemed too taboo or salacious or untouchable. Drugs, kinky sex, and absurd humor littered the pages— sometimes comprising the entire issue. You could get the latest news about the ‘net, independant comics, goth culture, punk music reviews, and everything in between. You might see a cyberpunk short story sharing a spread with a Schwa alien cartoon or recruitment advertisement from Church of the SubGenius. bOING bOING dripped with Gen X culture, and as with Frauenfelder, appealed to those fed up churning in a stuffy office all day or burning out in their McJob. bOING bOING, like a lot of technology-soaked publications of the ’80s, followed a natural evolution with roots in the counter-culture movement of the 1960’s. Instead of the dirty, free-loving and peace-wheeling hippies, bOING bOING was more in tune with the punks of ’77 who scornfully rejected the old political idealism and subconscious with a rebellious, no-bullshit attitude. Music, culture, and technology were getting more personal; the milieu was different. The average bOING bOING reader was more likely reading Amok Dispatch (1986) rather than the Whole Earth Catalog (1969), and listening to Black Flag instead of the Grateful Dead. Kerouac made way for Coupland. This was something new— this was theirs.

For issue eight, their first with full color, Frauenfelder and Sinclair moved back to California. They didn’t stick around in one place for too long, pin-balling from Hollywood to Los Angeles to San Francisco, and eventually back to L.A. throughout the remaining years of the zine. bOING bOING was booming throughout this period, and benefited from an influx of cash attributed to Mark being employed to design Billy Idol’s Cyberpunk (1993) album. While in Los Angeles (the first time), Frauenfelder was offered a job as a writer at a small magazine startup, also run by a husband and wife team, called Wired. “They saw Boing Boing and they really liked it,” Mark has said previously, “so they called me up and asked if I could come work for them as an editor and inject some of Boing Boing’s sensibility into the magazine.” The couple relocated to San Francisco, and set up bOING bOING on the first floor of the Wired building, then located at 544 2nd Street.

Wiley Wiggins who you may remember as Mitch Kramer in Richard Linklater’s Dazed and Confused (1993) was also actively involved in early ’90s cyberculture. He wrote for bOING bOING as well as Mondo 2000 and FringeWare Review.

Wired released its first issue in 1993, but before that, it was just a group of writers and publishers trying to throw together a new concept for a generation of MTV-watching punks, immersed in the fresh world of cyber-culture. Publisher and co-founder of Wired, Louis Rossetto, pitched his magazine concept by saying, “We’re trying to make a magazine that feels as if it has been mailed back from the future.” This fit in nicely with Frauenfelder’s style. The Wired building was truly a melting pot of San Francisco culture in the early ’90s. Wired had recently moved from the first floor, a large, open, warehouse-like space, to the second floor when it needed something roomier. bOING bOING moved into Wired’s old digs in the corner of the gigantic room, which was already buzzing with activity from other independent zine makers in the Bay Area. Other publications sharing the space included Dave Egger’s Might (a magazine aimed at Generation X), Just Go! (a travel magazine), Hum (a magazine for young South Asians), CUPS (a more eclectic culture zine), and Star Wars Universe (I think you can figure this one out). Mark continued to work on bOING bOING while also netting income from the burgeoning Wired, though Carla took over most of the production at this time. Issue 9 of bOING bOING would become notable with such content as an interview with Bruce Sterling about his new book The Hacker Crackdown (1993), a regular music column by Richard Kadrey, and a 7-page pastiche of Mondo 2000 (starting on the back cover, so it actually looks like a Mondo 2000 issue when upside down) featuring articles with titles like “I’m Gonna Morph You Up,” and “Virtual Neural Jacks.”

Cover and first page of the mONDO mONDO parody in bOING bOING issue 9 (1992). Read the whole issue here.

By this time, issues began to feel more and more refined— both in content and physical appearance. Once printed on cheap paper, the zine now had dazzling, glossy covers, and was filled with content from a loyal band of the fringe-elite. bOING bOING never seemed to lose its quirky, geeky, out-there edge that had been so crucial in cultivating the zine’s culture and feel. At its peak, bOING bOING reached a circulation of 17,500 issues. Unfortunately, nothing lasts forever.

By 1995, bOING bOING would release what many might consider its last “regular” issue, though the year also marks what many would say is a much more crucial event for bOING bOING: the launch of its website, which we can still visit some 23 years later. Behind the scenes, the independent printing industry was changing for the worst. In 1994, shortly before this penultimate issue of bOING bOING was released, the two largest independent magazine distributors in the country went bankrupt. bOING bOING ended up losing about $30,000 because of this, causing delays in the production of another issue. While the launch of may be seen as a deathblow to the zine, it might have actually been the only thing that saved it. It was clear that publishing on paper was not going to be a long term solution. Publishing on the ‘net could be done for free.

The print zine may have been fading, but that doesn’t mean the culture built around it was left to decay. 1995 became a year of handbooks. Aligning with The Real Cyberpunk Fakebook, a satirical cyberpunk handbook written by select Mondo 2000 staff, Frauenfelder, Sinclair, and bOING bOING regular Gareth Brawnwyn collaborated on the 205-page Happy Mutant Handbook, a guide to offbeat pop culture. Sinclair would also release her first book, Net Chick: A Smart-Girl Guide to the Wired World, an optimistic yet sassy guide for women carving out their place in the early days of the web. Further, Frauenfelder was continuing to work for Wired where he would attain the position of editor. bOING bOING ultimately released its final print issue, #15, in 1997 after a two year hiatus. Unlike previous issues, this one more closely resembled a book, with more standard binding and a squarish appearance; the contents however were the same weird and wacky that bOING bOING was known for.

The Happy Mutant Handbook (1995) was actually designed by Georgia Rucker, author Rudy Rucker’s daughter! Read the whole book here.

Frauenfelder would eventually leave Wired in 1998, following his tenure there with a stint as the “Living Online” columnist for Playboy from 1998 to 2002, a job he was recommended for by Playboy editor and former zinester Chip Rowe (who had published Chip’s Closet Cleaner in the early ’90s). Later, Frauenfelder would become editor-in-chief for Make: magazine, a DIY/hobbyist bimonthly, while also producing a few books before settling into a role at the Institute for the Future as a research director. He also serves as Editor-in-Chief and podcast co-host with Kevin Kelly (again, of Wired and Whole Earth Review fame) at Cool Tools, a site about the tried-and-test tools and gadgets. Sinclair would later publish a technothriller, Signal to Noise (1997), and become editor-in-chief of a Make: spin-off magazine titled Craft: which ran from 2006 to 2009. Frauenfelder still maintains top position on the masthead, along with bOING bOING zine regular David Pescovitz. Carla has contributed to the site as recently as 2016, but additional writing is currently provided by Cory Doctorow, Xeni Jardin, and Rob Beschizza.

In May 2011, Frauenfelder would publish a bOING bOING anthology of favorite interviews from the zine era in a free, online-only PDF file. “The first few issues of Boing Boing had print runs in the low hundreds, and the biggest was 17,500 copies. Today, the blog easily gets that many page views in an hour,” Frauenfelder states in the the article announcing the anthology. The zine may be gone, but its legacy lives on through “I think I’ll always be involved in some media. Who knows what Boing Boing will evolve into. But, I kind of imagine that it might not be too different than it is now,” Frauenfelder says in a 2012 interview, “I see myself continuing to make Boing Boing into an even better experience for its audience.”

For me, it can’t get much better than a three-color zine made by a husband and wife team exploring the weird and wonderful world. I only became aware of bOING bOING a few years back when I was searching for issues of Mondo 2000, and stumbled upon it quite accidentally. Before long, I was able to track down almost every issue and began to scan them, page by page, in an attempt to save them for future generations. I never did find the first two issues for purchase, but luckily I uncovered some PDFs of them online that were scanned at some point by Frauenfelder himself many years ago! After my scanning was complete, I uploaded each issue to the Internet Archive where you can download or browse them today, completely free. I’ve been lucky enough to have a few interactions with Mark Frauenfelder online after this, and he’s always been quick to answer my obscure questions about the old days and provide new insights. bOING bOING, the zine, remains a point of pride for him and he seems to love sharing it. It was and is something he loved, and he was there to see it mutate, evolve, and grow over time, while he did the same.

The print is dead, but the brain jack is warm. You can always go online.

Some of the links included in this article are Amazon affiliate links. If you would like to purchase these items, consider using the links provided and help support Neon Dystopia.


Building a Replica Hackers Pager

Ever since I saw Hackers (1995), I always wanted one of the iconic yellow pagers that Cereal Killer sports at various points during the film. I missed out on the whole era of pagers, but I always thought there was just something cool about them that seems a little less amazing now that we are in a text-messaging world.

The Motorola Advisor from the film.

Many months ago, I became aware of an awesome website called Hackers Curator that attempts to index every prop (among other great things) from the Hackers film, and even make some reproductions. Of course, they showcased the iconic Motorola Advisor pager, and even gave a custom-made replica away to an online buddy of mine via a scavenger hunt contest. I inquired to see if they had any for sale, and they did, but a single pager from them was outside of my price range. I thought I could so something similar for significantly less money — and it turns out that I can (you can too)!

I in no way want to sound as though I am disparaging Hackers Curator. I think they do a really good job, and I’ve even contributed a few scans to their site. If you don’t like the idea of piecing together supplies to customize your own pager, don’t have a lot of free time, or just don’t like getting spray paint over your hands, I’d definitely recommend you send them an email to see if they have any pagers in stock. I’d also like make it known that they have a video on their YouTube channel that outlines how they made one of the pagers. I got a few ideas from their video, but ultimately used a few different techniques and hope to share my individual findings (and source files!) to create a more complete build solution guide for tinkerers out there.

Build List

  • Motorola Advisor pager – $10+
  • Krylon Fluorescent Yellow spray paint – $4
  • Krylon All Purpose Bonding White Primer spray paint – $4
  • Masking tape – $1
  • Fine grit sand paper – $1
  • X-Acto Knife (or other precision cutting tool like a razor blade or box cutter- $1-$5
  • Scrap cardboard (to put the pager body on for painting) – Free
  • 5x Sheets waterslide decal paper (and a printer to print on it with) – $4-$8
  • 1x Sheet metallic-gold paper – $1
  • Motorola sticker (optional) – $3

The heart of this project is of course the Motorola Advisor pager. Technically, there are two different versions of the original Motorola Advisor, and the difference comes down to the arrow buttons having triangles inset into the rubber or just printed right on top. Cosmetically, this doesn’t seem to make much of a difference, but if you want to be accurate to the movie I believe the pager they use has the inset triangles. Also keep in mind there have been many Motorola pagers in the Advisor line, like the Advisor II, Advisor Gold, Advisor Elite, etc. I may have made some of those up, but it’s hard to tell when they have names like that. You just want the original blocky one. I ended up just buying the most inexpensive one I could find on eBay, for $10 including shipping. The internals in mine appear to work, but if you are just making a prop, it likely doesn’t matter if the thing works at all. You may also notice that a lot of these pagers have some other company’s name in the front nameplate where “Motorola Advisor” should be. This is fairly common, so unless you happen to find a sticker that will fit over top of the weird company’s name, you might want to pay a little more for a pager that actually says “Motorola Advisor.”

You’ll want to get some spray paint to paint the pager with, and I recommend a basic white primer to cover up the black plastic entirely, and fluorescent yellow paint to match the color of the pager in the film. For whatever reason this paint has awful reviews online, but works great and even glows under black light! More on that later. Aside from the paint, you will want some basic supplies like masking tape (to tape off areas on the pager you don’t want paint on), an X-Acto (or other precision cutting tool to slice of excess masking tape), some fine grit sand paper (to sand down some paint during finishing to make the pager look worn), and scrap cardboard (or wood, etc. to place the pager body on for painting). For these supplies, I used stuff I had around, which included 100 grit sandpaper that I probably should not have used as it was too low grit. You may want to get a variety of sandpaper and work your way down the grit levels. Lastly, before I forget, unless you have long fingernails, you are going to want some sort of pry tool like a small jewelers screwdriver or guitar pick (which is always good to keep in the tool box).

The last important items you will need to get are waterslide decal paper and metallic-gold craft paper. Waterslide paper allows you to print directly onto a paper-backed transparent plastic film that you will later apply to the pager’s screen (from the back). They make different types for laser and inkjet printers, so be sure to buy the proper type for the printer you have. I bought a pack of five sheets so I had some extras if I messed up or wanted to do a slightly different design at some point. The metallic-gold craft paper is easy to find in a giant sheet at any craft store, just inspect it before you buy it as some sheets looked streaky. We will use this gold paper as a backing for our waterslide paper.


Okay, so now we have our pager.

The majestic Motorola Advisor!

Flip it over and remove the battery cover. It should slide out from top to bottom.

Battery cover removed.

Next, we need to open it up. If we flip the pager onto its side, we can locate the locking plastic tab keeping it together. These pagers have a tool-less assembly, so we can pry up this piece of plastic by slipping a fingernail or a piece of plastic (okay, or a jewelers screwdriver) into the crease closest to the corner (shown at the right of the picture here) and sliding the cover to the right, towards the pried-up end.

The plastic cover should slide right out when you get it to this point.

At this point, the pager should basically break down into its components, which we can easily reassemble later. If you ever find a part that seems to be held in by adhesive (like a side of the screen), you can safely wiggle this loose using a small screwdriver and mild pressure. The actual LCD screen is attached to a separate plastic case piece through three plastic tabs that can be released (again) with a small screwdriver or prying device of some kind.

Depressing the tabs to release the screen.

Now, everything should be completely broken down.

All of the components separated.


Before we can actually do some painting, we need to tape off the areas that we don’t want any paint to get on. This includes, the screen, the name plate, plastic parts on the side, labels, or pretty much anything that isn’t black plastic. Apply tape liberally and use the precision knife to gently cut away excess.

Taping off the nameplate.

Make sure to also tape off components or clear plastics from the underside of the case as well! You don’t want back-spray to leave any paint flecks here. Also, I didn’t do this, but try to tape off the back of the locking plastic tab and corresponding parts of the case that the tab normally covers. This will make assembly and disassembly easier in the future if you want to get back inside the pager, the layers of paint can make it really hard to slide the tab out again!

Ready to go! There should be 5 pieces to paint.

The primer we have is designed to bond to plastic, so we should be good to go with a first coat. You might want to clean the pager’s shell with alcohol or maybe do some sanding here, but I didn’t find that necessary. Place the case pieces on some cardboard and paint them following the directions on the can. When done, follow the drying instructions as well. Two coats should cover the case completely.

Primer done!

Next up, the yellow paint! Again, follow the painting and drying instructions on the can. For this, I ended up doing three coats total, but two might be good enough.

Fluorescent yellow looking good!

We can now carefully remove the masking tape.

Tape removed.

At this point, we can start sanding down the edges of the pager to remove some layers of paint. Remember to work applying light pressure, as you can always take more paint away but not get any back. It helps if you keep a screenshot from the film nearby when working on your wear pattern.

After some sanding, we’re looking pretty good.

Preparing the Decal

The coolest part of this pager is going to be mimicking the display of the pager in the movie so it reads “GRAND CENTRAL HACK THE PLANET”. To achieve this, I had to combine a few different things.

First, I wanted make a canvas for the screen, so I made a Photoshop document sized at 2.628 inches by 0.872 inches (a little larger than the screen size) with a resolution of 250 pixels/inch.

Then, I wanted to work on the text. Instead of making the typeface from scratch, I found an almost identical typeface called LCD Solid, which is freely available. I was able to create two lines of text, and adjust the kerning so the characters were spaced out more like in the film.

Next, I used a screenshot from the film to draw the little display icons by tracing over them in the screenshot.  I ended up modifying them a bit to level them out and generally make them look a bit more flat. Ultimately, I was able to get a pretty close representation of the screen shown in the movie.

My completed screen.

You can download my finished PDF here for free. Please use it, and modify it, and make it better for other hackers to use!

The next step was to print it out on standard white computer paper, cut it down, and do a fit test to make sure it would look okay and not be cut off when it was printed on plastic for the final product.

Just holding a cut piece of white paper with the printed image shows how well it will fit.

Everything looked good, so now we can move on to printing on the waterslide decal paper. Our waterslide paper is clear plastic backed by white paper. After we print out our image on the plastic side, the paper is soaked in water and the backing slides off, leaving a “sticky” side we will affix to the back of our pager screen. Because of this, we will need to flip our newly created image horizontally before printing on the waterslide paper. Additionally, I copied and pasted the image many times to fill out the sheet of paper in the event that the application didn’t work or came out poorly. It is a good idea to do this to give several attempts as waterslide paper can be a bit tricky.

A big sheet of waterslide paper with the image printed all over it.

Now, we can cut away one of the decals and make sure it fits the space of the screen. Rough measuring can be helpful here.

Decal ready for application.

Follow the instructions included with waterslide paper to remove the backing. Generally, you will place the decal in a bowl of warm (not hot) water for 30 seconds then remove it. Flatten the decal out and line it up on the backside of the pager screen (text facing you). With your finder holding down the long edge of the decal, slowly work the backing up, away from your finger until it is completely removed. Use a cloth or your finger with light pressure to smooth out any wrinkles or air bubbles between the decal and the screen. Do not use a credit card or your fingernail if suggested by the waterslide paper instructions, this will scratch away some of the ink on the decal and leave it splotchy. If the decal doesn’t look good, don’t be afraid to start over. It can take a few tries to get the desired result.

Here is the applied decal posed next to a cropped screenshot of the pager from the film.

At this point, I assembled the unit, but was very dissatisfied by the gutter shadow between the screen and the display. Also, the display somehow had a ton of scratches that were not on the screen.

Look at that shadow!

You can also see that I applied my Motorola sticker to the nameplate at this point to make the pager look a little more stock. I could only find a “Motorola OPTRX” sticker for sale on eBay, so I used a Sharpie to black out the “OPTRX” text.

Here is the sticker before application.

But anyway, we want to eliminate that shadow. This is where the metallic-gold craft paper comes in. Cut a piece roughly the size of the screen, and place it between the screen and the display. No tape or glue is needed to secure it in place, it just stays in from friction. This is not only cheaper than spraying the area with gold paint, but it also makes it easier to change out the decal or reverse the whole modification so the original pager display can be used for any reason.

The completed pager.

One of my favorite properties of the fluorescent yellow paint is its ability to glow under black light.

The pager body pops under UV light.

Also, it looks pretty good in the holster.

Ready to be clipped on to a belt.


That finishes up the Hackers pager. There is a bit of room for improvement, but I’m really satisfied by the result. To see some of my progress posts and to see what others are doing, be sure to check the #hackerspager tag on Mastodon. In total this build cost me a bit less than $30.

Aside from showing this pager off at cons, I hope to one day look into modifying it to run POCSAG so it will act as an actual pager and not just a show piece. That’s definitely further down the line, however.

This guide is organic, and subject to change. Let me know if you attempt it, how it works for you, and if you successfully make a cool pager by using it! Don’t hesitate to reach out.

Hack the planet!