// #define DEBUG
//------------------------------------------------------------------------------------------------------------------------------------------
//  Данная программа подчитывает десятки воды которые показывает водомер  для правельного понимания количества нужно показания умножить на 10
//  https://microcontrollerslab.com/esp8266-nodemcu-mqtt-publish-subscribe-ds18b20-sensor-readings/   
//-------------------------------------------------------------------------------------------------------------------------------------------
#include <OneWire.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>
#include <DallasTemperature.h>

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

#include "max6675.h"   


// #include "\Development\branches\Karagayka_ESP8266\Arduino\libraries\All_data.h"                //  Обшие даные для обмена между устройствами 
#include  <EEPROMAnything.h>
#include  "BerretaControl.h"
// #include  "MAX6675_Thermocouple.h" //https://github.com/YuriiSalimov/MAX6675_Thermocouple

#define SCK_PIN 14    
#define CS_PIN 13     
#define SO_PIN 12     
#define ONE_WIRE_BUS  5              // определене для датчика температуры  D7
#define TEMPERATURE_PRECISION 11     // разрешение температур  

#define HEOTER_ON_OFF_PIN  2         // управление  нагревом  котла   
#define RESET_PIN        4         // передёрнуть  реле  если есть проблемы с котлом 
const int analogInPin = A0;               // Датчик давдения    

#define EEPROM_SIZE 1024


OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensor(&oneWire);

//replace with your network credentials
#define WIFI_SSID     "OpenWRT"
#define WIFI_PASSWORD "3d1wan10"

// Raspberry Pi Mosquitto MQTT Broker
#define MQTT_HOST IPAddress(192, 168, 3, 54)
#define MQTT_PORT 1883

//MQTT Topics
#define MQTT_PUB_TEMP_C "esp7_H/berreta/temperature"       // температура с термопары вкотле 
#define MQTT_PUB_TEMP_E "esp7_H/berreta/temper_wote"       // тепература  воды на выходе из котла 
#define MQTT_PUB_TEMP_B "esp7_H/berreta/pressure"         // давление воду в системе

#define MQTT_SUB_TEMP_F "esp7_H/berreta/TemperatureOUT/set"  // установка тепературы по термопаре работы котла  
#define MQTT_PUB_TEMP_F "esp7_H/berreta/TemperatureOUT"      // Возвращаем значение в OH при запуске последнего  
#define MQTT_SUB_TEMP_A "esp7_H/berreta/Reset/set"             // сборос  ошибок в котле 
#define MQTT_SUB_TEMP_D "esp7_H/berreta/AutoControl/set"       // On Off автоматического управлением котлом  
#define MQTT_PUB_TEMP_J "esp7_H/berreta/AutoControl"           // On Off Предыдущее состояние по управлению котла 
#define MQTT_SUB_STARTUP_OH "startup/oh"                       // Инициализация  переменных в  OH  из различных устройств 

#include <util.h>                             //  Своя  библиотека общего плана 

AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;

 MAX6675 thermocouple(SCK_PIN,CS_PIN,SO_PIN);

const int SensorDataPin = 14;   

unsigned long           prevDisMillis = 0;
unsigned long           currentMillis = 0;
unsigned long           prev_Millis   =  millis() - SEK_interval * 3600;
unsigned long           prev_Millis_2 =  millis() - SEK_interval * 3600;
unsigned long           prev_Millis_3 =  millis() - SEK_interval * 3600;
float                   old_Temper_water  = 0;
float                   l_Temper_water    = 0;

bool l_Control_Set = false;      // автоматизация выключена 

float  l_Temper_Heater = 0;     //текущая температура  котла 
float  old_Temper_Heater = 0; 
int   l_Heater_pressure = 0;   // Давление в отопление 
int   old_Heater_pressure = 0;   // Давление в отопление 

int     l_Temper_Set   = 0;    // целевая температура  котла 
#define  DELTA   3            // разница  между  верхней и нижней  границей 

int   Rest_Heate   = 0;        //  нажать удалённо кнопку резет   


void connectToWifi() {
  Serial.println("Connecting to Wi-Fi...");
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
     Serial.println("Connection Failed! Rebooting...");
     delay(1000);
     ESP.restart();
  }
  }
void dispetcher_serial(void)
{
  // обработка команд через последовательный порт
  if  (stringComplete) { 
     Serial.println(mon_buf);
    if (mon_check('v') || mon_end()) {                        // v получить версию устройства
       Serial.println(F("ArduinoNano v3 SN000024 BatchRoom V 1.0 "));
    }else if (mon_check('h') || mon_end()) {                                         // Help
      Serial.println(F(" lt tek sostoynie "));
      Serial.println(F(" le list errprom (nastroiki) "));
   }else if ( (mon_check('l') && mon_check('t'))  || mon_end()) {
      l_Temper_Heater = thermocouple.readCelsius();
      Serial.print(F(" T_Water = "));         Serial.print( l_Temper_water);   Serial.println();
      Serial.print(F(" T Heater = "));        Serial.print(l_Temper_Heater);   Serial.println();
      Serial.print(F(" T Set Неатер = "));    Serial.print(l_Temper_Set);      Serial.println();
      Serial.print(F(" T l_Control_Set = "));  Serial.print(l_Control_Set);      Serial.println();
      Serial.print(F(" T Неатер pressure = "));  Serial.print(l_Heater_pressure);      Serial.println();
      Serial.print(F(" HEOTER_ON_OFF_PIN >>     "));   Serial.print(digitalRead(HEOTER_ON_OFF_PIN));  
      Serial.print(F(" RESET_PIN >> "));               Serial.print(digitalRead(RESET_PIN));

    } else if ( (mon_check('l') && mon_check('e'))  || mon_end()) {                    // le  Смотрим ERRPROM
      Eerprom_list(0);
    }
  mon_end_parser();
 }  
}  

void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
  Serial.println("Publish received.");
  Serial.print("  topic: ");
  Serial.println(topic);
  String messageTemp;
  for (size_t i = 0; i < len; i++) {
    Serial.print((char)payload[i]);
    messageTemp += (char)payload[i];
  }
   if (String(topic) == MQTT_SUB_TEMP_A){        //  сборос  ошибок в котле 
    Serial.print("\n Clear Error Heat ");
    Serial.println(messageTemp);
    if (String(messageTemp) == "ON")  {
       digitalWrite(RESET_PIN,LOW);
       delay(1000);  
       digitalWrite(RESET_PIN,HIGH);
    }
   }else if (String(topic) == MQTT_SUB_TEMP_F){  // установка тепературы теплоносителя  
    Serial.print("\n Set Heater ");
    Serial.println(messageTemp);                 // записываем  выставленную температуру  в память 
    l_Temper_Set=messageTemp.toInt();
    EEPROM.begin(EEPROM_SIZE);
    EEPROM.put(1,l_Temper_Set);
    EEPROM.commit();    

  }else if (String(topic) == MQTT_SUB_TEMP_D){   // выключить  автоматизацию в котле 
    if (String(messageTemp) == "ON") { l_Control_Set = true; } else { l_Control_Set = false;}
    EEPROM.begin(EEPROM_SIZE);
    EEPROM.put(8,l_Control_Set);           // записываем предыдущее состояние котла  ВКЛ или ВЫКЛ
    EEPROM.commit();
  }else if (String(topic) == MQTT_SUB_STARTUP_OH){     //  поднять все настроечные переменные в  OH
    Serial.print("\n Startup  OH");
    Serial.println(messageTemp);
    uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP_F, 1, true, String(l_Temper_Set).c_str());   // Установленая верхняя температура в котле                          
       Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_TEMP_F,  packetIdPub2);
       if (l_Control_Set == true) {packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP_J, 1, true,"ON");}   // Установленная включённая автоматизация                      
        else {packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP_J, 1, true, "OFF");}     
       Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_TEMP_J,  packetIdPub2);
  }      
}
 
void onMqttUnsubscribe(uint16_t packetId) {
  Serial.println("Unsubscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
  Serial.println("Subscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
  Serial.print("  qos: ");
  Serial.println(qos);
}


void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
  Serial.println("Disconnected from Wi-Fi.");
  mqttReconnectTimer.detach(); 
  wifiReconnectTimer.once(2, connectToWifi);
}

void connectToMqtt() {
  Serial.println("Connecting to MQTT...");
  mqttClient.connect();
}

void onWifiConnect(const WiFiEventStationModeGotIP& event) {
  Serial.println("Connected to Wi-Fi.");
  connectToMqtt();
}


void onMqttConnect(bool sessionPresent) {
  Serial.println("Connected to MQTT.");
  Serial.print("Session present: ");
  Serial.println(sessionPresent);
  uint16_t packetIdSub = mqttClient.subscribe(MQTT_SUB_TEMP_A, 2);   // сборос  ошибок в котле 
  Serial.print("Subscribing at QoS 2, packetId: ");
  Serial.println(packetIdSub);
  packetIdSub = mqttClient.subscribe(MQTT_SUB_TEMP_F, 2);       // установка тепературы теплоносителя  
  Serial.print("Subscribing at QoS 2, packetId: ");
  Serial.println(packetIdSub);
  packetIdSub = mqttClient.subscribe(MQTT_SUB_TEMP_D, 2);       // Автоматическое  упраление котлом 
  Serial.print("Subscribing at QoS 2, packetId: ");
  Serial.println(packetIdSub);
  packetIdSub = mqttClient.subscribe(MQTT_SUB_STARTUP_OH, 2);   //  Достаем все  настроечные переменные для OH 
  Serial.print("Subscribing at QoS 2, packetId: ");
  Serial.println(packetIdSub);
  
}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  Serial.println("Disconnected from MQTT.");

  if (WiFi.isConnected()) {
    mqttReconnectTimer.once(2, connectToMqtt);
  }
}

void onMqttPublish(uint16_t packetId) {
  Serial.print("Publish acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}


void OTA_init(void);
void setup(void)
{  
  // setup_Dallas_Dev();
  Serial.begin(115200);
   delay(1000);  
  Serial.println(F("ArduinoNano v3 Vodomer  V 2.0 "));
    Serial.println(ESP.getFreeSketchSpace());  
   EEPROM.begin(EEPROM_SIZE);  
   EEPROM.get(1, l_Temper_Set);
   EEPROM.get(8, l_Control_Set); 
    
   pinMode(HEOTER_ON_OFF_PIN,OUTPUT);          // управление  нагревом  котла   
   pinMode(RESET_PIN,        OUTPUT);          // передёрнуть  реле  если есть проблемы с котлом 
   digitalWrite(RESET_PIN,HIGH);
   digitalWrite(HEOTER_ON_OFF_PIN,HIGH);

   /* Конфигурирования WiFi  */
   wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
   wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
 
  mqttClient.setCredentials("test","qwe12345");
  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.onSubscribe(onMqttSubscribe);
  mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onMessage(onMqttMessage);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  connectToWifi(); 
  OTA_init();
  ESP.wdtDisable();
  ESP.wdtEnable(1000);
 }

void loop(void)
{
      ArduinoOTA.handle();
     currentMillis = millis();
      // Считываем температуру с  термопары и управляем  котлом включить и выключить 
      if (currentMillis - prev_Millis  > (unsigned long) SEK_interval * 10) {
          prev_Millis  = currentMillis;
          l_Temper_Heater = thermocouple.readCelsius();     // температура с термопары 
          l_Temper_water  = sensor.getTempCByIndex(0);      //  температура воды на  выходе  из котла
          l_Heater_pressure = analogRead(analogInPin);    // Датчик давдения    
          sensor.requestTemperatures();                     //Запрашиваем подготовку температуры 

         if  ( ! l_Control_Set) {  digitalWrite(HEOTER_ON_OFF_PIN,HIGH); }  // всегда  контакты  замкнуты и работает термореле  котла 
           else {
              if  (l_Temper_Heater  > l_Temper_Set && digitalRead(HEOTER_ON_OFF_PIN)==HIGH)  // t превышает верхнюю границу 
                 { digitalWrite(HEOTER_ON_OFF_PIN,LOW); }  
              else if  ((l_Temper_Heater  < l_Temper_Set - DELTA) && digitalRead(HEOTER_ON_OFF_PIN)==LOW) // упало по нижней границе 
                  { digitalWrite(HEOTER_ON_OFF_PIN,HIGH); }
           } 
       }  
      
      // каждые три мнуты отправляем температуру котла */
    if ( currentMillis - prev_Millis_3  >  (unsigned long) SEK_interval * 30) { // MIN_interval ) {
          prev_Millis_3  = currentMillis;
          if (l_Temper_Heater  != old_Temper_Heater)  {
              old_Temper_Heater =  l_Temper_Heater;   
              uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP_C, 1, true, String(l_Temper_Heater).c_str());                            
              Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_TEMP_C, packetIdPub2);
           }
          if (l_Temper_water  != old_Temper_water)  {
              old_Temper_water =  l_Temper_water;   
              uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP_E, 1, true, String(l_Temper_water).c_str());                            
              Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_TEMP_E, packetIdPub2);
          }
          if (l_Heater_pressure  != old_Heater_pressure)  {
              old_Heater_pressure =  l_Heater_pressure;   
              uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP_B, 1, true, String(l_Heater_pressure).c_str());                            
              Serial.printf("Publishing on topic %s at QoS 1, packetId %i: ", MQTT_PUB_TEMP_B, packetIdPub2);
          }
      } 
     
        
 dispetcher_serial();
  // BlinkLadge_A();
     wdt_reset();
}
  void OTA_init(void) {
  // Port defaults to 8266
 //  ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
 //   ArduinoOTA.setHostname("myesp8266");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else {  // U_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
 }

 