/* automatic pill dispenser example code
for parts list, circuit diagram, and instructions see:
https://www.sciencebuddies.org/science-fair-projects/project-ideas/Elec_p105/electricity-electronics/automatic-pill-dispenser
This code assumes you are using the built-in RTC on an Arduino UNO R4. To modify the code for an external RTC, see:
https://www.sciencebuddies.org/science-fair-projects/references/how-to-use-an-arduino#step36
*/
// include libraries
#include <Servo.h>  
#include <LiquidCrystal.h>
#include <RTC.h>
Servo servo; // create servo object
// constant variables for pins
const int servoPin = 6;
const int buttonPin = 9;
const int ledPin = 7;
const int buzzerPin = 8;
// other variables
int angle = 0;
int angleIncrement = 45;  // default 45 degrees for 4 compartments, change for different number of compartments
int newAngle;
int buttonState;
int movementDelay = 50;
int debounceDelay = 1000;
// time variables
int year;
int month;
int day;
int hour;
int minutes;
int seconds;
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup(){ // setup code that only runs once
  pinMode(buttonPin, INPUT); // set button pin as input
  pinMode(ledPin,OUTPUT);    // set LED pin as output
  pinMode(buzzerPin,OUTPUT); // set buzzer pin as output
  digitalWrite(ledPin,LOW);  // make sure LED is off
  digitalWrite(buzzerPin,LOW);  // make sure buzzer is off
  servo.attach(servoPin);    // attach servo object to servoPin
  servo.write(angle);        // set servo to initial angle
  Serial.begin(9600);        // initialize serial for debugging
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // initialize the RTC and set the current date and time
  // you will need to manually adjust the time right before you upload the code
  RTC.begin();
  RTCTime startTime(12, Month::APRIL, 2024, 8, 59, 50, DayOfWeek::FRIDAY, SaveLight::SAVING_TIME_ACTIVE);
  RTC.setTime(startTime);
}
void loop(){  // code that loops forever
  updateLCD();  // display the current date and time on the screen (see function below)
  
  buttonState = digitalRead(buttonPin); // read button state (you can edit the code to advance the servo when you press the button, useful for debugging)
  Serial.println(angle);                // print the servo angle
  // detect certain times and rotate the servo mechanism ahead by one compartment. 
  // the example code rotates the servo once per minute when the seconds variable equals 0.
  // change to detect different times of day, for example (hours == 9 && minutes == 0 && seconds == 0) would detect 9:00:00 AM
  // use additional "else if" conditions to detect more than one time
  if(seconds == 0){               // check for seconds = 0 (one minute intervals)
    newAngle = angle + angleIncrement; // increase angle by increment
    if (newAngle<=180){           // if the new angle is less than or equal to 180, increase angle
    	while(angle < newAngle){    // increase angle until it reaches the new angle
      	angle = angle + 1;        // increase angle by 1
      	servo.write(angle);       // move the servo
        Serial.println(angle);    // print the angle
      	delay(movementDelay);     // delay to slow down movement
    	}
      // flash LED and buzzer
      flashLED(4,150);    // flashLED(number of flashes, delay in milliseconds), see function below
    }
    else{ // if the new angle is greater than 180, reset angle to 0
      while(angle>0){         // decrease angle until it reaches 0
        angle = angle - 1;    // decrease angle by 1
        servo.write(angle);   // move the servo
        Serial.println(angle);// print the angle
        delay(movementDelay); // delay to slow down movement
      }
    }
  }
}
void flashLED(int numFlashes, int flashDelay){  // alarm function to flash LED and sound buzzer
  lcd.clear();              // clear the LCD screen
  lcd.setCursor(0, 0);      // set cursor to top left
  lcd.print("Take medicine!");  // display message
  for (int i = 0; i<numFlashes; i++){  // loop to flash LED/buzzer numFlashes times
    digitalWrite(ledPin,HIGH);         // turn LED on
    digitalWrite(buzzerPin,HIGH);      // turn buzzer on
    delay(flashDelay);                 // delay
    digitalWrite(ledPin,LOW);          // turn LED off
    digitalWrite(buzzerPin,LOW);       // turn buzzer off
    delay(flashDelay);                 // delay
  }
  // wait for button press - the code will get stuck in this loop until you press the button
  while(digitalRead(buttonPin) == LOW){}; 
  delay(1000);    // delay before clearing screen
  lcd.clear();    // clear screen
}
void updateLCD(){    // function to update LCD screen
  // get current time from the RTC
  RTCTime currentTime;
  RTC.getTime(currentTime);
  // store current time variables
  year = currentTime.getYear();
  month = Month2int(currentTime.getMonth());
  day = currentTime.getDayOfMonth();
  hour = currentTime.getHour();
  minutes = currentTime.getMinutes();
  seconds = currentTime.getSeconds();
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 0);
  // print month/day/year (rearrange for different date formats)
  // add leading spaces if month or day are less than 10 to keep spacing consistent
  // (always use 2 characters for month and day)
  if(month<10){   // print extra space if month is less than 10
    lcd.print(" ");
  }
  lcd.print(month);  // print the month
  lcd.print("/");
  if(day<10){        // print extra space if the day is less than 10
    lcd.print(" ");
  }
  lcd.print(day);    // print the day
  lcd.print("/");
  lcd.print(year);   // print the year
  // print time in hh:mm:ss format
  // add leading zeroes if minutes or seconds are less than ten to keep spacing consistent
  lcd.setCursor(0, 1);  // move cursor to second row
  if(hour<10){          // print leading space if hour is less than 10
    lcd.print(" ");
  }
  lcd.print(hour);      // print the hour
  lcd.print(":");
  if(minutes<10){       // print leading zero if minute is less than 10
    lcd.print("0");
  }
  lcd.print(minutes);   // print the minute
  lcd.print(":");
  if(seconds<10){       // print leading zero if second is less than 10
    lcd.print("0");
  }
  lcd.print(seconds);   // print the second
}