Cellular conversion of vintage rotary phone

GSM conversion for rotary phone, using Arduino Pro Mini with Atmega328, a QCX601 SLIC module and a SIM800 breakout board.

Similar projects worth following
Ok, this has been done before, but anyway – here's my take on it! Most conversions have been pretty invasive on the vintage phone. I wanted a simple and clean conversion, keeping the phone intact and the mess inside to a minimum. The biggest issue would be supplying high voltage power to the bell ringer, but searching for solutions I stumbled upon the thing called SLIC – Subscriber Line Interface Circuit. Basically it would supply the phone with a built in local phone line, taking care of ringing and everything. The cheapest one I could find was the QCX601, so I ordered one of those. Hooked it up to an Arduino Pro Mini with code based on the datasheet specs, together with a SIM800 breakout module, and it worked like a charm! Well, almost :) When routing the audio with everything else via the SLIC, there was a horrible amount of GSM interference in the receiver during calls. Oh, well... no biggie. When connected directly to the SIM800 board, the interference was gone.


Schematics of the conversion.

Portable Network Graphics (PNG) - 94.13 kB - 12/09/2016 at 17:36



Arduino code for the project.

ino - 10.00 kB - 12/09/2016 at 17:35


  • 1 × Vintage rotary phone in working condition I used the L.M. Ericsson Dialog
  • 1 × Arduino Pro Mini, ATmega328 Using the 16MHz version with the 3.9V from battery directly to VCC seems to work fine
  • 1 × QCX601 SLIC module Using other modules would probably require changes to code, but nothing major
  • 1 × SIM800 or SIM900 series breakout board, mini version Make sure it's got breakouts for mic and speaker
  • 1 × Li-ion charger board for 18650 battery The kind with power output

View all 19 components

  • The basics of the code pt.2 - Switch Hook and pulses

    Johan Berglund12/12/2016 at 18:17 0 comments

    Now this needed some more attention. The switch hook pin (SHK) of the QCX601 not only tells us if the receiver is on hook or not. Due to the way pulse dialing works, it also presents us with the pulses from the rotary dial. To keep track of everything that's happening on that pin, we have to do some timing work again.

    With the receiver on hook SHK is LOW, and off hook SHK is HIGH, so pulses coming in will be SHK going low about 60 milliseconds for every pulse. If the time since the last pulse is more than 500 ms we know we just got a complete digit. For accuracy and immunity against false pulses we want 10 to 15 ms of debouncing, so that's another timing thing to the list. To know when we have a complete number, we assume that ten digits is a complete number (you can easily change this) and if the number is shorter, it will be dialed anyway if there are no new pulses for six seconds. Finally, if we put the receiver back on hook for two seconds or more, the dialing should be aborted and we should go back to the idle state. So, we have a bunch of timings to define...

    #define tNewDig 500     // time since last SHK rising edge before counting for next digit
    #define tHup 2000       // time since last SHK falling edge before hanging up/flushing number
    #define tComplete 6000  // time since last SHK rising edge before starting call
    #define tDebounce 15    // debounce time

    And then the code being looped while in the number getting state:

    // count groups of pulses on SHK (loop disconnect) until complete number
    // if single digit, fetch stored number
    // then make call
    if (pulses && (unsigned long)(currentMillis - lastShkRise) > tNewDig) {
      // if there are pulses, check rising edge timer for complete digit timeout
      digit = pulses - 1; // one pulse is zero, ten pulses is nine (swedish system)
      // for systems where ten pulses is zero, use code below instead:
      // digit = pulses % 10;
      Serial.println(digit); // just for debug
      // add digit to number string
      number += (int)digit;
      pulses = 0;
    if ((shkState == LOW) && (edge == 0)) {
      edge = 1;
    } else if ((shkState == HIGH) && (edge == 1)) {
      Serial.print(". "); // just for debug . . . . .
      edge = 0;
    if ((digits && (shkState == HIGH) && ((unsigned long)(currentMillis - lastShkRise) > tComplete)) || digits == 10) {
      // if completed number (full 10 digits or timeout with at least one digit)
      // check if shortnumber/fave and then tell GSM board to initiate call 
      if (digits == 1) getFave();
      Serial.print("Number complete, calling: ");
      number.toCharArray(numArray, 11);
      #if defined(GSM_MODULE)
      state = ACTIVE_CALL;
    if ((shkState == LOW) && (unsigned long)(currentMillis - lastShkFall) > tHup) {
      // reciever on hook, flush everything and go to idle state
      Serial.println("On hook. Flushing everything. Going idle.");
      state = IDLE_WAIT;

    Like that. As you can see, I also put in a speed dial function where single digit numbers are checked against a list with the getFave() function and replaced with complete numbers. Also, you can see the default state of the code is for use with Swedish phones where the dial goes from 0 to 9. That's the line saying...

    digit = pulses - 1;
    Most likely you don't want that, so you should use this line instead...
    digit = pulses % 10;
    That % in there (modulo) makes the digit value the remainder when dividing the number of pulses by ten. Basically it's using wrap around to get 0 instead of 10. I stole this little gem, I admit that. Looks so much nicer than the if-based alternative, and I got smarter by looking the % thing up ;)

    As for the debouncing of SHK, it's done whatever state we are in, and it looks like this. The currentMillis used for all timings is set here too.

    currentMillis = millis(); // get snapshot of time for debouncing and timing
    // read and debounce hook pin
    currentShkReading = digitalRead(shkPin);
    if (currentShkReading != lastShkReading) {
      // reset debouncing timer
    Read more »

  • The basics of the code pt.1 - Bell Ring Control

    Johan Berglund12/12/2016 at 12:11 0 comments

    Ok, so the main things I had to do dealing with the SLIC module was to have it create the ring signal, and to make the pulses and states of the switch hook pin control the actions of the phone. The ringing was the easier part, as it was pretty much exactly described in the QCX601 datasheet.

    Early on I had decided to base my code on timings using the millis() function, so the "ringing" took this form, based on that diagram:

    // Ringing interval 
    // How much time has passed, accounting for rollover with subtraction!
    if ((unsigned long)(currentMillis - ringPreviousMillis) >= ringInterval) {
      digitalWrite (rcPin,1); // Ring
      // Use the snapshot to set track time until next event
      ringPreviousMillis = currentMillis;
    if (digitalRead(rcPin) && ((unsigned long)(currentMillis - ringPreviousMillis) >= ringDuration)) {
        digitalWrite(rcPin, 0); // Silent after ring duration
    // 25Hz oscillation      
    // How much time has passed, accounting for rollover with subtraction!
    if ((unsigned long)(currentMillis - oscPreviousMillis) >= oscInterval) {
      // It's time to do something!
      if (digitalRead(rcPin)) {
        digitalWrite(hzPin, !digitalRead(hzPin)); // Toggle the 25Hz pin
      // Use the snapshot to set track time until next event
      oscPreviousMillis = currentMillis;
    The rflPin is set to be LOW all the time, and the timings are defined like this for typical Swedish ringing:
    #define oscInterval 20
    #define ringInterval 6000
    #define ringDuration 1200
    Basically, for every lap in the loop when in ringing state it does this...

    If it's time to sound the bell again, put the RC pin HIGH, then reset the time for this (wait 6 seconds until next go).

    If the RC pin is HIGH, and it has been 1.2 seconds since it was put HIGH, set the RC pin LOW to stop ringing.

    If it has been more than 20 ms since last toggle of the 25Hz pin, reset the time for this and toggle the 25Hz pin should the RC pin be HIGH.

    (And then there are checks if we should stop all the ringing and go to some other state, of course.)

View all 2 project logs

  • 1
    Step 1

    Plan the placement of components inside the telephone. I had no problem fitting the SIM800 mini board with antenna on the right, and the rest of the stuff on the left side. Using perf board, you can create a nice and compact PCB to fit in there, maybe even screw in place with a bracket.

  • 2
    Step 2

    Program your Arduino Pro Mini. You need to download and install the SIM900 library first. Then you edit the project Arduino code to set these two things:

    1: The kind of rotary dial you have. In Sweden where I live, the dial starts with 0 and ends with 9. This means that one pulse sent is a zero, and ten pulses equals number nine. If this is the case with your phone, you don't have to change anything. Otherwise, you edit the code where it says..

    digit = pulses - 1; // one pulse is zero, ten pulses is nine (swedish system)
    // for systems where ten pulses is zero, use code below instead:
    // digit = pulses % 10;
    //digit = pulses - 1; // one pulse is zero, ten pulses is nine (swedish system)
    // for systems where ten pulses is zero, use code below instead:
    digit = pulses % 10;
    That way, it sets the digit to the number of pulses sent, unless there are ten pulses, in which case the digit should be zero.
    2: Set your own favourite numbers in the getFave() function for speed dial. Just edit the numbers. Add more favourite numbers if you like. Just follow the pattern of the previous numbers. (To use a speed dial, you pick up the receiver, dial the single digit number and wait six seconds for number collection routine to time out. You can set a shorter time out if you want to. It's the tComplete number in milliseconds in the code.)

    Hook up your Pro Mini using the FTDI programmer and upload the sketch.

  • 3
    Step 3

    Solder stuff together (or use pins and dupont connectors where suitable) according to the schematic. Using a socket for the Pro Mini is a good idea. Makes it easy to replace it or remove it for re-programming.

View all 5 instructions

Enjoy this project?



robot797 wrote 04/19/2020 at 22:19 point


I am building this

but it has problems reconizing the on-hook switch

and the serial gets spammed with sim900 data

can you help me?

  Are you sure? yes | no

Kiureli Sammallahti wrote 10/12/2019 at 15:15 point

The project seems interesting - however I think I’m taking the easy way out to give my Ericsson Dialog new life in 2019 - I’m connecting it to a GSM gateway that understands pulse dialing. The questions I have are -

1) is the voltage (12V or so) a regular gateway would supply enough to ring the bell of the Dialog?

2) I’m stuck at the very first step - how to open the phone for replacing the phone socket plug wire with a modular plug wire? How do you open this thing?

  Are you sure? yes | no

ville.lindblom wrote 03/20/2019 at 10:15 point

Nice project, so nice that I must have that also. Just got all of components and next phase is to choose color of phone and assemble it ;)

  Are you sure? yes | no

nilssonoscaremil wrote 12/01/2018 at 08:51 point

Hi Johan!

I know it has been some time since you completed and posted your conversion but if you by any chance still remember/reads this thread. I would like to know if you had to do anything about the original speaker in the phone? Did you swap it for another speaker element or are you using the original one? And how  are you driving it? When i drive mine straight from my Adafruit Phona, the volume is still so low that its basically impossible to hear anything :) 

  Are you sure? yes | no

Johan Berglund wrote 12/01/2018 at 09:03 point

Hi, I guess that depends a lot on how hard the speaker element is to drive, and what the Adafriut Phona is capable of. I used the original speaker, and it worked fine with the GSM board I was using. If you are using a SLIC board like I did, you could try running the audio through the SLIC, not removing the handset wires from their original screw posts in the telephone. I got more interference that way, but it worked that too.

  Are you sure? yes | no

K.C. Lee wrote 12/12/2016 at 15:36 point

FYI:  Here is an open source DIY SLIC built using AVR/Arduino for those who are more adventurous. "A $10 ATA"

part 1:
part 3:
part 4:
part 5:

  Are you sure? yes | no

Saabman wrote 12/12/2016 at 02:36 point

this could be adapted to a working version of Maxwell smarts (agent 86) shoe phone 

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates