Several weeks ago, I introduced the idea for my “desktop telco” project in this post. My goal is to build a telephone switch that’s small enough to fit on my desk, accurate enough to appease dial-up modems up to 33.6k, expandable enough to support sophisticated retro computing projects, and cheap enough to undercut the market for commercial phone line simulators.

Before I’ll be able to connect a call, I first need to serve a phone line. Although the signals carried by analog phone lines are very simple, they’re also… weird. You can’t just hook a phone up to a couple Arduino pins and expect anything useful to happen. Here’s a brief, incomplete list of things that happen on a phone line:

  • There are two wires, one is called “tip” and the other is called “ring.”
  • The line’s “resting state” is -48 Volts DC (that’s 0 V at tip, and -48 V at ring)
  • When you take the phone off the hook, the line jumps to around -8 V, and tip isn’t necessarily a zero reference anymore.
  • To make the phone ring, the line voltage starts alternating at 20 Hz. Supposedly this is typically 75 Vrms, but can be as low as 40 Vrms.
  • When you talk into the phone, voice signals are filtered down to 300-3300Hz. In EE terms, fc=1 kHz, Q=0.33.

Besides building a circuit that meets these specifications, there’s isolation concerns, too. I need to make sure those high voltages don’t cause trouble for low-voltage logic and control electronics.

Thankfully, there’s an easy solution to most of these problems:

Photo of a KS0835F SLIC module.

This little board is called a “subscriber line interface card,” or SLIC. A SLIC takes in low-voltage power and signals on one end, and spits out a usable phone line on the other end. Using a dedicated SLIC board like this one means that all the high voltages are physically separated from sensitive signal processing and logic chips elsewhere in the design.

This particular SLIC is designated “KS0835F.” KS0835F modules are readily available on eBay from countless vendors. They can be had for less than $7 USD a piece, which is great news for my affordability requirement. I bought 6 of them, so I have plenty to tinker with (and spares if I break one). Here’s what the back looks like:

Photo of the back of a KS0835F SLIC module.

These boards are very densely packed. I would wonder how they make any money selling these for so little, but I have some guesses based on the cheesy soldering work.

Signs of cheapness aside, these little guys make it relatively easy to serve a real, functioning phone line. They generate a ringer signal from logic-level input signals, they provide logic-level off-hook detection, and they have analog audio in/out pins, which are crucial for detecting dial tones, and producing the various tones a phone is expected to make.

A KS0835F still needs many supporting parts, but the datasheet makes it pretty clear what you’ll need. I threw together a perfboard based on the datasheet’s schematic, and I had a working phone line!

Photo of the SLIC expander board, a perfboard featuring a KS0835F SLIC module and several supporting components.

To my surprise, this went off without a hitch. I connected my expansion board to 5V, plugged in a phone, and connected an LED to the KS0835F’s hook status pin. When I picked up the phone, the LED turned on. That’s a promising start, if not exciting.

What I really wanted at this phase was to make the phone ring. Doing this with the KS0835 isn’t terribly difficult - you pull its “ring mode” pin high, then feed it a 20hz square wave on its “fwd/rev” pin. But, if that’s all I did, the phone would just ring, forever. On a real phone line, the phone rings, then pauses, then rings again. So, there needs to be some extra control circuitry to enter and exit the “ringing” mode as well.

Here’s what I came up with:

Schematic diagram of the prototype ringer circuit.

The ringer circuit consists of a pair of astable 555 timers. The timer on the left is tuned for 0.2Hz with a 25% duty cycle. This part of the circuit is responsible for the “ring, then pause, then ring again” behavior. Its output feeds into an AND gate, and it also connects to the KS0835’s “ring mode” pin.

The timer on the right produces the 20Hz square wave. This timer is tunable using two potentiometers: RV1 calibrates the frequency, then RV2 calibrates the duty cycle.

Both timers are using a slightly unusual 555 timer circuit. It is the same as the common astable 555 timer circuit, except for the addition of two opposing 1N4148 diodes. These diodes decouple the timing capacitor’s charging path from its discharging path. Consider the 0.2Hz timer (on the left) - the timing capacitor is C5. When C5 is charging, current passes through R16, but not through R15. If the diodes weren’t there, C5 would charge through both resistors, taking much longer. But, regardless of the diodes, C5 always discharges through R15, and not R16.

The diodes reduce the timing capacitor’s charging time without affecting its discharging time. As a result, this diode-augmented circuit can produce duty cycles far below those that can be achieved with the traditional astable 555 timer circuit.

With all that figured out, the outputs of the two timers get combined through an AND gate, and that combined signal goes out to the fwd/rev line on the KS0835F. Here’s what the whole thing looked like:

Photo of the ringer prototype. A brown telephone sits on a table, to the left of a breadboard.

When I took the phone off the hook, the blue LED lit up. When I pressed the button, the phone rang. Cool!

While I was poking around, I decided to check the KS0835’s audio output pin to make sure the signal seemed sensible. When I pressed a number on the keypad, I got a lovely dual-tone signal:

Oscilloscope screenshot showing a dual-tone signal at about 2V peak-to-peak.

The Guts

By adjusting the potentiometers, I was able to tune the 20 Hz timer pretty well:

Oscilloscope screenshot showing a 20 Hz square wave.

Here’s the output from the 0.2 Hz timer:

Oscilloscope screenshot showing a 0.2 Hz pulse train, with about 28% duty cycle.

Okay, so the ~28% duty cycle doesn’t really match the 20-25% typical of a real phone line. After taking this screenshot, I replaced the 470k resistor with a 560k resistor, and it sounds a bit more “right.”

Something funny happens when I feed the ringer pulses to the phone. A significant amount of noise shows up on the pulse train, as shown here:

Oscilloscope screenshot showing a close-up of the ringer circuit output under normal operation. There is visible 67 kHz noise at about 200 mV peak to peak.

I’m certain it’s coming from the KS0835F. If I had to guess, there’s some kind of boost regulator on the board that’s switching at roughly 67 kHz. That wouldn’t be terribly surprising, given the KS0835F is tasked with turning 5 Volts into 48 Volts, with enough current to actuate a mechanical bell.

With this in mind, I’ll design the boards so that the KS0835F’s power source and the ringer circuit are thoroughly decoupled from the rest of the system. Hopefully that will keep this noise from propagating anywhere it might become harmful.

But wait, why do I need this circuit at all? Wouldn’t it be easier to simply drive a microcontroller pin in the necessary pattern to ring the phone? Well, yes, but that solution wouldn’t scale to meet my needs. Since I want this system to be modular, and it’s likely to end up using a simple 8-bit microcontroller with limited IO, I’m going to need to implement a bus architecture. That way, every phone line’s inputs and outputs can be multiplexed onto the same handful of pins. It will be a lot easier to do this if the line cards can generate their own ringer signals, and all I have to do is write a single bit that means “start ringing.”

Although the ringer circuit above works, it isn’t very efficient. The AND gate is unnecessary; instead, I could just feed the output of the 0.2Hz timer into the reset pin of the 20Hz timer. The upside of the AND gate is that it allows the 20Hz timer to run continuously, which made it easier to tune the potentiometers. But, that’s moot because you don’t really need the potentiometers either! As long as the 220nF capacitor’s tolerance is decent, you could swap out that whole resistor-potentiometer chain for a pair of 160k resistors. All of my 220nF capacitors have a 20% tolerance, so the potentiometers were necessary for this prototype. I’ll order some better capacitors for the final design.

Humming Time

There are two subsystems I could tackle next:

  • The tone generator, which will… generate tones.
  • The DTMF decoder, which will turn key presses into digits that can be read by a microcontroller.

I decided to knock out the tone generator first. This seemed like a good idea, since I didn’t feel confident about my preferred approach.

While I’d love to use a purpose-made chip for this task, call-progress tone chips seem to be incredibly rare and expensive. I could cook up a fully custom analog circuit to generate the various call-progress tone frequencies, but this would balloon the part count, and thus the cost of the board. Ideally, the microcontroller that will eventually handle the switching logic will also generate the tones. That will keep the part count down, without resorting to exotic special-purpose parts.

The obvious solution is Mozzi, an Arduino library that generates complex tones using a simple PWM pin. I’d like to use Arduino for this project, since the business logic isn’t terribly complex.

Mozzi was easy to set up, but the docs severely understate the CPU overhead. This code works fine:

void setup()
{
  startMozzi();
  toneGen1.setTone(Tone::Dial);
}

void loop()
{
  audioHook();
}

This code results in an output tone that’s rife with popping and crackling artifacts:

void loop()
{
  audioHook();

  if (digitalRead(HOOK) == HIGH) {
    toneGen1.setTone(Tone::Dial);
  } else {
    toneGen1.setTone(Tone::None);
  }
}

Also, no, setTone is not some complicated, long-running function.

With Mozzi, 2 audio outputs, and a plain ol’ Uno R3, your main loop can have essentially nothing that runs on every loop iteration except for the mandated audioHook() call. Anything more, and the audio output turns into a distorted mess.

The solution I came up with basically looks like this:

void loop()
{
  audioHook();

  if (!checkLoop()) {
    return;
  }

  if (digitalRead(HOOK) == HIGH) {
    toneGen1.setTone(Tone::Dial);
  } else {
    toneGen1.setTone(Tone::None);
  }
}

checkLoop only returns true once every 10 milliseconds. That’s slow enough to eliminate the audio distortions. It also means the switching logic can only run 100 times per second, but my gut tells me that will be adequate.

Updated Prototype

The updated prototype looks like this:

Photo of the prototype. A blue telephone sits on a table to the left of a breadboard and an Arduino.

Not the prettiest thing around, but hey, it does the job. I can ring the phone by plugging the white jumper wire into 5 Volts, and when I pick up the phone, I hear dial tone.

The updated ringer circuit is towards the left side of the breadboard. I implemented the changes described earlier, removing the potentiometers, tweaking the timing, and binning the 220nF capacitor:

Schematic diagram of the improved ringer circuit.

The right side of the breadboard features an ICL7660 voltage converter, one of my favorite ICs. This delightful little chip takes in a positive voltage, and spits out a negative voltage of the same magnitude. Its only required supporting parts are a couple of 10uF capacitors: one for timing, and a second for cleaning up its output. Here I also gave it a third cap, a 4.7 uF decoupling capacitor on its input.

I love the x7660 voltage converter chips for their simplicity. I especially like the Renesas’s version, the ICL7660, for its higher-than-usual 45 mA output current rating; most other models are rated for ~20 mA. Since a common application of these chips is to put two of them in parallel to reduce output impedance, I wonder if Renesas gets their higher current rating by simply putting two 7660 circuits in one trench coat.

Enough about voltage regulators. Here’s the interesting part. This circuit sits between the Arduino’s audio output, and the KS0835F’s audio input:

Schematic diagram of the tone generator’s filtering circuit

C1 serves only to filter out any DC component on the incoming signal. R4 and C4 form a low-pass filter with a 3700 Hz cutoff frequency. But, the heavy lifting is done by a multiple-feedback band pass filter formed by R1, R2, C2, C3, R3, and U1. This is effectively a unity-gain circuit when you take component tolerances into account (on paper, peak gain is 0.96). The filter’s pass band is 260-3600 Hz. This lines up nicely with the 300-3300 Hz band typical of classic analog phone service, giving a little breathing room on both ends.

Debugging

Although I had a working circuit, there was a problem. When I picked up the phone, I could hear the tiniest bit of high-pitched whine coming from the earpiece. It was hard to hear behind the much louder dial tone, but it was definitely there. Also, the audio signal heading into the KS0835F was hideous:

Oscilloscope screenshot showing a very noisy signal.

Eeeeww.

Here’s a close-up of the noise:

Oscilloscope screenshot showing a close-up of the noise. The oscilloscope reads the noise’s frequency as 16.39 kHz, 95.8 mV RMS.

Thanks to this 16.4 kHz noise, we’re clocking in at a nasty 13 dB SNR.

Besides the objectively terrible SNR, the high-pitched whine is just annoying. But, where is the noise coming from? I revisited my calculations for the filter circuit, to see if it might have been oscillating. It wasn’t. As previously mentioned, both the KS0835F and ICL7660 generate some noise, but that’s all far outside the audible range.

I spent an hour trying to track down the source of the noise. I tried different capacitors. I tried adding more decoupling capacitors. I replaced the ICL7660. I replaced the op amp. I removed the ringer circuit. Nothing was working.

Then, I disconnected the Arduino pin from the filter. The noise went away. I plugged the Arduino back into the filter. The noise came back, even though the phone was on-hook, and the Arduino ostensibly wasn’t producing any sound.

Wait, 16.4 kHz… that’s awfully close to 16,384.

#define MOZZI_PWM_RATE        16384

Oh. Duh.

I bumped up Mozzi’s PWM rate to 32768, and the noise problem suddenly got a lot better:

Oscilloscope screenshot showing a much cleaner signal.

The filtering circuit does a better job of attenuating this much higher 33 kHz noise. Its peak-to-peak amplitude is cut from 270 mV to 120 mV:

Oscilloscope screenshot showing a close-up of the reduced noise. The oscilloscope reads the noise’s amplitude as 27.7 mV RMS.

This brought the SNR up to about 23 dB. That’s not bad, considering we’re generating sine waves with an Arduino PWM pin.

Next Steps

I can make the phone ring, I can generate tones, and I can detect on/off hook status. That’s pretty good progress. I’m satisfied with the performance of the ringer circuit and the tone generator. It appears that the KS0835F will be adequate for my needs. I can confidently lock in these subsystems, and move on to the next steps. I’ve saved the hard parts for last, though. Next, I’ll start to decode the phone’s key presses.