Building RetroModem: Bringing Dial-Up Internet Back to Life

Project: RetroModem
Platform: Raspberry Pi / Linux
Author: S. M. Zaidi
Development log: July 2026 – Present

There is something uniquely nostalgic about the sound of a dial-up modem.

The dial tone. The DTMF digits. The ringing. Then that wonderfully awful burst of screeches, chirps and static before the computer finally announces:

CONNECT 33600

RetroModem began with a fairly simple idea:

Could a Raspberry Pi behave like a real dial-up modem well enough that an old computer could dial into it and reach the modern Internet?

The answer turned out to be yes.

But getting there involved serial ports, PPP, Asterisk, fake telephone numbers, modem command sets, GPIO buzzers, OLED displays, ALSA audio, vintage modem sounds — and considerably more debugging than the original idea suggested.

And that became half the fun.


July 2026 — The Experiment Begins

The project originally grew out of experiments with recreating a telephone/dial-up environment using a Raspberry Pi.

The first attempts involved Asterisk, SIP and real modem hardware. Asterisk 22.10.1 was compiled from source on the Raspberry Pi after the normal Debian package route proved problematic.

Eventually the Pi could answer calls and extensions could be dialled.

That proved the telephone side was possible.

But it also raised a more interesting question:

Did we actually need a complete telephone exchange just to make an old computer believe it was talking to a modem?

Probably not.

That led to RetroModem.

Instead of reproducing the entire telephone network, the Raspberry Pi itself would emulate the modem.


The First Modem

The basic architecture became:

Vintage PC
    │
    │ RS-232 / USB serial
    ▼
RetroModem
    │
    │ PPP
    ▼
Raspberry Pi
    │
    │ NAT
    ▼
Internet

The old computer sees what appears to be a conventional Hayes-compatible modem.

It can send familiar commands such as:

AT
ATZ
ATI
ATDT442708

RetroModem interprets those commands and responds as a physical modem would.

The number being dialled doesn't need to correspond to a real telephone line. RetroModem can map numbers to internal destinations.

The important thing is that, from the vintage computer's point of view, it just dialled a modem.


Getting PPP Working

The next major challenge was getting an actual network connection established.

After the simulated modem reports a connection, RetroModem releases the serial port and hands it to Linux pppd.

A successful connection eventually looked like:

Dialling '442708'

Node 1: PPP 192.168.99.1:192.168.99.2

Releasing serial port for pppd handoff

Starting pppd...

Node 1: PPP interface detected: ppp0

NAT enabled on wlan0

That moment was significant.

The vintage computer wasn't merely seeing a fake CONNECT message anymore.

It had a genuine PPP network connection through the Raspberry Pi.

RetroModem had effectively become a tiny dial-up ISP.


Making It Sound Like a Modem

Initially the project concentrated on functionality.

But a silent modem isn't much fun.

The next stage was recreating the sounds of a real telephone/modem connection.

A WAV audio backend was added with support for:

dialtone.wav
ring.wav
busy.wav
handshake.wav
pickup.wav
hangup.wav
no_carrier.wav
relay_click.wav

We also generated DTMF tones corresponding to the telephone number being dialled.

The sound generator was deliberately made slightly imperfect.

Instead of mathematically pristine digital tones, the generated audio included:

  • slight telephone-line hiss
  • softer attack and decay
  • level matching
  • subtle analogue character

The objective wasn't laboratory accuracy.

It was to make it feel like a telephone line.


The Missing Ringing Problem

One amusing bug appeared once the audio system was working.

RetroModem would:

dial number
→ handshake
→ CONNECT

Something felt wrong.

Then it became obvious.

Nobody had answered the telephone.

A real dial-up call normally sounds more like:

dial tone

DTMF digits

ring...
ring...

click

modem handshake

CONNECT

The dial engine was therefore changed so that the remote modem answers after approximately two or three rings.

It is a completely unnecessary delay from a networking perspective.

And completely essential from a retro-computing perspective.


Two Audio Systems

RetroModem also developed two different ways of producing sound.

A GPIO buzzer could generate simple tones, while the WAV backend could reproduce much more convincing telephone and modem audio through the Raspberry Pi's audio output.

The launcher therefore gained selectable audio backends.

That eventually exposed another problem.

The launcher could ask for an ALSA device, but presented choices such as:

hw:1,0

Technically correct.

Humanly useless.

The interface was changed so the user could instead select something understandable such as:

3.5mm / Line Out
HDMI Audio

RetroModem could deal with the ALSA plumbing underneath.


Why Is "Maximum" So Quiet?

Another audio problem appeared soon afterwards.

The launcher reported:

Volume: Maximum

Yet the modem was surprisingly quiet.

Opening alsamixer revealed why.

RetroModem's software volume was at maximum, but the actual ALSA hardware mixer wasn't.

The volume system was redesigned so presets controlled both the hardware mixer and software amplification:

PresetALSA mixerSoftware gain
Quiet40%100%
Normal70%110%
Loud90%120%
Very Loud100%140%
Maximum100%180%

Now selecting Maximum actually meant maximum.

A revolutionary concept. 😉


Adding an OLED

An SSD1306 OLED display was added to give the physical RetroModem some personality.

Early experiments weren't especially glamorous.

There were Python syntax errors, library API mismatches and refresh-performance problems.

Eventually the display became useful for showing things such as:

READY

DIALING
442708

RINGING

CONNECT
33600

ONLINE

and live connection information.

The OLED turned the Raspberry Pi from a box running Python into something that actually looked like a modem appliance.


A Boot Personality

Once the OLED and audio systems existed, another idea followed naturally.

Why shouldn't RetroModem have a startup sound?

The proposed sequence became:

♪ chime ♪

"Welcome to RetroModem... by S. M. Zaidi."

It's unnecessary.

Which is precisely why it belongs in a retro hardware project.


July 30, 2026 — A Much Bigger Idea

By this point RetroModem was behaving convincingly like a US Robotics Sportster 56K.

Commands such as:

ATI
ATI0
ATI1
ATI3
ATI5
ATI6
ATI7
AT&V
AT%V
AT+MS?

could return US Robotics-style responses.

But this exposed an architectural problem.

The emulator wasn't really emulating modems.

It was emulating one particular modem.

US Robotics-specific behaviour had gradually found its way into modem_core.py.

So the project took another turn.

Instead of one modem emulator, RetroModem would become a modem emulation framework.


Introducing Modem Profiles

A new structure was created:

modem_profiles/

    __init__.py
    base.py
    usr.py
    hayes.py

Every modem personality inherits from a common ModemProfile.

The US Robotics implementation became:

USRProfile

while a second personality was introduced:

HayesProfile

The intention is simple.

The core should handle:

Serial
PPP
Networking
Dialling
Audio
OLED
Connection state

The profile should handle:

Identity
ATI responses
Vendor AT commands
S-register defaults
Factory configuration
Modem personality

The core shouldn't care whether it is pretending to be a US Robotics modem, Hayes modem or anything else.


July 30, 2026 — First Profile Test

The first major test succeeded.

RetroModem started with:

Loaded modem profile: US Robotics Sportster 56K

The existing USR ATI commands were moved into usr.py.

Then the computer dialled:

ATDT442708

RetroModem answered, performed the simulated telephone sequence, started PPP and produced:

ppp0

NAT came online.

Nothing broke.

That was important because it proved the profile architecture could sit above the existing dial/PPP engine without interfering with it.


Where the Project Is Now — August 2026

The current refactor is removing the last US Robotics-specific behaviour from modem_core.py.

The target architecture is:

                    RetroModem
                        │
                ┌───────┴────────┐
                │                │
           Modem Core       Modem Profile
                │                │
        Serial / PPP          USR
        Networking            Hayes
        Audio                 Supra
        OLED                  ZyXEL
        Dial engine           Telebit

Selecting:

modem_profile = usr

will emulate a US Robotics Sportster.

Changing it to:

modem_profile = hayes

will emulate a Hayes modem.

The same serial connection.

The same PPP engine.

The same Raspberry Pi.

A completely different modem personality.


What's Next?

The immediate milestone is straightforward:

Remove every remaining US Robotics-specific assumption from modem_core.py.

Then both US Robotics and Hayes profiles will be tested against the same core.

Once that works, adding another modem should no longer mean rewriting RetroModem.

It should mean adding another profile.

And that opens up a rather entertaining possibility:

US Robotics Sportster
US Robotics Courier
Hayes Smartmodem
Hayes Optima
SupraFAXModem
ZyXEL
Telebit
MultiTech
Zoom

One Raspberry Pi.

A shelf full of virtual modems.


Development Log

I would keep a permanent section at the bottom of the page and add to it whenever we make a meaningful change:

July 2026 — Initial experiments: Asterisk/SIP experiments, serial modem communication and PPP testing.

Late July 2026 — RetroModem core: Hayes-compatible AT command interpreter, serial transport, PPP handoff and Internet NAT established.

Late July 2026 — Audio: Added buzzer and WAV backends, DTMF dialling, dial tone, ringing, busy tone, handshake and disconnect sounds.

Late July 2026 — Physical UI: SSD1306 OLED integrated with modem state and connection information.

30 July 2026 — Audio overhaul: ALSA output selection and combined hardware/software volume presets added.

30 July 2026 — Telephone simulation: Added realistic dial → ring → answer → handshake sequence.

30 July 2026 — Modem personalities: Introduced ModemProfile, USRProfile and HayesProfile architecture.

30 July 2026 — First profile migration: ATI0–ATI7 and US Robotics configuration commands successfully moved from the core into usr.py.

30 July 2026 — PPP regression test: US Robotics profile successfully dialled and established ppp0; Internet NAT remained operational after the architectural changes.

1 August 2026 — Profile refactor: Work begins on completely removing US Robotics-specific implementation from modem_core.py and establishing USR and Hayes as independent personalities.

Next: Complete USR extraction → test Hayes → make profile selection persistent in the RetroModem launcher.
------------------------------------------------------------------------------------------------------------

1 August 2026 — MyRetroCorner.uk Is Born

Today marks another milestone — myretrocorner.uk officially came to life.