#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

//*************************Изменить для себя *******************************************************
#define HourNightStart  23  //Время включения ночного режима
#define HourNightEnd  6     //Времы выключения ночного режима

#define AP_SSID                  "wlan-name"     // Логин к вашему WiFi(кавычки оставить)
#define AP_PASS                  "password"  //Пароль у вашему WiFi

#define TIMEZONE 3          // Часовой пояс
//**********************************************************************************

#define Key_Pr    5 //    ;Задержка на подавление дребезга короткого нажатия кнопок
#define Key_Long  50  //  ;Задержка на длительное нажатие (в циклах прерывания)

#define SH_CP  16
#define ST_CP  5
#define DS     4
#define LED    14
#define HV  12


static const uint8_t nixie_level[4][10] = {
  {1, 2, 6, 7, 5, 0, 4, 9, 8, 3},
  {6, 7, 5, 0, 4, 9, 8, 3, 1, 2},
  {5, 0, 4, 9, 8, 3, 1, 2, 6, 7},
  {4, 9, 8, 3, 1, 2, 6, 7, 5, 0}
};


char ssid[] = "";
char pass[] = "";


unsigned int  localPort = 2390;      // local port to listen for UDP packets
unsigned long ntp_time = 0;
long  t_correct        = 0;
unsigned long cur_ms   = 0;
unsigned long ms1      = 0;
unsigned long ms2      = 10000000UL;
unsigned long ms3      = 0;
unsigned long t_cur    = 0;
bool          points   = true;
unsigned int err_count = 0;
uint16_t     my_s = 0;
uint16_t     my_m = 0;
uint16_t     my_h = 0;

uint8_t     Btt0 = 0;
uint8_t     Btt1 = 0;
uint8_t     Btt2 = 0;
uint8_t     Btt3 = 0;
uint8_t     Btt4 = 0;
uint8_t     Btt5 = 0;
uint8_t     temp = 0;
uint8_t     lastMinute = 0;

uint16_t     ADCkey = 0;
uint8_t     Key_Timer;
uint8_t     Key_1_Count;
uint8_t     Key_2_Count;
uint8_t     Key_3_Count;

boolean   Key_1_Pr  = false;  //Кнопка 1 нажата (с учетом дребезга контактов)
boolean   Key_2_Pr  = false;  //Кнопка 2 нажата (с учетом дребезга контактов)
boolean   Key_3_Pr  = false;  //Кнопка 3 нажата (с учетом дребезга контактов)
boolean   Key_1_LP  = false;  //Кнопка 1 длительное удержание
boolean   Key_2_LP  = false;  //Кнопка 2 длительное удержание
boolean   Key_3_LP  = false;  //Кнопка 3 длительное удержание
boolean   Key_1_PrR = false;  //Отпускание кнопки 1 после короткого нажатия
boolean   Key_1_LPR = false;  //Отпускание кнопки 1 после длинного нажатия

bool      LedON   = true;
//bool      LedON_temp   = false;
bool      NtpNoConn   = false;

unsigned long Next_ms   = 3600000;

IPAddress timeServerIP;
const char* ntpServerName = "time.nist.gov";

const int NTP_PACKET_SIZE = 48;
byte packetBuffer[ NTP_PACKET_SIZE];
WiFiUDP udp;

void setup()
{
  Serial.begin(115200);
  Serial.println("");
  Serial.println("");
  Serial.print("Free Memory: ");
  // Соединение с WiFi
  Serial.println(ESP.getFreeHeap());
  if ( !ConnectWiFi(AP_SSID, AP_PASS) ) {
    Serial.println("Reset ESP8266 ...");
    ESP.reset();
  }
  delay(1000);
  // Инициализацият пинов
  pinMode(SH_CP, OUTPUT);
  pinMode(ST_CP, OUTPUT);
  pinMode(DS, OUTPUT);
  pinMode(LED, OUTPUT);
  pinMode(HV, OUTPUT);


  // Инициализауия UDP соединения с NTP сервером
  Serial.println("Starting UDP");
  udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(udp.localPort());

  digitalWrite(HV, HIGH);

}

void loop() {
  cur_ms       = millis();
  t_cur        = cur_ms / 1000;
  // Каждые 24 часа считываем время в интернете
  if ( cur_ms < ms2 || (cur_ms - ms2) > Next_ms ) {
    err_count++;
    // Делаем три  попытки синхронизации с интернетом
    NtpNoConn = false;
    if ( GetNTP() ) {
      ms2       = cur_ms;
      err_count = 0;
      t_correct = ntp_time - t_cur;
      NtpNoConn = true;
      Next_ms   = 86400000;
    }
    else {
      Next_ms = 60000; //соединяемся через минуту
    }
  }



  // Каждые  секунду меняем точку время
  if ( cur_ms < ms1 || (cur_ms - ms1) > 1000 ) {
    ms1 = cur_ms;
    ntp_time    = t_cur + t_correct;
    points = !points;
    DisplayTime();
  }
  
  //опрос кнопок
  if ( cur_ms < ms3 || (cur_ms - ms3) > 5 ) {
    ms3 = cur_ms;
    ADCkey = analogRead(A0);
    button_press();
    set_Mode();
  }

  // Если нет соединения с интернетом, перезагружаемся
  if ( err_count > 10 ) {
    Serial.println("NTP connect false");
    Serial.println("Reset ESP8266 ...");
    ESP.reset();

  }
  delay(100);
}


/**
 * Соединение с WiFi
 */
bool ConnectWiFi(const char *ssid, const char *pass) {
  // Три попытки соединения по WiFi
  for ( int i = 0; i < 3; i++) {
    Serial.print("\nConnecting to: ");
    Serial.println(ssid);
    WiFi.begin(ssid, pass);
    delay(1000);
    // Максиммум 12 раз проверка соединения
    for ( int j = 0; j < 12; j++ ) {
      if (WiFi.status() == WL_CONNECTED) {
        Serial.print("\nWiFi connect true: ");
        Serial.print(WiFi.localIP());
        Serial.print("/");
        Serial.print(WiFi.subnetMask());
        Serial.print("/");
        Serial.println(WiFi.gatewayIP());
        return true;
      }
      delay(1000);
      Serial.print(WiFi.status());
    }
  }
  Serial.println("\nConnect WiFi failed ...");
  return false;
}

void DisplayTimeRandom(void) {
  for (int q = 0; q < 10; q++) {
    Btt0 = 0;
    Btt1 = 0;
    Btt2 = 0;
    Btt3 = 0;
    Btt4 = 0;
    Btt5 = 0;
    temp = nixie_level[3][q];
    m2(temp);
    temp = nixie_level[2][q] ;
    m1(temp);
    temp = nixie_level[1][q];
    h2(temp);
    temp = nixie_level[0][q];
    h1(temp);
    if (my_h >= HourNightStart || my_h <= HourNightEnd ) {
      analogWrite(HV, 200);
      digitalWrite(LED, LOW);
    }
    else {
      analogWrite(HV, 0); digitalWrite(HV, HIGH);
      if (LedON) {
        digitalWrite(LED, HIGH);
      }
    }

    //      NtpNoConn=0;

    if (NtpNoConn == 1) {
      bitWrite(Btt5, 1, LOW );
    }
    else {
      bitWrite(Btt5, 1, HIGH);
    }

    if (points == 1) {
      bitWrite(Btt0, 0, HIGH);
    }
    else {
      bitWrite(Btt0, 0, LOW);
    }

    digitalWrite(ST_CP, LOW);
    // Последовательная передача данных на пин DS
    shiftOutMy(DS, SH_CP, MSBFIRST, Btt0);
    shiftOutMy(DS, SH_CP, MSBFIRST, Btt1);
    shiftOutMy(DS, SH_CP, MSBFIRST, Btt2);
    shiftOutMy(DS, SH_CP, MSBFIRST, Btt3);
    shiftOutMy(DS, SH_CP, MSBFIRST, Btt4);
    shiftOutMy(DS, SH_CP, MSBFIRST, Btt5);
    digitalWrite(ST_CP, HIGH);
    delay(10);
    digitalWrite(ST_CP, LOW);
    delay(30);
  }
}


/**
 * Выдача текущего времени на индикатор
 */
void DisplayTime(void) {


  my_s = ntp_time % 60;
  my_m = ( ntp_time / 60 ) % 60;
  my_h = ( ntp_time / 3600 ) % 24;
  if (!(lastMinute == my_m)) {
    lastMinute = my_m;
    DisplayTimeRandom();
  }
  Btt0 = 0;
  Btt1 = 0;
  Btt2 = 0;
  Btt3 = 0;
  Btt4 = 0;
  Btt5 = 0;
  temp = my_m % 10;
  m2(temp);
  temp = my_m / 10;
  m1(temp);
  temp = my_h % 10;
  h2(temp);
  temp = my_h / 10;
  h1(temp);


  if (my_h >= HourNightStart || my_h <= HourNightEnd ) {
    analogWrite(HV, 200);
    digitalWrite(LED, LOW);
  }
  else {
    analogWrite(HV, 0); digitalWrite(HV, HIGH);
    if (LedON) {
      digitalWrite(LED, HIGH);
    }
  }

  //      NtpNoConn=0;

  if (NtpNoConn == 1) {
    bitWrite(Btt5, 1, LOW );
  }
  else {
    bitWrite(Btt5, 1, HIGH);
  }

  if (points == 1) {
    bitWrite(Btt0, 0, HIGH);
  }
  else {
    bitWrite(Btt0, 0, LOW);
  }

  // Выливаем на регистры
  digitalWrite(ST_CP, LOW);
  // Последовательная передача данных на пин DS
  shiftOutMy(DS, SH_CP, MSBFIRST, Btt0);
  shiftOutMy(DS, SH_CP, MSBFIRST, Btt1);
  shiftOutMy(DS, SH_CP, MSBFIRST, Btt2);
  shiftOutMy(DS, SH_CP, MSBFIRST, Btt3);
  shiftOutMy(DS, SH_CP, MSBFIRST, Btt4);
  shiftOutMy(DS, SH_CP, MSBFIRST, Btt5);
  digitalWrite(ST_CP, HIGH);
  delay(10);
  digitalWrite(ST_CP, LOW);


  //  Serial.print("Time: ");
  //  Serial.print(my_h);
  //  Serial.print(":");
  //  Serial.print(my_m);
  //  Serial.print(":");
  //  Serial.println(my_s);
  //Serial.print("LED:  ");
  //Serial.println(LedON); 
}


/**
 * Посылаем и парсим запрос к NTP серверу
 */
bool GetNTP(void) {
  WiFi.hostByName(ntpServerName, timeServerIP);
  sendNTPpacket(timeServerIP);
  delay(1000);

  int cb = udp.parsePacket();
  if (!cb) {
    Serial.println("No packet yet");
    return false;
  }
  else {
    Serial.print("packet received, length=");
    Serial.println(cb);
    // Читаем пакет в буфер
    udp.read(packetBuffer, NTP_PACKET_SIZE);
    // 4 байта начиная с 40-го сождержат таймстамп времени - число секунд
    // от 01.01.1900
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // Конвертируем два слова в переменную long
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    // Конвертируем в UNIX-таймстамп (число секунд от 01.01.1970
    const unsigned long seventyYears = 2208988800UL;
    unsigned long epoch = secsSince1900 - seventyYears;
    // Делаем поправку на местную тайм-зону
    ntp_time = epoch + TIMEZONE * 3600;
    Serial.print("Unix time = ");
    Serial.println(ntp_time);
  }
  return true;
}

/**
 * Посылаем запрос NTP серверу на заданный адрес
 */
unsigned long sendNTPpacket(IPAddress& address)
{
  Serial.println("sending NTP packet...");
  // Очистка буфера в 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Формируем строку зыпроса NTP сервера
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  // Посылаем запрос на NTP сервер (123 порт)
  udp.beginPacket(address, 123);
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
}

void h1(uint8_t output) {
  switch (temp) {
    case 0:
      Btt4 = Btt4 + 0b00100000;
      break;
    case 1:
      Btt5 = Btt5 + 0b00001000;
      break;
    case 2:
      Btt5 = Btt5 + 0b00010000;
      break;
    case 3:
      Btt5 = Btt5 + 0b00100000;
      break;
    case 4:
      Btt5 = Btt5 + 0b01000100;
      break;
    case 5:
      Btt5 = Btt5 + 0b10000000;
      break;
    case 6:
      Btt4 = Btt4 + 0b00000010;
      break;
    case 7:
      Btt4 = Btt4 + 0b00000100;
      break;
    case 8:
      Btt4 = Btt4 + 0b00001000;
      break;
    case 9:
      Btt4 = Btt4 + 0b00010000;
      break;
  }
}
void h2(uint8_t output) {

  switch (temp) {
    case 0:
      Btt2 = Btt2 + 0b00000010;
      break;
    case 1:
      Btt4 = Btt4 + 0b01000000;
      break;
    case 2:
      Btt4 = Btt4 + 0b10000000;
      break;
    case 3:
      Btt3 = Btt3 + 0b00000010;
      break;
    case 4:
      Btt3 = Btt3 + 0b00000100;
      break;
    case 5:
      Btt3 = Btt3 + 0b00001000;
      break;
    case 6:
      Btt3 = Btt3 + 0b00010000;
      break;
    case 7:
      Btt3 = Btt3 + 0b00100000;
      break;
    case 8:
      Btt3 = Btt3 + 0b01000000;
      break;
    case 9:
      Btt3 = Btt3 + 0b10000000;
      break;
  }
}
void m1(uint8_t output) {

  switch (temp) {
    case 0:
      Btt1 = Btt1 + 0b00010000;
      break;
    case 1:
      Btt2 = Btt2 + 0b00000100;
      break;
    case 2:
      Btt2 = Btt2 + 0b00001000;
      break;
    case 3:
      Btt2 = Btt2 + 0b00010000;
      break;
    case 4:
      Btt2 = Btt2 + 0b00100000;
      break;
    case 5:
      Btt2 = Btt2 + 0b01000000;
      break;
    case 6:
      Btt2 = Btt2 + 0b10000000;
      break;
    case 7:
      Btt1 = Btt1 + 0b00000010;
      break;
    case 8:
      Btt1 = Btt1 + 0b00000100;
      break;
    case 9:
      Btt1 = Btt1 + 0b00001000;
      break;
  }
}
void m2(uint8_t output) {

  switch (temp) {
    case 0:
      Btt0 = Btt0 + 0b10000000;
      break;
    case 1:
      Btt1 = Btt1 + 0b00100000;
      break;
    case 2:
      Btt1 = Btt1 + 0b01000000;
      break;
    case 3:
      Btt1 = Btt1 + 0b10000000;
      break;
    case 4:
      Btt0 = Btt0 + 0b00000010;
      break;
    case 5:
      Btt0 = Btt0 + 0b00000100;
      break;
    case 6:
      Btt0 = Btt0 + 0b00001000;
      break;
    case 7:
      Btt0 = Btt0 + 0b00010000;
      break;
    case 8:
      Btt0 = Btt0 + 0b00100000;
      break;
    case 9:
      Btt0 = Btt0 + 0b01000000;
      break;
  }
}

void shiftOutMy(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val) {
  uint8_t i;

  for (i = 0; i < 8; i++) {
    if (bitOrder == LSBFIRST)
      digitalWrite(dataPin, !!(val & (1 << i)));
    else
      digitalWrite(dataPin, !!(val & (1 << (7 - i))));
    //     delay(1);
    digitalWrite(clockPin, HIGH);
    //      delay(1);
    digitalWrite(clockPin, LOW);
    //     delay(1);
  }
}


void set_Mode(void)
{

  if (Key_1_Pr) { // нажатие 1й кнопки вкл/выкл подсветки
    LedON = !LedON;
    if (LedON) {
      digitalWrite(LED, HIGH);
    }
    else {
      digitalWrite(LED, LOW);
    }
    Key_1_Pr = 0;
  }

  // Реакция на кнопки

  if (Key_2_Pr) { //нажатин на вторую кнопку - запрос времени
    NtpNoConn = false;
    if ( GetNTP() ) {
      ms2       = cur_ms;
      t_correct = ntp_time - t_cur;
      NtpNoConn = true;
      Next_ms   = 86400000;
    }

    Key_2_Pr = 0;
  }

  if (Key_1_LP == 1) {
    Key_1_LP = 0;
  }



}

// Опрос кнопок процедура візівается 200 раз в секунду
void button_press(void)
{

  if (ADCkey > 300)
  {
    Key_Timer = 0;
    if (Key_1_Count == 255)
    {
      return;
    } else {
      Key_1_Count++;
      if (Key_1_Count == Key_Pr) Key_1_Pr = 1;
      if (Key_1_Count == Key_Long) Key_1_LP = 1;
      return;
    }
  } else
  {
    Key_1_Count = 0;
    if (Key_1_Pr)   Key_1_PrR = 1;
    if (!Key_1_LP) {
      Key_1_LPR = 1;
      Key_1_PrR = 0;
    } else {
      Key_1_Pr = 0;
      Key_1_LP = 0;
    }
    //  return;

  }


  if (ADCkey > 150)

  {
    Key_Timer = 0;
    if (Key_2_Count == 255)
    {
      return;
    } else {
      Key_2_Count++;
      if (Key_2_Count == Key_Pr) Key_2_Pr = 1;
      if (Key_2_Count == Key_Long) Key_2_LP = 1;
      return;
    }

  } else
  {
    Key_2_Count = 0;
    Key_2_Pr = 0;
    Key_2_LP = 0;
    //  return;

  }
  if (ADCkey > 100)

  {
    Key_Timer = 0;
    if (Key_3_Count == 255)
    {
      return;
    } else {
      Key_3_Count++;
      if (Key_3_Count == Key_Pr) Key_3_Pr = 1;
      if (Key_3_Count == Key_Long) Key_3_LP = 1;
      return;
    }


  } else
  {
    Key_3_Count = 0;
    Key_3_Pr = 0;
    Key_3_LP = 0;
    //  return;

  }


}


