/*
  IoT Manager mqtt device client https://play.google.com/store/apps/details?id=ru.esp8266.iotmanager
  Based on Basic MQTT example with Authentication
  PubSubClient library v 1.99.1 https://github.com/Imroy/pubsubclient
  - connects to an MQTT server, providing userdescr and password
  - publishes config to the topic "/IoTmanager/config/deviceID/"
  - subscribes to the topic "/IoTmanager/hello" ("hello" messages from mobile device)

  #########################################################################
  ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
  ######       TO SELECT THE FONTS AND PINS YOU USE, SEE ABOVE       ######
  #########################################################################

  Tested with
  Arduino IDE 1.8.9
  ESP32 board
  PubSubClient library 2.7                   https://github.com/knolleary/pubsubclient
  ArduinoJson library 6.11.1                 https://github.com/bblanchon/ArduinoJson
  DS3231 Arduino Library 1.2.2               https://github.com/adafruit/RTClib
  Adafruit BME280 Library 1.0.8              https://github.com/adafruit/Adafruit_BME280_Library
  Adafruit Sensor 1.0.3                      https://github.com/adafruit/Adafruit_Sensor
  TFT_eSPI 1.4.11                            https://github.com/Bodmer/TFT_eSPI    https://www.arduinolibraries.info/libraries/tft_e-spi
  NTPClient 3.1.0 исправленная автором       https://github.com/taranais/NTPClient    https://github.com/arduino-libraries/NTPClient
  utf8rus()                                  http://zelectro.cc/lcd_russian_symbols
  Preferences.h отвечает за запись чтение информации из/в встроенную в контроллер ESP32  FLASH память


  sketch version : Курятник v.0.0.0.9 на ESP32
  IoT Manager    : 1.4.6 and above

  GPIO36 - Наличие Сети AC
  GPIO39 - Напряжение Аккумулятора DC
  GPIO34 - Энкодер A
  GPIO35 - Энкодер B
  GPIO32 - Энкодер Кнопка
  GPIO33 - Buzzer
  GPIO25 - RF 315/433МГц / DHT
  GPIO26 - ШИМ
  GPIO27 - Детектор нуля сети
  GPIO14 - Sensor DS18b20
  GPIO12 - Выход на тиристор
  GPIO13 - T_CS / Touch
  GPIO15 - CS_ILI9341
  GPIO2  - DC_ILI9341
  GPIO0  - Программатор
  GPIO4  - 74H595 DS
  GPIO16 - 74H595 SH_cp
  GPIO17 - 74H595 ST_cp
  GPIO5  - CS_SD-CARD
  GPIO18 - SCK  ILI9341 / SD-CARD / TOUCH
  GPIO19 - MISO ILI9341 / SD-CARD / TOUCH
  GPIO21 - SDA LCD1602_I2C_BME280_DD
  GPIO3  - RXD
  GPIO1  - TXD
  GPIO22 - SCL LCD1602_I2C_BME280_DD
  GPIO23 - MOSI ILI9341 / SD-CARD / TOUCH

*/
/*      История версий
  v.0.0.0.0 - Запуск Часы DS1307, LCD1602_I2C, ILI9341 в режиме отображение часов,
  v.0.0.0.2 - Запущено расширание портов на 74HC595
  v.0.0.0.3 - Запущено encoder
  v.0.0.0.4 - Запущено NTP
  v.0.0.0.5 - Исправление запуска устройства при отсутствии интернета и зависание при запросе NTP без интернета.
  v.0.0.0.6 - Изменение структуры программы. ( Функции теперь расположены в отдельных файлах )
  v.0.0.0.7 - Русифицирован интерфейс
  v.0.0.0.8a - Поиск циклического перезапуска mDNS виноват
  v.0.0.0.9

  I2C device found at address 0x27 ! LCD1602_I2C
  I2C device found at address 0x50 ! EEPROM
  I2C device found at address 0x68 ! Часы DS1307 (DS3231)
  I2C device found at address 0x76 ! BME280

*/

#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <EEPROM.h>

ESP8266WebServer server(80);
const char* host = "ESP8266";
#else
#include <WiFi.h>                       //  WIFIMANAGER-ESP32  // OTA
#include <WiFiClient.h>
#include <WebServer.h>                  //  WIFIMANAGER-ESP32                           WebServer-esp32 https://github.com/zhouhan0126/WebServer-esp32
#include <ESPmDNS.h>                    //  WIFIMANAGER-ESP32  // OTA                   DNSServer https://github.com/zhouhan0126/DNSServer---esp32      
#include <driver/adc.h>
#include "esp32-hal-ledc.h"
#include <Preferences.h>                                                                // отвечает за запись чтение информации из/в встроенную в контроллер ESP32  FLASH память

WebServer server(80);
const char* host = "ESP32";
#endif

//#include <PubSubClient.h>
//#include <WiFiClientSecure.h>           // ?
//#include <WiFiManager.h>                //  WIFIMANAGER-ESP32              ESP32          https://github.com/zhouhan0126/WIFIMANAGER-ESP32
#include <WiFiManager.h>                //  +              ESP-12                           https://github.com/tzapu/WiFiManager
//#include <ArduinoJson.h>
#include <ArduinoOTA.h>                 // OTA
//#include <DNSServer.h>                  //  WIFIMANAGER-ESP32 ?
#include <WiFiUdp.h>                    // OTA
#include "SimpleTimer.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include "RTClib.h"                                                                     // https://github.com/adafruit/RTClib        https://rxtx.su/mikrokontrollery/arduino/biblioteki-dlya-arduino/biblioteka-rtclib/ 
#include "DHTesp.h"                                                                     // https://github.com/beegee-tokyo/DHTesp
#include "BMx280I2C.h"                                                                  // https://github.com/christandlg/BMx280MI   https://bitbucket.org/christandlg/bmx280mi 
#include <LiquidCrystal_I2C.h>                                                          // https://github.com/johnrickman/LiquidCrystal_I2C
//#include <Adafruit_Sensor.h>                                                            //
//#include <Adafruit_BMP280.h>
#include "SSD1306Wire.h"                                                                // https://github.com/ThingPulse/esp8266-oled-ssd1306
#include "OLEDDisplayUi.h"
#include "icons.h"
#include <SPI.h>
#include <TFT_eSPI.h>                                                                   // Hardware-specific library
//#include <Update.h>                                                                     // ?  SPIFFS
#include "Encod_er.h"                                                                   // http://mypractic.ru/urok-55-rabota-s-inkrementalnym-enkoderom-v-arduino-biblioteka-encod_er-h.html#comment-5255
#include <NTPClient.h>
#include <SD.h>
#include "JPEGDecoder.h"                                                                // Декодер Jpeg файлов                      


// Include the header files that contain the icons
#include "wifiicons.h"
#include "batticons.h"
#include "digitnumicons.h"
#include "Alert.h"

#include "Page_Login.h"
#include "Page_Update.h"
#include "Page_Setting.h"
//#include "Page_Admin.h"
//#include "Page_General.h"
//#include "Page_Script.js.h"
//#include "Page_Style.css.h"
//#include "Page_NetworkConfiguration.h"

// ------------------------- НАСТРОЙКИ --------------------
#define RESET_CLOCK          0    // сброс часов на время загрузки прошивки (для модуля с несъёмной батарейкой). Не забудь закоментить и прошить ещё раз!
#define FEATURE_TFT          1    // Выбор экрана ILI9341
#define FEATURE_LCD          1    // Выбор экрана LCD1602_I2C
//#define DS3231_I2C               // Выбор наличие модуля аппаратных часов на DS3231
#define DS1307_I2C           1    // Выбор наличие модуля аппаратных часов на DS1307
#define SERIAL_DEBUG         1    // вывод в порт отладочной информации!
//#define SERIAL_DEBUG_TIME         // вывод в порт отладочной информации о времени
#define NTP_TIME_SINC        1    // разрешение синхронизации времени с сервером времени при первом запуске 
//#define NTP_TIME_VIEW             // вывод на экран времени полученное с сервера времени
#define TFT_DEBUG            1    // вывод на экран отладочной информации!
//#define NTP_TFT_DEBUG             // вывод на экран NTP отладочной информации!
#define FEATURE_PRINT_DATE   0    // вывод на экран расположение даты  
#define FEATURE_PRINT_MONTH  1    // вывод на экран Месяц прописью


WiFiManager wifiManager;

TFT_eSPI tft = TFT_eSPI();                                                            // Вызвать пользовательскую библиотеку

SimpleTimer timer;

#ifdef DS3231_I2C
  RTC_DS3231 rtc;
#endif
#ifdef DS1307_I2C
  RTC_DS1307 rtc;
#endif
DateTime now;
// Пин первоначальной настройки WiFi
int analogPin0 = 36;                                                                  // номер порта к которому подключено наличие Сети AC
int analogPin1 = 39;                                                                  // номер порта к которому подключено напряжение Аккумулятора DC

float vout1, vout2 = 0.0;
float R12 = 100000.0;                                                                 // сопротивление R12 (100K)
float R13 = 26000.0;                                                                  // сопротивление R13 (27K)    DC_sens
float R14 = 100000.0;                                                                 // сопротивление R14 (100K)
float R15 = 30000.0;                                                                  // сопротивление R15 (30K)    AC_sens

#define pin_CLK 34                                                                    // пин 34 подключаем к CLK энкодера
#define pin_DT 35                                                                     // пин 35 подключаем к DT энкодера
#define pin_SW 32                                                                     // пин 32 подключаем к SW энкодера
#define filtr_encoder 2                                                               // 5 выборок фильтрации

int encoder_value = 0;
int counter_encoder = 0;

unsigned long encoderMillis = 0;
unsigned long ShiftMillis = 0;
unsigned long buttomMillis = 0;
Encod_er encoder(pin_CLK, pin_DT, filtr_encoder);                                     // энкодер к выводам 35 и 34, 5 выборок фильтрации
int le, re = 0;                                                                       // для теста удалить

#define Beep 33                                                                       // номер порта к которому подключено Тестовый светодиод

int minvalue = 0;                                                                     // минимальное значение ADC
int maxvalue = 4095;                                                                  // максимальное значение ADC
int vin1, vin2 = 0;                                                                   // , vin3, vin4
int uin1, uin2, uin1raw, uin2raw = 0;                                                 // переменная для хранения считываемого значения
float voltage_value1, voltage_value2 = 0;
int battlevel1, battlevel2,  channel = 0;
bool charge = 0;
bool ntp_update = 0;
bool time_sync = 0;
int8_t rssi = 0;
bool status_led = 0;

// Объявляем переменные для меню
long timeButtonPressed = 1500;                                                        // Долгое удержание кнопки после 1,5 секунд

volatile int state = 0;                                                               // Статус одного шага - от 0 до 4 в одну сторону, от 0 до -4 - в другую

// Переменные хранящие состояние действия до его выполнения
volatile bool flagCW            = false;                                              // Было ли вращение по часовой стрелке
volatile bool flagCCW           = false;                                              // Было ли вращение против часовой стрелки
volatile bool flagButton        = false;                                              // Было ли нажатие кнопки
volatile bool flagButtonLong    = false;                                              // Было ли долгое удержание кнопки

volatile long timeButtonDown    = 0;                                                  // Переменная хранящая время нажатия кнопки
volatile bool isButtonDown      = false;                                              // Переменная хранящая время нажатия кнопки
volatile bool longPressReleased = false;                                              // Переменная для фиксации срабатывания долгого нажатия

int actualIndex = 0;


//==========================================================================================================================
//==========================================================================================================================
//==========================================================================================================================
//==========================================================================================================================
struct Button {
  const uint8_t PIN;
  uint32_t numberKeyPresses;
  bool pressed;
};

void print_status(void) {
  print_Info_Net();
  adc_read();
  print_wifi_level();
  print_batt_level();
  //  print_Temp_Hall_status();
}

void Display_Time_Now(void) {
//  #ifdef DS1307_I2C
//    DateTime now = rtc.now();                                                         // Время полученное из DS1307
//  #endif
//  #ifdef DS3231_I2C
//    DateTime now = rtc.now();                                                         // Время полученное из DS3231
//  #endif
  #ifdef FEATURE_TFT
    print_ili9341_clock_RTC();
  #endif
  #ifdef LCD1602_I2C
    print_Lcd1602_ckock_RTC();
  #endif
}

//void IRAM_ATTR isr() {
//  button1.numberKeyPresses += 1;
//  button1.pressed = true;
//}

//==========================================================================================================================
//==========================================================================================================================
//==========================================================================================================================
//==========================================================================================================================
void setup() {
  timer.setInterval(1000L, Display_Time_Now);
  //  timer.setInterval(600000L, set_setDateTime);

  actualIndex = getMenuIndexByID(0);                                                    // Main - актуальный элемент меню

  init_Serial();
  init_ILI9341();
  init_set_ILI9341();
  #ifdef SERIAL_DEBUG
    Serial.println("Booting");
  #endif
  #ifdef TFT_DEBUG
    tft.println("Booting");                                                             // Загрузка
  #endif
  init_set_ADC();
  init_pins_set_Shift();
  pins_clr_Shift();
  init_pins_set_Encoder();
  init_set_Net();
  //  init_pins_set_Buttom();
  #ifdef LCD1602_I2C
    init_Lcd1602();
  #endif
  #ifdef DS1307_I2C
    init_RTC();
  #endif
  #ifdef DS3231_I2C
    init_RTC();
  #endif
  pinMode(Beep, OUTPUT);
  int pin_SW_ = digitalRead(pin_SW);                                                    // Сдесь реакция на перемычку начальной установки конфигурации
  if (pin_SW_ == 0) {
    #ifdef SERIAL_DEBUG
        Serial.println("Setup");
    #endif
    #ifdef TFT_DEBUG
        tft.println("Setup");
    #endif
    if (!wifiManager.startConfigPortal("ESP_AP", "12345678")) {
      Serial.println("Not connected");
      delay(20000);
      ESP.restart();
    }
  }
  init_SD();
  SD_Info();
  Current_Net();
  print_Info_Net();
  init_timeClient();
  #ifdef SERIAL_DEBUG
    Serial.println("Init NTP service:");
  #endif
  set_setDateTime();

  //  attachInterrupt(pin_CLK, encode_rotary, CHANGE);                                    // Настраиваем обработчик прерываний по изменению сигнала
  //  attachInterrupt(pin_SW, Button, CHANGE);                                            // Настраиваем обработчик прерываний по изменению сигнала нажатия кнопки
  //  attachInterrupt(button1.PIN, isr, FALLING);

  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  // ArduinoOTA.setHostname("myesp32");

  // 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_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  })
  .onEnd([]()
  {
    Serial.println("\nEnd");
  })
  .onProgress([](unsigned int progress, unsigned int total)
  {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  })
  .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();




  //  /*use mdns for host name resolution*/
  //  if (!MDNS.begin(host))                                                              // http://esp32.local
  //  {
  //    Serial.println("Web OTA error!");                                                 // Error setting up MDNS responder!
  //    while (1)
  //    {
  //      delay(1000);
  //    }
  //  }
  //  tft.println("Web OTA included  ");                                                  // mDNS responder started
  //  /*return index page which is stored in serverIndex */
  server.on("/", HTTP_GET, []()
  {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.on("/serverIndex", HTTP_GET, []()
  {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []()
  {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []()
  {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START)
    {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN))                                         // start with max available size
      {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE)
    {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)
      {
        Update.printError(Serial);
      }
    }
    else if (upload.status == UPLOAD_FILE_END)
    {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      }
      else
      {
        Update.printError(Serial);
      }
    }
  });
  server.begin();
}

//==========================================================================================================================
//==========================================================================================================================
void loop() {
  timer.run();
  //  Buttoncheck();
  if (millis() >= (encoderMillis + 1)) {                                               // проверяем каждые 5мс (200 Гц)
    encoder.scanState();
    re += encode_rotary();
    Set_EXTPIN();
    //    test595();
    encoderMillis = millis();
  }
  if (millis() >= (ShiftMillis + 1000)) {                                             // проверяем каждые 1с (1 Гц)
    print_status();
    Blink();
    //    menuloop();
    ShiftMillis = millis();
  }
  if (millis() - timeButtonDown > timeButtonPressed && isButtonDown) {                // Время длительного удержания наступило
    flagButtonLong = true;
  }
  ArduinoOTA.handle();
  server.handleClient();
}