// Sleep Keeper Software V2.0 - Heart Rate Plotter
// Open the serial plotter and place the heart rate sensor on your finger to see the plot of your heartrate

#include <EEPROM.h>         // INCLUDE THE EEPROM LIBRARY SO THAT WE CAN STORE AND READ BACK DATA FROM MEMORY AFTER POWER OFF
#include <SPI.h>            // INCLUDE THE SPI COMMUNICATIONS LIBRARY
// NOW LET'S SET UP THE SD CARD
#include <SD.h>       // INCLUDE THE SD CARD LIBRARY.  IF YOU DON'T HAVE IT, IMPORT IT FROM GITHUB
File myFile;          // WE'RE GOING TO MAKE THIS DECLARATION FOR THE SD CARD READER/WRITER.  YOU ARE NAMING YOUR FILE.  YOU CAN CALL IT (File MrRogers) if you want.  Just leave it as myFile for now, though.
// LET'S SET UP THE DS3231 REAL TIME CLOCK
#include "RTClib.h"   // INCLUDE THE RTC LIBRARY
RTC_DS3231 rtc;       // DECLARE THE DS3231 RTC
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};   // THIS IS AN ARRAY.  WE'LL TALK MORE ABOUT ARRAYS LATER, BUT THEY ARE A VERY USEFUL WAY TO POINT TO/HAVE CONTROL OVER MANY INTEGERS ALL AT ONCE
// THE FOLLOWING LISTS ARE USED IN THE TIMESPEAK AND DATESPEAK PROGRAMS.  ALL OF THE VALUES IN EACH OF THE LISTS ARE ACTUALLY AUDIO POINTERS
// FOR ISNTANCE, int months[2] is 27, and points to the word "March" in the audio library
int months[] = {25 ,26 ,27 ,28, 29, 30, 31, 32, 33, 34, 35, 36}; // January to December
int days1[] = {55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73}; // days 1 to 19 
int hourz[] = {55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66}; // 1-12AM/1-12PM // 54 = 0
int day = 0;    // WE ARE GOING TO STORE REAL-TIME-CLOCK (RTC) DATA INTO THE FOLLOWING FIVE INTEGERS.  WHEN WE READ THE RTC, WE'LL STORE THE RETURNED INFO HERE
int month = 0;
int year = 0;
int hour = 0;
int minute = 0;
int hold = 0;
//
int sound = 0;         // WE'LL STORE OUR SOUND SAMPLING DATA IN THIS INTEGER
int timer = 0;
long calculator = 60000;
int heartrate = 0;
int average[] = {0,0,0,0,0,0,0,0,0,0};
int counter = 0;
int counter2 = 0; 
int SDstate = 0;
int lightval = 0;
int micval = 0;
int state = 0;
int profilestate = 0;
int nightmarecounter = 0;
int tripreason = 0;
int audiobaseline = 0;
int hearthigh = 0;  // Hearthigh and heartlow are used in the nightmare algorithm.  We create a window comparitor with them
int heartlow = 0;
// 
int profileheart = 0;  // This is used in the nightmare program
// THE FOLLOWING PIN DEFINITIONS ARE DEDICATED TO THE FOUR ON-BOARD AUDIO CHIPS.  BELOW THE PIN DEFINIATIONS ARE SOME AUDIO RELATIED INTEGER DECLARATIONS.
#define cs 9           // These are the chip-select pins for SPI. They control chips 1-4
#define csstop 8       // These are the chip-select pins for SPI. They control chips 1-4
int del=200;           // short 200ms delay
int value1 = 0x98;     // play command - This value never changes
int value2 = 0;        // voice address - when you place a hex value in this integer and call the "readout()" function, that specific word will play
int value3 = 0xA8;     // COUT ramp up - This value never changes
int value4 = 0xB8;     // COUT ramp down - This value never changes

// LET'S DEFINE THE RED, GREEN, AND YELLOW LED GPIO PINS (22, 23, AND 24)
#define RLED 4
#define GLED 3
#define DIPA 5
#define DIPB 6
#define PIR 7
#define SW2 2

// NOW WE NEED TO SET UP OUR INPUTS AND OUTPUTS, START SPI AND I2C COMMUNCATIONS, ETC
void setup() {
Serial.begin(9600);                   // START THE SERIAL MONITOR WITH A 9600 BAUD RATE
pinMode(DIPA,INPUT);                  // CHANNEL-A DIP SWITCH IS AN INPUT
pinMode(DIPB,INPUT);                  // CHANNEL-B DIP SWITCH IS AN INPUT
pinMode(RLED,OUTPUT);                 // CHANNEL-C DIP SWTICH (NOT CURRENTLY IN USE) 
pinMode(GLED,OUTPUT);                  
pinMode(PIR,INPUT);
pinMode(SW2,INPUT);
pinMode(A2,INPUT);
// Light is A0
// Mic is A1
delay(100);                              // 100ms SECOND DELAY


// YOU CAN LEAVE THIS IN, OR CUT IT OUR OF THE PROGRAM.  IF YOU HAVE THE SERIAL MONTIOR OPEN ON POWER UP, YOU WILL SEE THE FOUR DIGIT SECURITY CODE 
// WRITTEN TO THE SCREEN.  WE CAN CHANGE THIS CODE AT ANY TIME IN THE COMMAND PROMPT, AS THERE IS A PGRAM THAT ALLOWS FOR YOU TO CHANGE IT.
//Serial.print(EEPROM.read(0));   // READ EEPROM ADDRES 0, AND PRINT IT TO THE LCD
//Serial.print(EEPROM.read(1));   // SEE ABOVE/
//Serial.print(EEPROM.read(2));
//Serial.println(EEPROM.read(3));

// CHECK TO SEE IF THE REAL TIME CLOCK IS OPERATIONAL
if (! rtc.begin()) {                      // START/OPEN THE RTC.  SEE IF IT IS DETECTED.  IF IT ISN'T, PRINT THE FOLLOWING:
Serial.println("Couldn't find RTC");      // PRINT THIS TO THE SERIAL MONITOR
}

// CHANGE THE CURRENT DATE AND TIME IN THE RTC IF YOU WANT USING THE FOLLOWING CODE:
if(digitalRead(DIPB) == HIGH)        // IF TIMEDIP IS SET TO ON/HIGH, SET THE TIME TO OCTOBER 9TH, 2020 - 10:44:00 AM
{
rtc.adjust(DateTime(2021, 07, 30, 10, 15, 0)); // 2020 = YEAR, 3 = MONTH (OCTOBER), 9 = DAY, 10 = HOUR, 44 = MINUTES, 0 = SECONDS (CHANGE THIS TO YOUR CURRENT DATE/TIME)
}

Serial.print("Initializing SD card..."); // PRINT THIS TO THE SERIAL MONITOR
if (!SD.begin(10)) {                     // "!Serial" means NOT serial.  GPIO4 is the chip select line.  THIS MEANS, IF NO COMMS IS FOUND WHEN WE TALK TO THE SD CARD, THEN PRINT THE FOLLOWING TO THE SERIAL MONITOR
Serial.println("SD Card FAULTY or NOT installed!"); // PRINT THIS TO THE LCD.  NO SD CARD FOUND.  THIS IS BAD...  REMEMBER THAT THE "ln" MEANS "GO TO THE NEXT LINE/ PRESS ENTER"
SDstate = 1;
}
else
{
  Serial.println("initialization done.");// IF THE CARD IS THERE, AND ALL IS WELL, YOU'LL SEE THIS ON THE SERIAL MONITOR
}
 SPI.begin();            // TURN ON SPI COMMUNICATIONS
 SPI.setClockDivider(SPI_CLOCK_DIV32);  // low frequency SPI
 pinMode(cs,OUTPUT);     // Chip select pins is an output
 pinMode(csstop,INPUT);  // Chip select pins is an output
 digitalWrite(cs,HIGH);  // Set chip select to be HIGH (5v) by default.  The chip on the shield is selected when this line is brought low. 
 SPI.setBitOrder(MSBFIRST);   // OTP requires MSB first
 SPI.setDataMode(SPI_MODE0);  // Use MODE0, as all other modes to not work
 delay(100);  // Wait 100ms, then call rtccheck(); to read the RTC.
 rtccheck();
 delay(3000);
 
}

void loop(){           // This it he main loop.  A menu option will be made for this, but for now, you can comment in and comment out the functions...
heartplotter();
}


void heartplotter(){                  // When this function is called, open the serial plotter to view your heart beat
  while(digitalRead(SW2) == HIGH){    // The following will loop as long as you don't press the SW2 button.  Pressing the SW2 button will end the loop and this program.
    {
      Serial.println(analogRead(A2)); // Sample the A2 heartrate analog pin and plot it.  Try sampling A3 as opposed to A2, and change the gain on the heart rate using the on-board variable resistor.
    }
  }
}



void heart() 
{
 while(digitalRead(SW2) == HIGH){
  {
  timer = 0;                      // Holds the period of the heart rate
  while(analogRead(A2) < 150){  // Waits for a heart beat
    {}
  }
  digitalWrite(RLED,HIGH);
  while(analogRead(A2) < 150){ // Starts the timer, and adds 1 to it for every 1ms (while signal is high)
    {
      delay(1);
      timer = timer + 1;
    }
  }
  digitalWrite(RLED,LOW);
  delay(150);                     // Add 150ms to compensate for diastolic pulse
  timer = timer + 150;            // See above
  while(analogRead(A3) < 150){  // Wait again for the low pulse before the next rising edge.  Keep incrementing timer.
    {
      delay(1);
      timer = timer + 1;
    }
  }
      if(timer > 200){                // At this point, a heart beat has been measured.  If timer is more than 200ms, the HR is valid.  Should change to 300ms
      heartrate = calculator / timer; // Heartrate = a fixed 60000ms / the period = BPM                   
      average[counter] = heartrate;   // Add the current HR to the relative place in the "average" list (1-10)   
      counter = counter + 1;          // Starts at 0.  This is the placeholder for the averaging function below            
        if(counter == 10){            // If counter equals 10, let's take an average
        heartrate = 0;
        for(int i = 0 ; i < 10 ; i++){        // Do the following 10 times
         heartrate = heartrate + average[i];  // Add all ten samples together                    
        }
        heartrate = heartrate / 10;           // Average the ten samples
        Serial.print("AVG Heartrate:");
        Serial.print(heartrate);
        Serial.println(" BPM");
        counter = 0;                          // Clear the LCD
        timestamp();   
      }
      }
}
 }
}


void timestamp(){                                // THIS FUNCTION ACTS TO WRITE THE DATE TO AN SD CARD
    myFile = SD.open("sdtest.txt", FILE_WRITE);   // MAKE SURE THAT YOU HAVE A TEXT FILE CALLED "sdtest.txt" ON YOUR SD CARD.
    if (myFile) {                                 // IF THE FILE OPENS PROPERLY/IF THE FILE EXISTS, DO THE FOLLOWING.  OTHERWISE, SKIP TO THE BOTTOM OF THE CODE.  REMEMBER ME SAYING THIS, AS I WILL REFERENCE IT BELOW.              
    Serial.print("Writing to sdtest.txt...");     // WRITE THIS TO THE SERIAL MONITOR
    digitalWrite(GLED,HIGH);                      // TURN ON THE RED LED.  DON'T REMOVE POWER OR THE SD CARD WHEN THE RED LED IS ON.  THE RED LED ON MEANS THAT WE'RE WRITING TO THE SD CARD
    DateTime now = rtc.now();                     // ASK THE RTC FOR THE CURRENT DATE AND TIME
    // WRITE ALL OF THE RTC DATA TO THE SD CARD!  IT WILL ALL SHOW UP IN THE sdtest.txt FILE ON YOUR SD CARD.
    myFile.print("Time Stamp: ");                // PRINT THIS TO THESD CARD AND GO TO THE NEXT LINE (println)
    myFile.print(now.year(), DEC);    // PRINT THE YEAR IN DECIMAL (DEC)
    myFile.print('/');                // PRINT A FORWARD SLASH
    myFile.print(now.month(), DEC);   // PRINT THE MONTH
    myFile.print('/');                // ALL OF THIS CODE WAS IMPORTED FROM PROJECT#24.  NOTICE THE DIFFERENCE?  I'M TESTING YOU RIGHT NOW.  ARE YOU READING MY COMMENTS?  myFile.print means to print to the SD card. myFile.println means print, and skip to next line
    myFile.print(now.day(), DEC);     // PRINT THE CURRENT DAY
    myFile.print(" (");               // PRINT A BRACKET
    myFile.print(daysOfTheWeek[now.dayOfTheWeek()]);  // REMEMBER THAT ARRAY THAT WE TALKED ABOVE?  We use (daysOfTheWeek) in conjunction with now.daysOfTheWeek().  The number received in now.daysoftheweek points toward the day of the week (mon-sunday)
    // 1 = Monday, 7 = Sunday, etc.
    myFile.print(") ");               // PRINT ANOTHER BRACKET
    myFile.print(now.hour(), DEC);    // PRINT THE CURRENT HOUR.  HOURS ARE IN 24 MODE.  SO 1 = 1AM, 12 = NOON, 14 = 2PM, ETC.
    hold = (now.hour());              // SAVE THE HOUR IN hold FOR LATER ON.
    myFile.print(':');                // PRINT A COLON TO SEPARATE HOURS AND MINUTES
    myFile.print(now.minute(), DEC);  // PRINT THE CURRENT MINUTE
    myFile.print(':');                // PRINT A COLON TO SEPARATE MINUTES AND SECONDS
    myFile.print(now.second(), DEC);  // PRINT THE CURRENT SECOND
    myFile.print(" ");                // PRINT A SPACE TO SEPARATE SECONDS AND AM/PM
    if(hold < 12)                     // REMEMBER THAT WE SAVED HOURS IN hold?  IF hold IS LESS THAN 12, PRINT "AM".  OTHERWISE, PRINT "PM".
    {
    myFile.print("AM // ");              
    }
    else
    {
    myFile.print("PM // ");  
    }
    lightval = analogRead(0);
    if(tripreason == 0){
    myFile.print("BPM Average:");        // PRINT THIS TO THE SD CARD
    myFile.println(heartrate);   // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE SD CARD
    myFile.close();   
    while(digitalRead(A2) == HIGH){ // Starts the timer, and adds 1 to it for every 1ms (while signal is high)
    {}
    }
    }
    else if(tripreason == 1){
    myFile.print("Reason for LOG: ");        // PRINT THIS TO THE SD CARD
    myFile.print("Motion Detected. Light LVL: ");       // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE SD CARD
    myFile.print(lightval);        // PRINT THIS TO THE SD CARD
    myFile.print(" // Temperature: ");    // PRINT THIS TO THE SERIAL MONITOR
    myFile.print(rtc.getTemperature());   // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE LCD
    myFile.println(" C");                 // PRINT THIS TO THE LCD
    myFile.close();   
    delay(5000);
    }
    else if(tripreason == 3){
    myFile.print("Reason for LOG: ");        // PRINT THIS TO THE SD CARD
    myFile.print("You were snoring. Light LVL: ");       // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE SD CARD
    myFile.print(lightval);        // PRINT THIS TO THE SD CARD
    myFile.print(" // Temperature: ");    // PRINT THIS TO THE SERIAL MONITOR
    myFile.print(rtc.getTemperature());   // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE LCD
    myFile.println(" C");                 // PRINT THIS TO THE LCD
    myFile.close();   
    delay(5000);
    }
    else
    {
    myFile.print("Reason for LOG: ");        // PRINT THIS TO THE SD CARD
    myFile.print("Noise Detected. Light LVL: ");       // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE SD CARD
    myFile.print(lightval);        // PRINT THIS TO THE SD CARD
    myFile.print(" // Temperature: ");    // PRINT THIS TO THE SERIAL MONITOR
    myFile.print(rtc.getTemperature());   // ASK THE RTC FOR THE CURRENT TEMPERATURE AND PRINT IT TO THE LCD
    myFile.println(" C");                 // PRINT THIS TO THE LCD
    myFile.close();   
    delay(5000);
    }
    tripreason = 0;
    Serial.println(" Done...");   
    } else {
    // REMEMBER WHAT I TALKED ABOUT ABOVE?  IF THE TEXT FILE DIDN'T EXIST, OR IF THE CHIP SELECT PIN ISN'T WORKING CORRECTLY, THEN ALL OF THE ABOVE CODE WILL BE OVERLOOKED, AND THIS WILL HAPPEN.
    Serial.println("error opening sdtest.txt");     // IF THIS HAPPENS, MAKE SURE THAT YOU HAVE EVERYTHING CONNECTED PROPERLY.  
  }
  digitalWrite(GLED,LOW);
    
}     // WELL THAT WAS AN ADVENTURE, WASN'T IT?  



void readout()                        // This function plays the audio bite pointed to by the value in 'hold'
{                                     // It only takes four lines of code and a delay (If you want it) to play an audio bite.  
   // ramp up
  digitalWrite(cs,LOW);               // Set chipselect to low, and then send over the fixed value in 'value3', and then 0x00 via SPI to the audio chip
  SPI.transfer(value3);
  SPI.transfer(0x00);
  digitalWrite(cs,HIGH);              // Once done the transver, set chip select high again.
  delay(100);                         // Wait 100ms
  digitalWrite(cs,LOW);               // Do the same thing again, but send over the fixed value in 'value1', and then the pointer value in 'hold'
  SPI.transfer(value1);
  SPI.transfer(hold);
  digitalWrite(cs,HIGH);
  delay(1000);                        // Wait one second, then wait to ensure that the chip STOP signal isn't high.
  while(digitalRead(csstop) == HIGH){ // DO nothing while high
    {}
  }
   delay(5);                          // Wait 5ms, then send over another set of commands to ramp the chip down
  //ramp down
  digitalWrite(cs,LOW);
  SPI.transfer(value4);
  SPI.transfer(0x00);
  digitalWrite(cs,HIGH);
  delay(10);
}


void rtccheck(){                      // THIS PROGRAM GETS THE DATE FROM THE RTC AND PRINTS IT TO THE SERIAL MONITOR
    DateTime now = rtc.now();         // ASK THE RTC FOR THE CURRENT DATE AND TIME
    Serial.println("The date is: ");  // PRINT THIS TO THE SERIAL MONITOR AND GO TO THE NEXT LINE (println)
    Serial.print(now.year(), DEC);    // PRINT THE YEAR IN DECIMAL (DEC)
    Serial.print('/');                // PRINT A FORWARD SLASH
    Serial.print(now.month(), DEC);   // PRINT THE MONTH
    Serial.print('/');          
    Serial.print(now.day(), DEC);     // PRINT THE CURRENT DAY
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);  // REMEMBER THAT ARRAY THAT WE TALKED ABOVE?  We use (daysOfTheWeek) in conjunction with now.daysOfTheWeek().  The number received in now.daysoftheweek points toward the day of the week (mon-sunday)
    // 1 = Monday, 7 = Sunday, etc.
    Serial.print(") ");
    Serial.print(now.hour(), DEC);    // PRINT THE CURRENT HOUR.  HOURS ARE IN 24 MODE.  SO 1 = 1AM, 12 = NOON, 14 = 2PM, ETC.
    hold = (now.hour());              // SAVE THE HOUR IN hold FOR LATER ON.
    Serial.print(':');                // PRINT A COLON TO SEPARATE HOURS AND MINUTES
    Serial.print(now.minute(), DEC);  // PRINT THE CURRENT MINUTE
    Serial.print(':');                // PRINT A COLON TO SEPARATE MINUTES AND SECONDS
    Serial.print(now.second(), DEC);  // PRINT THE CURRENT SECOND
    Serial.print(" ");                // PRINT A SPACE TO SEPARATE SECONDS AND AM/PM
    if(hold < 12)                     // REMEMBER THAT WE SAVED HOURS IN hold?  IF hold IS LESS THAN 12, PRINT "AM".  OTHERWISE, PRINT "PN".
    {
    Serial.print("AM");              
    }
    else
    {
    Serial.print("PM");  
    }
    Serial.println();                 // SKIP TO THE NEXT LINE
} // THIS IS THE END OF THIS FUNCTION
