The original logic records after the handset is 'picked up' and a .wav file plays and then a beep, stops recording after 'hanging up', and then can be played back immediately by pressing a button while the phone is still 'hung up'. I partially implanted logic to read the rotary dial itself. I'd like the option to record by dialing "1-0-1" while the handset is up, playback the last message with "4-1-1" while the handset is up, and delete the last message by dialing "7-7-7" while the handset is up.
I figure a wait or delay of 5 seconds between dialed numbers before the phone should switch back to "Case: Prompt."
I'm also adding another button to re-record the greeting message after a 10 seconds of holding down said button. I'd imagine there'd be a statement in 'startRecording()' to overwrite the 'greeting.wav' audio file. I tried, I really have.
Code: Select all
/**
**/
// INCLUDES
// The default "sketchbook" location in which Arduino IDE installs libraries is:
// C:\Users\alast\Documents\Arduino
// However, the TeensyDuino installer installs libraries in:
// C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries
// To ensure the correct libraries are used when targetting Teensy platform in Arduino IDE, go File->Preferences and change the sketchbook location to avoid conflicts with Arduino libraries.
// When targetting Arduino boards, change it back again to default
#include <Audio.h>
#include <Bounce2.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <TimeLib.h>
// DEFINES
// Define pins used by Teensy Audio Shield
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14
// And those used for inputs
#define HOOK_PIN 0
#define PLAYBACK_BUTTON_PIN 1
// GLOBALS
// Inputs
AudioSynthWaveform waveform1; // To create the "beep" sfx
AudioInputI2S i2s2; // I2S input from microphone on audio shield
AudioPlaySdRaw playRaw1; // Play .RAW audio files saved on SD card
AudioPlaySdWav playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
// Outputs
AudioRecordQueue queue1; // Creating an audio buffer in memory before saving to SD
AudioMixer4 mixer; // Allows merging several inputs to same output
AudioOutputI2S i2s1; // I2S interface to Speaker/Line Out on Audio shield
// Connections
AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer
AudioConnection patchCord2(playRaw1, 0, mixer, 1); // raw audio to mixer
AudioConnection patchCord3(playWav1, 0, mixer, 2); // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)
AudioControlSGTL5000 sgtl5000_1;
// Filename to save audio recording on SD card
char filename[15];
// The file object itself
File frec;
// Use long 40ms debounce time on hook switch
Bounce buttonRecord = Bounce(HOOK_PIN, 40);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 8);
// Keep track of current state of the device
enum Mode {Initialising, Ready, Prompting, Recording, Playing};
Mode mode = Mode::Initialising;
void setup() {
Serial.begin(9600);
while (!Serial && millis() < 5000) {
// wait for serial port to connect.
}
// Note that Serial.begin() is not required for Teensy -
// by default it initialises serial communication at full USB speed
// See https://www.pjrc.com/teensy/td_serial.html
// Serial.begin()
Serial.println(__FILE__ __DATE__);
// Configure the input pins
pinMode(HOOK_PIN, INPUT_PULLUP);
pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);
// Audio connections require memory, and the record queue
// uses this memory to buffer incoming audio.Was 50.
AudioMemory(70);
// Enable the audio shield, select input, and enable output
sgtl5000_1.enable();
// Define which input on the audio shield to use (AUDIO_INPUT_LINEIN / AUDIO_INPUT_MIC)
//sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
//Below is increased from 0.5
//Decrease if the speaker sounds distorted
sgtl5000_1.volume(0.55);
// Play a beep to indicate system is online
waveform1.begin(WAVEFORM_SINE);
waveform1.frequency(440);
waveform1.amplitude(0.5);
wait(250);
waveform1.amplitude(0);
delay(1000);
// Initialize the SD card
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here if no SD card, but print a message
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
// Value in dB - 65 max - was 15 - input volume
sgtl5000_1.micGain(17);
// Synchronise the Time object used in the program code with the RTC time provider.
// See https://github.com/PaulStoffregen/Time
setSyncProvider(getTeensy3Time);
// Define a callback that will assign the correct datetime for any file system operations
// (i.e. saving a new audio recording onto the SD card)
FsDateTime::setCallback(dateTime);
mode = Mode::Ready;
}
void loop() {
// First, read the buttons
buttonRecord.update();
buttonPlay.update();
switch(mode){
case Mode::Ready:
// Rising edge occurs when the handset is lifted - activates when previously low value is high
//Note: falllingEdge and risingEdge were switched b/c phone is on closed circuit
//MAY NEED TO CHANGE FOR LIFT + DIAL to playback messages!!!!!
if (buttonRecord.risingEdge()) {
Serial.println("Handset lifted");
mode = Mode::Prompting;
}
else if(buttonPlay.fallingEdge()) {
//playAllRecordings();
playLastRecording();
}
break;
case Mode::Prompting:
// Wait a second for users to put the handset to their ear
wait(1000);
// Play the greeting (SAVED ON SD CARD) inviting them to record their message
//'Thanks for celebrating with us and taking the time to record a message'
playWav1.play("greeting.wav");
// Wait until the message has finished playing
while (!playWav1.isStopped()) {
// Check whether the handset is replaced
buttonRecord.update();
// Handset is replaced
if(buttonRecord.fallingEdge()) {
Serial.println("Hung up");
playWav1.stop();
mode = Mode::Ready;
return;
}
}
startRecording();
break;
case Mode::Recording:
// Handset is replaced
if(buttonRecord.fallingEdge()){
// Debug log
Serial.println("Stopped Recording");
// Stop recording
stopRecording();
// Play audio tone to confirm recording has ended. C chord
waveform1.frequency(523.25);
waveform1.amplitude(0.7);//was .9
wait(50);
waveform1.amplitude(0);
wait(50);
waveform1.amplitude(0.7); //was .9
wait(50);
waveform1.amplitude(0);
}
else {
continueRecording();
}
break;
case Mode::Playing:
break;
}
}
void startRecording() {
// Debug message
Serial.println("Started Recording");
// Play the tone sound effect/TONE A/ "leave a message at the tone" to be included in message
waveform1.frequency(440);
waveform1.amplitude(0.7);//was .9
wait(250);
waveform1.amplitude(0);
// Start the recording function
// Find the first available file number
for (uint8_t i=0; i<9999; i++) {
// Format the counter as a five-digit number with leading zeroes, followed by file extension
snprintf(filename, 11, " %05d.RAW", i);
//The '%50d' can be used to playback only the last message recorded!!!!
// Create if does not exist, do not open existing, write, sync after write
if (!SD.exists(filename)) {
break;
}
}
frec = SD.open(filename, FILE_WRITE);
if(frec) {
Serial.print("Recording to ");
Serial.println(filename);
queue1.begin();
mode = Mode::Recording;
}
else {
Serial.println("Couldn't open file to record!");
}
}
void continueRecording() {
// Check if there is data in the queue
if (queue1.available() >= 2) {
byte buffer[512];
// Fetch 2 blocks from the audio library and copy
// into a 512 byte buffer. The Arduino SD library
// is most efficient when full 512 byte sector size
// writes are used.
memcpy(buffer, queue1.readBuffer(), 256);
queue1.freeBuffer();
memcpy(buffer+256, queue1.readBuffer(), 256);
queue1.freeBuffer();
// Write all 512 bytes to the SD card
frec.write(buffer, 512);
}
}
void stopRecording() {
// Stop adding any new data to the queue
queue1.end();
// Flush all existing remaining data from the queue
while (queue1.available() > 0) {
// Save to open file
frec.write((byte*)queue1.readBuffer(), 256);
queue1.freeBuffer();
}
// Close the file
frec.close();
mode = Mode::Ready;
}
void playAllRecordings() {
// Recording files are saved in the root directory
File dir = SD.open("/");
while (true) {
File entry = dir.openNextFile();
if (!entry) {
// no more files
Serial.print("No more files to play! ");
entry.close();
break;
}
int8_t len = strlen(entry.name());
if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
Serial.print("Now playing ");
Serial.println(entry.name());
// Play a short beep before each message
waveform1.amplitude(0.32);
wait(250);
waveform1.amplitude(0);
// Play the file
playRaw1.play(entry.name());
mode = Mode::Playing;
}
entry.close();
while (playRaw1.isPlaying()) {
buttonPlay.update();
buttonRecord.update();
// Button is pressed again
if(buttonPlay.risingEdge() || buttonRecord.fallingEdge()) {
playRaw1.stop();
Serial.println("Stopped playback; resetting to Mode:Prompt");
mode = Mode::Ready;
return;
}
}
}
// All files have been played
Serial.print("Done Playing ");
mode = Mode::Ready;
}
void playLastRecording() {
// Find the first available file number
Serial.println("Play last recording.");
uint16_t idx = 0;
for (uint16_t i=0; i<9999; i++) {
// Format the counter as a five-digit number with leading zeroes, followed by file extension
snprintf(filename, 11, " %05d.raw", i);
// check, if file with index i exists
if (!SD.exists(filename)) {
idx = i - 1;
break;
}
}
// now play file with index idx == last recorded file
snprintf(filename, 11, " %05d.raw", idx);
Serial.println(filename);
playRaw1.play(filename);
mode = Mode::Playing;
while (playRaw1.isPlaying()) { // this works for playWav
buttonPlay.update();
buttonRecord.update();
// Button is pressed again
if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge()) { //was "buttonPlay.fallingEdge()" 12/11/22
playRaw1.stop();
Serial.println("Stopped playback; resetting to Mode:Ready");
mode = Mode::Ready;
return;
}
}
// file has been played
Serial.println("Done Playing ");
mode = Mode::Ready;
}
// Retrieve the current time from Teensy built-in RTC
time_t getTeensy3Time(){
return Teensy3Clock.get();
}
// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
// Return date using FS_DATE macro to format fields.
*date = FS_DATE(year(), month(), day());
// Return time using FS_TIME macro to format fields.
*time = FS_TIME(hour(), minute(), second());
// Return low time bits in units of 10 ms.
*ms10 = second() & 1 ? 100 : 0;
}
// Non-blocking delay, which pauses execution of main program logic,
// but while still listening for input
void wait(unsigned int milliseconds) {
elapsedMillis msec=0;
while (msec <= milliseconds) {
buttonRecord.update();
if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
}
}