/*
  Stephen Mock, Version 1.0, 7/26/19
  Youtube Video: https://www.youtube.com/watch?v=bIIDHT86DI4&feature=youtu.be
  See: /Users/craigmckendree/Documents/Arduino/State_Machine

  1. Ultrasonic 2: Holds item to be reminded
  2. Ultrasonic 1: Watches door for exit

*/


//Setting Constants for Electronics

//Ultrasonic 1 
  #define trigPin1 13
  #define echoPin1 12

//Ultrasonic 2
  #define trigPin2 9
  #define echoPin2 8

//RGB LED
  #define bluePin 3
  #define greenPin 5
  #define redPin 6

//Buzzer
  #define buzzer 2


//Setting Constants for CODE
  int state_fsm = 0;  // Starting State is 0 
// Timers
  unsigned long timer1_start = 0; //Timer for state (1)
  unsigned long timer1_check = 0; //Timer used to check if 5 minutes has passed, to go from state (2) to state (0) 
  unsigned long timer2_start = 0; // Timer inbetween state (2)
  unsigned long timer2_check = 0; // Timer to wait 5s to allow for transitions from state (3) to state (4) (Quality of Life) 
  unsigned long timer3_start = 0; // Timer for state (4)
  unsigned long timer3_check = 0; // Timer used to check if 10s passed to go from state (5) to state (6)

// Distances (EDIT for your situation!)
  int distance1Threshold = 5; // threshold before ultrasonic 1 triggers (in cm)
  int distance2Threshold = 75; //  threshold before ultrasonic 2 triggers (in cm)

// Wait Times (EDIT for your situation!) 
  unsigned long state1WaitTime = 1000*5*60; // 5 minute timer if you take the object, but dont leave the room
  unsigned long state5WaitTime = 10000; // 10s timer when you return to the room, but dont put back the object 


// Functions
// Purpose of these functions is to make the code much more readable, and to encapsulate many of the functions in the main loop into single lines. Modularity!


  // This function records the distance measured in cm as calculated from the ultrasonic sensor 
  float recordDist(int trigPin,int echoPin) { 
    float duration, distance;
    digitalWrite(trigPin, LOW); 
    delayMicroseconds(2);
   
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    
    duration = pulseIn(echoPin, HIGH);
    distance = (duration / 2) * 0.0344;
    return distance; // This final number is what is important for the rest of the code...
  }
  
  
// Function to set the color of RGB LED when in different states
void setColor(int red, int green, int blue) {
  analogWrite(redPin, red);
  analogWrite(greenPin, green);
  analogWrite(bluePin, blue);
}
  
//Function to beep when there is an alarm/switching between states
void beep(int del) { // del = length of delay, used to differentiate sounds for active buzzer
  for (int i = 0; i < 25; i++) {
    digitalWrite(buzzer,HIGH);
    delay(del);//wait for del time
    digitalWrite(buzzer,LOW);
    delay(del);//wait for del time
  }
}


// Just setting up the various pins needed... nothing special
void setup() {
  Serial.begin (9600); // Can you use the serial if you want!
  pinMode(trigPin1, OUTPUT);
  pinMode(echoPin1, INPUT);
  pinMode(trigPin2, OUTPUT);
  pinMode(echoPin2, INPUT);
  pinMode(redPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(buzzer,OUTPUT);
}


void loop() {
  float distance1, distance2;

  // Sensor 1
   distance1 = recordDist(trigPin1, echoPin1);
   Serial.print("Distance 1 cm: "); // Use the distance measured by serial to determine the thresholds for when you want the ultrasonics to trigger
   Serial.print(distance1);

  // Sensor 2 
   distance2 = recordDist(trigPin2, echoPin2);
   Serial.print(" Distance 2 cm: ");
   Serial.println(distance2);

   SM_alarm(distance1,distance2); // Calling the state machine!
   
   delay(200); // CM: was 50, Need a delay otherwise recording to fast and weird data might occur
}


// STATE MACHINE!
// State machine has 7 different states (indexing at 0), and is the behavior is described in the video.
// This state machine uses a switch statement to differentiate each state, and when transitions occur, the state_fsm changes accordingly.

void SM_alarm(float distance1, float distance2) {
  Serial.print("Current FSM State: "); // Great for debugging, to see what state you are in.
  Serial.println(state_fsm);
    
  switch (state_fsm) {  
    case 0: //resting state, waiting for keys to be removed, but will alarm when user leaves the room without the object
      setColor(0,255,0); // Green LED
      if (distance1 > distance1Threshold) { //ultrasonic detects distance greater than 3 cm, meaning the object has moved away, transition to state 1 
        state_fsm = 1;
      }
  
      if (distance2 < distance2Threshold) { // buzzer if the user passes by the second ultrasonic, to remind them to take their object before leaving
        beep(2);
      }
      break;
      
    case 1: // timer #1 start
      timer1_start = millis(); //Goes directly to next state, 1 == 1
      state_fsm = 2;
      break;
      
    case 2: // Detection State 1: check if 5minutes has passed with no transitions, OR go back to state 0 if Ultrasonic1 detects the object have been placed back
      setColor(0,0,255); // Outputs Yellow
      if (distance1 < distance1Threshold) { // detects that the object is back, goes to intial resting state
        state_fsm = 0;
      }
    
      timer1_check = millis(); // running clock to see if 5 minutes have passed
        
      if (timer1_check - timer1_start > state1WaitTime){ // If 5 minutes have passed
        state_fsm = 6; // go to the alarm phase
      }
    
      if (distance2 < distance2Threshold) {  //If the second Ultrasonic gets passed by, go to waiting phase state (3)
        beep(3); // acknowledging the user has left
        state_fsm = 3;
        timer2_start = millis(); // must wait 5 seconds before able to go to detection 2 (state (5)) phase from waiting phase (state (3))
        // This is needed because it takes a few seconds (more than a 50ms delay...) to pass by something, especially like your front door. 
      }
      break;
        
    case 3: // Waiting Period: when owner is out of the house with the object, device waits for the owner to return 
      setColor(153,0,153); // Output Purple
        
      if (distance1 < distance1Threshold) { // detects that the object is back, goes to intial resting state
        state_fsm = 0;
      }
  
      timer2_check = millis(); // running clock to see if 5s passed
        
      if (distance2 < distance2Threshold && (timer2_check - timer2_start > 5000)) { // when the owner returns to the house, and when 5s has passed
        beep(4);
        state_fsm = 4; 
      }
      break;
        
    case 4: // timer #3 start
      timer3_start = millis(); //Goes directly to next state (5) 1 == 1
      state_fsm = 5;
      break;
        
    case 5: // Detection State 2: Waiting for keys to return within 10s, otherwise alarming!
      setColor(255,128,0); // Outputs orange color
  
      if (distance1 < distance1Threshold) { // detects that the object is back, goes to intial resting state
        state_fsm = 0;
      }
  
      timer3_check = millis();  // Timer checking if 10s has passed, and if so, transitioning to alarm phase (6)    
        
      if (timer3_check - timer3_start > state5WaitTime){ // If 10 seconds have passed and user doesnt put back item
        state_fsm = 6; // go to the alarm phase!
      }
      break;
       
    case 6: //Alarm phase: Simply beeping rapidly until the object is placed back.
      setColor(255,0,0); // output red
      beep(1);

      if (distance1 < distance1Threshold) { // detects that the object is back, goes to intial resting state
        state_fsm = 0;
      } 
      break;
  }
}