// Модуль сохранения/чтения настроек во флэше
#include <Preferences.h>
#include <nvs.h>

Preferences prefs;

void ValidateSamSetup() {
  if (SamSetup.HeaterResistant < 1) SamSetup.HeaterResistant = 15;
  if (SamSetup.LogPeriod < 5) SamSetup.LogPeriod = 10;
  if (SamSetup.autospeed >= 100) SamSetup.autospeed = 10;
  SamSetup.Cull_N_Min = constrain(SamSetup.Cull_N_Min, 0, 100);             // минимальные обороты, %
  SamSetup.Cull_N_Max = constrain(SamSetup.Cull_N_Max, 10, 100);             // максимальные обороты, %
  SamSetup.Cull_T_ON = constrain(SamSetup.Cull_T_ON, 10, 50);                // Т включения
  SamSetup.Cull_T_Max = constrain(SamSetup.Cull_T_Max, 20, 50);               // T максимальных оборотов
  if (!SamSetup.UseWV && !SamSetup.UseWP && (SamSetup.MixerMode == 3) && SamSetup.MixerPWM > 1023) SamSetup.MixerPWM = 1023;
  SamSetup.BMPFltr = constrain(SamSetup.BMPFltr, 0, 99);
}
void UseSamSetup() {
  heaterPID.SetTunings(SamSetup.Kp, SamSetup.Ki, SamSetup.Kd);
  SteamSensor.SetTemp = SamSetup.SetSteamTemp;
  PipeSensor.SetTemp = SamSetup.SetPipeTemp;
  WaterSensor.SetTemp = SamSetup.SetWaterTemp;
  TankSensor.SetTemp = SamSetup.SetTankTemp;
  ACPSensor.SetTemp = SamSetup.SetACPTemp;
  SteamSensor.Delay = SamSetup.SteamDelay;
  PipeSensor.Delay = SamSetup.PipeDelay;
  WaterSensor.Delay = SamSetup.WaterDelay;
  TankSensor.Delay = SamSetup.TankDelay;
  ACPSensor.Delay = SamSetup.ACPDelay;

  CopyDSAddress(SamSetup.SteamAdress, SteamSensor.Sensor);
  CopyDSAddress(SamSetup.PipeAdress, PipeSensor.Sensor);
  CopyDSAddress(SamSetup.WaterAdress, WaterSensor.Sensor);
  CopyDSAddress(SamSetup.TankAdress, TankSensor.Sensor);
  CopyDSAddress(SamSetup.ACPAdress, ACPSensor.Sensor);
  SteamSensor.ErrCount = -110;
  PipeSensor.ErrCount = -110;
  WaterSensor.ErrCount = -110;
  TankSensor.ErrCount = -110;
  ACPSensor.ErrCount = -110;
  
  MaxPower = SamSetup.HeaterResistant > 1 ? (230 * 230 / SamSetup.HeaterResistant) : 3000; // Номинальная мощность ТЭН-а

  if (SamSetup.UseWV) SetWatherValve2(OFF);// закрываем дополнительный клапан воды
  
  NTP.setTimeOffset(SamSetup.TimeZone * 3600);
  //Задаем параметры для сенсора уровня флегмы
    if (SamSetup.HLS==2)  whls.setType(HIGH_PULL); else  whls.setType(LOW_PULL);
}
static void cleanupNVS() {
  Serial.println("Очистка NVS...");
  prefs.begin("device-config", false);
  prefs.clear();
  prefs.end();
}
// Функции для проверки и сохранения параметров
bool saveParamIfChangedUChar(const char* key, uint8_t value) {
  if (!prefs.isKey(key) || prefs.getUChar(key) != value) {
    prefs.putUChar(key, value);    
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getUChar(key, 99))
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 99)),1);
    return true;
  }
  return false;
}

bool saveParamIfChangedUShort(const char* key, uint16_t value) {
  if (!prefs.isKey(key) || prefs.getUShort(key) != value) {
    prefs.putUShort(key, value);
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getUShort(key, 9999)) 
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 9999)),1);
    return true;
  }
  return false;
}

bool saveParamIfChangedFloat(const char* key, float value) {
  if (!prefs.isKey(key) || prefs.getFloat(key) != value) {
    prefs.putFloat(key, value);
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getFloat(key, 99.99)) 
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 99.99)),1);
    return true;
  }
  return false;
}

bool saveParamIfChangedBool(const char* key, bool value) {
  if (!prefs.isKey(key) || prefs.getBool(key) != value) {
    prefs.putBool(key, value);
    DbgMsg("!!!Сохранен "+ String(key) + ", Value="+String(value),1);
    if (value != prefs.getBool(key, 0)) 
   // DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 0)),1);
    return true;
  }
  return false;
}

bool saveStringIfChanged(const char* key, const char* value) {
  String stored = prefs.getString(key, "");
  if (!prefs.isKey(key) || stored != value) {
    prefs.putString(key, value);
    DbgMsg("!!!Сохранена строка"+ String(key) + ", Value="+String(value),1);
    if (String(value) != prefs.getString(key, ""))
    //DbgMsg("!!!Несовпадение при сохранении "+ String(key) + ", Value="+String(value)+", Read="+String(prefs.getUShort("key", 0)),1);
    return true;
  }
  return false;
}

bool saveArrayIfChanged(const char* key, const void* array, size_t size) {
  bool changed = !prefs.isKey(key);
  if (!changed) {
    uint8_t stored_array[size];
    prefs.getBytes(key, stored_array, size);
    changed = (memcmp(stored_array, array, size) != 0);
  }
  if (changed) {
    prefs.putBytes(key, array, size);
    DbgMsg("!!!Сохранен "+ String(key),1);
  }
  return changed;
}

void save_profile() {
  prefs.begin("device-config", false);
  
  // Основные параметры
  saveParamIfChangedUChar("mode", SamSetup.Mode);
  saveParamIfChangedUChar("disp", SamSetup.disp);
  saveParamIfChangedUChar("PrSens", SamSetup.PressureSensor);
  saveParamIfChangedUShort("XGZ_K", SamSetup.XGZ_K);
  saveParamIfChangedFloat("DistTemp", SamSetup.DistTemp);
  saveParamIfChangedUChar("DistTimeF", SamSetup.DistTimeF);
  saveParamIfChangedBool("UsePCorrect", SamSetup.UsePreccureCorrect);
  saveParamIfChangedBool("UseTPrCor", SamSetup.UseTPrCor);
  saveParamIfChangedBool("useautospeed", SamSetup.useautospeed);
  saveParamIfChangedUChar("autospeed", SamSetup.autospeed);
  saveParamIfChangedBool("UAPD", SamSetup.useautopowerdown);
  saveParamIfChangedFloat("MaxPValue", SamSetup.MaxPressureValue);
  saveParamIfChangedFloat("Max_St_T", SamSetup.Max_St_T);
  saveParamIfChangedFloat("Max_ACP_T", SamSetup.Max_ACP_T);
  saveParamIfChangedFloat("Ch_Pwr_Md_St_T", SamSetup.Ch_Pwr_Md_St_T);
  saveParamIfChangedBool("UseBuzzer", SamSetup.UseBuzzer);
  saveParamIfChangedBool("UseBBuzzer", SamSetup.UseBBuzzer);
  saveParamIfChangedBool("ChangePBuzzer", SamSetup.ChangeProgramBuzzer);
  saveParamIfChangedBool("UseLog", SamSetup.UseLog);
  saveParamIfChangedBool("NoLogCh", SamSetup.NoLogCh);
  saveParamIfChangedBool("UseStLog", SamSetup.UseStLog);
  
  // Цвета
  saveStringIfChanged("SteamColor", SamSetup.SteamColor);
  saveStringIfChanged("PipeColor", SamSetup.PipeColor);
  saveStringIfChanged("WaterColor", SamSetup.WaterColor);
  saveStringIfChanged("TankColor", SamSetup.TankColor);
  saveStringIfChanged("ACPColor", SamSetup.ACPColor);
  
  // Адреса датчиков
  saveArrayIfChanged("SteamAddr", SamSetup.SteamAdress, sizeof(SamSetup.SteamAdress));
  saveArrayIfChanged("PipeAddr", SamSetup.PipeAdress, sizeof(SamSetup.PipeAdress));
  saveArrayIfChanged("WaterAddr", SamSetup.WaterAdress, sizeof(SamSetup.WaterAdress));
  saveArrayIfChanged("TankAddr", SamSetup.TankAdress, sizeof(SamSetup.TankAdress));
  saveArrayIfChanged("ACPAddr", SamSetup.ACPAdress, sizeof(SamSetup.ACPAdress));
  
  // Температурные параметры
  saveParamIfChangedFloat("DeltaSteamTemp", SamSetup.DeltaSteamTemp);
  saveParamIfChangedFloat("SetSteamTemp", SamSetup.SetSteamTemp);
  saveParamIfChangedUShort("SteamDelay", SamSetup.SteamDelay);
  saveParamIfChangedFloat("DeltaPipeTemp", SamSetup.DeltaPipeTemp);
  saveParamIfChangedFloat("SetPipeTemp", SamSetup.SetPipeTemp);
  saveParamIfChangedUShort("PipeDelay", SamSetup.PipeDelay);
  saveParamIfChangedFloat("DeltaWaterTemp", SamSetup.DeltaWaterTemp);
  saveParamIfChangedFloat("SetWaterTemp", SamSetup.SetWaterTemp);
  saveParamIfChangedUShort("WaterDelay", SamSetup.WaterDelay);
  saveParamIfChangedFloat("DeltaTankTemp", SamSetup.DeltaTankTemp);
  saveParamIfChangedFloat("SetTankTemp", SamSetup.SetTankTemp);
  saveParamIfChangedUShort("TankDelay", SamSetup.TankDelay);
  saveParamIfChangedFloat("DeltaACPTemp", SamSetup.DeltaACPTemp);
  saveParamIfChangedFloat("SetACPTemp", SamSetup.SetACPTemp);
  saveParamIfChangedUShort("ACPDelay", SamSetup.ACPDelay);
  saveParamIfChangedBool("UseDS", SamSetup.UseDS);
  // NTC параметры
  saveParamIfChangedBool("UseNTC", SamSetup.UseNTC);
  for (uint8_t i = 0; i < 5; i++) {
    String key = "NTC_dT" + String(i + 1);
    saveParamIfChangedFloat(key.c_str(), SamSetup.NTC_dT[i]);
  }
  
  // Характеристика термисторов
  saveArrayIfChanged("m_adc", SamSetup.m_adc, sizeof(SamSetup.m_adc));
  
  // Насосы
  saveParamIfChangedUShort("StepMl", SamSetup.StepperStepMl);  
  saveParamIfChangedUShort("NBK_StepMl", SamSetup.NBK_StepperStepMl);
  saveParamIfChangedBool("StRev1", SamSetup.StRev1);
  saveParamIfChangedBool("UsePump2", SamSetup.UsePump2);
  saveParamIfChangedFloat("SpPump2", SamSetup.SpPump2);
  saveParamIfChangedBool("StRev2", SamSetup.StRev2);
  
  // Водяные настройки
  saveParamIfChangedBool("UseWP", SamSetup.UseWP);
  saveParamIfChangedFloat("Trg_W_T", SamSetup.Trg_W_T);
  saveParamIfChangedFloat("Alrm_Wt_T", SamSetup.Alrm_Wt_T);
  saveParamIfChangedFloat("Max_Wt_T", SamSetup.Max_Wt_T);
  saveParamIfChangedFloat("Opn_Vlv_Tnk_T", SamSetup.Opn_Vlv_Tnk_T);
  saveParamIfChangedFloat("D_Cls_Vlv", SamSetup.D_Cls_Vlv);
  saveParamIfChangedUChar("PWM_L_V", SamSetup.PWM_L_V);
  saveParamIfChangedUChar("PWM_St_V", SamSetup.PWM_St_V);
  saveParamIfChangedUChar("UseWV", SamSetup.UseWV);
  saveParamIfChangedUChar("StabTime", SamSetup.StabTime);
  saveParamIfChangedUChar("BMPFltr", SamSetup.BMPFltr);

  // Миксер
  saveParamIfChangedUChar("MixerMode", SamSetup.MixerMode);
  saveParamIfChangedUShort("MixerPWM", SamSetup.MixerPWM);  
  
  // Сервопривод
  saveArrayIfChanged("servoDelta", SamSetup.servoDelta, sizeof(SamSetup.servoDelta));
  
  // Питание
  saveParamIfChangedUChar("PwrType", SamSetup.PwrType);
  saveParamIfChangedBool("CheckPower", SamSetup.CheckPower);
  saveParamIfChangedFloat("HeaterR", SamSetup.HeaterResistant);
  saveParamIfChangedUShort("PwStartPause", SamSetup.PwStartPause);
  
  // Реле
  saveParamIfChangedBool("Rele1", SamSetup.rele1);
  saveParamIfChangedBool("Rele2", SamSetup.rele2);
  saveParamIfChangedBool("Rele3", SamSetup.rele3);
  saveParamIfChangedBool("Rele4", SamSetup.rele4);
  
  // Вентилятор
  saveParamIfChangedBool("PWM_Cull", SamSetup.PWM_Cull);
  saveParamIfChangedUShort("Cull_N_Min", SamSetup.Cull_N_Min);
  saveParamIfChangedUShort("Cull_N_Max", SamSetup.Cull_N_Max);
  saveParamIfChangedFloat("Cull_T_ON", SamSetup.Cull_T_ON);
  saveParamIfChangedFloat("Cull_T_Max", SamSetup.Cull_T_Max);
  
  // PID
  saveParamIfChangedFloat("Kp", SamSetup.Kp);
  saveParamIfChangedFloat("Ki", SamSetup.Ki);
  saveParamIfChangedFloat("Kd", SamSetup.Kd);
  saveParamIfChangedFloat("StbVoltage", SamSetup.StbVoltage);
  saveParamIfChangedFloat("BVolt", SamSetup.BVolt);
  saveParamIfChangedBool("UseST", SamSetup.UseST);
  saveParamIfChangedFloat("Boilng_T", SamSetup.Boilng_T);
  saveParamIfChangedFloat("Heat_Dt", SamSetup.Heat_Dt);
  saveParamIfChangedFloat("Axlr_Heat_Dt", SamSetup.Axlr_Heat_Dt);
  
  // НБК
  saveParamIfChangedFloat("Nbk_Tn", SamSetup.Nbk_Tn);
  saveParamIfChangedFloat("NbkIn", SamSetup.NbkIn);
  saveParamIfChangedFloat("NBK_Mult_Pause", SamSetup.NBK_Mult_Pause);
  saveParamIfChangedFloat("NbkDelta", SamSetup.NbkDelta);
  saveParamIfChangedFloat("NbkDM", SamSetup.NbkDM);
  saveParamIfChangedFloat("NbkDP", SamSetup.NbkDP);
  saveParamIfChangedFloat("NBK_Pump_Limit", SamSetup.NBK_Pump_Limit);
  saveParamIfChangedFloat("NbkSteamT", SamSetup.NbkSteamT);
  saveParamIfChangedFloat("NbkOwPress", SamSetup.NbkOwPress);
  saveParamIfChangedBool("Use_NBK_Delta_P", SamSetup.Use_NBK_Delta_Pressure);
  saveParamIfChangedFloat("NBK_WPrc", SamSetup.NBK_WPrc);
  
  // Сеть
  saveStringIfChanged("SSID", SamSetup.SavedSSID);
  saveStringIfChanged("PASS", SamSetup.SavedPASS);
  saveParamIfChangedBool("UseBlynk", SamSetup.UseBlynk);
  saveStringIfChanged("BlynkUrl", SamSetup.BlynkUrl);
  saveStringIfChanged("blynkauth", SamSetup.blynkauth);
  saveParamIfChangedBool("UseTg", SamSetup.UseTg);
  saveStringIfChanged("tgtoken", SamSetup.tg_token);
  saveStringIfChanged("tgchatid", SamSetup.tg_chat_id);
  saveStringIfChanged("videourl", SamSetup.videourl);
  saveParamIfChangedBool("UseMQTT", SamSetup.UseMQTT);
  saveStringIfChanged("MQTT_Serv", SamSetup.MQTT_Serv);
  saveParamIfChangedUShort("udpLPort", SamSetup.udpLPort);
  // Прочие настройки
  saveParamIfChangedBool("Use_BMP180", SamSetup.Use_BMP180);
  saveParamIfChangedUChar("HLS", SamSetup.HLS);
  saveParamIfChangedBool("UseHLS_D", SamSetup.UseHLS_D);
  saveParamIfChangedUChar("UseWS", SamSetup.UseWS);
  saveParamIfChangedUShort("Ws_Calbr", SamSetup.Ws_Calbr);
  saveParamIfChangedBool("UseBTN", SamSetup.UseBTN);
  saveParamIfChangedBool("UseA_BTN", SamSetup.UseA_BTN);
  saveParamIfChangedUChar("TimeZone", SamSetup.TimeZone);
  saveParamIfChangedUChar("LogPeriod", SamSetup.LogPeriod);
  saveParamIfChangedBool("UseBdT_Aset", SamSetup.UseBdT_Aset);
  saveParamIfChangedBool("dbg", SamSetup.dbg);
  saveStringIfChanged("MQTT_User", SamSetup.MQTT_User);
  saveStringIfChanged("MQTT_Pass", SamSetup.MQTT_Pass);
  saveParamIfChangedUShort("MQTT_Port", SamSetup.MQTT_Port);
  saveParamIfChangedUShort("UDP_Port", SamSetup.UDP_Port);
  saveParamIfChangedBool("flag", SamSetup.flag);
  saveParamIfChangedBool("r_ASt", SamSetup.r_ASt);
  saveParamIfChangedBool("OTA", SamSetup.OTA);
  saveParamIfChangedBool("LUA", SamSetup.LUA);
  saveParamIfChangedBool("WS", SamSetup.WSerial);
  saveParamIfChangedUShort("D_Speed", SamSetup.D_Speed);
  saveParamIfChangedUShort("D_Steps", SamSetup.D_Steps);
  prefs.end();
}

void read_config() {
    prefs.begin("device-config", true);
        SamSetup.flag = prefs.getUChar("flag", 255); // В случае несовпадения ключа чистим NVS и грузим конфиг по умолчанию
        if (SamSetup.flag != Setup_Key) {
          prefs.clear();
          }
  // читаем отдельные параметры
  SamSetup.Mode = prefs.getUChar("mode", 0);
  SamSetup.disp = prefs.getUChar("disp", 1);
  SamSetup.PressureSensor = prefs.getUChar("PrSens", 0);
  SamSetup.XGZ_K = prefs.getUShort("XGZ_K", 32);
  SamSetup.DistTemp = prefs.getFloat("DistTemp", 98.5);
  SamSetup.DistTimeF = prefs.getUChar("DistTimeF", 16);
  SamSetup.UsePreccureCorrect = prefs.getBool("UsePCorrect", true);
  SamSetup.UseTPrCor = prefs.getBool("UseTPrCor", false);
  SamSetup.useautospeed = prefs.getBool("useautospeed", false);
  SamSetup.autospeed = prefs.getUChar("autospeed", 10);
  SamSetup.useautopowerdown = prefs.getBool("UAPD", false);
  SamSetup.MaxPressureValue = prefs.getFloat("MaxPValue", 0.0);
  SamSetup.UseBuzzer = prefs.getBool("UseBuzzer", false);
  SamSetup.UseBBuzzer = prefs.getBool("UseBBuzzer", false);
  SamSetup.ChangeProgramBuzzer = prefs.getBool("ChangePBuzzer", false);
  strlcpy(SamSetup.SteamColor, prefs.getString("SteamColor", "#de1717").c_str(), sizeof(SamSetup.SteamColor));
  if (prefs.isKey("SteamAddr")) prefs.getBytes("SteamAddr", SamSetup.SteamAdress, sizeof(SamSetup.SteamAdress)); 
    else CopyDSAddress(STEAM_ADRESS, SamSetup.SteamAdress);
  SamSetup.DeltaSteamTemp = prefs.getFloat("DeltaSteamTemp", 0.0);
  SamSetup.SetSteamTemp = prefs.getFloat("SetSteamTemp", 0.5);
  SamSetup.SteamDelay = prefs.getUShort("SteamDelay", 20);
  strlcpy(SamSetup.PipeColor, prefs.getString("PipeColor", "#336b05").c_str(), sizeof(SamSetup.PipeColor));
  if (prefs.isKey("PipeAddr")) prefs.getBytes("PipeAddr", SamSetup.PipeAdress, sizeof(SamSetup.PipeAdress));
    else CopyDSAddress(PIPE_ADRESS, SamSetup.PipeAdress);
  SamSetup.DeltaPipeTemp = prefs.getFloat("DeltaPipeTemp", 0.0);
  SamSetup.SetPipeTemp = prefs.getFloat("SetPipeTemp", 0.5);
  SamSetup.PipeDelay = prefs.getUShort("PipeDelay", 20);
  strlcpy(SamSetup.WaterColor, prefs.getString("WaterColor", "#2117ab").c_str(), sizeof(SamSetup.WaterColor));
  if (prefs.isKey("WaterAddr")) prefs.getBytes("WaterAddr", SamSetup.WaterAdress, sizeof(SamSetup.WaterAdress));
    else CopyDSAddress(WATER_ADRESS, SamSetup.WaterAdress);
  SamSetup.DeltaWaterTemp = prefs.getFloat("DeltaWaterTemp", 0.0);
  SamSetup.SetWaterTemp = prefs.getFloat("SetWaterTemp", 50);
  SamSetup.WaterDelay = prefs.getUShort("WaterDelay", 20);
  strlcpy(SamSetup.TankColor, prefs.getString("TankColor", "#271c1c").c_str(), sizeof(SamSetup.TankColor));
  if (prefs.isKey("TankAddr")) prefs.getBytes("TankAddr", SamSetup.TankAdress, sizeof(SamSetup.TankAdress));
    else CopyDSAddress(TANK_ADRESS, SamSetup.TankAdress);
  SamSetup.DeltaTankTemp = prefs.getFloat("DeltaTankTemp", 0.0);
  SamSetup.SetTankTemp = prefs.getFloat("SetTankTemp", 0.9);
  SamSetup.TankDelay = prefs.getUShort("TankDelay", 20);
  strlcpy(SamSetup.ACPColor, prefs.getString("ACPColor", "#fb4141").c_str(), sizeof(SamSetup.ACPColor));
  if (prefs.isKey("ACPAddr")) prefs.getBytes("ACPAddr", SamSetup.ACPAdress, sizeof(SamSetup.ACPAdress));
    else CopyDSAddress(ACP_ADRESS, SamSetup.ACPAdress);
  SamSetup.DeltaACPTemp = prefs.getFloat("DeltaACPTemp", 0.0);
  SamSetup.SetACPTemp = prefs.getFloat("SetACPTemp", 43);
  SamSetup.ACPDelay = prefs.getUShort("ACPDelay", 20);
  SamSetup.UseNTC = prefs.getBool("UseNTC", false);
  SamSetup.UseDS = prefs.getBool("UseDS", true);
  for (uint8_t i = 0; i < 5; i++) {
      String key = "NTC_dT" + String(i + 1);
      SamSetup.NTC_dT[i] = prefs.getFloat(key.c_str(), 0.0);
  }
  size_t m_adc_len = prefs.getBytesLength("m_adc");
  if (m_adc_len == sizeof(SamSetup.m_adc)) {
      prefs.getBytes("m_adc", SamSetup.m_adc, sizeof(SamSetup.m_adc));
  } else {
      const uint16_t m_adc_default[151] = { 32101, 32049, 31993, 31935, 31874, 31810, 31742, 31670, 31595, 31516, 31433, 31346, 31255, 31160, 31060, 30956, 30847, 30732, 30614, 30489, 30360, 30226, 30085, 29940,
                    29789, 29632, 29469, 29300, 29126, 28945, 28758, 28566, 28367, 28162, 27951, 27733, 27510, 27280, 27045, 26803, 26556, 26303, 26044, 25780, 25510, 25235, 24954, 24669,
                    24379, 24085, 23786, 23483, 23176, 22865, 22551, 22234, 21914, 21592, 21267, 20940, 20612, 20282, 19951, 19619, 19286, 18954, 18621, 18288, 17956, 17625, 17296, 16967,
                    16640, 16315, 15992, 15672, 15354, 15039, 14726, 14417, 14112, 13809, 13511, 13216, 12925, 12638, 12355, 12076, 11802, 11532, 11267, 11006, 10749, 10497, 10250, 10007,
                    9769, 9536, 9307, 9083, 8864, 8649, 8439, 8233, 8032, 7835, 7643, 7455, 7272, 7092, 6917, 6746, 6579, 6416, 6257, 6102, 5951, 5803, 5660, 5519,
                    5383, 5249, 5119, 4993, 4870, 4749, 4632, 4518, 4407, 4299, 4194, 4091, 3991, 3894, 3799, 3707, 3617, 3529, 3444, 3361, 3281, 3202, 3125, 3051,
                    2978, 2908, 2839, 2772, 2707, 2643, 2582 };         // Характеристика термисторов по умолчанию
      memcpy(SamSetup.m_adc, m_adc_default, sizeof(SamSetup.m_adc));
  }
  SamSetup.StepperStepMl = prefs.getUShort("StepMl", STEPPER_STEP_ML);
  SamSetup.NBK_StepperStepMl = prefs.getUShort("NBK_StepMl", 500);
  SamSetup.StRev1 = prefs.getBool("StRev1", false);
  SamSetup.UsePump2 = prefs.getBool("UsePump2", true);
  SamSetup.SpPump2 = prefs.getFloat("SpPump2", 0.05);
  SamSetup.StRev2 = prefs.getBool("StRev2", false);
  SamSetup.UseWP = prefs.getBool("UseWP", false);
  SamSetup.Trg_W_T = prefs.getFloat("Trg_W_T", 48.0);
  SamSetup.Alrm_Wt_T = prefs.getFloat("Alrm_Wt_T", 70.0);
  SamSetup.Max_Wt_T = prefs.getFloat("Max_Wt_T", 75.0);
  SamSetup.Max_St_T = prefs.getFloat("Max_St_T", 98.0);
  SamSetup.Max_ACP_T = prefs.getFloat("Max_ACP_T", 70.0);
  SamSetup.Ch_Pwr_Md_St_T = prefs.getFloat("Ch_Pwr_Md_St_T", 39.0);
  SamSetup.Opn_Vlv_Tnk_T = prefs.getFloat("Opn_Vlv_Tnk_T", 77.0);
  SamSetup.D_Cls_Vlv = prefs.getFloat("D_Cls_Vlv", 20.0);
  SamSetup.PWM_L_V = prefs.getUChar("PWM_L_V", 30);
  SamSetup.PWM_St_V = prefs.getUChar("PWM_St_V", 40);
  SamSetup.UseWV = prefs.getUChar("UseWV", 0);
  SamSetup.StabTime = prefs.getUChar("StabTime", 10);
  SamSetup.BMPFltr = prefs.getUChar("BMPFltr", 50);
  if (prefs.isKey("servoDelta")) {
      prefs.getBytes("servoDelta", SamSetup.servoDelta, sizeof(SamSetup.servoDelta));
  } else {
      memset(SamSetup.servoDelta, 0, sizeof(SamSetup.servoDelta));
  }
  SamSetup.PwrType = prefs.getUChar("PwrType", 4);
  SamSetup.CheckPower = prefs.getBool("CheckPower", false);
  SamSetup.HeaterResistant = prefs.getFloat("HeaterR", 15.2);
  SamSetup.PwStartPause = prefs.getUShort("PwStartPause", 2000);
  SamSetup.rele1 = prefs.getBool("Rele1", false);
  SamSetup.rele2 = prefs.getBool("Rele2", false);
  SamSetup.rele3 = prefs.getBool("Rele3", false);
  SamSetup.rele4 = prefs.getBool("Rele4", false);
  SamSetup.PWM_Cull = prefs.getBool("PWM_Cull", false);
  SamSetup.Cull_N_Min = prefs.getUShort("Cull_N_Min", 30);
  SamSetup.Cull_N_Max = prefs.getUShort("Cull_N_Max", 100);
  SamSetup.Cull_T_ON = prefs.getFloat("Cull_T_ON", 30.0);
  SamSetup.Cull_T_Max = prefs.getFloat("Cull_T_Max", 35.0);
  SamSetup.Kp = prefs.getFloat("Kp", 150.0);
  SamSetup.Ki = prefs.getFloat("Ki", 1.4);
  SamSetup.Kd = prefs.getFloat("Kd", 1.4);
  SamSetup.StbVoltage = prefs.getFloat("StbVoltage", 100.0);
  SamSetup.BVolt = prefs.getFloat("BVolt", 230.0);
  SamSetup.UseST = prefs.getBool("UseST", false);
  SamSetup.Boilng_T = prefs.getFloat("Boilng_T", 98.9);
  SamSetup.Heat_Dt = prefs.getFloat("Heat_Dt", 1.0);
  SamSetup.Axlr_Heat_Dt = prefs.getFloat("Axlr_Heat_Dt", 4.0);
  SamSetup.Nbk_Tn = prefs.getFloat("Nbk_Tn", 98.5);
  SamSetup.NbkIn = prefs.getFloat("NbkIn", 120.0);
  SamSetup.NBK_Mult_Pause = prefs.getFloat("NBK_Mult_Pause", 0.2);
  SamSetup.NbkDelta = prefs.getFloat("NbkDelta", 0.5);
  SamSetup.NbkDM = prefs.getFloat("NbkDM", 100.0);
  SamSetup.NbkDP = prefs.getFloat("NbkDP", 1.0);
  SamSetup.NBK_Pump_Limit = prefs.getFloat("NBK_Pump_Limit", 30.0);
  SamSetup.NbkSteamT = prefs.getFloat("NbkSteamT", 68.5);
  SamSetup.NbkOwPress = prefs.getFloat("NbkOwPress", 50.0);
  SamSetup.Use_NBK_Delta_Pressure = prefs.getBool("Use_NBK_Delta_P", false);
  SamSetup.NBK_WPrc = prefs.getFloat("NBK_WPrc", 100.0);
  strlcpy(SamSetup.SavedSSID, prefs.getString("SSID", "").c_str(), sizeof(SamSetup.SavedSSID));
  strlcpy(SamSetup.SavedPASS, prefs.getString("PASS", "").c_str(), sizeof(SamSetup.SavedPASS));
  SamSetup.UseBlynk = prefs.getBool("UseBlynk", false);
  strlcpy(SamSetup.BlynkUrl, prefs.getString("BlynkUrl", "samovar-tool.ru").c_str(), sizeof(SamSetup.BlynkUrl));
  strlcpy(SamSetup.blynkauth, prefs.getString("blynkauth", "").c_str(), sizeof(SamSetup.blynkauth));
  SamSetup.UseTg = prefs.getBool("UseTg", false);
  strlcpy(SamSetup.tg_token, prefs.getString("tgtoken", "").c_str(), sizeof(SamSetup.tg_token));
  strlcpy(SamSetup.tg_chat_id, prefs.getString("tgchatid", "").c_str(), sizeof(SamSetup.tg_chat_id));
  strlcpy(SamSetup.videourl, prefs.getString("videourl", "").c_str(), sizeof(SamSetup.videourl));
  SamSetup.UseMQTT = prefs.getBool("UseMQTT", false);
  strlcpy(SamSetup.MQTT_Serv, prefs.getString("MQTT_Serv", "ora1.samovar-tool.ru").c_str(), sizeof(SamSetup.MQTT_Serv));
  SamSetup.Use_BMP180 = prefs.getBool("Use_BMP180", true);
  SamSetup.HLS = prefs.getUChar("HLS", 0);
  SamSetup.UseHLS_D = prefs.getBool("UseHLS_D", false);
  SamSetup.UseWS = prefs.getUChar("UseWS", 0);
  SamSetup.Ws_Calbr = prefs.getUShort("Ws_Calbr", 1000);
  SamSetup.UseBTN = prefs.getBool("UseBTN", false);
  SamSetup.UseA_BTN = prefs.getBool("UseA_BTN", false);
  SamSetup.TimeZone = prefs.getUChar("TimeZone", 3);
  SamSetup.LogPeriod = prefs.getUChar("LogPeriod", 10);
  SamSetup.UseBdT_Aset = prefs.getBool("UseBdT_Aset", true);
  SamSetup.dbg = prefs.getBool("dbg", false);
  strlcpy(SamSetup.MQTT_User, prefs.getString("MQTT_User", "samovar").c_str(), sizeof(SamSetup.MQTT_User));
  strlcpy(SamSetup.MQTT_Pass, prefs.getString("MQTT_Pass", "samovar-tool.ru").c_str(), sizeof(SamSetup.MQTT_Pass));
  SamSetup.MQTT_Port = prefs.getUShort("MQTT_Port", 1883);
  SamSetup.UDP_Port = prefs.getUShort("UDP_Port", 12345);
  SamSetup.udpLPort = prefs.getUShort("udpLPort", 0);
  SamSetup.r_ASt = prefs.getBool("r_ASt", false);
  SamSetup.OTA = prefs.getBool("OTA", false);
  SamSetup.LUA = prefs.getBool("LUA", false);
  SamSetup.WSerial = prefs.getBool("WS", false);
  SamSetup.UseLog = prefs.getBool("UseLog", false);
  SamSetup.NoLogCh = prefs.getBool("NoLogCh", false);
  SamSetup.UseStLog = prefs.getBool("UseStLog", false);
    // Миксер
  SamSetup.MixerMode = prefs.getUChar("MixerMode", 0);
  SamSetup.MixerPWM = prefs.getUShort("MixerPWM", 1023);
  SamSetup.D_Speed = prefs.getUShort("D_Speed", 200);
  SamSetup.D_Steps = prefs.getUShort("D_Steps", 360 / 1.8 * 16 / 20);

  if (SamSetup.dbg) {
    nvs_stats_t nvs_stats;
    esp_err_t err = nvs_get_stats(NULL, &nvs_stats);
    
    if (err == ESP_OK) {
      Serial.println("=== Информация о NVS ===");
      Serial.printf("Всего записей: %d\n", nvs_stats.total_entries);
      Serial.printf("Использовано записей: %d\n", nvs_stats.used_entries);
      Serial.printf("Свободных записей: %d\n", nvs_stats.free_entries);
      Serial.printf("Всего пространства: %d байт\n", nvs_stats.total_entries * 32); // примерно 32 байта на запись
      Serial.printf("Свободно: %d байт\n", nvs_stats.free_entries * 32);
      Serial.println("========================");
    } else {
      Serial.println("Ошибка получения статистики NVS");
    }
  }
  prefs.end();
  if (SamSetup.flag != Setup_Key) {
    SamSetup.flag = Setup_Key;
    save_profile();
  }


    ValidateSamSetup();
    UseSamSetup();
    Samovar_Mode = (SAMOVAR_MODE)SamSetup.Mode;
}