Fairy Tale Phone

Hello Arduino Community

Here's my recent project as a X-Mas gift for my daughters. Using an old telephone with dial wheel and build in a Pro Mini with SD Card to play fairy tales over the ear phone.
I am not yet finished since I am waiting for the SD break out. Until now I soldered some pins on a MicroSD Adapter and a L78L33 regulator to get the stuff working properly

Here's some impressions:

Fist try to catch the impulse signal from the dial wheel and count them. Also playing with tone().

Prototype with all components

And here's the code with the TMRpcm library

/*
=============================================================================================
Fairy Tale Phone 1.0
by ReDiculum (Oct. - Dez. 2013)
-------------------------------
X-Mas 2013 Arduino project.
Using an old Zellweger Modell 70 (anno 1982) telephone with dial wheel.
The wheel can be spinned to count impulses and then playing one of ten different
audio fairies stored as 8bit 16.000khz mono WAV files named from 1.wav to 10.wav

SD Card > Arduino pinout:

CS (1)   -> Pin 4 (Chip select)
MOSI (2) -> Pin 11
MISO (7) -> Pin 12
SCK  (5) -> Pin 13

Ear phone  -> Pin 9 (protect with Diode against Pin 3) and GND
Dial wheel -> Pin 2 and GND
 
=============================================================================================

TMRh20's pcm library for Arduino
https://github.com/TMRh20/TMRpcm
*/
#include <SD.h>
#include <TMRpcm.h>
#include <SPI.h>

#define IMPULSE_PIN 2 // Digital Pin 2
#define PHONE_PIN 3 // Digital Pin 3 (PWM)
#define SD_CHIPSELECT_PIN 4  // SD 
#define LED_PIN 13 // Digital Pin 13
#define TIME_WAIT 2000 // Waiting 2sec during dial wheel turns. Sufficient to catch 10 impulses
int countImpulse = 0;
unsigned long timeWait = 0;

TMRpcm music;  // Create object "music" from class "TMRpcm"

/* Sum of running millis and TIME_WAIT. 
Must be long integer if kids are not dialing quickly :)
*/
unsigned long timeWaitEnd = 0; 

void setup() {
  Serial.begin(57600); // Set console to 57600 baud
  Serial.println("=== FairyTalePhone based on TMRpcm ===");  

  pinMode(LED_PIN, OUTPUT); // onboard LED
  
  music.speakerPin = 9; // Set Pin for SD card audio output

  /* Turn on internal pullup resistor, because we pull the pin to ground.
  When the wheel gives impulse, the contact is opened (no current). That's
  when we have a digitalRead = 1.
  */
  digitalWrite(IMPULSE_PIN, HIGH);

  Serial.println("Ready to dial ...");

  // Play tone and wait here until the dial wheel has sent his first impulse 
  tone(PHONE_PIN,420);
  while (digitalRead(IMPULSE_PIN) == 0) {
    delay(1); 
  }
  noTone(PHONE_PIN); // Pssht! Here we go!

  Serial.print("Counting impulses ");
  timeWaitEnd = millis() + TIME_WAIT; // Summarize already passed millis since Arduino runs and TIME_WAIT.

  // Count all the dialed impulses until timeWaitEnd has reached
  while (millis() < timeWaitEnd ) {      
    if (digitalRead(IMPULSE_PIN) == 1) {
      /* Increment the amount of impulses if the contacts are opened.
      The wheel pulses with an interval of approx 70ms
      */
      countImpulse++; 
      Serial.print(".");
      digitalWrite(LED_PIN, HIGH); // Blink LED ...
      tone(PHONE_PIN,1000); // ... and short tone (70ms) of 1khz to fill the gap
      delay(70); 
      digitalWrite(LED_PIN,LOW); 
      noTone(PHONE_PIN); 
    }
  }

  Serial.println(" "); // New line

  /* If the dial wheel has been turned more than once by nervous kids
  and the amount of counted impulses exceeds 10, we just restart Arduino
  */
  if (countImpulse > 10) {
    Serial.print("Count exceeded 10 ("); Serial.print(countImpulse); Serial.println("), restarting"); 
    hangOff();
    asm volatile ("  jmp 0"); // Soft Reset
  } else {    
    Serial.print("Preparing fairy tale number "); Serial.println(countImpulse);
    
    // Initialize SD Card with library SimpleSDAudio
    if (!SD.begin(SD_CHIPSELECT_PIN)) {
      tone(PHONE_PIN,100); delay(250); noTone(PHONE_PIN); delay(400);
      tone(PHONE_PIN,80); delay(600); noTone(PHONE_PIN);
      Serial.println("Error initializing SD Card");
      while(1);
    }

    /* define a char array with an index size of 6 and convert
    integer "countImpulse" to char "track" using utoa() function
    http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html 
    */
    char track[6]; 
    utoa(countImpulse,track,10);
    // Append the suffix .wav to the char array
    strcat(track, ".wav");
    
    // Play the audio file
    music.play(track);
    Serial.print("Playing..."); Serial.println(track);
    while (music.isPlaying() == 1) {
      true; // play WAV file number and wait until it's finished
    }
    hangOff(); // That's it, we jump into loop() function    
  }
}

void loop() {
  ;// Nothing to do
  //asm volatile ("  jmp 0"); // Soft Reset
}

void hangOff(){
  // Hang off tone 5x
  delay(1000); 
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
}

You say "fairy tales"; unless they are very short, are you including a function to remember the present position (and rewind a couple of seconds) when it goes on-hook?

Paul__B:
You say "fairy tales"; unless they are very short, are you including a function to remember the present position (and rewind a couple of seconds) when it goes on-hook?

I will upload max 5min swiss fairy tales which I ripped form old compact cassettes (tapes).
By hooking on, the power will interrupt the Pro Mini. Hooking off will power on the board. No store of current playing position.

This is a cool project...

Finally I also used the SimpleSDAudio library and finished the project. Here's the final code and some pictures

/*
=============================================================================================
Fairy Tale Phone 1.0
by ReDiculum (Oct. - Dez. 2013)
-------------------------------
X-Mas 2013 Arduino project.
Using an old Zellweger Modell 70 (anno 1982) telephone with dial wheel.
The wheel can be spinned to count impulses and then playing one of ten different
audio fairies stored as 8bit full-rate (62500) mono PCM files named from 1.PCM to 10.PCM

SD Card > Arduino pinout:

CS (1)   -> Pin 10 (Chip select)
MOSI (2) -> Pin 11
MISO (7) -> Pin 12
SCK  (5) -> Pin 13

Ear phone  -> Pin 9 (protect with Diode against Pin 3) and GND
Dial wheel -> Pin 2 and GND
 
=============================================================================================

SimpleSDAudio library for audio playback from SD card
http://www.hackerspace-ffm.de/wiki/index.php?title=SimpleSDAudio
*/
#include <SimpleSDAudio.h>

#define IMPULSE_PIN 2 // Digital Pin 2. Read impulses
#define PHONE_PIN 3 // Digital Pin 3 (PWM) for phone tone output (protect with Diode against Pin 9)
#define SD_CHIPSELECT_PIN 10  // Digital Pin 10. SD Card chip select.
#define LED_PIN 13 // Digital Pin 13
#define TIME_WAIT 2000 // Waiting 2sec during dial wheel turns. Sufficient to catch 10 impulses
int countImpulse = 0; // The magic number to "dial" the music track number.

/* Sum of running millis and TIME_WAIT. 
Must be long integer if kids are not dialing quickly :)
*/
unsigned long timeWaitEnd = 0; 

void setup() {
  Serial.begin(57600); // Set console to 57600 baud
  Serial.println("=== FairyTalePhone based on SSDA ===");  

  pinMode(LED_PIN, OUTPUT); // onboard LED

  /* Turn on internal pullup resistor, because we pull the pin to ground.
  When the wheel gives impulse, the contact is opened (no current). That's
  when we have a digitalRead = 1.
  */
  digitalWrite(IMPULSE_PIN, HIGH);

  Serial.println("Ready to dial ...");

  // Play tone and wait here until the dial wheel has sent his first impulse 
  tone(PHONE_PIN,420);
  while (digitalRead(IMPULSE_PIN) == 0) {
    delay(1); 
  }
  noTone(PHONE_PIN); // Pssht! Here we go!

  Serial.print("Counting impulses ");
  timeWaitEnd = millis() + TIME_WAIT; // Summarize already passed millis since Arduino runs and TIME_WAIT.

  // Count all the dialed impulses until timeWaitEnd has reached
  while (millis() < timeWaitEnd ) {      
    if (digitalRead(IMPULSE_PIN) == 1) {
      /* Increment the amount of impulses if the contacts are opened.
      The wheel pulses with an interval of approx 70ms
      */
      countImpulse++; 
      Serial.print(".");
      digitalWrite(LED_PIN, HIGH); // Blink LED ...
      tone(PHONE_PIN,1000); // ... and short tone (70ms) of 1khz to fill the gap
      delay(70); 
      digitalWrite(LED_PIN,LOW); 
      noTone(PHONE_PIN); 
    }
  }

  Serial.println(" "); // New line

  /* If the dial wheel has been turned more than once by nervous kids
  and the amount of counted impulses exceeds 10, we just restart Arduino
  */
  if (countImpulse > 10) {
    Serial.print("Count exceeded 10 ("); Serial.print(countImpulse); Serial.println("), restarting"); 
    hangOff();
    asm volatile ("  jmp 0"); // Soft Reset
  } else {
    Serial.print("Preparing fairy tale number "); Serial.println(countImpulse);
    

    // Set Chip Select Pin
    SdPlay.setSDCSPin(SD_CHIPSELECT_PIN);
    // Initialize SD Card with library SimpleSDAudio
    if (!SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO | SSDA_MODE_AUTOWORKER)) {
      tone(PHONE_PIN,100); delay(250); noTone(PHONE_PIN); delay(400);
      tone(PHONE_PIN,80); delay(600); noTone(PHONE_PIN);
      Serial.println("Error initializing SD Card");
      while(1);
    }

    /* define a char array with an index size of 6 and convert
    integer "countImpulse" to char "track" using utoa() function
    http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html 
    */
    char track[6]; 
    utoa(countImpulse,track,10);
    // Append the suffix .PCM to the char array
    strcat(track, ".PCM");
    
    // Set the audio file if it exists and play it
    if (SdPlay.setFile(track)) {
      Serial.print("Playing..."); Serial.println(track);
      SdPlay.play();
      while(!SdPlay.isStopped()) {
        ;
      }
      SdPlay.deInit();
      hangOff();
      // That's it, we jump into loop() function
    } else {
      SdPlay.deInit();
      Serial.print("File "); Serial.print(track); Serial.println(" not found");
      tone(PHONE_PIN,80); delay(250); noTone(PHONE_PIN); delay(400);
      tone(PHONE_PIN,120); delay(600); noTone(PHONE_PIN);
    }
  }
}

void loop(void) { 
    ; // Nothing to do here
    //asm volatile ("  jmp 0"); // or Soft Reset?
}

void hangOff(){
  // Hang off tone 5x
  delay(1000); 
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
}

Fritzing Layout





Thanks for sharing, this is an absolutely awesome project.

Superb!
deep bow

Here's an updated version with shared pin for sound and tone. And I implemented the power sleep to save batteries.

/*
=============================================================================================
Fairy Tale Phone 1.1
by ReDiculum (Oct. - Dec. 2013)
-------------------------------
X-Mas 2013 Arduino project.
Using an old Zellweger Modell 70 (anno 1982) telephone with dial wheel.
The wheel can be spinned to count impulses and then playing one of ten different
audio fairies stored as 8bit full-rate (62500) mono PCM files named from 1.PCM to 10.PCM

SD Card > Arduino pinout:

CS (1)   -> Pin 10 (Chip select)
MOSI (2) -> Pin 11
MISO (7) -> Pin 12
SCK  (5) -> Pin 13

Ear phone (tone) -> Pin 3 and GND
Ear phone (sound) -> Pin 9 and GND
Dial wheel -> Pin 2 and GND


Changelog v1.1 (Dec 2015):
- Share the same pin for sound and tone by implementing deInit()
- Power sleep after fairy tale finished or error to save battery in case kids forget to hang up
- Power sleep after 10s if wheel hasn't been dialed to save battery (hook off by accident)
=============================================================================================

SimpleSDAudio library for audio playback from SD card
http://www.hackerspace-ffm.de/wiki/index.php?title=SimpleSDAudio
*/
#include <SimpleSDAudio.h>
#include <avr/sleep.h>

#define IMPULSE_PIN 2 // Digital Pin 2. Read impulses from dial wheel
//#define PHONE_PIN 3 // Digital Pin 3 (PWM) for phone tone output
#define PHONE_PIN 9 // Share phone tone and SDAudio pin. Always deInit before using tone()
#define SD_CHIPSELECT_PIN 10  // Digital Pin 10. SD Card chip select.
#define LED_PIN 13 // Digital Pin 13
byte countImpulse = 0; // The magic number to "dial" the music track number.

/* Sum of running millis and TIME_WAIT. 
Must be long integer if kids are not dialing quickly :)
*/
unsigned long timeWaitEnd, timeWait; 

void setup() {
  //Serial.begin(57600); // Set console to 57600 baud
  //Serial.println("=== FairyTalePhone based on SSDA ===");  

  pinMode(LED_PIN, OUTPUT); // onboard LED

  /* Turn on internal pullup resistor, because we pull the pin to ground.
  When the wheel gives impulse, the contact is opened (no current). That's
  when we have a digitalRead = 1.
  */
  digitalWrite(IMPULSE_PIN, HIGH);

  //Serial.println("Ready to dial ...");

  // Play tone and wait here until the dial wheel has sent his first impulse 
  tone(PHONE_PIN,420);
  timeWaitEnd = millis()+ 10000; // Summarize already passed millis since Arduino runs and 10s
  while (digitalRead(IMPULSE_PIN) == 0) {
    if (millis() > timeWait) {
      hangOff();
      sleepNow();
    }
    delay(1); 
  }
  noTone(PHONE_PIN); // Pssht! Here we go!

  //Serial.print("Counting impulses ");
  timeWaitEnd = millis() + 2000; // Summarize already passed millis since Arduino runs and 2s.

  // Count all the dialed impulses until timeWaitEnd has reached
  while (millis() < timeWaitEnd ) {      
    if (digitalRead(IMPULSE_PIN) == 1) {
      /* Increment the amount of impulses if the contacts are opened.
      The wheel pulses with an interval of approx 70ms
      */
      countImpulse++; 
      //Serial.print(".");
      digitalWrite(LED_PIN, HIGH); // Blink LED ...
      tone(PHONE_PIN,1000); // ... and short tone (70ms) of 1khz to fill the gap
      delay(70); 
      digitalWrite(LED_PIN,LOW); 
      noTone(PHONE_PIN); 
    }
  }

  //Serial.println(" "); // New line

  /* If the dial wheel has been turned more than once by nervous kids
  and the amount of counted impulses exceeds 10, we just restart Arduino
  */
  if (countImpulse > 10) {
    //Serial.print("Count exceeded 10 ("); //Serial.print(countImpulse); //Serial.println("), restarting"); 
    hangOff();
    sleepNow();
  //asm volatile ("  jmp 0"); // Soft Reset
  } else {
    //Serial.print("Preparing fairy tale number "); //Serial.println(countImpulse);
    

    // Set Chip Select Pin
    SdPlay.setSDCSPin(SD_CHIPSELECT_PIN);
    // Initialize SD Card with library SimpleSDAudio
    if (!SdPlay.init(SSDA_MODE_FULLRATE | SSDA_MODE_MONO | SSDA_MODE_AUTOWORKER)) {
      tone(PHONE_PIN,100); delay(250); noTone(PHONE_PIN); delay(400);
      tone(PHONE_PIN,80); delay(600); noTone(PHONE_PIN);
      //Serial.println("Error initializing SD Card");
      sleepNow();
    }

    /* define a char array with an index size of 6 and convert
    integer "countImpulse" to char "track" using utoa() function
    http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html 
    */
    char track[6]; 
    utoa(countImpulse,track,10);
    // Append the suffix .PCM to the char array
    strcat(track, ".PCM");
    
    // Set the audio file if it exists and play it
    if (SdPlay.setFile(track)) {
      //Serial.print("Playing..."); //Serial.println(track);
      SdPlay.play();
      while(!SdPlay.isStopped()) {
        ;
      }
      SdPlay.deInit();
      hangOff();
      sleepNow();
      // That's it, we go sleeping
    } else {
      SdPlay.deInit();
      //Serial.print("File "); //Serial.print(track); //Serial.println(" not found");
      tone(PHONE_PIN,80); delay(250); noTone(PHONE_PIN); delay(400);
      tone(PHONE_PIN,120); delay(600); noTone(PHONE_PIN);
      sleepNow();
    }
  }
}

void loop(void) { 
    ; // Nothing to do here
}

void hangOff(){
  // Hang off tone 5x
  delay(1000); 
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  tone(PHONE_PIN,450); delay(250);
  noTone(PHONE_PIN); delay(250);
  delay(1000);
}

void sleepNow() {
  // Power sleep mode from AVR http://playground.arduino.cc/Learning/ArduinoSleepCode
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();
}

Because I had some requests by friends to make them a fairy tale phone too, I designed a board to easily attach the Pro Mini and it's components. Find it at OshPark

The SD pins are designed to attach/solder a micro SD break out directly. It must have the pinout like this product from Banggood:

ecb55019bb69c62ff6454eb13bef1a6d.png

1c92d866c34c7c83d3ac5e3a2f662eb3.png