//==========================================================RVMK
  #include "freertos/FreeRTOS.h"
  #include "esp_log.h"
  #include "driver/uart.h"
  #include <Arduino.h>
  #include "Settings.h"
  #include <sys/types.h>
  #include <stdint.h>
  typedef enum {
    RMVK_INT = 0,
    RMVK_OK,
    RMVK_ON
  } rmvk_res_t;
  typedef struct  {
    volatile uint8_t conn;
    volatile uint8_t on;
    volatile uint8_t VI;
    volatile uint8_t VO;
    volatile uint8_t VS;
  } rmvk_t;
  extern rmvk_t rmvk;
  void RMVK_init(void);
  void rmvk_read();
  void rmvkFn(void* arg);
  uint8_t RMVK_cmd(const char* cmd, rmvk_res_t res);
  uint16_t RMVK_set_out_voltge(uint16_t voltge);
  uint16_t RMVK_set_state(uint16_t new_state);
  uint16_t RMVK_set_on(uint16_t state);
  uint16_t RMVK_select_mem(uint16_t sm);
  uint16_t RMVK_get_in_voltge();
  uint16_t RMVK_get_out_voltge();
  uint16_t RMVK_get_store_out_voltge();
  bool RMVK_get_state();
  bool RMVK_get_conn();
  uint8_t get_rmvk_state();
  uint8_t get_rmvk_conn();
  #define MAX_VOLTAGE 230
  #define BUF_SIZE (1024)
  #define RD_BUF_SIZE (BUF_SIZE)
  #define RMVK_DEFAULT_READ_TIMEOUT 110
  #define RMVK_READ_DELAY 200
  #define RMVK_BAUD_RATE 9600
  #define RMVK_TASK_DELAY 5000
  uart_port_t RMVK_UART = UART_NUM_1;


  /*
    1. "АТ+VI?" - возвращает напряжение в сети
    2. "АТ+VO?" - возвращает реальное напряжение на выходе РМВ-К
    3. "АТ+VS?" - возвращает значение, которое установлено на выходе
    4. "АТ+VS=xxx" - устанавливает напряжение на выходе: если успешно, то возвращает посланное значение,
    иначе возвращает "error". Напряжение вводится ххх - т.е. обязательно с незначащими нулями, например -
    АТ+VS=087. Напряжение изменяется в рабочем диапазоне от 40 до 230 Вольт.
    5. "АТ+ON?" - возвращает ON, если разрешено напряжение на выходе и OFF, если выход отключен.
    6. "АТ+ON={1 or 0} - команда управляет напряжением на выходе: =0 выключает , =1 включает. Команда
    выключения блокирует РМВ-К, на дисплее OFF. Блокировка сохраняется даже после отключения и
    повторного включения РМВ-К. Для снятия блокировки необходимо подать команду АТ+ON=1 или нажать
    кнопки К1.
    7. "AT+SM=х", где х от 0 до 9 (select from memory). Ответ "ок". Выбрать установленное напряжение из
    ячейки памяти.
    7
  */

  StaticSemaphore_t xSemaphoreBuffer;
  rmvk_t rmvk;

  uint8_t RMVK_cmd(const char* cmd, rmvk_res_t res) {
    size_t cmd_len;
    int len;
    cmd_len = strlen(cmd);
    char cmd_buf[cmd_len + 2];
    const char * pc = cmd_buf;
    uint8_t buf[10];//10*8=80 бит, при плохом раскладе  получим за 80*1000/9600 ~ 8,5 ms
    String s;
    if ( xSemaphore != NULL )
    {
      DbgMsg("cmd = " + String(cmd),1);
      if ( xSemaphoreTake( xSemaphore, ( TickType_t ) ((RMVK_DEFAULT_READ_TIMEOUT * 7) / portTICK_RATE_MS)) == pdTRUE)
      {
        sprintf(cmd_buf, "%s\r", cmd);
        uart_flush(RMVK_UART);
        cmd_len = uart_write_bytes(RMVK_UART, pc, strlen(pc));
        len = uart_read_bytes(RMVK_UART, buf, sizeof(buf), RMVK_DEFAULT_READ_TIMEOUT / portTICK_RATE_MS);
        buf[len] = '\0';
        DbgMsg("buf = " + String((const char *)&buf),1);
        xSemaphoreGive( xSemaphore );
        if (len > 0) {
          rmvk.conn = 1;
          switch (res)
          {
            case RMVK_INT:
              return atoi((const char *)&buf);
              break;
            case RMVK_OK:
              if (strncmp((const char *)&buf, "OK", 2) == 0) return 1;
              else return 0;
              break;
            case RMVK_ON:
              if (strncmp((const char *)&buf, "ON", 2) == 0) return 1;
              else return 0;
              break;
            default:
              return atoi((const char *)&buf);
              break;
          }
        }
        else
        {
          rmvk.conn = 0;
        }

        return rmvk.conn;
      }
      else
      {
        rmvk.conn = 0;
        DbgMsg("UART RMVK  = BUSY",1);//увидим что не получили семафор
      }
    }
    return rmvk.conn;
  }

  uint16_t RMVK_get_in_voltge() {
    char cmd[] = "AT+VI?";
    rmvk.VI = RMVK_cmd(cmd, RMVK_INT);
    return rmvk.VI;
  }

  uint16_t RMVK_get_out_voltge() {
    char cmd[] = "AT+VO?";
    //    char cmd[]="AT+VI?";
    rmvk.VO = RMVK_cmd(cmd, RMVK_INT);
    return rmvk.VO;
  }

  uint16_t RMVK_get_store_out_voltge() {
    char cmd[] = "AT+VS?";
    rmvk.VS = RMVK_cmd(cmd, RMVK_INT);
    return rmvk.VS;
  }
  bool RMVK_get_state() {
    char cmd[] = "AT+ON?";
    rmvk.on = RMVK_cmd(cmd, RMVK_ON) > 0;
    return rmvk.on;
  }
  uint16_t RMVK_set_out_voltge(uint16_t voltge) {
    uint16_t ret;
    char cmd[20];
    if (!rmvk.on)RMVK_set_on(1);
    if (voltge > MAX_VOLTAGE)voltge = MAX_VOLTAGE;
    sprintf(cmd, "AT+VS=%03d", voltge);
    ret = RMVK_cmd(cmd, RMVK_INT);
    return ret;
  }


  bool RMVK_get_conn() {
    return (rmvk.conn > 0);
  }

  uint16_t RMVK_set_on(uint16_t state) {
    uint16_t ret;
    char cmd[20];
    if (state > 0)state = 1;
    else state = 0;
    sprintf(cmd, "AT+ON=%d", state);
    ret = RMVK_cmd(cmd, RMVK_ON);
    return ret;
  }
  uint16_t RMVK_select_mem(uint16_t sm) {
    int ret;
    char cmd[20];
    sprintf(cmd, "AT+SM=%d", sm);
    ret = RMVK_cmd(cmd, RMVK_OK);
    return ret;
  }

  void rmvk_read()
  {
    rmvk.VI = RMVK_get_in_voltge();
    vTaskDelay(RMVK_READ_DELAY / portTICK_PERIOD_MS);
    rmvk.VO = RMVK_get_out_voltge();
    vTaskDelay(RMVK_READ_DELAY / portTICK_PERIOD_MS);
    rmvk.VS = RMVK_get_store_out_voltge();
    vTaskDelay(RMVK_READ_DELAY / portTICK_PERIOD_MS);
    rmvk.on = RMVK_get_state() > 0;
  }

  uint8_t get_rmvk_state() {
    return rmvk.on;
  }
  uint8_t get_rmvk_conn() {
    return rmvk.conn;
  }


  void rmvkFn(void* arg) {
    rmvk_read();
  }

  void RMVK_init(void) {
    uart_config_t uart_config = {
      .baud_rate = RMVK_BAUD_RATE,
      .data_bits = UART_DATA_8_BITS,
      .parity = UART_PARITY_DISABLE,
      .stop_bits = UART_STOP_BITS_1,
      .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
      .rx_flow_ctrl_thresh = 122,
    };
    uart_param_config(RMVK_UART, &uart_config);
    uart_set_pin(RMVK_UART, TXD2, RXD2, -1, -1);
    uart_driver_install(RMVK_UART, BUF_SIZE * 2, BUF_SIZE * 2,  0, NULL, 0);

    xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer );
    xSemaphoreGive( xSemaphore );
    RMVK_set_on(0);
    rmvk_read();
  }
//==========================================================Universal Protocol (Stab AVR)
  class PowerStabilizer {
  private:
      static const uint8_t HEX_CONV_ERROR = 255;
      static const uint8_t MODE_NORMAL = 0b00;
      static const uint8_t MODE_BOOST = 0b01;
      static const uint8_t MODE_OFF = 0b10;
      static const uint8_t MODE_EMERGENCY = 0b11;
      static const uint8_t ERROR_NETWORK_VOLTAGE = 0b100;
      static const uint8_t ERROR_INSUFFICIENT_V = 0b1000;
      static const uint8_t PARAM_VOLTAGE = 0b01;
      static const uint8_t PARAM_CURRENT = 0b10;
      static const uint8_t PARAM_POWER = 0b11;
      
      static const uint8_t REPORT_LENGTH = 14;
      char reportBuffer[REPORT_LENGTH];
      uint8_t bufferIndex = 0;
      uint8_t lastMode = 0;
      uint16_t lastMainValue = 0;
      uint16_t lastAuxValue = 0;
      uint8_t lastDataComposition = 0;
      uint32_t lastUpdateTime = 0;
      bool deviceConnected = false;

      uint8_t charToHex(const char c);
      bool parseReport();
      
  public:
      void begin(uint32_t baudRate = 9600);
      bool update();
      
      // Методы получения данных
      bool isNormalMode();
      bool isBoostMode();
      bool isOffMode();
      bool isEmergencyOff();
      bool hasNetworkVoltage();
      bool voltageIsSufficient();
      uint16_t getMainValue();
      float getMainVoltage();
      float getMainCurrent();
      float getMainPower();
      uint16_t getAuxValue();
      float getAuxVoltage();
      float getAuxCurrent();
      float getAuxPower();
      float getNetworkVoltage();
      float getLoadResistance();
      float getRatedPower();
      uint32_t getLastUpdateTime();
      bool isDataFresh(uint32_t timeout = 2000);
      bool isConnected() { return deviceConnected; }

      // Методы управления
      void setNormalMode();
      void setBoostMode();
      void setOffMode();
      void setPower(uint16_t power);
      void requestAuxParameter(uint8_t paramCode);
  };
 // Реализация методов класса PowerStabilizer
  uint8_t PowerStabilizer::charToHex(const char c) {
      if (c >= '0' && c <= '9') return c - '0';
      if (c >= 'A' && c <= 'F') return c - 'A' + 10;
      if (c >= 'a' && c <= 'f') return c - 'a' + 10;
      return HEX_CONV_ERROR;
  }
  bool PowerStabilizer::parseReport() {
      if (bufferIndex != REPORT_LENGTH || reportBuffer[0] != 'T') {
          return false;
      }
      for (uint8_t i = 1; i < REPORT_LENGTH-1; i++) {
          if (charToHex(reportBuffer[i]) == HEX_CONV_ERROR) {
              return false;
          }
      }
      lastMode = (charToHex(reportBuffer[3]) << 4) | charToHex(reportBuffer[4]);
      lastMainValue = ((uint32_t)charToHex(reportBuffer[5]) << 12) |
                    ((uint32_t)charToHex(reportBuffer[6]) << 8) |
                    ((uint32_t)charToHex(reportBuffer[7]) << 4) |
                    charToHex(reportBuffer[8]);
      lastDataComposition = (charToHex(reportBuffer[1]) << 4) | charToHex(reportBuffer[2]);
      lastAuxValue = ((uint32_t)charToHex(reportBuffer[9]) << 12) |
                    ((uint32_t)charToHex(reportBuffer[10]) << 8) |
                    ((uint32_t)charToHex(reportBuffer[11]) << 4) |
                    charToHex(reportBuffer[12]);
      lastUpdateTime = millis();
      return true;
  }
  void PowerStabilizer::begin(uint32_t baudRate) {
      Serial2.begin(baudRate);
  }
  bool PowerStabilizer::update() {
      while (Serial2.available() > 0) {
          char c = Serial2.read();
          if (bufferIndex == 0 && c != 'T') {
              continue;
          }
          reportBuffer[bufferIndex++] = c;
          if (c == 0x0D && bufferIndex == REPORT_LENGTH) {
              bool result = parseReport();
              deviceConnected = result; // Обновляем статус подключения
              bufferIndex = 0;
              return result;
          }
          if (bufferIndex >= REPORT_LENGTH) {
              bufferIndex = 0;
          }
      }
      return false;
  }
  bool PowerStabilizer::isNormalMode() { return (lastMode & 0b11) == MODE_NORMAL; }
  bool PowerStabilizer::isBoostMode() { return (lastMode & 0b11) == MODE_BOOST; }
  bool PowerStabilizer::isOffMode() { return (lastMode & 0b11) == MODE_OFF; }
  bool PowerStabilizer::isEmergencyOff() { return (lastMode & 0b11) == MODE_EMERGENCY; }
  bool PowerStabilizer::hasNetworkVoltage() { return !(lastMode & ERROR_NETWORK_VOLTAGE); }
  bool PowerStabilizer::voltageIsSufficient() { return !(lastMode & ERROR_INSUFFICIENT_V); }
  uint16_t PowerStabilizer::getMainValue() { return lastMainValue; }
  float PowerStabilizer::getMainVoltage() { return (lastDataComposition & 0b11) == PARAM_VOLTAGE ? lastMainValue : 0; }
  float PowerStabilizer::getMainCurrent() { return (lastDataComposition & 0b11) == PARAM_CURRENT ? lastMainValue : 0;}
  float PowerStabilizer::getMainPower() { return (lastDataComposition & 0b11) == PARAM_POWER ? lastMainValue : 0; }
  uint16_t PowerStabilizer::getAuxValue() { return lastAuxValue; }
  float PowerStabilizer::getAuxVoltage() { return ((lastDataComposition >> 2) & 0b111111) == 0b000001 ? lastAuxValue : 0; }
  float PowerStabilizer::getAuxCurrent() { return ((lastDataComposition >> 2) & 0b111111) == 0b000010 ? lastAuxValue : 0; }
  float PowerStabilizer::getAuxPower() { return ((lastDataComposition >> 2) & 0b111111) == 0b000011 ? lastAuxValue : 0; }
  float PowerStabilizer::getNetworkVoltage() { return ((lastDataComposition >> 2) & 0b111111) == 0b000101 ? lastAuxValue : 0; }
  float PowerStabilizer::getLoadResistance() { return ((lastDataComposition >> 2) & 0b111111) == 0b000110 ? lastAuxValue : 0; }
  float PowerStabilizer::getRatedPower() { return ((lastDataComposition >> 2) & 0b111111) == 0b000111 ? lastAuxValue : 0; }
  uint32_t PowerStabilizer::getLastUpdateTime() { return lastUpdateTime; }
  bool PowerStabilizer::isDataFresh(uint32_t timeout) {   return (millis() - lastUpdateTime) <= timeout; }
  void PowerStabilizer::setNormalMode() {      Serial2.write("M0\r");  }
  void PowerStabilizer::setBoostMode() {Serial2.write("M1\r");  }
  void PowerStabilizer::setOffMode() { Serial2.write("M2\r"); }
  void PowerStabilizer::setPower(uint16_t power) {
      char cmd[6];
      cmd[0] = 'P';
      uint8_t nibble1 = (power >> 12) & 0x0F;
      uint8_t nibble2 = (power >> 8) & 0x0F;
      uint8_t nibble3 = (power >> 4) & 0x0F;
      uint8_t nibble4 = power & 0x0F;
      cmd[1] = nibble1 < 10 ? nibble1 + '0' : nibble1 - 10 + 'A';
      cmd[2] = nibble2 < 10 ? nibble2 + '0' : nibble2 - 10 + 'A';
      cmd[3] = nibble3 < 10 ? nibble3 + '0' : nibble3 - 10 + 'A';
      cmd[4] = nibble4 < 10 ? nibble4 + '0' : nibble4 - 10 + 'A';
      cmd[5] = '\r';
      Serial2.write(cmd, 6);
  }
  void PowerStabilizer::requestAuxParameter(uint8_t paramCode) {
      char cmd[4];
      cmd[0] = 'N';
      uint8_t nibble1 = paramCode >> 4;
      uint8_t nibble2 = paramCode & 0x0F;
      cmd[1] = nibble1 < 10 ? nibble1 + '0' : nibble1 - 10 + 'A';
      cmd[2] = nibble2 < 10 ? nibble2 + '0' : nibble2 - 10 + 'A';
      cmd[3] = '\r';
      Serial2.write(cmd, 4);
  }
  PowerStabilizer UP_Reg;

//==========================================================Modbus
 #include <ModbusMaster.h>
 ModbusMaster inverter;
  int modbusAddress = 10;
  int modbus_baudRate = 9600;
  int registerWriteAddr = 0x2000;
  int registerReadAddr = 0x3000;
  int frequencyRegister = 0x2001;
  bool startInverter() {
    uint8_t result = inverter.writeSingleRegister(registerWriteAddr, 0x0001);
    return (result == inverter.ku8MBSuccess);
  }

  bool stopInverter() {
    uint8_t result = inverter.writeSingleRegister(registerWriteAddr, 0x0000);
    return (result == inverter.ku8MBSuccess);
  }

  bool ModbusSetPower(float percent) {
    float freq = 50.0 * (percent / 100.0);
    uint16_t value = freq * 100; // 0.01 Гц
    uint8_t result = inverter.writeSingleRegister(frequencyRegister, value);
    return (result == inverter.ku8MBSuccess);
  }

  bool writeRegister(int addr, int value) {
    uint8_t result = inverter.writeSingleRegister(addr, value);
    return (result == inverter.ku8MBSuccess);
  }

  uint16_t readRegister(int addr) {
    uint8_t result = inverter.readHoldingRegisters(addr, 1);
    if (result == inverter.ku8MBSuccess) {
      return inverter.getResponseBuffer(0);
    }
    return 0xFFFF;
  }

//Основная логика================================================================
  void StatusKVIC(); //Регулятор KVIC
  void Sort_Response(String& resp, uint8_t stage);
  void StatusStabAVR();
  void StatusRMVK();
  void StatusUniProtocol();
  void StatusMODBUS_RTU();
  String resp;
  void InitPower() {
    if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
        if (Serial2) {
          Serial2.end();
          delay(50);
          Serial2.flush();
      }
      switch (SamSetup.PwrType) { 
      case KVIC:{ 
          DbgMsg("Init KVIC",1);
          Serial2.setTimeout(300);
          Serial2.setRxBufferSize(0);
          Serial2.begin(38400, SERIAL_8N1, RXD2, TXD2);
        break; }
      case KVIC9600:{
          DbgMsg("Init KVIC9600",1);
          Serial2.setTimeout(300);
          Serial2.setRxBufferSize(0);
          Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
        break; }
      case RMVK: {// работаем с RMVK
          DbgMsg("Init RMVK",1);
          Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
          Serial2.end();
          RMVK_init();
        break;}
      case STAB_AVR: {//Если SEM_AVR иницииурем порт
        DbgMsg("Init STAB_AVR",1);
        Serial2.setTimeout(500);
        Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
        break;}
      case UNI_PROTOCOL: {
        DbgMsg("Init STAB_AVR Uni protocol",1);
        UP_Reg.begin();
        break;}
        case MODBUS_RTU: {//Если  иницииурем порт
        DbgMsg("Init MODBUS_RTU",1);
        Serial2.setTimeout(500);
        Serial2.begin(modbus_baudRate, SERIAL_8N1, RXD2, TXD2);
        inverter.begin(modbusAddress, Serial2);
        break;}
      case PWM: {
        DbgMsg("Init PWM",1);
        Power_PWM.attachPin(TXD2, 3, 1, 50, 10);       // channel 3, timer 1//10- разрядность 0-1023, 50-частота, TXD2 - пин TX Serial2
        break;}
        default:{
          
        break;}
    }
      switch (SamSetup.PwrType) { // единицы измерений регуляторов
      case KVIC: case KVIC9600: case RMVK: {
          PwrMSG_str = "Напряжение";
          PwrSign = "В";
        break;}
      case STAB_AVR: case UNI_PROTOCOL: case MODBUS_RTU: case PWM:{
          PwrMSG_str = "Мощность";
          PwrSign = "Вт";
        break;}
      default:{
          PwrMSG_str = "";
        break;}
    }
      switch (SamSetup.PwrType) { // множитель инкрементов регуляторов
      case KVIC: case KVIC9600: {PwrFactor = 1; break;}
      case RMVK: {PwrFactor = 2; break;}
      case STAB_AVR: case UNI_PROTOCOL: case MODBUS_RTU: case PWM: {PwrFactor = 20; break;}
      default: {PwrFactor = 0; break;}
    }
    xSemaphoreGive(xSemaphoreAVR);
    }
  }
  void triggerPowerStatus(void *parameter) {// Чтение состояния регуляторов, запускается как отдельный таск
      while (true) {
          switch (SamSetup.PwrType) { // единицы измерений регуляторов
          case KVIC: case KVIC9600:
              {StatusKVIC();
            break;}
          case RMVK:
              {StatusRMVK();
            break; }     
          case STAB_AVR:
              {StatusStabAVR();
            break;}
          case UNI_PROTOCOL:
              {StatusUniProtocol();
            break;}
          case MODBUS_RTU:
              {StatusMODBUS_RTU();
            break;}
          case PWM: {
              current_power_volt = target_power_volt; //при ШИМе проверять нечего
              vTaskDelay(2000 / portTICK_PERIOD_MS);
          break;}  
          default:
              vTaskDelay(2000 / portTICK_PERIOD_MS);
            break;
          }
      }
  }
  void StatusKVIC() { //Регулятор KVIC
    static int i;
      if (PowerOn) {
        if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
        Serial2.flush();
        vTaskDelay(300 / portTICK_PERIOD_MS);
        if (Serial2.available()) {
          resp = Serial2.readStringUntil('\r');
          i = resp.indexOf("T");
          if (i < 0) {
            vTaskDelay(50 / portTICK_PERIOD_MS);
            resp = Serial2.readStringUntil('\r');
          }
          resp = resp.substring(i, resp.length());
          DbgMsg("RESP_T =" + resp.substring(0, 1),1);
          if (resp.substring(0, 1) == "T" && resp.length() == 8 ) {
            resp = resp.substring(1, 9);
          DbgMsg("RESP_F =" + resp.substring(1, 7),1);
          DbgMsg("RESP_MODE =" + resp.substring(6, 7),1);
            int cpv = hexToDec(resp.substring(0, 3));
            //Если напряжение больше 249 или меньше 10 - не корректно получено значение от регулятора, оставляем старое значение
            if (cpv > 30 && cpv < 2490) {
              current_power_volt = cpv / 10.0F;
              target_power_volt = hexToDec(resp.substring(3, 6)) / 10.0F;
              current_power_mode = resp.substring(6, 7);
              vTaskDelay(100 / portTICK_PERIOD_MS);
            }
          }
        }
        xSemaphoreGive(xSemaphoreAVR);
      } 
      }
      else {
        vTaskDelay(400 / portTICK_PERIOD_MS);
      }
  }
  void StatusRMVK() { //  Регулятор RVMK
  uint16_t v;
      if (PowerOn) {               
        if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {              
        current_power_volt = RMVK_get_out_voltge();
        vTaskDelay(RMVK_READ_DELAY / portTICK_PERIOD_MS);
        v = RMVK_get_store_out_voltge();
        vTaskDelay(RMVK_READ_DELAY / portTICK_PERIOD_MS);
        rmvk.on = RMVK_get_state() > 0;
        if (v != 0) {
          target_power_volt = v;
        }
      }
      xSemaphoreGive(xSemaphoreAVR);
      }
      vTaskDelay(RMVK_READ_DELAY / 5 / portTICK_PERIOD_MS);
  }
  void StatusStabAVR() {  //Регулятор SТAВ AVR
  static  uint16_t v;
      if (PowerOn) {         
        if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((RMVK_DEFAULT_READ_TIMEOUT) / portTICK_RATE_MS)) == pdTRUE) {
          vTaskDelay(RMVK_READ_DELAY / 10 / portTICK_PERIOD_MS);
          Serial2.flush();
          vTaskDelay(10 / portTICK_RATE_MS);
          Serial2.print("АТ+SS?\r");
          for (int i = 0; i < 2; i++) {
            vTaskDelay(RMVK_READ_DELAY / portTICK_RATE_MS);
            if (Serial2.available()) {
              resp = Serial2.readStringUntil('\r');
              Sort_Response(resp, 0);
              //DbgMsg("CPM=" + current_power_mode,1);
              break;
            }
          }
          xSemaphoreGive(xSemaphoreAVR);
        }
        vTaskDelay(RMVK_READ_DELAY / 5 / portTICK_PERIOD_MS);
        if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((RMVK_DEFAULT_READ_TIMEOUT) / portTICK_RATE_MS)) == pdTRUE) {
          vTaskDelay(RMVK_READ_DELAY / 10 / portTICK_PERIOD_MS);
          Serial2.flush();
          vTaskDelay(10 / portTICK_RATE_MS);
          Serial2.print("АТ+VO?\r");
          for (int i = 0; i < 2; i++) {
            vTaskDelay(RMVK_READ_DELAY / portTICK_RATE_MS);
            if (Serial2.available()) {
              resp = Serial2.readStringUntil('\r');
              Sort_Response(resp, 1);
              //DbgMsg("CPV=" + resp,1);
              break;
            }
          }
          xSemaphoreGive(xSemaphoreAVR);
        }
        vTaskDelay(RMVK_READ_DELAY / 5 / portTICK_PERIOD_MS);
        if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((RMVK_DEFAULT_READ_TIMEOUT) / portTICK_RATE_MS)) == pdTRUE) {
          vTaskDelay(RMVK_READ_DELAY / 10 / portTICK_PERIOD_MS);
          Serial2.flush();
          vTaskDelay(10 / portTICK_RATE_MS);
          Serial2.print("АТ+VS?\r");
          for (int i = 0; i < 2; i++) {
            vTaskDelay(RMVK_READ_DELAY / portTICK_RATE_MS);
            if (Serial2.available()) {
              resp = Serial2.readStringUntil('\r');
              //DbgMsg("TPV=" + resp,1);
              Sort_Response(resp, 2);
              break;
            }
          }
          xSemaphoreGive(xSemaphoreAVR);
        }
        vTaskDelay(RMVK_READ_DELAY / 5 / portTICK_PERIOD_MS);
      }
      vTaskDelay(RMVK_READ_DELAY / 5 / portTICK_PERIOD_MS);
  }
  void StatusUniProtocol() {
      if (PowerOn) {               
          if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {              
              // Обновляем данные через класс PowerStabilizer
              bool dataUpdated = UP_Reg.update();
              rmvk.conn = UP_Reg.isConnected();
              
              if (dataUpdated && UP_Reg.isDataFresh()) {
                  // Получаем основные параметры из стабилизатора
                  if (UP_Reg.getMainPower() > 0) {
                      current_power_volt = UP_Reg.getMainPower();
                  } else if (UP_Reg.getMainVoltage() > 0) {
                      current_power_volt = UP_Reg.getMainVoltage();
                  } else {
                      current_power_volt = UP_Reg.getMainValue();
                  }
                  
                  // Получаем целевое значение
                  if (UP_Reg.getAuxPower() > 0) {
                      target_power_volt = UP_Reg.getAuxPower();
                  } else if (UP_Reg.getAuxVoltage() > 0) {
                      target_power_volt = UP_Reg.getAuxVoltage();
                  } else {
                      target_power_volt = UP_Reg.getAuxValue();
                  }
                  
                  // Определяем режим работы
                  if (UP_Reg.isOffMode() || UP_Reg.isEmergencyOff()) {
                      rmvk.on = false;
                      current_power_mode = POWER_SLEEP_MODE;
                  } else {
                      rmvk.on = true;
                      current_power_mode = UP_Reg.isBoostMode() ? POWER_SPEED_MODE : POWER_WORK_MODE;
                  }
              } else if (!UP_Reg.isDataFresh(5000)) {
                  // Данные устарели
                  DbgMsg("Нет связи со стабилизатором.",1);
                  rmvk.conn = 0;
              }
              xSemaphoreGive(xSemaphoreAVR);
          }
      } else {
          // Сброс значений при выключенном питании
          current_power_volt = 0;
          target_power_volt = 0;
          current_power_mode = POWER_SLEEP_MODE;
          rmvk.on = false;
      }
      vTaskDelay(RMVK_READ_DELAY / 5 / portTICK_PERIOD_MS);
  }
  void StatusMODBUS_RTU() {
    vTaskDelay(2000 / portTICK_PERIOD_MS);//код
  }
  void Sort_Response(String& resp, uint8_t stage){ // Сортировака ответов для 
    uint16_t v;
  if (resp.length()>0) {
      switch (resp[0]) { // Сортируем ответы по префиксу
          case 'M':
            resp.remove(0, 1);
            current_power_mode =resp;
            break;
          case 'C':
            resp.remove(0, 1);
            current_power_volt = resp.toInt();
            break;
          case 'T':
            resp.remove(0, 1);
            v = resp.toInt();
            if (v != 0)  target_power_volt = v;
            break;
          default://вариант для старых STAB AVR, всё равно работать должно
              switch (stage) { 
                    case 0:
                    current_power_mode =resp;
                    break;
                    case 1:
                    current_power_volt = resp.toInt();
                    break;
                    case 2:
                    v = resp.toInt();
                    if (v != 0)  target_power_volt = v;
                    break;
              }
            break;
        }
    }
  }
  unsigned int hexToDec(String hexString) {
    unsigned int decValue = 0;
    int nextInt;

    for (uint8_t i = 0; i < hexString.length(); i++) {

      nextInt = int(hexString.charAt(i));
      if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9);
      if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15);
      if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15);
      nextInt = constrain(nextInt, 0, 15);

      decValue = (decValue * 16) + nextInt;
    }

    return decValue;
  }
  void get_current_power() { //получаем текущие параметры работы регулятора напряжения
    if (!PowerOn) {
      current_power_volt = 0;
      target_power_volt = 0;
      current_power_mode = "N";
      current_power_p = 0;
      return;
    }
    switch (SamSetup.PwrType) { // единицы измерений регуляторов
    case KVIC: case KVIC9600: case RMVK:
    {current_power_p = target_power_volt;
    break;}
    case PWM: {
      current_power_volt = target_power_volt; //при ШИМе проверять нечего
      current_power_p = target_power_volt;
    break;  
    }
    default:
    {current_power_p = current_power_volt * current_power_volt / SamSetup.HeaterResistant;
    break;}
        }
  }
  void set_current_power(float Volt) { //устанавливаем напряжение для регулятора напряжения
    if (!PowerOn) return;
    DbgMsg("Set current power =" + (String)Volt,1);
    uint8_t minV = (SamSetup.PwrType >= STAB_AVR) ? 40 : 100;
    if (Volt < minV) {
      set_power_mode(POWER_SLEEP_MODE);
      return;
    } else {
      set_power_mode(POWER_WORK_MODE);
    }
    target_power_volt = Volt;
    vTaskDelay(100 / portTICK_PERIOD_MS);
    //DbgMsg("Ждем семафор...",1);
  if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((1000) / portTICK_RATE_MS)) == pdTRUE) {
    DbgMsg("Семафор получен",1);
    switch (SamSetup.PwrType) { // формирование и отправка команды
      case KVIC: case KVIC9600:
        {String hexString = String((int)(Volt * 10), HEX);
        Serial2.print("S" + hexString + "\r");
        break;}
      case RMVK:
        {RMVK_set_out_voltge(Volt);   
        break;}      
      case STAB_AVR:
        {  String Cmd;
        int V = Volt;
        if (V < 100) Cmd = "0";
        else
        Cmd = "";
        Cmd = Cmd + (String)V;
        Serial2.print("АТ+VS=" + Cmd + "\r");//rus
        //Serial2.print("AT+VS=" + Cmd + "\r");//eng
        break;}
      case UNI_PROTOCOL:  
      // Устанавливаем мощность для Universal Protocol
        /*// Устанавливаем режим работы, если выключен
        if (!UP_Reg.isNormalMode() && !UP_Reg.isBoostMode()) {
            UP_Reg.setNormalMode();
        }*/
        // Устанавливаем мощность
        UP_Reg.setPower(Volt);
        DbgMsg("UNI_PROTOCOL: Set power to " + String(Volt),1);
        break;
      case MODBUS_RTU:
        ModbusSetPower(Volt);
        break;  
      case PWM: {
        Power_PWM.write(map(Volt, 0, MaxPower, 0, 1023));
        break; }
    }
    target_power_volt = Volt;
    xSemaphoreGive(xSemaphoreAVR);
    } else  DbgMsg("Недождались семафора!",1);
  }
  void set_power_mode(String Mode) {
    if (current_power_mode == Mode) return;
    current_power_mode = Mode;
    vTaskDelay(50 / portTICK_PERIOD_MS);
    DbgMsg("Set power mode= " + Mode,1);
    if (Mode == POWER_SLEEP_MODE) {
    if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((1000) / portTICK_RATE_MS)) == pdTRUE)  { 
      switch (SamSetup.PwrType) { // формирование и отправка команды
        case KVIC:  case KVIC9600:
          {Serial2.print("M" + Mode + "\r"); //kvic
          break;}
        case RMVK:
          {RMVK_set_on(0);// rmvk
          break; }     
        case STAB_AVR:
          { Serial2.print("АТ+ON=0\r");
          break;}
        case UNI_PROTOCOL:  
        // Выключаем стабилизатор через Universal Protocol
          UP_Reg.setOffMode();
          DbgMsg("UNI_PROTOCOL: Set to OFF mode",1);
          break;
        case MODBUS_RTU:
          stopInverter();
          break; 
        case PWM: {
          Power_PWM.write(0);
        break; } 
      }
      xSemaphoreGive(xSemaphoreAVR);
    }
    } else if (Mode == POWER_SPEED_MODE) {
      vTaskDelay(SamSetup.PwStartPause / portTICK_PERIOD_MS);//настраиваемая задержка для включения регулятора
      if (xSemaphoreTake(xSemaphoreAVR, (TickType_t)((1000) / portTICK_RATE_MS)) == pdTRUE)  {
      switch (SamSetup.PwrType) { // формирование и отправка команды
        case KVIC: case KVIC9600:
          {Serial2.print("M" + Mode + "\r"); //kvic
          break;}
        case RMVK:
          {RMVK_set_on(1); // rmvk
          RMVK_set_out_voltge(MAX_VOLTAGE);
          break;}      
        case STAB_AVR:
          { //Serial2.flush();
            Serial2.print("АТ+ON=1\r");
          break;}
        case UNI_PROTOCOL:  
            UP_Reg.setBoostMode();
            /*// Устанавливаем максимальную мощность
            UP_Reg.setPower(MaxPower);
            vTaskDelay(50 / portTICK_PERIOD_MS);*/
            DbgMsg("UNI_PROTOCOL: Set to BOOST mode with max power",1);
          break;
        case MODBUS_RTU:
            startInverter();
            ModbusSetPower(100);
          break;
        case PWM: {
          Power_PWM.write(1023);
        break; }
      }
      xSemaphoreGive(xSemaphoreAVR);
      }
    }
  }
  void check_power_error() { //Проверим, что заданное напряжение/мощность не сильно отличается от реального (наличие связи с регулятором, пробой семистора)
    if (SamSetup.CheckPower && current_power_mode == POWER_WORK_MODE && current_power_volt > 0 && abs((current_power_volt - target_power_volt) / target_power_volt) > 0.1) {//10%
      power_err_cnt++;
      //if (power_err_cnt == 2) SendMsg(("Ошибка регулятора!"), ALARM_MSG);
      if (power_err_cnt > 6) set_current_power(target_power_volt);
      if (power_err_cnt > 12) {
        delay(1000);  //Пауза на всякий случай, чтобы прошли все другие команды
        set_buzzer(true);
        set_power(false);
        SendMsg(("Аварийное отключение! Ошибка управления нагревателем."), ALARM_MSG);
      }
    } else
      power_err_cnt = 0;
  }
  void set_power(bool On) {
    if (alarm_event && On) {
      return;
    }
    PowerOn = On;
    if (On) {
      digitalWrite(RELE_CHANNEL1, SamSetup.rele1);
      power_text_ptr = (char *)"OFF";
      if (SamSetup.PwrType != NO_POVER_REG) {

          set_power_mode(POWER_SPEED_MODE);
      } else {
          current_power_mode = POWER_SPEED_MODE;
          digitalWrite(RELE_CHANNEL4, SamSetup.rele4);
      }
    } else {
      digitalWrite(RELE_CHANNEL4, !SamSetup.rele4);
      acceleration_heater = false;
      if (SamSetup.PwrType != NO_POVER_REG) {
        vTaskDelay(700 / portTICK_PERIOD_MS);
        set_power_mode(POWER_SLEEP_MODE);
        vTaskDelay(200 / portTICK_PERIOD_MS);
      } else {
        current_power_mode = POWER_SLEEP_MODE;
      }
      power_text_ptr = (char *)"ON";
      sam_command_sync = SAMOVAR_RESET;
      digitalWrite(RELE_CHANNEL1, !SamSetup.rele1);
    }
  }
  float toPower(float value) { // конвертер в мощность ( V | W ) => W
  if (SamSetup.PwrType >= STAB_AVR) 
      return value; // если нечто иное возвращаем неизменным
    else {
        float R = SamSetup.HeaterResistant > 1 ? SamSetup.HeaterResistant : 15;
        return value * value / R; //если от kvic или RVMK пересчитываем в P
    }
  }
  float SQRT(float num) { // псевдо корень
    if (num < 0) {
      return -1.0f;
    }
    if (num == 0) {
        return 0.0f;
    }
    float guess = num;
    float prev_guess;
    do {
      prev_guess = guess;
      guess = (guess + num / guess) / 2;
    } while (abs(guess - prev_guess) > 0.01);
    return guess;
  }
  float fromPower(float value) { // конвертер из мощности: W => ( V | W )
  if (SamSetup.PwrType >= STAB_AVR)
      return value;
  else {
        static float prev_W = 0.0f;
        static float prev_V = 0.0f;
        if (value != prev_W) {
            prev_W = value;
            float R = SamSetup.HeaterResistant > 1 && SamSetup.HeaterResistant < 200 ? SamSetup.HeaterResistant : 15;
            prev_V = SQRT(value * R);
        }
        return prev_V;
  }
  }
