/*=====================================================================================
    
  ----------------------------------------------------------------------------------------
  Настрока дисплея: 1.8 TFT SPI 128x160  V1.1 на чипе ST7735
  Эту конфигурацию необходимо внести в файл настроек /TFT_eSPI/User_Setup.h
  #define ST7735_DRIVER
  #define TFT_RGB_ORDER TFT_RGB
  #define TFT_WIDTH  128
  #define TFT_HEIGHT 160
  #define TFT_MISO  PIN_D6
  #define TFT_MOSI  PIN_D7
  #define TFT_SCLK  PIN_D5
  #define TFT_CS    PIN_D8
  #define TFT_DC    PIN_D3
  #define TFT_RST   PIN_D4
  #define SMOOTH_FONT
  #define SPI_FREQUENCY  40000000
  #define SPI_READ_FREQUENCY  20000000
  #define SPI_TOUCH_FREQUENCY  2500000
  ----------------------------------------------------------------------------------------
  SDO/MISO       --> D6 (or leave disconnected if not reading TFT)       - не нужен
  LED            --> VIN (or 5V, see below)                              +
  SCK            --> D5                                                  +
  SDI/MOSI/SDA   --> D7                                                  +
  DC (RS/AO)     --> D3                                                  +
  RESET          --> D4 (or RST, see below)                              +
  CS             --> D8 (or GND, see below)                              +
  GND            --> GND (0V)                                            +
  VCC            --> 5V or 3.3V                                          +
  ----------------------------------------------------------------------------------------
  Для WokWi применено ILI9341 320x240 на неполный экран
  =====================================================================================*/
#include <Arduino.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <WiFi.h>
// #include <NTPClient.h>
#include <GyverNTP.h>
#include <WiFiUdp.h>
//#include <PZEM004Tv30.h>      // Для счетчика
//#include <SoftwareSerial.h>   // PZEM004T
#include "Fonts.h"

TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);

const char *ssid = "Wokwi-GUEST";
const char *password = "";

WiFiUDP ntpUDP;
GyverNTP timeClient(3);
const char *ntpServer = "pool.ntp.org";
// const long gmtOffset_sec = 10800; // Часовой пояс * 3600 , Москва +3 GTM
// const int daylightOffset_sec = 0;

// цвета
unsigned short grays[24];
unsigned short back = TFT_MAGENTA;
unsigned short blue = 0x0250;

#define c1 0xBDD7 // белый
#define c2 0x18C3 // серый
#define c3 0x9986 // красный
#define c4 0x2CAB // зеленый
#define c5 0xBDEF // золотой

// Оттенки серого
uint16_t lines[11] = {0};
uint16_t gra[60] = {0};

// electrical
double KWH;
double todayKWH = 0;
double WH;
int dot = 0;
String lbl[3] = {"Напр.", "Ток", "Част."};
float data[3] = {220.80, 16.84, 49.90};

bool PZEM_online, MQTT_online, TIME_online, WiFi_online = false;

// счетчик
int fromTop = 93;
int left = 50;
int width = 60;
int heigth = 20;

//SoftwareSerial pzemSWSerial(D1, D2);  // Для WokWi
//PZEM004Tv30 pzem(pzemSWSerial);       // отключим счетчик PZEM

// для показаний
float u1 = 0.0, i1 = 0.0, f1 = 0.0, p1 = 0.0, e1 = 0.0;
float p_max = 0, p_min = 99999999;
float u_avg = 0.0, i_avg = 0.0, f_avg = 0.0, p_avg = 0.0;
int u_count = 0, i_count = 0, f_count, p_count = 0;

// дни недели
String days[7] = {"Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"};

// для таймеров
uint32_t ms, ms1 = 0, ms2 = 0, ms3 = 0, ms4 = 0, ms_ok = 0;
uint32_t tm = 0;
uint32_t t_cur = 0;
long t_correct = 0;

//------------------------------------------------------------------------
void connectToWifi(void);
void define_level_of_gray();
void initSCREEN();
void setTime();
//------------------------------------------------------------------------

void setup()
{
  define_level_of_gray();

  tft.init();
  tft.setRotation(3);
  tft.setSwapBytes(true);
  tft.fillScreen(TFT_BLACK);

  spr.createSprite(160, 128);
  spr.setSwapBytes(1);

  connectToWifi();
  timeClient.begin();
}

void loop()
{

  ms = millis();

  // Опрос показаний PZEM
  if (ms1 == 0 || ms < ms1 || (ms - ms1) > 500)
  {
    ms1 = ms;
    t_cur = ms / 1000;
    tm = t_cur + t_correct;
    float u2, i2, f2, p2, e2;
    for (int i = 0; i < 3; i++)
    {
      //u2 = pzem.voltage();
      u2 = 222;
      if (u2 != NAN && u2 >= 0)
      {
        PZEM_online = true;
        u1 = u2;
        u_avg += u1;
        u_count++;
        break;
      }
      else
      {
        PZEM_online = false;
      };
    }
    for (int i = 0; i < 3; i++)
    {
      i2 = 14;
      //i2 = pzem.current();
      if (i2 != NAN && i2 >= 0)
      {
        PZEM_online = true;
        i1 = i2;
        i_avg += i1;
        i_count++;
        break;
      }
      else
      {
        PZEM_online = false;
      };
    }
    for (int i = 0; i < 3; i++)
    {
      p2 = 22;
      //p2 = pzem.power();
      if (p2 != NAN && p2 >= 0)
      {
        PZEM_online = true;
        p1 = p2;
        p_avg += p1;
        p_count++;
        break;
      }
      else
      {
        PZEM_online = false;
      };
    }
    for (int i = 0; i < 3; i++)
    {
      f2 = 50.1;
      //f2 = pzem.frequency();
      if (f2 != NAN && f2 >= 0)
      {
        PZEM_online = true;
        f1 = f2;
        f_avg += f1;
        f_count++;
        break;
      }
      else
      {
        PZEM_online = false;
      };
    }
    for (int i = 0; i < 3; i++)
    {
      e2 = 44;
      //e2 = pzem.energy();
      if (e2 != NAN && e2 >= 0)
      {
        PZEM_online = true;
        e1 = e2;
        break;
      }
      else
      {
        PZEM_online = false;
      };
    }
    if (p_max < p1)
      p_max = p1;
    if (p_min > p1)
      p_min = p1;
  }

  data[0] = u1;
  data[1] = i1;
  data[2] = f1;

  if (!WiFi.isConnected())
    WiFi_online = false;
  else
    WiFi_online = true;

  if (!timeClient.synced())
    timeClient.updateNow();
  timeClient.tick();

  initSCREEN();
  delay(10);
}

//------------------------------------------------------------------------

void initSCREEN()
{

  spr.setTextDatum(0);
  spr.fillSprite(blue);
  spr.fillSmoothRoundRect(1, 1, 158, 126, 2, TFT_BLACK, blue);

  // Центр --- нагрузка
  spr.fillSmoothRoundRect(40, 4, 115, 120, 2, grays[19], TFT_BLACK);
  spr.loadFont(UbuntuCondensed_Regular_10);
  spr.setTextColor(grays[8], grays[19]);
  spr.drawString(F("Нагрузка"), 57, 48);
  spr.setTextColor(TFT_BLACK);

  // подложка под Watt букву
  spr.fillRectVGradient(93, 64, 9, 12, TFT_ORANGE, TFT_RED);
  spr.drawSmoothRoundRect(91, 62, 2, 3, 12, 14, grays[19], grays[19]);
  spr.drawString(F("W"), 94, 65);
  spr.setTextColor(grays[8], grays[22]);

  // Окна --- напряжение -- ток -- частота
  for (int i = 0; i < 3; i++)
  {
    spr.fillSmoothRoundRect(47 + (i * 36), 19, 30, 28, 2, grays[22], grays[19]);
    spr.drawString(lbl[i], 47 + (i * 36) + 3, 20);
  }
  spr.unloadFont();
  spr.loadFont(AndersonSupercar1);
  spr.setTextColor(grays[4], grays[22]);
  for (int i = 0; i < 3; i++)
  {
    spr.drawFloat(data[i], 1, 47 + (i * 36) + 3, 34);
  }
  spr.unloadFont();

  // Название титул
  spr.loadFont(ComputerPixel7);
  spr.setTextColor(TFT_ORANGE, grays[19]);
  spr.drawString(F("ЭНЕРГОмеIр"), 47, 6);
  // spr.drawFastHLine(95, 6, 27, TFT_ORANGE);
  spr.fillRectHGradient(95, 6, 29, 1, TFT_RED, TFT_ORANGE);
  spr.unloadFont();

  // Середина
  spr.fillRect(49, 51, 1, 26, grays[7]);
  spr.fillRect(49, 58, 50, 1, grays[7]);

  spr.drawSpot(49, 51, 2, grays[7], grays[19]);
  spr.drawSpot(49, 51 + 26, 2, grays[7], grays[19]);
  spr.drawSpot(49 + 50, 58, 2, grays[7], grays[19]);

  // График
  spr.fillSmoothRoundRect(107, 50, 43, 30, 2, grays[22], grays[19]);

  for (int i = 0; i < 5; i++)
    spr.drawFastHLine(109, 58 + (i * 4), 40, grays[16]);
  spr.drawFastHLine(108, 78, 41, grays[7]);
  spr.drawFastVLine(108, 57, 21, grays[7]);
  spr.setTextColor(grays[10], grays[19]);
  spr.loadFont(UbuntuCondensed_Regular_10);
  spr.drawString(F("За 24 ч."), 115, 49);

  spr.setTextColor(grays[10], grays[19]);
  spr.drawString(F("Сег:"), 130, 83);
  spr.drawLine(130, 92, 151, 92, grays[5]);
  spr.setTextColor(grays[4], grays[19]);
  spr.drawFloat(e1, 1, 131, 95); // ---------------- ЗНАЧЕНИЕ -----------------
  spr.setTextColor(grays[10], grays[19]);
  spr.drawString(F("Макс:"), 130, 103);
  spr.drawLine(130, 112, 151, 112, grays[5]);
  spr.setTextColor(grays[4], grays[19]);
  spr.drawFloat(378, 1, 131, 114); // -------------- ЗНАЧЕНИЕ -----------------
  spr.unloadFont();

  // Текущая мощность
  spr.loadFont(AndersonSupercar2);
  spr.setTextColor(grays[2], grays[22]);
  spr.drawFloat(p1, 1, 54, 64);
  spr.unloadFont();

  // "Железный" циферблат
  spr.setTextDatum(4);
  spr.fillSmoothRoundRect(47, 83, 80, 39, 3, c1, grays[19]);
  spr.fillSmoothRoundRect(left + 7, fromTop - 4, width - 5, heigth, 3, c2, c1);
  spr.fillSmoothRoundRect(left + 8, fromTop - 3, width - 6, heigth, 3, c1, c2);
  spr.fillRect(left + 6, fromTop - 1, width - 10, 3, c1);
  spr.fillRect(left + 25, fromTop - 4, width - 40, 3, c1);
  spr.fillSmoothRoundRect(left, fromTop, width, heigth, 3, blue, c1);
  spr.fillSmoothRoundRect(left + 54, fromTop, 21, heigth, 3, c3, c1);
  spr.fillRect(left + 54, fromTop, 2, heigth, c1);
  spr.fillSmoothCircle(left + 55, fromTop + heigth, 3, c1);
  spr.fillSmoothCircle(left + 55, fromTop + heigth, 2, c2);
  spr.loadFont(UbuntuCondensed_Regular_10);
  spr.setTextColor(c2, c1);
  spr.drawString("kWh", left + 36, fromTop - 4);
  for (int i = 0; i < 5; i++)
  {
    spr.fillRectHGradient(left + (3) + (i * 10), fromTop + 4, 8, 12, TFT_BLACK, gra[2]);
    for (int j = 0; j < 6; j++)
      if (j == 3)
        spr.drawLine(left + (9) + (i * 10), fromTop + 4 + (j * 2), left + (10) + (i * 10), fromTop + 4 + (j * 2), lines[j]);
      else
        spr.drawLine(left + (10) + (i * 10), fromTop + 4 + (j * 2), left + (10) + (i * 10), fromTop + 4 + (j * 2), lines[j]);
  }
  spr.fillRectHGradient(left + 59, fromTop + 4, 14, 12, TFT_BLACK, gra[2]);
  for (int j = 0; j < 6; j++)
    if (j == 3)
      spr.drawLine(left + 70, fromTop + 4 + (j * 2), left + 71, fromTop + 4 + (j * 2), lines[j]);
    else
      spr.drawLine(left + 71, fromTop + 4 + (j * 2), left + 71, fromTop + 4 + (j * 2), lines[j]);
  spr.drawLine(left + 6, fromTop + 21, left + 6, fromTop + 23, blue);
  spr.drawLine(left + 6, fromTop + 23, left + 13, fromTop + 23, blue);
  spr.drawLine(left + 47, fromTop + 21, left + 47, fromTop + 23, blue);
  spr.drawLine(left + 47, fromTop + 23, left + 40, fromTop + 23, blue);
  spr.setTextColor(c2, c1);
  spr.drawString("X1", left + 67, fromTop + 25);
  spr.setTextColor(blue, c1);
  spr.drawString("TOTAL", left + 28, fromTop + 25);
  spr.setTextColor(c2, c1);
  // Цыфры на табло счётчика
  spr.setTextColor(c1, TFT_BLACK);
  spr.drawString("0", left + 6, fromTop + 11);
  spr.drawString("0", left + 16, fromTop + 11);
  spr.drawString("0", left + 26, fromTop + 11);
  spr.drawString("0", left + 36, fromTop + 11);
  spr.drawString("0", left + 46, fromTop + 11);
  spr.drawString("00", left + 64, fromTop + 11);
  spr.unloadFont();

  // Левая часть ---- время --- дата --- день --- индикаторы
  spr.loadFont(AndersonSupercar2);
  spr.setTextColor(grays[6], TFT_BLACK);
  spr.setTextDatum(0);
  if (timeClient.timeString() == "Not sync")
  {
    spr.drawString("--:--", 6, 6);
  }
  else
  {
    spr.drawString(timeClient.timeString().substring(0, 5), 6, 6);
  }
  spr.unloadFont();
  spr.fillRect(8, 22, 1, 20, grays[13]);
  spr.fillRect(8, 32, 26, 1, grays[13]);
  spr.loadFont(UbuntuCondensed_Regular_10);
  spr.setTextColor(grays[10], TFT_BLACK);
  if (timeClient.timeString() == "Not sync")
  {
    spr.drawString("--.--", 12, 23);
    spr.drawString("--", 12, 33);
    TIME_online = false;
  }
  else
  {
    spr.drawString(timeClient.dateString().substring(0, 5), 12, 23); // + "." + String(timeClient.dateString().substring(5, 7))
    spr.drawString(days[timeClient.weekDay()], 12, 33);
    TIME_online = true;
  }

  // Индикаторы OnLine
  spr.drawString("PZEM", 5, 80);
  if (PZEM_online)
    spr.drawSpot(35, 84, 2, c4, grays[10]);
  else
    spr.drawSpot(35, 84, 2, c3, grays[10]);

  spr.drawString("MQTT", 5, 89);
  if (MQTT_online)
    spr.drawSpot(35, 93, 2, c4, grays[10]);
  else
    spr.drawSpot(35, 93, 2, c3, grays[10]);

  spr.drawString("Время", 5, 98);
  if (timeClient.synced())
    spr.drawSpot(35, 102, 2, c4, grays[10]);
  else
    spr.drawSpot(35, 102, 2, c3, grays[10]);

  spr.drawString("WiFi", 5, 107);
  if (WiFi_online)
    spr.drawSpot(35, 111, 2, c4, grays[10]);
  else
    spr.drawSpot(35, 111, 2, c3, grays[10]);

  spr.unloadFont();

  spr.pushSprite(0, 0);
}

void connectToWifi()
{
  tft.loadFont(UbuntuCondensed_Regular_10);
  tft.setTextColor(grays[10], TFT_BLACK);
  tft.print("Connecting..");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(300);
    tft.print(".");
  }
  tft.println("");
  tft.println("  WiFi connected.");
  tft.println("  IP address:     ");
  tft.println(WiFi.localIP());
  delay(3200);
  tft.fillScreen(TFT_BLACK);
  tft.unloadFont();
}

void define_level_of_gray()
{
  // Определим оттенки серого
  int co = 240;
  for (int i = 0; i < 24; i++)
  {
    grays[i] = tft.color565(co, co, co);
    co = co - 10;
  }
  for (int i = 0; i < 50; i++)
    gra[i] = tft.color565(i * 5, i * 5, i * 5);

  lines[0] = gra[10];
  lines[1] = gra[20];
  lines[2] = gra[30];
  lines[3] = gra[40];
  lines[4] = gra[30];
  lines[5] = gra[20];
  lines[6] = gra[10];
}