There has been a resurgence recently in BBSing. While the days of phone lines are long gone, more and more BBS's are popping up - some new, some returning from dusty backup media. Without phones and modems, these "modern" BBS's are reachable over the Internet via a telnet connection.
Obviously, the best way to connect to a BBS is through an (at least loosely) era-appropriate system and a serial communications program. There are several purpose-built devices out there that emulate a modem command set for just this purpose - the WiModem232 from cbmstuff.com being one of the more recent additions. If you are looking for an out of the box option for some retro BBSing, you can't go wrong with this device.
I'm a bit of a tinkerer and I wanted to build a DIY device similar to the WiModem232. I wanted to retrofit it into an existing modem for a look of authenticity. I also wanted to connect the LEDs on the front of the modem so that they actually indicate modem status. And I wanted to add PPP capability so that I can connect some of my "newer" old systems to the Internet without needing an Ethernet card.
Having used Raspberry Pi's and tcpser before, I decided to use them as the foundation of my project. It took me awhile to find all of the information I needed, so this article is an attempt to gather the important bits in one place. My intent here is not to write a step by step guide, but to put all of the information one might need to do a similar project into one place.
The Raspberry Pi Zero W is a natural match for tcpser. It's small, powerful, has an onboard UART (i.e. serial port), onboard Wi-Fi, and I can buy them at my local Micro Center for only $5. The Pi does have a few limitations that we need to work around...
TTL vs RS-232
To make the onboard UART compatible with a standard RS-232 serial port, a logic level shifter is needed. There is an IC called the MAX-232 that will do this, and tons of information is available on building this basic circuit. But why reinvent the wheel? Several companies make small boards with a DB-9 port, MAX-232, and all supporting circuitry already onboard. I purchased this one from my local Micro Center for about $15. There are plenty of other options out there. I'd recommend finding one that has CTS/RTS pins, some do not. One with TX/RX LEDs on them to indicate activity could be handy as well.
These modules have all of the goodies already in place - from a hardware perspective it's simply a matter of connecting everything as shown on the left. I had to desolder / remove the header pins on my Schmartboard module, and then simply used some small gauge wire to connect the appropriate pins together.
Connecting it all together
The connections shown in red are the only truly necessary connections: Power, and TX, RX, and ground. You'll note that in the diagram, the TX pin on the Pi should connect to the RX pin on the serial board, and vice versa. This wires it up in such a way that you need a standard cable, not a null modem cable, to connect to your PiModem. This is the way traditional modems work, so it made sense to me to do it this way.
Note: The Schmartboard labels the VIN pin as +5V, but it supports 5v or 3.3v. You will want to follow my diagram and power it with pin 1, the 3.3v output of the Pi. The MAX232 uses the input voltage as a reference, and will output this voltage to the Pi's RX pin, which expects 3.3v. You might damage the Pi if you actually connect 5v to the Schmartboard device.
The lines in gold are for hardware flow control, which is a mechanism that modems and computers traditionally use to signal back and forth if they are ready to receive data (i.e. there is room in the buffer). For systems that support it, this can make things stable at higher baud rates It's usually not necessary at slower baud rates (2400 or below). This will be covered in more detail below.
The blue connections are used to control some LEDs on the front of the modem, and are optional as well. I will cover this in more detail later on.
You will need to power your Raspberry Pi as well. There are various schools of thought on this. The easiest way is to use the on-board micro USB power port. I simply supply +5v directly to the header pins (4 and 6) directly. This is frowned upon by some, as the Pi does not have any protection or regulation in place on these pins, but it has worked well for me.
First, you will need to get Raspbian onto your Pi. There are two flavors of Raspbian available - a desktop version, and a lite version. I suggest the lite version.
There are several ways to install Raspbian on your Pi. I took the headless approach, or you can follow the official documentation. Using the headless method, you perform the installation and basic configuration (enable SSH and configure Wi-Fi) from a regular computer. The remaining instructions assume that you have you have a Pi Zero W connected to your wireless network, it is running Raspbian, and you are able to connect to it via SSH.
Edit config.txt and cmdline.txt
In the /boot directory, you will need to edit two files. These adjustments enable the onboard UART, disable Bluetooth so that we have access to the "good" UART, and disable the serial console so that the UART is free for tcpser to use.
In /boot/config.txt, add the following lines to the bottom of the file:
# Disable Bluetooth dtoverlay=pi3-disable-bt # Enable UART enable_uart=1
Once config.txt is edited, reboot your Pi:
sudo reboot
By default, once the UART is enabled, the Pi will open a serial console on the port, which makes it unusable by anything else. You will need to edit the file /boot/cmdline.txt and change any existing console= value to tty1. The end result should look something like this, all on one line.
dwc_otg.lpm_enable=0 console=tty1 root=PARTUUID=0d4f86da-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
Once these adjustments are done, you will need to reboot your pi a second time:
sudo reboot
Download and compile tcpser
There are several versions of tcpser out there. We will be using this one from FozzTexx (of /r/retrobattlestations fame). It has several improvements over the original. Execute the following commands to download and compile it:
cd ~ wget http://ftp.podsix.org/pub/pimodem/fozztexx-tcpser.zip unzip fozztexx-tcpser.zip cd tcpser-master make sudo cp -p tcpser /usr/local/binNote: The above instructions use a locally archived copy of tcpser to guarantee compatibility. You may prefer to clone the latest version directly from Github instead. Congratulations, you've now downloaded and installed tcpser! There's still plenty to do, but now is a good time to take a little break and test what we've done thus far:
tcpser -s 1200 -d /dev/ttyAMA0
If tcpser started without error, but you do not receive an OK in response to AT, the most likely cause is something with your serial cable or wiring. Double check all of your connections, and make sure you are using a straight cable and not a null modem cable. You can try swapping the TX/RX pins as well.
Hardware handshaking (CTS/RTS)
I mentioned previously that the CTS/RTS pins were optional. That is true, but whether or not we connected them - and whether or not we use them - will determine how we configure tcpser, and how you will want to configure your terminal program.
By default, tcpser enables hardware flow control, which means it will try to make use of the CTS/RTS pins. Since tcpser emulates a Hayes compatible modem, this behavior can be controlled with the Hayes command set. The &K series of commands defines whether or not the modem (or tcpser in our case) will have flow control enabled. Here are the relevant &K commands:
Command | Description |
&K0 | Disables all flow control |
&K3 | Enables hardware (CTS/RTS) flow control (default) |
&K4 | Enables software (XON/XOFF) flow control |
&K6 | Enables both hardware and software flow control |
There are two ways to use Hayes commands in tcpser - by issuing the commands from within our terminal emulator, or by specifying them on the command line. If the CTS/RTS pins are not wired up, or if you will exclusively be using older systems that don't need and/or don't support hardware flow control, it may be best to always disable it at the tcpser command line. We will cover how to do this when we are creating our startup scripts below.
If you did wire up the CTS/RTS pins and would like the flexibility of enabling/disabling hardware flow control, simply use the relevant AT commands when needed. Most terminal programs allow you to define a modem init string - simply define it as AT&K0 on systems that do not need/support hardware flow control, and AT&K3 on systems that do.
Ultimately, you always want to make sure that tcpser, your computer / terminal emulation software, and the wiring in place between the two are all in agreement with regards to hardware handshaking. This may require some experimentation.
I also mentioned earlier that CTS/RTS is not enabled by default on the Pi. If you did connect the CTS/RTS pins and want to be able to use hardware flow control, you will need to download and compile a small program:
cd ~ wget http://ftp.podsix.org/pub/pimodem/mholling-rpirtscts.zip unzip mholling-rpirtscts.zip cd rpirtscts-master rm -f rpirtscts make sudo cp -p rpirtscts /usr/local/binThis program must also be called before we start tcpser - we'll cover that just ahead.
Startup scripts
Now that we have our software in place, we need to get it to start up automatically at boot. To do this, we will create a shell script to handle the startup of tcpser, and a systemd service to handle automatically starting everything as a service at boot.
First, create the following files. Use your favorite text editor, and make sure to use sudo for proper permissions. If you are unfamiliar with editing text files through SSH, here is a helpful guide on how to use nano.
/usr/local/bin/start_tcpser.sh
#!/bin/bash phonebook="/home/pi/phonebook.txt" baud='9600' dev='/dev/ttyAMA0' for i in `cat $phonebook`; do n="$n -n$i " done sudo /usr/local/bin/rpirtscts on /usr/local/bin/tcpser -l4 -s ${baud} -d ${dev} $n
Note: If you are not using CTS/RTS, remove the rpirtscts line. You will also want to add -i '&K0' to the tcpser command line to permanently disable flow control. Also, adjust the baud rate as desired. I've found 9600 to be reasonable for most of my machines, but you may wish to use 1200 or 2400 if you are using some earlier machines. And finally, if you are using a USB to Serial adapter, you will more than likely want to change the dev line to /dev/ttyUSB0.
/etc/systemd/system/tcpser.service
[Unit] Description=Modem emulator tcpser After=network.target [Service] Type=simple User=pi WorkingDirectory=/usr/local/bin ExecStart=/usr/local/bin/start_tcpser.sh Restart=on-abort [Install] WantedBy=multi-user.target
/home/pi/phonebook.txt
5551000=bbs.fozztexx.com 5551001=particlesbbs.dyndns.org:6400
Note: You can customize this to include or exclude any BBSes you wish. The startup script looks for this file, so it should be there with at least one entry. If you don't wish to use phonebook functionality, you can omit this file and modify start_tcpser.sh to remove this feature.
Once all of the above is in place, you can enable and start your new tcpser service. Run the following commands:
sudo chmod +x /usr/local/bin/start_tcpser.sh sudo systemctl daemon-reload sudo systemctl enable tcpser sudo systemctl start tcpser
Testing it all out
Now that everything is running, you should be ready to connect to a BBS. Let's try it out.
If everything went well, you can now use your favorite terminal program's built in phone book functionality. Simply add to or modify /home/pi/phonebook.txt to add additional phone numbers. Make sure to restart tcpser with the command service tcpser restart any time you do.
You can also connect to BBSes, or any other telnet host, by typing the actual hostname and port number instead of a phonebook entry. For example:
ATDT particlesbbs.dyndns.org:6400
Many (most?) terminal programs are not going to accept long alphanumeric strings as a phone number, so using the phonebook features of tcpser allow you to use your terminal program's built in directory feature.
Since my PiModem is built into an old modem, I wanted to re-purpose some of the front panel lights so that they actually work. The modem I chose to "upcycle" has 6 LEDs on the front, I decided to re-purpose 5 of them: Power, TR, CD, TX, and RX.
Hardware
First I needed to isolate the built-in LEDs so that they are no longer attached to any bits of the original modem circuitry. I used a multimeter to trace everything out, and then a set of precision tools (a Dremel and a drill) to cut through any traces needed. If I were doing it all over again, I would likely have discarded the original internals and glued some new LEDs to the case.
Next I needed to determine how the positive side of each LED would be connected and controlled. For the Power LED, I simply connected it directly to the power source (with a 1k resistor in series, of course).
The Schmartboard serial level module has TX and RX LEDs on board, so I simply extended a wire for each, connecting the positive side of the modem LED to the positive side of the existing LED. This was a bit challenging as the Schmartboard LEDs are extremely small SMT components. I managed to destroy both LEDs in the process, but it didn't matter since the board is hidden away in the modem, and the TX/RX LEDs on the front of the modem are now functional.
Finally, I connected the remaining two LEDs - TR and CD - to GPIO pins on the Raspberry Pi. I connected the TR LED to pin 18 (GPIO 24), and the CD LED to pin 16 (GPIO 23). I placed a 1k resistor in series between the positive side of the LED and the GPIO pin.
Software
Since the TR and CD LEDs are connected to GPIO pins on the Pi, a bit of software is needed to make them do something. On a real modem, the TR LED means Terminal Ready, while the CD LED means Carrier Detect. I wanted to keep the functionality similar, with the TR light indicating that tcpser was running and ready to go, and the CD light indicating that the "modem" was connected to something.
I contemplated modifying the tcpser code to control the LEDs directly, but I decided not to go down that path just yet. Instead I created a script that uses the fuser command to determine if the serial port is locked, and the netstat command to see if tcpser has any active connections. It's a somewhat rough approach, but it works. The script is started by systemd and checks every 2 seconds to see if either LED should be toggled. A companion Python script handles setting the GPIO pins as outputs and turning the lights on or off.
Assuming you have your own TR and CD LEDs to control, and they are tied to the same GPIO pins as mine, the following instructions should work. If not, feel free to use any parts of my code you wish to craft your own solution.
cd ~ wget http://ftp.podsix.org/pub/pimodem/LED.py wget http://ftp.podsix.org/pub/pimodem/set_leds.sh sudo mv LED.py set_leds.sh /usr/local/bin
chmod +x /usr/local/bin/LED.py /usr/local/bin/set_leds.sh
wget http://ftp.podsix.org/pub/pimodem/set_leds.service sudo mv set_leds.service /etc/systemd/system
sudo systemctl daemon-reload sudo systemctl enable set_leds sudo systemctl start set_leds
Install pre-requisites
We will need to add some additional software packages to our PiModem.
sudo apt install telnet xinetd telnetd pppNext, set the pppd binary needs the setuid bit set, which allows it to run with elevated permissions.
sudo chmod a+s /usr/sbin/pppd
Create ppp user
We will be creating a new user for the ppp service to run under. This user will have pppd set as its shell.
sudo useradd -m ppp sudo usermod -aG dip ppp sudo usermod -s /usr/sbin/pppd ppp sudo touch /home/ppp/.hushlogin
Configure pppd
The pppd package puts several default configuration files into the /etc/ppp directory. We will be completely replacing two of them. First, move the originals out of the way:
sudo mv /etc/ppp/options /etc/ppp/options.orig sudo mv /etc/ppp/pap-secrets /etc/ppp/pap-secrets.origAnd then replace them with the following content:
/etc/ppp/options
# We will be doing PPP over Telnet - disable serial control. local # Terminate connection if remote side stops responding. lcp-echo-interval 30 lcp-echo-failure 4 # Debug adds a lot of detail into the system logs regarding PPP negotiation. # This is helpful in debugging client issues. debug # IP addresses to use in local:remote format. We use NAT to share # the Wi-Fi connection, make sure these are outside of your real subnet. 192.192.1.1:192.192.1.2 # Other sensible options asyncmap 0 passive noipx/etc/ppp/pap-secrets
# Allow any username/password * * "" *
Configure xinetd to enable ppp over telnet
Normally PPP establishes sessions over a serial line or a modem. We want to take a somewhat abnormal approach and configure it to answer on a telnet port. This allows pppd and tcpser to interoperate, while also maintaining the ability to connect to telnet BBSes.
Create the following files:
/etc/xinetd.d/pppd
service pppd { type = UNLISTED flags = REUSE socket_type = stream wait = no user = root server = /usr/sbin/in.telnetd server_args = -h -L /usr/local/bin/ppplogin disable = no bind = 127.0.0.1 port = 2323 }/usr/local/bin/ppplogin
#!/bin/bash /bin/login -f ppp
We now need to set the ppplogin script we created to be executable and enable xinetd.
sudo chmod +x /usr/local/bin/ppplogin sudo systemctl enable xinetd sudo systemctl restart xinetdBefore continuing, make sure that xinetd is listening on port 2323:
pi@raspberrypi:~ $ netstat -an |grep :2323 tcp 0 0 0.0.0.0:2323 0.0.0.0:* LISTEN
Enable Network Address Translation
When your PiModem establishes a PPP session with the attached computer, it will bring up a new interface with a pair of IP addresses that we defined above in the options file. We need to configure iptables to perform Network Address Translation (NAT), allowing ppp to share the IP address of your Pi's Wi-Fi connection.
Run the following commands to configure IP masquerading (i.e. NAT):
sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE sudo sh -c "iptables-save > /etc/iptables.rules"We also need to to enable IP forwarding. Edit /etc/sysctl.conf and uncomment the ip_forward line - it should be around line 28:
# Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1And finally, we need to create a simple script to restore the iptables rules upon reboot:
/etc/network/if-pre-up.d/iptables
#!/bin/bash iptables-restore < /etc/iptables.rules
Make the script executable, and then reboot the Pi:
sudo chmod +x /etc/network/if-pre-up.d/iptables sudo reboot
Once the Pi has rebooted, make sure that IP forwarding is enabled, and that the masquerade rule is in place:
pi@raspberrypi:~ $ cat /proc/sys/net/ipv4/ip_forward 1 pi@raspberrypi:~ $ sudo iptables -t nat -L POSTROUTING -nv Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 6 573 MASQUERADE all -- * wlan0 0.0.0.0/0 0.0.0.0/0
Add an entry to your tcpser phonebook
If you followed my tcpser setup guide, you should have a file called phonebook.txt in your home directory. You will want to add an entry for your new fake dialup ISP:
echo "5559000=localhost:2323" >> /home/pi/phonebook.txt sudo service tcpser restartCongratulations! You now have your own dialup ISP. Welcome to the 90s!
Client Configuration
Exact configuration will vary from client to client. Configure your PPP client to dial 5559000, or whatever you specified in your phonebook file above. You will also want to make sure the baud rate matches whatever you have tcpser set to. Experiment with increasing it - I've successfully run up to 57600 on a Mac LC III. Higher baud rates have proven unreliable.
You'll also need to configure DNS servers. You will usually want to specify your home router's IP address, but you can also try using Google's DNS servers (8.8.8.8 and 8.8.4.4). You'll want to leave most other items set to automatic or left blank. Your PiModem will automatically assign an IP address and handle negotiation of compression. If asked for a username/password, you can put in any thing you like. Here are a few screenshots of a working FreePPP configuration: