// INCLUDES // For handling software debouncing of mechanical switch contacts #include "Bounce2.h" // For serial connection to DFPlayer audio module #include // For playing sounds from DFPlayerMini module using the serial connection // Modified from DFRobot/DFRobotDFPlayerMini #include "DFRobotDFPlayerMini.h" // CONSTANTS // This switch closes when the handset is liftet const byte hookPin = A0; // This switch closes when dialling starts const byte dialPin = A2; // This switch opens and closes the number of times equal to the number dialled const byte numberPin = A1; // Two pins connected either side of the solenoid that ring the bells const byte ringerPins[] = {2, 3}; // Connections to DFPlayer module const byte Tx = 4; const byte Rx = 5; // A button attached to this pin can trigger an incoming call const byte incomingCallPin = 6; // What's the max number of digits that can be dialled in a number? // If the number dialled exceeds this amount and is not recognised, the invalid number tone will play const int maxNumDigitsInPhoneNumber = 6; // GLOBALS // The char representation of the number dialled (+1 to allow for string-terminating character \0) char number[maxNumDigitsInPhoneNumber + 1]; // The digit currently being dialled int currentDigit; // How many pulses have been detected for the current digit int pulseCount; // States in which the telephone can be typedef enum { Idle, Dialtone, Dialling, InvalidNumber, Connecting, Connected, Ringing, Engaged, Disconnected } stateType; // Assume that the handset starts stateType state = Idle; // Create debounce objects for each mechanical switches Bounce hookSwitch = Bounce(); Bounce dialSwitch = Bounce(); Bounce numberSwitch = Bounce(); Bounce incomingCallSwitch = Bounce(); // Initialise a soft serial interface SoftwareSerial softwareSerial(Rx, Tx); // RX, TX // Create an object to access the DFPlayer DFRobotDFPlayerMini dfPlayer; // The time at which the ringer last was activated unsigned long lastRingTime; // The file number of the audio recording to play when a call is connected int audioFileToPlay; void setup() { // Start the serial connection Serial.begin(9600); //Print some useful debug output - the filename and compilation time Serial.println(__FILE__); Serial.println("Compiled: " __DATE__ ", " __TIME__); // Declare pin inputs and attach debounce objects pinMode(hookPin, INPUT_PULLUP); hookSwitch.attach(hookPin); hookSwitch.interval(5); pinMode(dialPin, INPUT_PULLUP); dialSwitch.attach(dialPin); dialSwitch.interval(5); pinMode(numberPin, INPUT_PULLUP); numberSwitch.attach(numberPin); numberSwitch.interval(5); pinMode(incomingCallPin, INPUT_PULLUP); incomingCallSwitch.attach(incomingCallPin); incomingCallSwitch.interval(5); // Initialise ringer pins as outputs for(int i=0; i<2; i++){ pinMode(ringerPins[i], OUTPUT); digitalWrite(ringerPins[i], LOW); } Serial.print(F("Initialising software serial interface to DFPlayer...")); for(int i=0; i<10; i++) { softwareSerial.begin(9600); delay(500); if(dfPlayer.begin(softwareSerial)){ Serial.println(F("OK!")); break; } else { Serial.print("."); } if(i == 9) { Serial.println(F("Failed :(")); return; } } // Set volume (value from 0 to 30) dfPlayer.volume(30); // Ensure that dialled phone number is empty memset(number, 0, sizeof number); } void loop() { // Read the current state of all switches hookSwitch.update(); dialSwitch.update(); numberSwitch.update(); incomingCallSwitch.update(); // If the receiver is replaced, it doesn't matter what we were doing - make the phone become idle inmediatlely! if(hookSwitch.rose()) { Serial.println(F("Handset Replaced")); state = Idle; dfPlayer.stop(); memset(number, 0, sizeof number); currentDigit = 0; pulseCount = 0; } switch(state) { case Idle: if(hookSwitch.fell()) { Serial.println("Receiver Lifted"); // Play the dial tone dfPlayer.playMp3Folder(1); // Update the state state = Dialtone; } if(incomingCallSwitch.fell()) { Serial.println("Incoming Call"); state = Ringing; audioFileToPlay = 7; } break; case Dialtone: if(dialSwitch.fell()) { state = Dialling; Serial.println("Started dialling"); // Stop the dial tone dfPlayer.stop(); } break; case Dialling: if(numberSwitch.rose()) { pulseCount++; } // Check wether the dial has returned all the way if(dialSwitch.rose()) { // In most dial mechanisms, the digit "0" is encoded as 10 pulses if(pulseCount == 10) { pulseCount = 0;} Serial.print(pulseCount); // Append the most recent digit dialled onto the end of the number array number[currentDigit] = pulseCount | '0'; // Increment the counter currentDigit++; // Test the number dialled against known codes if(strncmp(number, "911", 3) == 0) { Serial.print(F(" Connecting")); state = Connecting; audioFileToPlay = 1002; } else if(strncmp(number, "666", 3) == 0) { Serial.println(F( "Connecting")); state = Connecting; audioFileToPlay = 5; } else if(strncmp(number, "12345", 5) == 0) { Serial.println(F(" Engaged")); state = Engaged; dfPlayer.playMp3Folder(3); } else if(currentDigit == maxNumDigitsInPhoneNumber) { Serial.println(F(" Invalid Number")); state = InvalidNumber; dfPlayer.playMp3Folder(4); } else { // Initalise the next value number[currentDigit] = 0; pulseCount = 0; } } break; case Connecting: // Play the ringing dfPlayer.playMp3Folder(2); // Adjust this line for how long you want the phone to ring before being answered delay(8000); // Answer the call and play the conversation dfPlayer.playMp3Folder(3); delay(900); dfPlayer.playMp3Folder(audioFileToPlay); state = Connected; break; case Engaged: break; case InvalidNumber: break; case Connected: break; case Ringing: int now = millis(); if(now - lastRingTime > 4000) { // UK phones call .4 second on, then .2 second off // Total of 0.4 seconds on for(int j=0; j<2; j++) { for(int i=0; i<20; i++){ // We check if the call was answered here to interrupt the ringing loop hookSwitch.update(); if(hookSwitch.fell()) { // Setting j=2 causes outer loop to break j=2; // break causes inner loop to break break; } digitalWrite(ringerPins[0], i%2); digitalWrite(ringerPins[1], 1-(i%2)); delay(20); } // 0.2 seconds off delay(200); } // Stop ringing digitalWrite(ringerPins[0], LOW); digitalWrite(ringerPins[1], LOW); lastRingTime = now; } if(hookSwitch.fell()) { Serial.println("Call answered!"); state = Connected; // Play the sound file calle "0001_XXXX.wav"/"0001_XXXX.mp3" folder of the SD card dfPlayer.playMp3Folder(audioFileToPlay); } break; } }