Skip to content

ktownsend-personal/RetroPhone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RetroPhone

Work-in-progress

Free for anyone to copy from. I live by the motto "inspire, not require" as much as I can.

This is a hobby project to make a few old phones interactive for my retro room so that visitors can experience old-school landline phones without having to buy phone service.

There is a lot of info in this README, but also some useful stuff in the docs folder like my schematic and some reference materials. Many of the folders also have their own README file. The src/README.md file is a good place to start for the code.

Goals

  • Accurately replicate a real phone experience
    • physical ringing
    • call progress tones
    • rotary dialing (pulse dialing)
    • tone dialing (DTMF)
    • automated phone service messages
  • Dial a few phone numbers and get a simulated response from the "other end"
  • Call one of the other phones on display and talk to whoever answers

Optional Goals

  • wifi for signaling and digitized voice between phones
  • having other devices elsewhere on the Internet and dial them
  • use a digital voice assistant when dialing operator (like Alexa or Google)
  • status web page, possibly with configurable options
    • could have it run just when on-hook, or maybe only if a special maintenance number is dialed and terminate when phone is hung up

Inspiration & Motivation

  • Telephone Central Office Simulator (video) and related GitHub repo from GadgetReboot

    I have a great appreciation for GadgetReboot's willingness to collaborate on our similar projects. He graciously filled in a lot of gaps I had on the hardware side, and I still have much to learn. My military electronics training is about 25 years stale now and I never had the benefit of practical engineering of electronic circuits... all theory and no substance once could say.

  • I got my first ESP32 many months ago and it's been sitting patiently on my workbench waiting for me to get around to it. I never even did a flashing LED demo...leave it to me to start out with something complicated!
  • I've been wanting to get back into tinkering with electronics for many, many years. My software career has been great, but I still have that longing to build stuff with real parts. I've loved both computer programming and electronics since I first discovered them as a child...ESP32 is a great way to do both!
  • I like old phones and I've started to grow a small collection. Why not make them do something?
  • This project has a special connection to a childhood memory. When I was around 12 years old I experimented with the house phone line by putting a speaker on it and hearing a dialtone. I was fascinated, and learned how to dial it by making and breaking the connection rapidly to call a friend and discovered he could hear me if I shouted into the speaker. I took a few old phones apart to study their innards and learned a few more things about them in the process. My parents never knew about that, of course!

Currently Functional

  • physical ringing for incoming call, currently triggered by a button
  • real call progress tones and system messages
    • configurable for North America or United Kingdom sounds
    • mp3 playback for system messages
    • playback sequencing of tones and slices of mp3 files to build a full message, like reading out the number you dialed during not in service error message
  • tone and pulse dialing
  • configuration by dialing star-codes (or 22 instead of * when pulse dialing)
  • dialing any 7-digit number will play appropriate audio for ring, busy or error depending on first digit (temporary demo until I have calls working between devices)
  • RGB LED for status colors & patterns representing call states

Next Steps

  • add filter to block 20Hz ring signal from SLIC's audio out line (mostly to keep it off my external speaker when it's ringing)
  • trunk line via wifi, or wired if wifi affects audio quality

Challenges

  • high voltage ring signal overlaps going off-hook slightly
    • minimized to about 100ms by skipping debounce of SHK
    • appears to be a circuit delay in the SLIC detecting off-hook while ringing
    • might be normal; need to test a real phone line
  • The SLIC's audio output pin has the 20Hz ringing signal while ringing (at audio level, not high voltage), likely requiring a filter or an isolation mechanism (relay, solid-state relay, other options?)
  • software-DTMF with PhoneDTMF library requires too much sample time in the main loop and it murders Mozzi so it requires not using dialtone, and I had repeating digit issues during steady tone presses
    • While trying a new strategy to generate call progress tones I found that PhoneDTMF doesn't murder the new dialtone. It doesn't hear the DTMF tone sometimes, but seems to get it most of the time. The new call progress tone techique uses I2S to the local DAC like Mozzi, but runs as a FreeRTOS task feeding a buffer with multiple samples at a time instead of a single sample during each main loop iteration.
    • I wonder if PhoneDTMF could run as a task with FreeRTOS and have better results?
  • Using Preferences.h to save settings was hanging on the prefs.begin() call at startup. I couldn't find anyone else online having this issue, but I eventually figured out it was a timing issue of some sort. A short delay before calling prefs.begin() was needed. A 50ms delay seems sufficient. I occasionally saw hangs when reading values during startup(), so if you see that try increasing the delay.
  • stopping dialtone fast enough to not hear a blip of it after dialing 1 as first digit required some special handling
    • primarily affects mechanical rotary dial phone; my electronic slimline phone mutes line audio long enough that it wasn't an issue
    • added special callback to stop dialtone on very first instant of SHK falling edge without waiting for debounce
    • dialing-started callback must wait for rising edge to avoid dialing mode change on hangup
    • reduced tone generation chunk size from 576 to 33 (17ms to 1ms) so audio task loop detects cancellation faster and less audio in the buffer to finish playing
    • I wonder if the original POTS system had that issue too?
  • I2S with the internal DAC on the ESP32 has a quirk with popping when I2S first starts due to the pin level being 0V or floating at some non-midpoint value and suddenly jumping to the midpoint value (the zero-signal level).
    • This is partly solvable by initializing the pin with a ramp signal before starting I2S, except for the case where the pin level is floating rather than 0V so we can't predict the starting level to ramp from and our ramp causes a popping sound. We might be able to solve that with a pulldown resistor on the pin, maybe 100K. I need to test this.
    • This isn't much of a real problem since the user is unlikely to have the handset to their ear at startup. I only notice it when I have my external speaker hooked up while testing
  • The SLIC audio output has a popping sound when the ESP32 restarts. I haven't yet determined if it's a power supply issue since we are powering 3V3 from the ESP32 board.
    • Just like the I2S popping at startup, this isn't much of a problem during normal operation.

Notable

  • SHK pin from SLIC bounces a lot, so it requires debounce logic.

    • GadgetReboot noticed it bounces longer when powered 3.3V vs. 5V
  • RM and FR pins on the SLIC are both necessary for ringing. The RM pin sets the higher ringing voltage, and the FR pin flip flops the polarity on high and low cycles. Both are definitely needed, although the electronic ringer on my Sony slimline works fine with just FR toggled, the physical bell on the Snoopy phone requires the RM to have enough power to physically move the armature.

  • The missing fundamental phenomenon is really interesting. Although phones only use frequencies 300Hz to 3400Hz 150 to 3750 Hz, we actually hear lower tones due to this phenomenon.

Thoughts

  • would interrupts be useful in this project so we can put the device to sleep when idle, but still wake up for incoming wifi call or off-hook pin?
  • special response for 911 dialing to clearly say it's not a real phone and cannot be used for emergencies
  • digitizing bitrate can be fairly low because a real phone system caps the upper frequency at 3400 Hz
  • could use an IO pin to control power to the hardware DTMF decoder and level shifter to reduce power consumption, but only necessary if I want to run on a battery
  • PhoneDTMF software decoding might run better as a FreeRTOS task... needs experimentation
  • the DAC has left and right outputs, but since this project is mono we could use the same DAC to generate two independent mono signals if we get fancy with our audio generation loop; I don't have a scenario where that would be useful yet, but it's a cool idea

C++ Tidbits

Although I'm a seasoned software engineer, I'm new to C++ so I've got some notes others may find helpful.

  • for (const auto &line : lines){} enumerate a local array (not pointer to array) ref

  • Serial.printf("..%s..", stringvar.c_str()) to print String type with printf because %s epxects C style string not String object and will garble the value or even crash the app

  • passing arrays to functions, good explanation

    In a nutshell, arg is simple pointer and you pass array directly when calling, but really a pointer to first array value is passed and in the func you can access array values by index like normal...but you can't infer the length and must pass that value separately.

Hardware

Software

Call Progress Modes

I made this chart to help me track what transitional modes I should implement and what is active during each mode. Work in progress and likely to change as I get deeper into it and discover which assumptions aren't correct.

Phone column represents user experience (interactive elements like handset audio, bell ringer). MF- prefix means multi-frequency tone. The Dialer detects dialed numbers on local phone, which is not really in the Trunk category but I didn't want another column just for the Dialer.

State Hook Trunk Phone Notes
Idle ON LISTEN Website • website active for status, statistics & configuration (maybe all the time if not affecting operation)
Ready OFF Dialer MF-Dialtone • switch to Dialing as soon as first number dialed
Tone-Dialing OFF Dialer • restart timeout after each dialed number (maybe not necessary; how does real phone system do it?)
Pulse-Dialing OFF Dialer • restart timeout after each dialed number (maybe not necessary; how does real phone system do it?)
Connecting OFF ROUTE • negotiate connection
Busy OFF MF-Busy • start timeout
Route Fail OFF Recording • "number not in service"
• start timeout
Ringing OFF LOOP MF-Ring • must send signaling to keep route alive
• receiving end can optionally disconnect if no answer after custom duration
Connected OFF AUDIO Live Audio
Disconnected OFF • call audio stream terminated by remote end (ringing or active call)
• start timeout in case user fails to hang up
Timeout OFF Recording, MF-Howler • left off hook too long unconnected
• how long is appropriate for timeout?
• "please hang up and try your call again", then play howler
Abandoned OFF • gave up waiting for you to hang up, so line is abandoned and services are disabled until back on hook
Incoming ON LOOP Physical Ringing • remote end must send repeated or continuous signal, which abandons ringing if it ends

Call Progress Flow

flowchart TD
  subgraph Originator
    idle3(idle) -- off-hook --> ready
    ready -->|dialing\nstarts| dialing(dialing\npulse or tone)
    ready -->|off-hook\ntoo long| timeout
    dialing -->|enough\nnumbers\ndialed| connecting
    dialing -->|off-hook\ntoo long| timeout
    timeout -->|howler\nignored| abandoned
    connecting --> busy
    connecting --> fail
    connecting --> ringing
    ringing --> connected
    ringing --> disconnected
    connected --> disconnected
    connected -- on-hook --> idle(idle)
    disconnected -->|off-hook\ntoo long| timeout
    disconnected -- on-hook --> idle
    timeout -- on-hook --> idle
    abandoned -- on-hook --> idle
  end
  subgraph Receiver
    idle4(idle) --> incoming2[incoming\n-ringing-]
    incoming2 -->|ignored until caller\ngives up or too many\nrings aborts the call| idle2
    incoming2 -->|answered| connected2[connected]
    connected2 --> disconnected2[disconnected]
    connected2 -- on-hook --> idle2(idle)
    disconnected2 -- on-hook --> idle2
    disconnected2 --> timeout2[timeout]
    timeout2 --> abandoned2[abandoned]
    timeout2 -- on-hook --> idle2
    abandoned2 -- on-hook --> idle2
  end
  connecting -.->|routing| incoming2
  ringing <-.->|signaling\nbidirectional| incoming2
  connected <-.->|audio\nactive| connected2
Loading

About

ESP32 project to simulate a telephone network

Resources

Stars

Watchers

Forks

Packages

No packages published