#include <MCP3424.h>
#include <OneWire.h>
#include "HCMotor.h" 
#include <FlowMeter.h>
#include <DallasTemperature.h>// DS18B20

#define PUMP1_PIN 8
#define PUMP2_PIN 9
#define PUMP3_PIN 10
#define DS_PIN 4
#define EC_POS A8
#define EC_NEG A9
#define pH_Po A0
#define C1_PIN 3
#define C2_PIN 4
#define C3_PIN 5

MCP3424 adc(0x68);
FlowMeter fm1 = FlowMeter(C1_PIN);
FlowMeter fm2 = FlowMeter(C2_PIN);
FlowMeter fm3 = FlowMeter(C3_PIN);
OneWire oneWireDS(DS_PIN);
DallasTemperature temperature(&oneWireDS);
HCMotor pump;

float ec25_calibration = 1.413;/// default Milwaukee conductivity calibration solution 1413 uS/cm
float pH_calibration = 4.01;/// default Milwaukee pH solution 4.01 pH

void fm1ISR() {fm1.count();}
void fm2ISR() {fm2.count();}
void fm3ISR() {fm3.count();}
void pump_regulator1(int pwm);
void pump_regulator2(int pwm);
void pump_regulator3(int pwm);
double ec_calibration(float ec25 = ec25_calibration);
double ph_calibration(float ph_cal = pH_calibration);
double get_drop(Channel CH);
double ac_drop(Channel CH);
double dc_drop(Channel CH);
double get_temp(int index = 0);
double get_ph();
double get_ec();

int ph_state=1;
long unsigned int start_time_pH=0;
unsigned int settle_time_pH=20000;
unsigned int on_time_pH=2000;

int ec_state=1;
long unsigned int start_time_EC=0;
unsigned int settle_time_EC=20000;
unsigned int on_time_EC=2000;

double target_pH=6;
double target_EC=1.1;
double pH=0;
double EC=0;
double T3=0;
double C1=0;
double C2=0;
double C3=0;
int pump1_pwm=600;
int pump2_pwm=600;
int pump3_pwm=600;
float K_EC = 10.41; //cell constant for ec sensor
float K_pH = 30.51;  //cell constant for ph sensor
Channel Rw = CH2;
Channel Rp = CH1;
static char * errmsg[] = {"", "underflow", "overflow", "i2c", "in progress", "timeout"};

void setup(void) {
  Serial.begin(9600);
  Wire.begin();
  delay(100);
  temperature.begin();
  delay(100);
  pinMode(EC_POS,OUTPUT);
  pinMode(EC_NEG,OUTPUT);
  digitalWrite(EC_POS,LOW);
  digitalWrite(EC_NEG,LOW);
  
  adc.generalCall(GC_RESET);
  adc.creg[CH1].bits = { GAINx1, SR16B, ONE_SHOT, CH1, 1 };
  adc.creg[CH2].bits = { GAINx1, SR14B, ONE_SHOT, CH2, 1 };
  adc.creg[CH3].bits = { GAINx1, SR16B, ONE_SHOT, CH3, 1 };
  adc.creg[CH4].bits = { GAINx1, SR18B, ONE_SHOT, CH4, 1 };
  adc.startNewConversion(CH1);

  attachInterrupt(INT1, fm1ISR, RISING);
  attachInterrupt(INT2, fm2ISR, RISING);  
  attachInterrupt(INT3, fm3ISR, RISING);  

  fm1.reset();
  fm2.reset();
  fm3.reset();
   
  pump.Init();
  pump.attach(0, DCMOTOR, PUMP1_PIN); 
  pump.attach(1, DCMOTOR, PUMP2_PIN); 
  pump.attach(2, DCMOTOR, PUMP3_PIN); 
  pump.DutyCycle(0, 100);
  pump.DutyCycle(1, 100);
  pump.DutyCycle(2, 100);
}

void loop(void) {
  //------------------------UART COMUNICATION--------------------------   
  String pole;
  if (Serial.available() > 0){
    String var = Serial.readStringUntil(';');
    double val = Serial.readStringUntil('\n').toDouble();
    //pole = Serial.readString(); // read the incoming byte:
    //Serial.print(" I received:");
    //Serial.println(pole);
    
    if (var == "hodnoty"){
      Serial.print(pH);
      Serial.print(";");
      Serial.print(EC);
      Serial.print(";");
      Serial.print(T3);
      Serial.print(";");
      Serial.print(C1);
      Serial.print(";");
      Serial.print(C2);
      Serial.print(";");
      Serial.print(C3);
      Serial.println("");
    }
    if (var == "pH"){
      target_pH=val;
      Serial.println("pHSET;");
    }
    if (var == "pHon"){
      on_time_pH=val;
      Serial.println("pHonSET;");
    }
    if (var == "pHsettle"){
      settle_time_pH=val;
      Serial.println("pHsettleSET;");
    }      
    if (var == "pHcal"){
      K_pH=ph_calibration(val);
      Serial.println("pHcalDONE;");
    }
    if (var == "pHstate"){
      ph_state=val;
      Serial.println("pHstateSET;");
    }
    
    if (var == "EC"){
      target_EC=val;
      Serial.println("ECSET;");
    }
    if (var == "ECon"){
      on_time_EC=val;
      Serial.println("EConSET;");
    }
    if (var == "ECsettle"){
      settle_time_EC=val;
      Serial.println("ECsettleSET;");
    }   
    if (var == "ECcal"){
     K_EC=ec_calibration(val);
     Serial.println("ECcalDONE;");
    }  
    if (var == "ECstate"){
      ec_state=val;
      Serial.println("ECstateSET;");
    }
  }
  //--------------------------------------------------------------------
  T3=get_temp();
  pH=get_ph();
  if (ph_state==1){
  if (millis()- start_time_pH >= settle_time_pH){
    if(pH<target_pH){
      pump_regulator1(pump1_pwm);
        delay(on_time_pH);
      pump_regulator1(0);
      start_time_pH=millis();
    }
    else if(pH>target_pH){
      pump_regulator2(pump2_pwm);
        delay(on_time_pH);
      pump_regulator2(0);
      start_time_pH=millis();
    }
  }
  }

  EC=get_ec();
  if(ec_state==1){
  if (millis()- start_time_EC >= settle_time_EC){
    if(EC<target_EC){
      pump_regulator3(pump3_pwm);
        delay(on_time_EC);
      pump_regulator3(0);
      start_time_EC = millis();
    } 
  }
  }
  fm1.tick(1000);
  fm2.tick(1000);
  fm3.tick(1000);
  C1=fm1.getTotalVolume()/1000;
  C2=fm2.getTotalVolume()/1000;
  C3=fm3.getTotalVolume()/1000; 
  delay(4000);
}
//////////////////////////////////////////////////////////
void pump_regulatorr1(int pwm){
  int s_pump1;
  s_pump1 = map(pwm, 0, 1024, 0, 100); 
  pump.OnTime(0, s_pump1 );  
}
void pump_regulator2(int pwm){
  int s_pump2;
  s_pump2 = map(pwm, 0, 1024, 0, 100);  
  pump.OnTime(1, s_pump2 );   
}
void pump_regulator3(int pwm){
  int s_pump3;
  s_pump3 = map(pwm, 0, 1024, 0, 100);  
  pump.OnTime(2, s_pump3 ); 
}
///////////////////////////////////////////////////////////////////
double get_temp(int index){
  temperature.requestTemperatures();
  //Serial.print("Teplota cidla DS18B20: ");
  //Serial.print(temperature.getTempCByIndex(index));
  //Serial.println(" stupnu Celsia");
  return temperature.getTempCByIndex(index);
}
//////////////////////////////////////////////////////////////////
double get_ph(){
  int array_ph[10];
  int temp_ph;
  unsigned long int total_ph = 0;

  //Measuring ph
  for (int i = 0; i < 10; i++) {
    array_ph[i] = analogRead(pH_Po);
    delay(15);
  }

  //Sorting ph ascend
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (array_ph[i] > array_ph[j]) {
        temp_ph = array_ph[i];
        array_ph[i] = array_ph[j];
        array_ph[j] = temp_ph;
      }
    }
  }

  for (int i = 2; i < 8; i++) {
    total_ph += array_ph[i];
  }
  
  float avg_ph = (float)total_ph * 5.0 / 1024 / 6;
  float ph = -5.70 * avg_ph + K_pH;
  //Serial.print("Namerene pH: ");
  //Serial.println(ph);
  return ph;
}
///////////////////////////////////////////////////////////////////////////////
double get_ec(){
  double array_drop[10];
  double temp_drop;
  double total_drop = 0;
  dc_drop(Rp); //odpad
  for (int i = 0; i < 5; i++) {
   array_drop[i]=dc_drop(Rp);
   //array_drop[i]=ac_drop(Rp);
  }
  
  for (int i = 0; i < 5; i++) {
      if (array_drop[i] < 0 ) {
      array_drop[i]=array_drop[i]*-1;
      }
  }

  for (int i = 0; i < 5; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (array_drop[i] > array_drop[j]) {
        temp_drop = array_drop[i];
        array_drop[i] = array_drop[j];
        array_drop[j] = temp_drop;
      }
    }
  }

  for (int i = 1; i < 4; i++) {
    total_drop += array_drop[i];
  }
  
  double avg_drop = total_drop / 3;
  //Serial.print("avgdrop: ");
  //Serial.println(avg_drop);
  double ec = K_EC*(1000/((5-avg_drop)*805/avg_drop));
  //Serial.print("Namerene EC: ");
  //Serial.println(ec);
  return ec;
}
////////////////////////////////////////////////////////////////////////
double dc_drop(Channel CH){
   digitalWrite(EC_POS,HIGH);
   get_drop(CH);//Not valid, charging probe capacity 
   double val=get_drop(CH);
   digitalWrite(EC_POS,LOW);
   //Serial.println(val, DEC); 
   return val;
}
/////////////////////////////////////////////////////////////////////
double ac_drop(Channel CH){
   digitalWrite(EC_POS,HIGH);
   get_drop(CH);
   delay(500);
   double val1=get_drop(CH);
   digitalWrite(EC_POS,LOW);
   digitalWrite(EC_NEG,HIGH);
   get_drop(CH);
   delay(500);
   double val2=-get_drop(CH);
   digitalWrite(EC_NEG,LOW);
  // Serial.println((val1+val2)/2, DEC); 
   return (val1+val2)/2;
}
//////////////////////////////////////////////////////////////////////
double get_drop(Channel CH){
    double val;
    adc.startNewConversion(CH);
    delay(200);
    ConvStatus err = adc.read(CH, val, false);
    if (err == R_OK) {
      //Serial.println(val, DEC); 
    } else if (err != R_IN_PROGRESS) {
      //Serial.print("conversion error: ");
      //Serial.println(errmsg[err]);
    }
    return val;
}
///////////////////////////////////////////////////////////////////
double ec_calibration(float ec25){
  double array_drop[10];
  double temp_drop;
  double total_drop = 0;
  double start_temp = get_temp(); 
  for (int i = 0; i < 5; i++) {
   array_drop[i]=dc_drop(Rp);
   delay(1500);
  }
  double end_temp = get_temp(); 
  
  for (int i = 0; i < 5; i++) {
      if (array_drop[i] < 0 ) {
      array_drop[i]=array_drop[i]*-1;
      }
  }

  for (int i = 0; i < 5; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (array_drop[i] > array_drop[j]) {
        temp_drop = array_drop[i];
        array_drop[i] = array_drop[j];
        array_drop[j] = temp_drop;
      }
    }
  }

  for (int i = 1; i < 4; i++) {
    total_drop += array_drop[i];
    //Serial.println(array_drop[i]);
  }
  
  double avg_drop = total_drop / 3;
    //Serial.println("avg drop:");
   //Serial.println(avg_drop);
  double g_s = 1000/((5-avg_drop)*805/avg_drop); //result in mS
    //Serial.println("vodivost:");
  //Serial.println(g_s);
  if ((end_temp<=start_temp+0.5)&&(end_temp>=start_temp-0.5)){
     //Serial.println("teplota je v povolenych mezich");
    } else {
     // Serial.println("teplota kolisa");
  }
 double Kt=(1+0.019*(((start_temp+end_temp)/2)-25));
  //Serial.println("Konduktivita roztoku:");
  //Serial.println(Kt,DEC);
  double Kc=Kt/g_s;
  //Serial.println("Kalibracni konstanta:");
  //Serial.println(Kc,DEC);
  return Kc; 
}
  ///////////////////////////////////////////////////////////////////////////////
double ph_calibration(float ph_cal){
  int array_ph[10];
  int temp_ph;
  unsigned long int total_ph = 0;

  //Measuring ph
  for (int i = 0; i < 10; i++) {
    array_ph[i] = analogRead(pH_Po);
    delay(15);
  }

  //Sorting ph values ascend
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (array_ph[i] > array_ph[j]) {
        temp_ph = array_ph[i];
        array_ph[i] = array_ph[j];
        array_ph[j] = temp_ph;
      }
    }
  }

  for (int i = 2; i < 8; i++) {
    total_ph += array_ph[i];
  }
  
  float avg_ph = (float)total_ph * 5.0 / 1024 / 6;
  float Kph = ph_cal + 5.70 * avg_ph;
  //Serial.print("Kalibracni konstanta: ");
  //Serial.println(Kph);
  return Kph;
}




