Long ago, I built a circuit which enabled a PC to monitor the phone line, and control the music system in our old house. Whenever somebody rang, it would pause the music, and announce the caller using voice synthesis. It was cool. Details are on a pre-blog page.
Alas, when we moved house, the hardware got lost, and ever since, we have had to manually pause the music, and look at the tiny display on the phone to see who was calling. I had been meaning to reinstate the automatic system for ages; it had actually been quite useful.
Photo © Flickr user GijsbertPeijs
The other motivation for this project was as an excuse to play with the Raspberry Pi.
I had been reading about these very cool devices — a tiny Linux machine costing around €45. The Raspberry Pi Foundation, the charitable body who are making this all happen, have done an amazing amount of work and achieved a huge amount.
It was quite nostalgic to receive my Raspberry Pi, connect it to the TV, and watch slightly fuzzy text scroll up the screen as it booted. Just like the Dragon 32 I played with a lot when I was young. Ah, the good old days.
This project does not fully exploit the Raspberry Pi’s power, so I might try to think of other jobs it could do.
Interface to phone line
The old version of this project involved capturing the audio of the caller-ID signal, and decoding it in software. That was fun, but I didn’t want to re-do it all. This time I wanted to try using USB modems. Rummaging around on eBay revealed modems based on the Conexant 93010 chipset. A brief read of the data sheet suggested that such a modem would take care of monitoring everything at a fairly high level (local on-/off-hook state, ringing detection, CID), but more careful reading revealed it wouldn’t, or at least not obviously to me.
But there was a feature which I thought I could press into service. The modem can read the tip/ring voltage, so I thought I could poll this and assess the line’s on-/off-hook state that way. This worked; I could poll the voltage at say 50Hz and detect on-/off-hook changes. But if the phone rang during this polling, sometimes the modem would not give me the ringing or caller-id messages. Gah. Perhaps it’s too busy responding to my voltage-measurement requests.
Whatever the underlying reason, I adopted the obvious solution and bought another modem, so I could have one for polling the line voltage, and one for notifying me of ringing or caller-id:
Quite a bit of the time on the old project was spent grovelling around in the server software for the music system. This is written in Perl, and I didn’t really feel like diving into that again. I therefore declared voice synthesis out-of-scope for this project. The rest of the requirements for control of the music server could easily be met without having to write any Perl; or, more to the point, read any.
Instead I used Python: it has threading, serial I/O for talking to the modems, and a package for controlling the Logitech Media Server (as the Slimserver or Squeezeserver or Squeezecenter is now known — temporarily, it seems: see Epilogue below).
I did ponder, given the telephony application, whether this would also be a good excuse to learn Erlang. Some people on the intertubes are playing with Erlang on RPi. That can be a separate project.
The overall system design is a handful of threads communicating via messages queues:
A few more details about these pieces:
There is a thread per modem:
This thread tells the modem to report caller-ID information, then just waits for text to arrive from the modem’s serial port. The only strings it should get are: RING, which is passed straight down the message queue; or a caller-ID string, which is parsed into a (date, time, number) tuple and then passed as a message down the queue.
The other modem thread polls the tip/ring voltage every 0.2″ (a lower polling frequency than in the initial experiments), and uses hysteresis with some experimentally-chosen thresholds to decide whether the phone is off- or on-hook. When the state changes, the thread sends a message (either ‘off-hook’ or ‘on-hook’) down the queue
The next piece is a thread which reads these messages, and turns them into a higher-level description of the state of the phone line. The main states are
- Inbound call
- Outbound call
with some transient housekeeping states as well. Transition between the states are triggered either by receipt of a message, or after a time-out:
Transitions or, almost equivalently, entry to a state can generate a message to the Media Server Controller telling it what to do and/or what’s going on. These messages are shown in this figure as small circles.
Caller-ID is fudged a bit. Receipt of a caller-ID message at any point just sets a variable holding the ‘current live caller-ID information’. This is cleared on entry to the Quiescent state. The ‘CID’ small circle means that the current caller-ID information is sent down the message queue to the Media Server Controller.
Media Server Controller
Finally, there’s a thread which controls the Logitech Media Server. It pauses the music, resumes the music, and displays status and caller-ID information, based on the messages it receives. A slight wrinkle is that the Media Server (to which the Controller connects via TCP) handles the ‘display text for 2″’ command in a blocking fashion. During this time, messages from the State Machine can be piling up, so they have to be processed in a batch.
It is here that we translate incoming caller-ID phone numbers into names, if known. This is driven by a CSV dump of our Google contacts list, so will need refreshing now and again. The actual look-up is done using a key consisting of the number with all non-digits removed, and also any leading zeros removed. This handles the inconsistent way in which local, national, and international incoming numbers are presented.
The system works well. The Raspberry Pi holds a TCP connection to the Media Server for many days, seemingly indefinitely. The Raspberry Pi has been very stable, after doing some initial updates to get the latest kernel and so on. Currently, making a new TCP connection after power out or reboot of either device is not automatic. I might fix this if it becomes annoying.
There are a couple of things which would enhance this:
- Log calls to a database.
- Bring voice synthesis back into scope. This would be a fairly major undertaking within the existing Perl software, but I could possibly rig up some small speakers.
- Detect brief on-hook periods during a call. Once I accidentally put the phone down for a second or two while on the phone; the call remained, but this system thought it was a new call. A short-lived ‘hook-flash’ state before returning to Quiescent would fix this.
While writing this post, I found a report that Logitech are discontinuing the Squeezebox line of products. Ugh. This is a shame — I’ve enjoyed the combination of audio quality, small form-factor yet good display, open development process, and free-software friendliness. Not looking forward to having to do all that research again if/when my Squeezeboxes give up the ghost.