//adrian works in this directory at least

#include 
#include 
#include 

#define I2C_ADDR    0x27 // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 1;

//for tachometer
float value = 0;
int rev = 0;
float rpm;
unsigned long oldtime = 0;
unsigned long time;
boolean firstTimeRising = true;
const int DELTA = 2;//mn change for display update
float oldF = -DELTA - 1; //to trigger change from 0
// used for centrifugationTimeUpdate
const float CENTRIFUGATION_TIME_ACCEPTABLE_RANGE_PERCENTAGE = .10;//min rev for centrifugationStartTime update
unsigned long  centrifugationStartTime = millis();

//
//RCF or G-force= 1.12  x  R  x  (RPM/1000)² where radius in mm
float EPPENDORF_45_18_11 = 1.12  * 72.5; //18 place fixed angle rotorfor 1.5-2 ml tubes radius =7.3 cm https://www.eppendorf.com/CA-en/centrifuge-speed-calculator/

LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);

void setup()
{
  lcd.begin(16, 2);               //initialize LCD
  attachInterrupt(digitalPinToInterrupt(2), isr, RISING); //attaching the interrupt on 2 only for Nano
  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  lcd.clear();
  lcd.print("Setup Finished");
  Serial.begin(9600);
  while (!Serial);
  // pinMode(2, INPUT_PULLUP);
  Serial.println("\nG-Force");
}

void loop()
{
  if (oldF < 20) {
    delay(1000);
  }
  else {
    delay(300);// 1/3 second
  }
  //Serial.println("in loop");
  //Serial.println(digitalRead(2));
  detachInterrupt(0);           //detaches the interrupt
  time = millis() - oldtime;    //finds the time
  float   f = (((float)rev * 1000 / time)) ; //calculates rpm
  if (abs(f - oldF  ) <= DELTA) {
    f = (f + oldF) / 2;
  }
  if (rev == -1) {
    f = 0;
    time = 0;
    rpm = 0;
  } else {
    rpm = (f * 60);   //calculates rpm
  }
  lcd.clear();
  updateCentrifugationTime(f);
  oldF = f;
  updateValue(f);
  rev = -1;
  attachInterrupt();
  oldtime = millis();  //saves the current time
  //  Serial.println("centrifugationStartTime:");
  //  Serial.println(centrifugationStartTime);
  //  Serial.println("centrifugationTime:");
  Serial.println("rev:");
  Serial.println(rev);
}

void isr() //interrupt service routine
{
  rev++;
}

void updateValue(float f) {
  //lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(calcG(), 0);
  lcd.print("G ");

  //
  lcd.setCursor(0, 1);
  lcd.print(rpm, 0); //strip decimals
  lcd.print("RPM ");
  lcd.print(f, 1);
  lcd.print("Hz" );
  //optional
  //lcd.print(" s:");
  //lcd.print( time);
  //  lcd.print(" s:");
  //  lcd.print( time);
  //  lcd.print(" r" );
  //  lcd.print(rev);
}

void updateCentrifugationTime(float f) {
  //start block to update centrifugation time. By my definition is the time when f>10 (600RPM)a dn the value of frequency varies less then
  float rangeDelta = oldF * CENTRIFUGATION_TIME_ACCEPTABLE_RANGE_PERCENTAGE;
  if (f < 10 || (f < oldF * (1. - CENTRIFUGATION_TIME_ACCEPTABLE_RANGE_PERCENTAGE)) || (f > oldF * (1. + CENTRIFUGATION_TIME_ACCEPTABLE_RANGE_PERCENTAGE)))  {
    centrifugationStartTime = millis();
  }
  float centTime = ((float) millis() - centrifugationStartTime) / 1000;
  lcd.setCursor(7, 0);
  if (centTime < 60) {
    lcd.print(centTime, 0); //in sec
    lcd.print("s"); //in sec
  } else {
    lcd.print(centTime / 60, 0);
    lcd.print("m");
    lcd.print(centTime - 60 * (int)(centTime / 60), 0);
    lcd.print("s");
  }
  float tempG = calcG();
  if (tempG < 1000) {
    lcd.print(tempG, 0);
    lcd.print("G");
  } else {
    lcd.print(calcG() / 1000, 0);
    lcd.print("kG");
  }
}

void attachInterrupt()
{
  attachInterrupt(digitalPinToInterrupt(2), isr, RISING); //attaching the interrupt on 2// only for Nano
}

float calcG() {
  return EPPENDORF_45_18_11 * (rpm / 1000) * (rpm / 1000);
}
//
///////original code from
//#include
//LiquidCrystal lcd(12, 11, 6, 5, 4, 3);
//float value = 0;
//float rev = 0;
//int rpm;
//int oldtime = 0;
//int time;
//
//void isr() //interrupt service routine
//{
//  rev++;
//}
//
//void setup()
//{
//  lcd.begin(16, 2);               //initialize LCD
//  attachInterrupt(0, isr, RISING); //attaching the interrupt
//}
//
//void loop()
//{
//  delay(1000);
//  detachInterrupt(0);           //detaches the interrupt
//  time = millis() - oldtime;    //finds the time
//  rpm = (rev / time) * 60000;   //calculates rpm
//  oldtime = millis();           //saves the current time
//  rev = 0;
//  lcd.clear();
//  lcd.setCursor(0, 0);
//  lcd.print("___TACHOMETER___");
//  lcd.setCursor(0, 1);
//  lcd.print(     rpm);
//  lcd.print(" RPM");
//  lcd.print("   ");
//  attachInterrupt(0, isr, RISING);
//
//}

//original lcd with shield code
//void loop()
//{
//  // Backlight on/off every 3 seconds
//  lcd.setCursor (0, 1);       // go to start of 2nd line
//  lcd.print(n++, DEC);
//  lcd.setBacklight(LOW);      // Backlight off
//  delay(3000);
//  lcd.setBacklight(HIGH);     // Backlight on
//  delay(3000);
//}