Building your own pulse to tone (DTMF) converter

Related article: A crash course in using GPO telephones in the modern world

A few months ago I purchased a Rotatone module for one of my vintage telephones. It’s a great product, but in the event that you find something you don’t like about its functionality, there’s not a lot you can do about it, because it’s not an open source product. Also if on a very tight budget, you could save yourself a bit of money building your own too.

Rotatone module (red) installed into a GPO 746F

Fortunately there are a few projects kicking around the internet which make for quite a good starting point. One of the earlier efforts was undertaken by Boris Cherkasskiy and subsequently improved on by Arnie Weber on this bitbucket repository. Arnie includes some gerbers with this, which I’ve fired off to Seeedstudio PCB and made some of my own of, forming the basis of this project. I have to say it is quite a luxury to be building someone else’s design for once!

Arnie Weber’s pulse-to-tone converter PCB – as constructed by him
For the benefit of those who don’t use Eagle (like me) this is Arnie’s rendition of Boris’s original design.

All of these are based on the application note AVR314 from Atmel, which includes some tricky code that generates DTMF tones using the PWM module of an AVR. Complicated stuff which I am not going to go into the details of.

My build of the original design

My changes to the design

My version of the PCB

When I built the original design, I found that while it did work – there were a couple of problems with it:

  • It would only dial on a VOIP terminal adapter (Cisco VIC in my case). My BT Landline didn’t seem to recognise the DTMF tones
  • It presents a significant AC load on the line, attenuating speech by 70%, making conversations rather hard to hear

For these reasons I wouldn’t recommend building the original design. Below is my revised schematic:

The main change to the design is the power supply. By replacing the Zener and Resistor arrangement with a 78L05 – the AC load is virtually nothing, meaning speech volume is now the same as it was before installing it.

We can see how this problem sneaked in: R1 of the original design was first specified to be 2K, which kept impedance in check, but left it with a voltage droop problem, this was revised to 220 ohm, which fixed the droop problem, but then meant the adapter was shunting the line through the 200uF capacitor. Having a 78L05 fixes all of this, and we can do away with that 200uF capacitor while we’re at it.

This also happens to fix the BT landline problem, as that was simply that the tones were too quiet, thanks to it attenuating its own output!

I also removed the resistors on the HW de-bounce circuit, now utilising the AVR’s internal pull-ups. We don’t need that transistor either – this has been replaced by a resistor (R1) which effectively does the same thing, with the AC path now through the VCC pin of the AVR, then through the 78L05.

My version of the PCB installed in a GPO 746F

One drawback of this design is that the power supply of the AVR is coupled to the line, meaning we can hear it executing instructions through the earpiece. This is not a problem in practice however because my version of the code puts the AVR in power-down mode during all idle times, thus eliminating the problem.

It is however still possible to hear the AVR processing each pulse through the earpiece. This is heard as a “click” which in my opinion is desirable as this emulates the clicking noises which would have been heard when the rotary dial was in circuit.

Fitted to a 332F (Wedged under the 303A key)

My version of the code

I have made quite a few alterations from the original implementation – adding “Redial” functionality, as well as simplifying the programming of speed dials.

As previously mentioned, in my implementation the AVR is in power-down mode during all idle times, essential when building my version of the hardware.

You can get my code here on github. Alternatively the code on Arnie’s repository will work – provided that internal pull-ups are enabled on PB1 and PB2. Functionality differs significantly.

Gerbers for my version of the PCB

Can be downloaded here.

If you are new to this, mine were built with Seeedstudio’s $4 PCB service.  Default options on their service are OK for this board.

Wiring it up

The exacts of how these are wired varies significantly from phone to phone, you’re on your own there. It is safe to say that interchangeability between converter designs is good. My GPO 746F was originally wired for a Rotatone, and I found that this board dropped straight in its place, with the red wire connected to LINE, and the blue wire connected to GND.

I kept the original blue, pink and grey wires for the connection to the dial, which are wired on as pictured.

Use with NZPO phones (reversed dial)

NZPO Issue GPO phones with reversed dial (Left: 332F, Right: NZPO 100 – New Zealand made 746F)

In the unlikely event you are installing this device into one of these phones, you’ll need different firmware for the converter. Select the “NZ” version of the .HEX file when downloading from the link below.

The reason is because while the numbers appear on the dial in reverse order, the mechanism behind is identical to GPO phones, so this must be translated in software.

I believe there are other dial layouts which at present are unsupported however could be supported easily by modifying and rebuilding the source code.

Shopping list

I ordered most of the parts for my board from the Mouser UK website.

  • 1x ATTINY85V
  • 1x 78L05 5V Regulator
  • 1x 4 MHz crystal
  • 2x 22pF ceramic capacitors
  • 4x 0.1uF (100nF) ceramic capacitors
  • 1x 680 ohm resistor
  • 1x 27V 1W zener diode
  • 1x 4 position 8.25mm pitch barrier terminal i.e. OSTYK42104030, Molex 38700-7504, Molex 38700-7304 (with mounting ends).

Programming the AVR

There’s quite a few different ways to do this, I’ll let you do your own research. Your programmer will need:

The .HEX file from here

Correctly set fuse bytes:

  • LFUSE: 0xFD
  • HFUSE: 0xDF
  • EFUSE: 0xFF

The user manual (for boards running my code)

Normal dialing

Just pull the dial round from the desired number to the fingerstop and release immediately.

Dialing * and #

for * Pull the dial from ‘1’ and hold on the fingerstop for 2 seconds until you hear a beep, release, and a tone for * will be generated.

For # do the same but use ‘2’.

Redial

Pull the dial around from ‘3’ and hold on the fingerstop for 2 seconds. Release when you hear the beep, and the last number will be redialed.

Speed dials

There are 7 speed dial locations. 0, 4, 5, 6, 7, 8, 9. Pull the dial around  to the fingerstop and hold for 2 seconds, release when you hear the beep and the desired number will be dialed.

Programming a speed dial

Pull the number of the desired location around to the fingerstop and hold for 4 seconds, you will hear a two-tone beep. Release the dial.

You are now in programming mode. Now simply dial out the number you wish to store, releasing the dial immediately after each digit. You will hear a simple beep after digit. Once complete, hang up, the number is now saved.

When doing this you will likely have your telephone exchange furiously beeping at you because you haven’t dialed a number. If annoying, you can enter the BT “quiet line test” (For people in the UK) during programming.

28 thoughts on “Building your own pulse to tone (DTMF) converter”

  1. Good morning!
    Very impressed reading about your pulse-to-tone converter. I have tried most of the commercial ones and found them all lacking in one respect or another.

    May I ask you two questions please?
    1. Does using your converter require the user to wait a palpable amount to time between digits?
    2. Are you in a position to sell me a PCB and a programmed ATTINY85V?
    This will be for my own private use and not sold on , nor will I do anything evil with it. Happy to pay a generous price, as I appreciate the cost of developing projects like this.
    Thanks and congratulations on a great website!

    1. If you’re wanting to “buy” something from me, probably easier to just buy the Rotatone. It’s 95% the same as this! This project is just for D.I.Yers.

      Actually the main difference is that this board has a 4s timeout between speed-dial and program-speed-dial whereas the Rotatone is fixed at 1.5s (which for me was too short).

      The Rotatone also has some ability to configure various unlikely-to-need-to-be-changed settings by dialing digits on to the “3” position (redial) which I haven’t bothered with.

      On my design the delay from last pulse to tone is 128ms, the tone is 100ms long so you could then start dialing another digit after that. It’s 228ms minimum between digits.

  2. I made a few mods to tweak the code to my liking.

    I made the state revert back to normal STATE_DIAL state after the redial or # or * functions are used. Previously, the ATTINY85 remained in the STATE_SPECIAL_L1 state until hangup.

    I added the capability of storing a # or * in redial memory.

    I added the capability of adding a * or # into any memory location.

    the updated main.c is at:

    http://projectmf.homelinux.com/pulsetotone/

    Don

  3. Thanks for the detailed instructions. I was able to make the boards and program the ATTiny with little fuss. Just a question, does the +line out need to go through the hook or just straight onto the positive on the line where it comes into the phone.

  4. Question:
    During dialling, are the dial pulses sent forward or shunted out?
    I am investigating the use of this pulse to dtmf converter with a modified WE 7A Rotary exchange. If pulses are sent forward they will cause the 7A Rotary connecting circuit to go into premature release.

      1. OK …. I’m working on it. Compiles fine with Atmel Studio and I’ve just programmed an ATTiny45 successfully. I’ll keep you posted.
        PS: Are you a n ex pat Kiwi ?

  5. In the original schematic, the 4K7 resistor and 100N cap filtered the PWM signal from the micro to the bass of the emitter follower. Are you relying on C1 and C2 100N caps to do this now ? Cheers

  6. Using Atmel Studio to compile and progam an ATtiny45: – No good at all. Runs crazy with all sorts of whistles and buzzes. An ATtiny85 programs and runs fine.
    There are a number of differences between the 2 chips apart from memory sizes. Perhaps Studio is not taking care of some housekeeping with the change of micro. I told it I was using a tiny45 in the project configuration too. I’m an assembly language freak so I’m used to taking care of this stuff. All I can think of is some C compiler setting needs to be checked or other. I’m not a C compiler expert.
    Wow , with a working tiny85, it draws very little current even when dial switches are pulling enabled pullups to ground. Some clever disabling of pullups when not needed I suspect. Nice work Matt. Cheers Jim

    1. As an assembly language freak you are better prepared than 95% of others to make the necessary changes to run on tiny45. I’m happy to incorporate them if you end up doing so.

      1. Looks like I haven’t got enough SRAM in the Tiny45 (256 bytes)
        I’m not an AVR GCC expert but guess that constant program data (DTMF data perhaps) is being copied to SRAM on startup maybe.

        1. Yeah I think it is. A while back I experimented with putting it in flash – I believe it worked so should be possible to move it.

          1. Ok succeeded in reducing SRAM usage by 128 bytes (Sin Table) by keeping its use in program memory only.
            So it doesn’t get shifted to SRAM on startup like before.
            Runs fine like this on a Tiny85, but still not on a Tiny45.
            I even reduced the EEPROM use by reducing size of SPEED_DIAL_SIZE to 4 digits temporarily.
            This is starting to piss me right off now !

  7. Makefile reads ‘MCU = attiny45’
    SRAM is reduced to 54 bytes.
    EEPROM 32 bytes (8 slots x 4 digits)
    Program: 2306 bytes (1153 words)
    Fuse bits are the same. Low FD High DF Ext. FF
    Runs fine on the tiny85 but not th 45

  8. My reason originally was because I have plenty of DIP tiny45s and one SOIC 85. Now my reason for wasting time is because there’s an unsolved problem which shouldn’t exist.

  9. Success ! Had to switch off all optimisation to -O0 plus also needed sin table in program memory only. (Not copied to SRAM on startup)
    This is weird shit. I’m done with C compilers for now. I’m heading back to ASM where no one messes with my bits and if it doesn’t work, it’s only my fault! Thanks for your great work Matt. Cheers from NZ

  10. Re: “executing instructions through the earpiece.”
    This can be eliminated or reduced in amplitude if desired by continuing to use the 2nd spare mute switch on the dial (746 phone) wired accross the earpiece. A suitable resistor could be placed in series, to adjust the amplitude if you don’t want to completely eliminate the clicks. 746 phones have 2 mute switches, one wired accross the microphone and one accross the earpiece. In fact it would be even more authentic to use this because currently the dial tone is still sounding while the first number is being dialed, right up untill the first DTMF tones are generated. Normal landline operation has the dial tone cease immediately after start of clockwise rotation of dial.

      1. Sure, but you’re better to just have a look here: https://hannahgoldblattmddn251.files.wordpress.com/2013/04/schematic.jpg
        It’s not actually a change at all. This is standard wiring for a 746 and many other phones.
        D1 and D2 are the rotary dial mute switch (normally open) terminals for the earpiece and D3 and D4 usually used for the microphone.
        Just re-connect the spare unused dial switch in parallel with the earpiece T1 and T2 or experiment with a resistance value in series to just reduce the click volume only.

  11. PS: The only reason you do still hear low level dial clicks on a standard landline is because the dial mute switch accross the earpiece is not zero resistance accross its contacts. If it was a perfect short, you’d hear nothing.

Leave a Reply

Your email address will not be published. Required fields are marked *