#define PIN_PH4502C     34                        // measuring  pH - PH-4502C sensor
#define VIN             3.3
#define PH_REF          -5.7
#define PH_SAMPLES          10                  // [x] pocet vzorku - prumerna hodnota
#define PH_SAMPLES_INTERVAL 1000                // [ms] perioda cteni senzoru 

int PH_ARRAY[PH_SAMPLES];                       // pamet pro prumernou hodnotu x poctu vzorku
int idxArrayPH = 0;

float reqPH = 6.4;                              // [-] required pH                          MEM
float actPH = 0.0;                              // [-] actual pH
int actPHv;                                     // [V] analog pin voltage
float avgPH = 0.0;                              // [-] prumerna hodnota pH z x vzorku

float hysPH = 0.1;                              // [-] hysteresis for regulation
float minPH = 5.0;                              // [-] limit min pH                         MEM
float maxPH = 7.0;                              // [-] limit max pH                         MEM

// CO2
#define dKH   17.92                             // [°] carbonate hardness                   MEM
float actCO2;
float minCO2 = 0.0;                             // limit min CO2      
float maxCO2 = 400;                             // limit max CO2


bool firstPHSample = true;
unsigned long tmcPH;                            // [ms] pH measurement sampling cycle



// Kalibrace
#define PH_CAL_REF          6.86                // pH – hodnota pouzita jako referencni pri kalibraci
#define PH_DELAY_C_SAMPLES  500                 // [ms] perioda cteni senzoru - kalibrace
#define PH_C_SAMPLES        7                   // [x] pocet vzorku pri kalibraci
bool autoCalPH = false;                         // auto calibration – change calVal
bool autoCalPHM = false;                        // aut. kalibrace - pamet 
unsigned long tmcPHC;                           // [ms] pH calibration sampling cycle
float calPH = 21.34;                            // [-] nastavit hodnotu pri kalibraci

bool a_PH_min, a_PH_max = false;                // sensor PH-4502C (water) - pH
bool a_CO2_min, a_CO2_max = false;
long timeButton;
bool ButtonM = false;
#define LED 2
//----------------------------------------------+----------------------------------------------
void setup() {
  Serial.begin(115200);
  pinMode(27, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  bool Button = (!digitalRead(27));
  if (Button && !ButtonM) {                    // je-li ted stisknuto tlacitko
    ButtonM = true;
    timeButton = millis();
    digitalWrite(LED, !digitalRead(LED));       // zmenit stav LED
    autoCalPH = true;
  }
  else if (!Button && ButtonM && (millis() - timeButton) > 500) {
    ButtonM = false;
  }

  readPH();
}

//----------------------------------------------+----------------------------------------------
//  pH value - sensor PH-4502C
//----------------------------------------------+----------------------------------------------
//
void readPH() {                                 // funkce cteni dat z PH-4502C

//----------------------------------------------+----------------------------------------------
//  Automaticka kalibrace senzoru
//
  if (autoCalPH) {                              // automaticka kalibrace -> start
    if (autoCalPH && !autoCalPHM) {
      autoCalPHM = true;
      idxArrayPH = 0;
      Serial.println("START kalibrace pH");
    }
    if (millis() - tmcPHC > PH_DELAY_C_SAMPLES) { // perioda cteni senzoru - kalibrace
      tmcPHC = millis();
      PH_ARRAY[idxArrayPH] = analogRead(PIN_PH4502C);// akt.hodnota pH senzoru do bufferu
      Serial.println("Vstup : " + String(PH_ARRAY[idxArrayPH]));
      idxArrayPH++;
      if (idxArrayPH > PH_C_SAMPLES) {          // kalibrace dokoncena -> vypocet kalibracni hodnoty "calPH"
        calPH = -PH_REF * (getAverageValue(PH_ARRAY, PH_C_SAMPLES) * VIN / 4095.0) + PH_CAL_REF;
        autoCalPH = false;
        autoCalPHM = false;
 //       idxArrayPH = 0;
 //        for (int i = 0; i < PH_SAMPLES; i++) {    
 //       PH_ARRAY[i] = actPHv;
 //       }
        Serial.println("KONEC kalibrace calPH = " + String(calPH, 2));
      }
    }
  }
//----------------------------------------------+----------------------------------------------
// Cteni hodnoty pH ze senzoru a vypocet mnozstvi CO2
//

  if (millis() - tmcPH > PH_SAMPLES_INTERVAL && !autoCalPH) {
    tmcPH = millis();
    actPHv = analogRead(PIN_PH4502C);           // vstupni hodnota = aktualni hodnota pH [mV]

    if (firstPHSample) {                        // prvni cyklus -> napln buffer prvni zmerenou hodnotou
      for (int i = 0; i < PH_SAMPLES; i++) {    
        PH_ARRAY[i] = actPHv;
      }
      firstPHSample = !firstPHSample;
    } else {                                    // cyklus mereni pH 
      if (idxArrayPH > PH_SAMPLES) {            // pokud je max.pocet vzorku dosazen -> zacni od nuly
        idxArrayPH = 0;
      }
      PH_ARRAY[idxArrayPH] = actPHv;            // akt.hodnota pH do bufferu
      idxArrayPH++;
      actPH = PH_REF * (float)actPHv * VIN / 4095.0 + calPH;                     // aktualni pH

      avgPH = PH_REF * (getAverageValue(PH_ARRAY, PH_SAMPLES) * VIN / 4095.0) + calPH; // prumer. hodnota
      a_PH_min = (actPH < minPH);                 // prekrocen horni limit pH
      a_PH_max = (actPH > maxPH);                 // prekrocen horni limit pH

      // CO2 calculation; (pow function instead of "10^6,37-phValue",
      // whitch is not defined in Wiring language
      actCO2 = 12.84 * dKH * pow(10, (6.37 - avgPH));
      a_CO2_min = actCO2 < minCO2;
      a_CO2_max = actCO2 > maxCO2; 

      Serial.print("Vstup " + String(actPHv));
      Serial.print("  pH " + String(actPH, 2));
      Serial.print("  ø " + String(avgPH, 2));
      Serial.println("  CO2 " + String(actCO2));
    }
  }
}

float getAverageValue(int* ARRAY, int AVG_ARRAY_SIZE) { // vrati prumernou hodnotu
  float AMOUNT = 0;
  if (AVG_ARRAY_SIZE <= 0){
    Serial.println("ERROR!/n");
    return 0;
  }
  for (byte i = 0; i < AVG_ARRAY_SIZE; i++) {
    AMOUNT += ARRAY[i];
  }
  return (AMOUNT / AVG_ARRAY_SIZE);
}