/*
#include <iarduino_RTC.h>    
#include <LiquidCrystal_I2C.h>  
#include <OneWire.h>
#include <DallasTemperature.h>
*/
#include "configs.h"
#include "lcd.h"
#include "Sensor.h"
#include "motor.h"
#include "PZEM004Tv30.h"

// Режим работы аппарата
eMode modeWork = nonMode; 
//крылья ноги и хвосты
eCurMode modeCurWork = modeNull; 
//*------------------------------------------------------------
// переменные температуры и мощности
float _temp[SENSOR_COUNT], _power, _voltage, _current, setPower, minPower, maxPower, stepPower;
currentData currentPowerData;
//*-----------------------------------------------------------
// Переменные мощности
volatile int nullCross, setT=2251, minT = 2250, maxT = 1;
val_float powerCurWork[MODE_COUNT][CURMODE_COUNT];
EEManager memPower(powerCurWork);

// Флаги остановки
boolean flagStop = true, flagDef = true, flagPressKey = false;
// Флаг первоначальной инициализации текущего процесса
boolean initMode;
// Переменные времени
uint8_t D, M, Y, h, m, s, W, _h, _m, _s;
// Временные метки работы
uint32_t startTime, curTime, curWTime, timeBeep;
// Температура дефлегматора
float minTempDef, maxTempDef, minTempDefC, maxTempDefC, stepTempDef;
val2_float tempDef[MODE_COUNT][CURMODE_COUNT];
EEManager memDef(tempDef);
// Mотор насоса
int minRPM, maxRPM, stepRPM, currentRPM;
// Управление пикалкой
volatile uint8_t bCnt = 0, bTime = 0;


//------------------------------------------------------------------------------------
//
//   Основные установки
//
void setup() {
  Serial.begin(115200);
  //-----------------------------
  // Светодиодики
  ledInit();

  //-----------------------------
  // Монитор
  lcdInit();
  startTime = getTimeUnix();
  
  //-----------------------------
  // Power value settin
  setupPower();
  //minPower = 0; maxPower = 3000; stepPower = 10;
  byte start = memPower.begin(0, EE_KEY);
  
  //-----------------------------
  // Temperature deflegmator setting
  setupDefTemp();
  start = memDef.begin(memPower.nextAddr(), EE_KEY);
  //minTempDef = 20.0; maxTempDef = 70.0; minTempDefC = 55.0; maxTempDefC = 60.0; stepTempDef = 1;
  
  //-----------------------------
  // Инициализация таймеров
  TamersInit();
  
  //-----------------------------
  pinMode(pBuzzer, OUTPUT);   // пикалка
  Buzzer(1);
  
  //-----------------------------
  // Установка даты и времени
  if (Y <= 17) inputTimeValue("Input date/time:");
  do {
    modeWork = setupMenu();
  } 
  while (modeWork == nonMode);


  //-----------------------------
  // подключение прерывания частоты сети
  attachInterrupt(0, zerroCross, RISING);

  //-----------------------------
  // Подключение сенсоров
  sensorInit();

  // инициализация моторов
  // Stepper motor value settings
  minRPM =10; maxRPM =150; stepRPM = 1; currentRPM = 9;
  
  //-----------------------------
  // Дефлегматор
  pPumpInit();
  motorInit();
}

//*--------------------------------------------------------------------------------------------
// Инициализация мощности
void setupPower(){
  //--------------------- первый перегон
  // Разогрев
  powerCurWork[firstMode][modeStart].min   = 2000;
  powerCurWork[firstMode][modeStart].max   = 3500;
  powerCurWork[firstMode][modeStart].step  = 10;
  powerCurWork[firstMode][modeStart].value = 3500;
  // Головы
  powerCurWork[firstMode][modeHead].min   = 1000;
  powerCurWork[firstMode][modeHead].max   = 1700;
  powerCurWork[firstMode][modeHead].step  = 10;
  powerCurWork[firstMode][modeHead].value = 1200;
  // Рабочий процесс
  powerCurWork[firstMode][modeBody].min   = 1200;
  powerCurWork[firstMode][modeBody].max   = 1800;
  powerCurWork[firstMode][modeBody].step  = 10;
  powerCurWork[firstMode][modeBody].value = 1500;
  // Хвосты
  powerCurWork[firstMode][modeTail].min   = 1500;
  powerCurWork[firstMode][modeTail].max   = 2500;
  powerCurWork[firstMode][modeTail].step  = 10;
  powerCurWork[firstMode][modeTail].value = 1800;
  
  //--------------------- второй перегон
  // Разогрев
  powerCurWork[secondMode][modeStart].min   = 2000;
  powerCurWork[secondMode][modeStart].max   = 3500;
  powerCurWork[secondMode][modeStart].step  = 10;
  powerCurWork[secondMode][modeStart].value = 3500;
  // Головы
  powerCurWork[secondMode][modeHead].min   = 1000;
  powerCurWork[secondMode][modeHead].max   = 1700;
  powerCurWork[secondMode][modeHead].step  = 10;
  powerCurWork[secondMode][modeHead].value = 1200;
  // Рабочий процесс
  powerCurWork[secondMode][modeBody].min   = 1200;
  powerCurWork[secondMode][modeBody].max   = 1800;
  powerCurWork[secondMode][modeBody].step  = 10;
  powerCurWork[secondMode][modeBody].value = 1500;
  // Хвосты
  powerCurWork[secondMode][modeTail].min   = 1500;
  powerCurWork[secondMode][modeTail].max   = 2500;
  powerCurWork[secondMode][modeTail].step  = 10;
  powerCurWork[secondMode][modeTail].value = 1800;

  //--------------------- НБК
  // Разогрев
  powerCurWork[continuiusMode][modeStart].min   = 2000;
  powerCurWork[continuiusMode][modeStart].max   = 3500;
  powerCurWork[continuiusMode][modeStart].step  = 10;
  powerCurWork[continuiusMode][modeStart].value = 3500;
  // Рабочий процесс
  powerCurWork[continuiusMode][modeBody].min   = 1200;
  powerCurWork[continuiusMode][modeBody].max   = 1800;
  powerCurWork[continuiusMode][modeBody].step  = 10;
  powerCurWork[continuiusMode][modeBody].value = 1500;
  
}
//*--------------------------------------------------------------------------------------------
// Инициализация диапазона температуры дефлегматора
void setupDefTemp(){
  //  -------------------- первый перегон
  //  головы
  tempDef[firstMode][modeHead].min      = 50;
  tempDef[firstMode][modeHead].max      = 70;
  tempDef[firstMode][modeHead].minValue = 63;
  tempDef[firstMode][modeHead].maxValue = 65;
  tempDef[firstMode][modeHead].step     = 1;
  // рабочий процесс
  tempDef[firstMode][modeBody].min      = 50;
  tempDef[firstMode][modeBody].max      = 75;
  tempDef[firstMode][modeBody].minValue = 67;
  tempDef[firstMode][modeBody].maxValue = 69;
  tempDef[firstMode][modeBody].step     = 1;
  // хвост
  tempDef[firstMode][modeTail].min      = 50;
  tempDef[firstMode][modeTail].max      = 75;
  tempDef[firstMode][modeTail].minValue = 68;
  tempDef[firstMode][modeTail].maxValue = 70;
  tempDef[firstMode][modeTail].step     = 1;
    
  //  -------------------- второй перегон
  //  головы
  tempDef[secondMode][modeHead].min      = 50;
  tempDef[secondMode][modeHead].max      = 70;
  tempDef[secondMode][modeHead].minValue = 63;
  tempDef[secondMode][modeHead].maxValue = 65;
  tempDef[secondMode][modeHead].step     = 1;
  // рабочий процесс
  tempDef[secondMode][modeBody].min      = 50;
  tempDef[secondMode][modeBody].max      = 75;
  tempDef[secondMode][modeBody].minValue = 67;
  tempDef[secondMode][modeBody].maxValue = 69;
  tempDef[secondMode][modeBody].step     = 1;
  // хвост
  tempDef[secondMode][modeTail].min      = 50;
  tempDef[secondMode][modeTail].max      = 75;
  tempDef[secondMode][modeTail].minValue = 68;
  tempDef[secondMode][modeTail].maxValue = 70;
  tempDef[secondMode][modeTail].step     = 1;    
  }
//*--------------------------------------------------------------------------------------------
// Инициализация таймеров
void TamersInit(){

//------ Timer1 ---------- Таймер задержки времени открытия триака после детектирования нуля (0 триак не откроется)
  TCCR1A = 0x00;  //
  TCCR1B = 0x00;    //
  TCCR1B = (0 << CS12) | (1 << CS11) | (1 << CS10) | (1 << WGM12); // Тактирование от CLK.
  OCR1A = 0;           // Верхняя граница счета. Диапазон от 0 до 65535.
  // Частота прерываний будет = Fclk/(N*(1+OCR1A))
  // где N - коэф. предделителя (1, 8, 64, 256 или 1024)
  TIMSK1 |= (1 << OCIE1A) | (1 << TOIE1); // Разрешить прерывание по совпадению и переполнению
  //TIMSK1 = 0x00; //запретить прерывания
  PORTC &=  ~(1 << PORTC0);
  flagStop = true;

//------ Timer4 ----------  Настройка таймера 130 Гц чтение энкодера
  TCCR4A = 0; // установить регистры в 0
  TCCR4B = 0;
  // Установка битов CS10 и CS12 на коэффициент деления 1024
  TCCR4B |= (1 << CS40) | (1 << CS42);
  OCR4A = 120; // установка регистра совпадения
  TCCR4B |= (1 << WGM42); // включение в CTC режим
  TIMSK4 |= (1 << OCIE4A);

//------ Timer5 ---------- Настройка ШИМ для помпы
  TCCR5A = 0; TCCR5B = 0;
  TCCR5B |= (1<<CS50);    // prescaler 0
  ICR5  = 500;
  OCR5A = 150;
  TCCR5A |= (1<<COM5B1);
  TCCR5B |= (1<<WGM53);  
}

//*--------------------------------------------------------------------------------------------
// Проверка состояния кнопок и энкодера
// Вызов опроса состояния 
//

ISR (TIMER4_COMPA_vect)
{
  tick();
  if (bCnt > 0) {
    if (bTime == 0) PORTG |= (1 << PORTG2);
    if (bTime >= TIME_BUZER) {
      PORTG &= ~(1 << PORTG2);
      bCnt--;
      //Serial.printf("bCnt  = %u\n", bCnt);
      if (bCnt == 0) bTime=1;
    }
    if ((PORTG >> PORTG2) & 1) {
      bTime++;
      //Serial.printf("bCnt++  = %u, bTime = %u\n", bCnt, bTime);
    }
    else {
      bTime--;
      //Serial.printf("bCnt--  = %u, bTime = %u\n", bCnt, bTime);
    }
  }
}

//---------------------------------------------------------------------------------------------
//Обработка таймера по совпадению нуля
ISR (TIMER1_COMPA_vect)
{
  uint8_t oldSREG = SREG;                   // запомнинаем были ли включены прерывания
  cli(); 
  PORTC |=  (1 << PORTC0);
  TCNT1 = 65535 - 200;  //Импульс включения симистора 65536 -  1 - 4 мкс, 2 - 8 мкс, 3 - 12 мкс и тд
  SREG = oldSREG;
}

ISR (TIMER1_OVF_vect) { //timer1 overflow
  uint8_t oldSREG = SREG;                   // запомнинаем были ли включены прерывания
  cli(); 
  PORTC &=  ~(1 << PORTC0);
  TCNT1 = OCR1A + 1;
  SREG = oldSREG;
}

//*--------------------------------------------------------------------------------------------
// Прерывангие по пересечениею нуля
//

void zerroCross(){
  uint8_t oldSREG = SREG;                   // запомнинаем были ли включены прерывания
  cli(); 
  TCNT1 = 0;
  OCR1A = setT;
  SREG = oldSREG;
}

//*--------------------------------------------------------------------------------------------
// Бибикалка
//
void Buzzer(uint8_t _cnt){
  bCnt = _cnt;
}

unsigned long prevoisMillisLCD, prevoisMillisPW, prevoisMillisT, pmW;
unsigned int nomSensor;

void loop() {
  static float _t0, _t1, _t2, _t3, _t4;
  //static float _tmpPower;

  //Serial.println("loop");
  unsigned long thisMillis;
  thisMillis = millis();
  // Опрос состояния датчика мощности
  if (thisMillis - prevoisMillisPW >= 50){
     currentPowerData = getPowerMetr();
     _power = currentPowerData.power;
     prevoisMillisPW = thisMillis;
  }
  // Опрос датчиков температуры
  if (thisMillis - prevoisMillisT >= 100){
    _temp[nomSensor] = getTemp(nomSensor);
    nomSensor++;
    if (nomSensor >= SENSOR_COUNT) nomSensor = 0;
    prevoisMillisT = thisMillis;
  }
  // Прорисовка экрана
  if (thisMillis - prevoisMillisLCD >= 1000){
     printSensor();
     prevoisMillisLCD = thisMillis;
     nullCross = 0;
  }
  //Регулировка мощности
  
  if (flagStop) {
    if (setPower != 0){
      TIMSK1 |= (1 << OCIE1A) | (1 << TOIE1);
      flagStop = false;
      //Serial.println(F("Enable intrrupt"));
    }
  }
  else{
    if (setPower == 0){
      TIMSK1 = 0x00;
      PORTC &=  ~(1 << PORTC0);
      flagStop = true;
      //Serial.println(F("Disable intrrupt"));
    }
    else{
      if (TIMSK1 == 0x00)  TIMSK1 |= (1 << OCIE1A) | (1 << TOIE1);
      if (thisMillis - pmW > 25){
        // регулировка мощности
        // Уменьшаем мощность
        if (_power > setPower + 10) setT++;
        // Увеличиваем мощность
        if (_power < setPower - 10) setT--;
        if (_power > setPower + 10 && _power < setPower - 10) setT = setT;
        if (setT > minT) setT = minT;
        if (setT < maxT) setT = maxT;
        //Serial.printf("setPower = %7.2f, _power = %7.2f, setT = %u\n", setPower, _power, setT);
        pmW = thisMillis;
      }
    }
  }
  // Вызов меню установки мощности и других параметров
  if (!flagPressKey){
    if (isHolded()) setupParam();
  }
  
  // обработка самогоноварения
  if (modeCurWork == 0){   //Мах мощность и разгон для всех
    modeCurWork = modeStart;
    setPower = maxPower;
    setT = maxT;
    initMode = false;
  }

  switch (modeWork) {
    case firstMode:
       doFirstModeProcess();
       break;
    case secondMode:
      doSecondModeProcess();
       break;
    case continuiusMode:
       doContinuiusModeProcess();
       break;
  }
 /*
  // SUPER ALARM !!!!!!!!!
  if ( _temp[4] > 55) {
    setPower = 0;
  }
  */
}
//------------------------------------------------------------------------------------
//
// первый перегон
//
void doFirstModeProcess(){
static boolean flagHeadStart;
uint32_t _timeTemp;
  
  // Управление работой дефлегматора
  if (flagDef){
    if (_temp[1] > 50) pumpStart();
    else { 
      pumpStop();
      fanStop();
    }
  }
  _timeTemp = getTimeUnix();  // временная метка текущего цикла
  switch (modeCurWork){
    case modeNull:
      if (!initMode){
        setPower = powerCurWork[modeWork][modeNull].value;
        curWTime = getTimeUnix();
    }
    break;

    case modeStart:           // разгон аппарата
      if (!initMode) {
        setPower = powerCurWork[modeWork][modeCurWork].value;
        flagStop = false;
        curTime  = getTimeUnix();
        curWTime = curTime;
        initMode = true;
        onLed(pRed1);
      }
      // нагрев верхней точки до 70 градусов, переход к режиму отбора голов
      if (_temp[2] > 70){
        initMode = false;
        offLed(pRed1); onLed(pGreen1);
        modeCurWork = modeHead;
      }
      break;
    case modeHead:
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = _timeTemp;
        initMode = true;
        onLed(pRed2);
        flagHeadStart = true;
        fanStart();
      }
      if (_timeTemp - curWTime < 300 ){           // не даем кипеть и ипаряться, выбиваем головы 
        if (_temp[2] > 75){
          setPower = powerCurWork[modeWork][modeCurWork].value - 200;
          onLed(pBlue2);
          offLed(pGreen2);
          fanStart();
        }
        else{
          setPower = powerCurWork[modeWork][modeCurWork].value + 200;
          offLed(pBlue2);
          onLed(pGreen2);
        }
      }
      else{
        // чисто получаем головы при испарении
        if (setPower != powerCurWork[modeWork][modeCurWork].value) {  // очищаем переменные для извлечения голов
          flagHeadStart = false;
          setPower = powerCurWork[modeWork][modeCurWork].value;
          offLed(pBlue2);
          offLed(pGreen2);
        }
        if (_timeTemp - curWTime > 1200 ){          // Конец времени выпаривания голов
          flagPressKey = true;
          if (_timeTemp - timeBeep >= TIME_BUZER){
            timeBeep = _timeTemp;
            Buzzer(2);
          }
          if (isPress()){
            flagPressKey = false; initMode = false;
            offLed(pRed2); onLed(pGreen2);
            modeCurWork = modeBody;
          }
        }
      }
      break;
    case modeBody:                                 // Режим извлечения тела
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = _timeTemp;
        initMode = true;
        onLed(pRed3);
      }
      if (_temp[1] > 95.00){           // тестовая хрень работаем по температуре
        flagPressKey = true;
        if (_timeTemp - timeBeep >= TIME_BUZER){
          timeBeep = _timeTemp;
          Buzzer(3);
        }
        if (isPress()){
          flagPressKey = false; initMode = false;
          offLed(pRed3); onLed(pGreen3);
          modeCurWork = modeTail;
        }
      }
      break;
    case modeTail:
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = getTimeUnix();
        initMode = true;
        onLed(pRed4);
      }
      if (_temp[1] > 99.00){
        flagPressKey = true;
        if (_timeTemp - timeBeep >= TIME_BUZER){
          timeBeep = _timeTemp;
          Buzzer(5);
        }
        if (isPress()){
          flagPressKey = false; initMode = false;
          offLed(pRed4); onLed(pGreen4);
          modeCurWork = modeNull;
        }
      }
      break;
  }
  if (!flagHeadStart){
    if (flagDef && _temp[3] >= maxTempDefC) fanStart();
    if (flagDef && _temp[3] <= minTempDefC) fanStop();
  }
}
//------------------------------------------------------------------------------------
//
//   Второй перегон
//
void doSecondModeProcess(){
static boolean flagHeadStart;
uint32_t _timeTemp;
  
  // Управление работой дефлегматора
  if (_temp[1] > 50) pumpStart();
  else {pumpStop();fanStop();}
  
  _timeTemp = getTimeUnix();  // временная метка текущего цикла
  switch (modeCurWork){
    case modeStart:           // разгон аппарата
      if (!initMode) {
        setPower = powerCurWork[modeWork][modeCurWork].value;
        flagStop = false;
        curTime  = _timeTemp;
        curWTime = curTime;
        initMode = true;
        onLed(pRed1);
      }
      // нагрев верхней точки до 70 градусов, переход к режиму отбора голов
      if (_temp[1] > 65){
        initMode = false;
        offLed(pRed1); onLed(pGreen1);
        modeCurWork = modeHead;
      }
      break;
    case modeHead:
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = _timeTemp;
        initMode = true;
        flagHeadStart = true;
        onLed(pGreen2);
        fanStart();
      }
      if (_timeTemp - curWTime <= 600 ){           // не даем кипеть и испаряться, выбиваем головы 
        if (_temp[2] > 65.00){
          setPower = powerCurWork[modeWork][modeCurWork].value - 200;
          onLed(pBlue2); offLed(pGreen2);
        }
        else{
          setPower = powerCurWork[modeWork][modeCurWork].value + 200;
          offLed(pBlue2); onLed(pGreen2);
        }
      }
      else{
        // чисто получаем головы при испарении
        if (flagHeadStart) {  // очищаем переменные для извлечения голов
          flagHeadStart = false;
          onLed(pRed2); offLed(pBlue2); offLed(pGreen2);
          
          if (setPower != powerCurWork[modeWork][modeCurWork].value) {  // очищаем переменные для извлечения голов
              setPower = powerCurWork[modeWork][modeCurWork].value;
          }
        }
        if (_timeTemp - curWTime > 3600 ){          // Конец времени выпаривания голов
          flagPressKey = true;
          if (_timeTemp - timeBeep >= TIME_BUZER){
            timeBeep = _timeTemp;
            Buzzer(2);
          }
          if (isPress()){
            flagPressKey = false; initMode = false;
            offLed(pRed2); onLed(pGreen2);
            modeCurWork = modeBody;
          }
        }
      }
      break;
    case modeBody:                                 // Режим извлечения тела
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = _timeTemp;
        initMode = true;
        onLed(pRed3);
      }
      if (_temp[0] > 91.00){           // тестовая хрень работаем по температуре
        flagPressKey = true;
        if (_timeTemp - timeBeep >= TIME_BUZER){
          timeBeep = _timeTemp;
          Buzzer(3);
        }
        if (isPress()){
          flagPressKey = false; initMode = false;
          offLed(pRed3); onLed(pGreen3);
          modeCurWork = modeTail;
        }
      }
      break;
    case modeTail:
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = getTimeUnix();
        initMode = true;
        onLed(pRed4);
      }
      if (_temp[0] > 99.00){
        flagPressKey = true;
        if (_timeTemp - timeBeep >= TIME_BUZER){
          timeBeep = _timeTemp;
          Buzzer(5);
        }
        if (isPress()){
          flagPressKey = false; initMode = false;
          offLed(pRed4); onLed(pGreen4);
          modeCurWork = modeNull;
        }
      }
      break;
  }
  if (!flagHeadStart){
    if (_temp[3] >= tempDef[modeWork][modeCurWork].maxValue) fanStart();
    if (_temp[3] <= tempDef[modeWork][modeCurWork].minValue) fanStop();
  }
}

//------------------------------------------------------------------------------------
//
//   Режим НБК
//
void doContinuiusModeProcess(){
uint32_t _timeTemp;
   _timeTemp = getTimeUnix(); 
   pPumpRPM();
   switch (modeCurWork){
      case modeStart:           // разгон аппарата
      if (!initMode) {
        setPower = powerCurWork[modeWork][modeCurWork].value;
        flagStop = false;
        curTime  = getTimeUnix();
        curWTime = curTime;
        initMode = true;
        onLed(pRed1);
      }
      // нагрев верхней точки до 97 градусов, переход к режиму отбора голов
      if (_temp[2] > 97){
        initMode = false;
        offLed(pRed1); onLed(pGreen1);
        modeCurWork = modeBody;
        pPumpStart();
        currentRPM = 25;
        pPumpRPM();
      }
      break;  
      case modeBody:                                 // Режим извлечения тела
      if (!initMode){
        setPower = powerCurWork[modeWork][modeCurWork].value;
        curWTime = _timeTemp;
        initMode = true;
        onLed(pRed3);
      }
      break;
  }
 
}
GND5VSDASCLSQWRTCDS1307+
D0D1D2D3D4D5D6D7GNDLOGIC