// Библиотеки
  #include <Arduino.h>
  #include <ESP8266WiFi.h>
  #include <EncButton.h>
  #include "ASOLED_ESP8266.h"
  #include "Stab_config.h"
  #include <ArduinoOTA.h>
  #include <ESPAsyncWebServer.h>
  #include <ESP8266mDNS.h>
  #include <WiFiUdp.h>
  #include <EEPROM.h>
// Переменные
  bool error = false;                          // Защита от пробития симистора
  float voltage = 0, current = 0,  energy= 0;  // Измеряемые значения
  uint16_t power = 0;                          // Мощность текущая 
  int16_t powerSetpoint = 0;                   // Задание мощности
  uint16_t maxPower = PMAX_TEN;                // максимально достигнутая
  bool insufficientVoltage = false;            // Флаг недостаточного напряжения
  bool boostLoadActive = false;                // Флаг для блокировки регулировки на время прогрузки
  // PZEM
  bool PzemEnClear = false;                    // Флаг команды на обнуление счетчика
  // Глобальные переменные для управления временем и состоянием
  uint32_t lastDelayChange = 0;  // Время последнего изменения delayTime
  float filteredPower = 0;            // Фильтрованное значение мощности
  uint32_t timeJump=0;           // задержка начала проверки симистора на пробитие
  volatile uint32_t dtTime = 0;       // Расчетное смещение истинного пересечения с нулем от фронта падения импульса
  // Регулятор (triak.ino)
  uint32_t tm, tm2;                           // Переменные для задержек калибровки и разгона
  int8_t nPL=31;                                   // Калибровочный массив
  uint16_t PL[32] = {10000, 9951, 9836, 9733, 9570, 9370, 9134, 8832, 8517, 8130, 7707, 7289, 6836, 6382, 5922, 5444, 
                      4948, 4452, 3962, 3460, 2964, 2480, 2032, 1645, 1294, 986, 719, 508, 338, 211, 120, 66};
  uint16_t PLt[32];                                 // массив для временных значений при калибровке
  bool REG = true;                                  // Флаг работы регулятора
  bool boostMode = false;                           // Флаг режима разгона
  volatile bool heatEnabled = false;                // Флаг включения нагрева
  bool TrueMAXPowerTEN=true;                        // Флаг действительности номинала ТЭНа
  uint8_t timeReg = DEFAULT_TIME_REG;               // Переодичность регулирования 
  uint8_t K_upr = DEFAULT_K_UPR;                    // Коэффициент воздействия

  int16_t UDP_Port = 12345; // Порт UDP рассылки

  // USART с Самоваром
  String pendingResponse = "";              // Ответ, который нужно отправить

  // WiFi
  String savedSSID, savedPass;
  bool hotspotMode = false;
  int8_t RSSI = 0;
  String localIP = "";
  AsyncWebServer server(80);          // Объект вэбсервера
  volatile bool timer_armed = false;  // Флаг взведенного таймера на снятие импульса включения симистора
  volatile long delayTrue = 0;        // Измеренная задержка для отладки
  volatile bool StartZC= true;        // Флаг начального определения смещения пересечения с 0
  // Датчик пересечения с 0
  volatile uint32_t zeroCrossTime = 0;        // Время последнего срабатывания детектора нуля дорасчетное
  volatile uint32_t lastZeroCrossTime = 0;    // Время предыдущего пересечения с нулём дорасчетное
  volatile uint32_t ZeroCrossTime_F = 0;      // Время предыдущего пересечения с нулём падение
  volatile uint32_t lastZeroCrossTime_R = 0;  // Время предыдущего пересечения с нулём подъем
  // Переменные для управления семистором
  volatile uint32_t delayTime = 9500;          // Задержка для открытия симистора (по умолчанию максимальная)
  uint32_t delayLow = DEFAULT_DELAY_LOW;       // Нижний предел задержки открытия симистора (0)
  uint32_t delayHigh = DEFAULT_DELAY_HIGH;     // Верхний предел задержки открытия симистора (9500)
  uint16_t TimeStep; // Шаг времени в таблице мощностей
  float sensitivity_table[31]; // мкс/% Таблица обратная таблице PL для быстрого расчета грубой коррекции
  // Функции
  void boost(bool SW);
  void triakLoop();
  void initModeFromEEPROM();

// Запуск...
void setup() {
    pinMode(boostPin, OUTPUT);  digitalWrite(boostPin, LOW); // Инициализация пинов 
    pinMode(heatPin, OUTPUT);   digitalWrite(heatPin, HIGH);
    pinMode(triacPin, OUTPUT);  digitalWrite(triacPin, LOW);
    pinMode(zeroCrossPin, INPUT);
    EEPROM.begin(EEPROM_SIZE);
    initModeFromEEPROM();         // Чтение сохраненных во флэш параметров
    initOLED();                   // Инициализация дисплея
    InitButton();                 // Инициализация кнопок
    Serial.begin(9600);
    delay(100); // Даем время на инициализацию
    PZEMInit();                   // Инициализация измерений
    Serial.println("\nStart...");

    initWiFi();                  // Загрузка сохраненных данных и  запуск Wi-Fi
    setupWebServer();            // Запуск веб-сервера
    initOTA();                   // Запуск ОТА
    StaticOLEDDisplay();         // Выводим на дисплей статические названия полей
    zeroCrossInit();             // Инициализация датчика пересечения с 0
    for (uint8_t i=0; i<32; i++) Serial.print(String(PL[i]) + ", "); Serial.println();
}
//Основной цикл
void loop() { 
  MDNS.update(); 
 ArduinoOTA.handle();                // Обработка ОТА
 USART_GetRequests();                // Принимаем по UART запросы от Самовара
 USART_SendReport();                 // Отправляем ответ Самовару по UART
 ButtonLoop();                       // Обработка кнопки
 yield();
  static unsigned long timeout = millis(); //остальное раз в секунду
  if (millis() - timeout >= 1000) {    
    timeout = millis();                    // перезапуск секундного таймера 
    PZEMLoop();                            // Читаем параметры электросети
    #ifdef ENABLE_UDP_BROADCAST
    broadcastStatus();                     // Шлём UDP отчеты
    #endif

    triakLoop();                           // Обработка симистора
    updateOLEDDisplay();                   // Выводим инфу на дисплей
    static unsigned long timeout2 = millis(); // раз в 20 секунд
    if (millis() - timeout2 >= 20000) { 
      timeout2 = millis(); 
      loopWIFI();                            // проверка работы WiFi и реконнект
    }    
  }    
}