
/*************************************************************************************

  Kevin Lo, March 2015

  This program will show PH , Room Temperature and Water Temperature on the LCD panel.
  Also support serial communication.
 
  Connection: 
  1) Plug the LCD Keypad to the UNO
  2) Connect Arduino D2 to PH Meter Board T2 (DS18B20)
  3) Connect Arduino A1 to PH Meter Board T1 (LM35)
  4) Connect Arduino A2 to PH Meter Board P0 (PH)
  5) Connect Arduino 5V to PH Meter Board Vcc
  6) Connect Arduino GND to PH Meter Board GND
  
  Require Library :
  LiquidCrystal : http://arduino.cc/en/Reference/LiquidCrystal
  OneWire : http://www.pjrc.com/teensy/td_libs_OneWire.html
  DallasTemperature : http://milesburton.com/Dallas_Temperature_Control_Library

  Serial Communication :
  Send command in HEX format . 
  AA 01 01 BB , Enquiry DS18B20 temperature
  AA 01 02 BB , Enquiry LM35 temperature
  AA 01 03 BB , Enquiry PH reading
  AA 01 04 BB , Enqyiry DS18B20 , LM35 and Ph

  Version :
  v0.1 5/3/2015    First Version 

**************************************************************************************/

#include <LiquidCrystal.h>
//#include <OneWire.h>
//#include <DallasTemperature.h>
#include <EEPROM.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);           // select the pins used on the LCD panel
//#define ONE_WIRE_BUS 2                         // DS18B20 connect to Pin 2 
#define MOTOR1 3                              // jfs Pumpenrelais 1 Kontrolle
#define MOTOR2 2                               // jfs Pumpenrelais 2 An/Aus

int Pumpe = 0;                                // jfs Pumpenkontrolle 0=aus 1=Kontrolle 2=an

#define FLOWRATE 20
#define MAXTUB 25
double tub[] = {0.13,0.19,0.25,0.38,0.44,0.51,0.57,0.64,0.76,0.89,0.95,1.02,1.09,1.14,1.22,1.30,1.42,1.52,1.65,1.75,1.85,2.06,2.29,2.54,2.79,3.17};
double ml[] =  {0.0011,0.0023,0.0041,0.0094,0.013,0.017,0.021,0.026,0.036,0.049,0.056,0.063,0.072,0.078,0.088,0.098,0.11,0.13,0.15,0.16,0.17,0.20,0.24,0.27,0.31,0.35};
int tubid = 0;
int ee_tubid = 0;
double korrf = 0.915;  // 22.09.2020 vorher 0.71
unsigned long   start_timer;                  // jfs Timer start im Automodus
unsigned long   time_flow;                    // aktuelle Zeitsapnne
double flow;                                  // zugeflossene Menge ml[tubid]*20 -> ml/min 
                                              // msec/60000 -> min

//OneWire oneWire(ONE_WIRE_BUS);
//DallasTemperature sensors(&oneWire);

// the current address in the EEPROM (i.e. which byte
// we're going to write to next)
int addrph4 = 10;
int addrph7 = 20;

#define STX 0xAA                                // define STX for serial communication
#define ETX 0XBB                                // define ETX for serial communication

byte RxCmd [4] = {0,0,0,0};

// define some values used by the panel and buttons
int lcd_key     = -1;
int adc_key_in  = 0;
int adc_key_prev  = -1;
int CurrentMode = 0;                           // 0 = Normal Display , 1 = Debug1 , 2 = Debug2
int CalSelect = 0;                             // 0 = PH4 Calibration Select , 1 = PH7 Calibration Select

const int NumReadings = 20;                    // number of reading for LM35
int Index = 0;                                 // index
//int TempReadings[NumReadings];                 // array for store LM35 readings
//int TempTotal = 0;                             // LM35 running total
//int TempAverage = 0;                           // LM35 average reading
//double TempValue = 0;                          // LM35 Temperature Data in Human Reading Format after calculation

int PhReadings[NumReadings];                   // array for store PH readings
int PhTotal = 0;                               // PH running total
int PhAverage = 0;                             // PH average reading

double Ph7Buffer = 6.86;                       // For PH7 buffer solution's PH value , 7 or 6.86
double Ph4Buffer = 4.01;                       // For PH4 buffer solution's PH value , 4 or 4.01

//double Ph7Reading = 625;                       // PH7 Buffer Solution Reading.
//double Ph4Reading = 727;                       // PH4 Buffer Solution Reading.

int Ph7Reading = 625;                       // PH7 Buffer Solution Reading.
int Ph4Reading = 727;                       // PH4 Buffer Solution Reading.





//double Ph4Reading = EEPROM.read(addrph4);                       // PH7 Buffer Solution Reading.
//double Ph7Reading = EEPROM.read(addrph7); 


double PhRatio = 0;                            // PH Step
double PhValue = 0;                            // Ph Value in Human Reading Format after calculation

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

int read_LCD_buttons(){                        // read the buttons
    adc_key_in = analogRead(0);                // read the value from the sensor 
    delay(10);                                 // switch debounce delay. Increase this delay if incorrect switch selections are returned.
    int k = (analogRead(0) - adc_key_in);      // gives the button a slight range to allow for a little contact resistance noise
    if (5 < abs(k)) return btnNONE;            // double checks the keypress. If the two readings are not equal +/-k value after debounce delay, it tries again.
    //lcd.print(adc_key_in);                   // read button value and print for calibrate 
    
    // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
    // we add approx 50 to those values and check to see if we are close
    // We make this the 1st option for speed reasons since it will be the most likely result

    if (adc_key_in > 1000) return btnNONE; 
    if (adc_key_in < 50)   return btnRIGHT;  
    if (adc_key_in < 150)  return btnUP; 
    if (adc_key_in < 350)  return btnDOWN; 
    if (adc_key_in < 550)  return btnLEFT; 
    if (adc_key_in < 750)  return btnSELECT;  
    return btnNONE;                // when all others fail, return this.
}

int hys = 1;        // Hysterese zur Messwertglättung
int lcv = 0;

void reading(){                                   // Reading LM35 and PH Data
   // Samplin LM35 and PH Value
  //TempTotal= TempTotal - TempReadings[Index];    // subtract the last reading:       
  PhTotal= PhTotal - PhReadings[Index];          // subtract the last reading:       
  //TempReadings[Index] = analogRead(1);           // read from the sensor : LM35  
  PhReadings[Index] = analogRead(2);             // read from the sensor : PH 
  //TempTotal= TempTotal + TempReadings[Index];    // add the reading to the temperature total:     
  PhTotal= PhTotal + PhReadings[Index];          // add the reading to the ph total:     
  Index = Index + 1;                             // advance to the next position in the array:                

  if (Index >= NumReadings){                     // if we're at the end of the array...         
  Index = 0;                                     // ...wrap around to the beginning:                         
  //TempAverage = TempTotal / NumReadings;         // calculate the average:
  lcv = PhTotal / NumReadings;             // calculate the average:
  }
  //TempValue = (double) TempAverage / 3.4  * (5/10.24);   // LM35 connect to CA3140 for amplify 3 time  Serial.print(PhAverage);
  //Serial.print(PhAverage);
  if (PhAverage == 0){
    PhAverage = lcv;
  } else {
    if (lcv+1 > PhAverage ){
      PhAverage=lcv;
    }
    if (lcv-1 < PhAverage ){
      PhAverage=lcv;
    }
  }

  //Serial.print("  ");
  //Serial.print(lcv);
  //Serial.print("  ");
  
  
  PhValue = (Ph7Reading - PhAverage) / PhRatio + Ph7Buffer;    // Calculate PH vorher PhAverage

  //Serial.println(PhValue);
}



void setup(){
  EEPROM.get(ee_tubid, tubid);
  if ((tubid <0) || (tubid>MAXTUB)){
    tubid=0;
  }
  pinMode(MOTOR1, OUTPUT);
  pinMode(MOTOR2, OUTPUT);
  pinMode(MOTOR1,HIGH);
  pinMode(MOTOR2,LOW); 
  Pumpe = 1;

   EEPROM.get(addrph4,Ph4Reading);

   EEPROM.get(addrph7,Ph7Reading);

  lcd.begin(16, 2);                                                                // start LCD library
    
   //for (int TempThisReading = 0; TempThisReading < NumReadings; TempThisReading++)  // initialize all the LM35 readings to 0:
   //TempReadings[TempThisReading] = 0; 
   
  for (int PhThisReading = 0; PhThisReading < NumReadings; PhThisReading++)        // initialize all the Ph readings to 0:
  PhReadings[PhThisReading] = 0; 
   
  PhRatio = (Ph4Reading - Ph7Reading) / (Ph7Buffer - Ph4Buffer);                 // Calculate Ph Ratio 
      
  Serial.begin(9600);
  while(Serial.available()) Serial.read();                                          // empty RX buffer
  Serial.println("Starting");
  PhAverage = 0;
}


void loop(){

 for (int i = 0 ; i < 4 ; i++) {
    RxCmd[i] = 0;
  }
/*
  if (Serial.available()) {
   delay(2);
    RxCmd[0] = Serial.read(); 
    if (RxCmd[0] == STX) {
        int i =1;
      while(Serial.available()) {
        delay(1);
        RxCmd[i] = Serial.read();
        //if (RxCmd[i]>127 || i>7)      break;                                    //Communication error
        if (RxCmd[i]==ETX) {
          break;                                                                  //Read all data 
        }
        i++;
      }
    }
  }
*/
if (Serial.available()) {
   delay(2);
    RxCmd[0] = Serial.read(); 
    if (RxCmd[0] == '<') {
        int i =1;
      while(Serial.available()) {
        delay(1);
        RxCmd[i] = Serial.read();
        //if (RxCmd[i]>127 || i>7)      break;                                    //Communication error
        if (RxCmd[i]== '>') {
          break;                                                                  //Read all data 
        }
        i++;
        
      }
    }
  }
if (RxCmd[1] == '1') {
  switch (RxCmd[2]) {
    case '1': // Pump on
      pinMode(MOTOR1, HIGH);
      pinMode(MOTOR2, HIGH);
      Pumpe = 2;
      Serial.println("<P1>"); // Bestätigung
      break;
    case '2': // Pump off
      pinMode(MOTOR1, HIGH);
      pinMode(MOTOR2, LOW);
      Pumpe = 1;
      Serial.println("<P0>"); // Bestätigung
      break;
    case '3': // Tube selection (3-28)
      tubid = RxCmd[3] - '3'; // Convert to 0-25
      if (tubid >= 0 && tubid <= MAXTUB) {
        EEPROM.put(ee_tubid, tubid);
        Serial.print("<T");
        Serial.print(tubid);
        Serial.println(">"); // Bestätigung
      }
      break;
    case '4': // Send pH value
      Serial.print("<#");
      Serial.print(PhValue, 2);
      Serial.println(">");
      break;
    case '5': // Start calibration (4 or 7)
      // Implement calibration mode
      break;
  }
}





if (CurrentMode == 0){                        // Nomral Display Mode

  reading();                                 // Reading LM35 and PH Data for display 
  //lcd.setCursor(13,0);
  //lcd.print("PH ");
  lcd.setCursor(0,0);
  lcd.print("Pumpe ");
  lcd.setCursor(7,0);
  lcd.print(Pumpe);
  lcd.setCursor(0,1);
  lcd.print(tub[tubid]);
  
  //lcd.setCursor(0,0);                        // set the LCD cursor position 
  //lcd.print("Room");  
  //lcd.setCursor(0,1);
  //lcd.print("Water");  
  //lcd.setCursor(6,0);
  //lcd.print(TempValue);                      // display room temperature value (LM35)
  delay(1);                                  // delay in between reads for stability     
   
  // Display 18B20 Temperature
  //lcd.setCursor(6,1);                       // move cursor to second line "1" and 6 spaces over
  //sensors.requestTemperatures();            // Read DS18B20 data
  //lcd.print(sensors.getTempCByIndex(0));    // Display DS18B20 Data

  // Display PH Data 
  lcd.setCursor(13,0);
  lcd.print("PH");
  lcd.setCursor(12,1);
  lcd.print(PhValue);                        // display PH value
  delay(1);                                  // delay in between reads for stability   

}

if (CurrentMode == 1){ // Pumpe aus Kontrolle an
  lcd.setCursor(0,0);
  lcd.print("Pumpe aus >>");
}

if (CurrentMode == 2){ // Pumpenkontrolle aus
  lcd.setCursor(0,0);
  lcd.print("Kontrolle aus >>");  
}

if (CurrentMode == 3){ // Pumpe an Kontrolle an
  lcd.setCursor(0,0);
  lcd.print("Pumpen an >>");  
}

if (CurrentMode == 4){ // PH4 Calibration Mode
  reading();
  lcd.setCursor(0,0);
  lcd.print("PH4 Cal. Mode");
  lcd.setCursor(0,1);
  lcd.print("C:");
  lcd.setCursor(2,1);
  lcd.print(Ph4Reading);
  lcd.setCursor(9,1);
  lcd.print("R:");
  lcd.setCursor(11,1);
  lcd.print(PhAverage);
}

if (CurrentMode == 5){ // PH7 Calibration Mode
  reading();
  lcd.setCursor(0,0);
  lcd.print("PH7 Cal. Mode");
  lcd.setCursor(0,1);
  lcd.print("C:");
  lcd.setCursor(2,1);
  lcd.print(Ph7Reading);
  lcd.setCursor(9,1);
  lcd.print("R:");
  lcd.setCursor(11,1);
  lcd.print(PhAverage);
}

if (CurrentMode == 6){ // Schlauchauswahl vorschlagen
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Schlauch >>");
}

if (CurrentMode == 10){ // Schlauchauswählen
  lcd.setCursor(0,0);
  lcd.print(tub[tubid]);
  lcd.setCursor(0,1);
  lcd.print(ml[tubid]);
}
if (CurrentMode == 11){ // Auto modus läuft
  reading();                                 // Reading LM35 and PH Data for display 
  time_flow=millis() - start_timer ;
  flow = (20*time_flow)/60000;                    // vergangene sec
  flow = ml[tubid]*flow*korrf;                  // ml/min
  lcd.setCursor(0,0);
  lcd.print("ml");
  lcd.setCursor(0,1);
  lcd.print(flow);
  // Display PH Data 
  lcd.setCursor(13,0);
  lcd.print("PH");
  lcd.setCursor(12,1);
  lcd.print(PhValue);                        // display PH value
  delay(10);                                  // delay in between reads for stability   
  
}
if (CurrentMode == 12) { // Auto modus stopped
  delay(100);
  //reading();
  //lcd.setCursor(4,0);
  //lcd.print("Stop");
  lcd.setCursor(0,0);
  lcd.print("used ml");
  lcd.setCursor(0,1);
  lcd.print(flow);
  // Display PH Data 
  lcd.setCursor(13,0);
  lcd.print("PH");
  lcd.setCursor(12,1);
  lcd.print(PhValue);                        // display PH value
  delay(100);      
}

   lcd.setCursor(0,1);             // move to the begining of the second line
   adc_key_prev = lcd_key ;       // Looking for changes

   lcd_key = read_LCD_buttons();   // read the buttons

   if (adc_key_prev != lcd_key)
   {
    switch (lcd_key){
      case btnDOWN:{ 
        if (CurrentMode==10){
          tubid -=1;
          if (tubid == -1){
            tubid = MAXTUB;    
          }
        } else {
         lcd.clear();
         CurrentMode +=1;
         if (CurrentMode == 7){
           CurrentMode = 0;
         } 
        }        
        break;
      }
      case btnUP:{
        if (CurrentMode==10){
          tubid += 1;
          if (tubid == MAXTUB){
            tubid=0;
          }
        } else {
        lcd.clear();
        CurrentMode -= 1;
        if (CurrentMode == -1){
          CurrentMode = 6;
        }
        }
        break;
      }
      case btnLEFT:{
        lcd.clear();
        CurrentMode =0;
        break;
      }
      case btnRIGHT:{
        lcd.clear();
        if ( CurrentMode == 1){ //Pumpe aus
          pinMode(MOTOR1,HIGH);
          pinMode(MOTOR2,LOW);
          Pumpe=1;
          CurrentMode = 0;
          break;
        }
        if ( CurrentMode == 2) { //Pumpenkotrolle
          pinMode(MOTOR1,LOW);
          pinMode(MOTOR2,LOW);
          Pumpe=0;
          CurrentMode = 0;
          break;
        }
         if ( CurrentMode == 3) { //Pumpe an
          pinMode(MOTOR1,HIGH);
          pinMode(MOTOR2,HIGH);
          Pumpe=2;
          CurrentMode = 0;
          break;
        }
        if ( CurrentMode == 4) { //ph 4 wert speichern
          Ph4Reading = (int) PhAverage;
          Serial.println(Ph4Reading);
          EEPROM.put(addrph4,Ph4Reading);
          int p ;
          EEPROM.get(addrph4,p);
          Serial.println(p);
          
          CurrentMode = 0;
          break;
        }
        if ( CurrentMode == 5) { //ph 7 wert speichern
          Ph7Reading = (int) PhAverage;
          Serial.println(Ph7Reading);
          EEPROM.put(addrph7,Ph7Reading);
          int p ;
          EEPROM.get(addrph7,p);
          Serial.println(p);

          CurrentMode = 0;
          break;
        }
        if (CurrentMode == 6 ) { // nun die Schläuche auswählen
          CurrentMode = 10;
          break;
        }
        if ( CurrentMode == 10) { // Schlauchindex speichern
          // todo
          EEPROM.put(ee_tubid,tubid);
          CurrentMode = 0;
          break;
        }
      
      case btnSELECT:{
        if (CurrentMode == 0){ // Automodus starten
              pinMode(MOTOR1,HIGH);
              pinMode(MOTOR2,HIGH);
              lcd.clear();
              start_timer = millis();
              CurrentMode = 11;
            break;
        }
        if (CurrentMode == 11){
               pinMode(MOTOR1,HIGH);
               pinMode(MOTOR2,LOW);
              CurrentMode = 12;
           break;
        }
        if (CurrentMode == 12){
              CurrentMode = 0;
          break;
        }
      }
    }
   }
   }


}
