//-------------------------------------Системные и отладочные
void printTaskMemoryUsage() {
    Serial.println("\n=== ИСПОЛЬЗОВАНИЕ ПАМЯТИ ЗАДАЧАМИ ===");
    
    TaskStatus_t* taskStatus = nullptr;
    uint32_t taskCount = uxTaskGetNumberOfTasks();
    
    taskStatus = (TaskStatus_t*)pvPortMalloc(taskCount * sizeof(TaskStatus_t));
    
    if (taskStatus) {
        taskCount = uxTaskGetSystemState(taskStatus, taskCount, NULL);
        
        for (uint32_t i = 0; i < taskCount; i++) {
            Serial.printf("Задача: %-15s | Stack free: %4d\n",
                         taskStatus[i].pcTaskName,
                         taskStatus[i].usStackHighWaterMark);
        }
        
        vPortFree(taskStatus);
    }
}
  void printDetailedPWMInfo() {
    Serial.println("=== Detailed PWM Info ===");
    
    String pwmObjects[] = {"Servo", "Culler", "Power", "Pump"};
    ESP32PWM* pwmArray[] = {&servo_pwm, &Culler_PWM, &Power_PWM, &pump_pwm};
    
    for (int i = 0; i < 4; i++) {
        bool isActive = false;
        int channel = -1;
        int pin = -1;
        try {
            channel = pwmArray[i]->getChannel();
            pin = pwmArray[i]->getPin();
            // Если канал валидный и пин установлен, считаем активным
            isActive = (channel >= 0) && (pin >= 0);
        } catch (...) {
            isActive = false;
        }
        
        if (isActive) {
            Serial.printf("%s: pin=%d, channel=%d, timer=%d, bits=%d, freq=%.1f, duty=%d/%d\n",
                        pwmObjects[i].c_str(),
                        pin,
                        channel,
                        pwmArray[i]->getTimer(),
                        pwmArray[i]->getResolutionBits(),
                        pwmArray[i]->readFreq(),
                        pwmArray[i]->read(),
                        (1 << pwmArray[i]->getResolutionBits()));
        } else {
            Serial.println(pwmObjects[i] + ": NOT ATTACHED (channel=" + String(channel) + ", pin=" + String(pin) + ")");
        }
    }
    
    /*// Альтернативный способ проверки через статические данные класса
    Serial.println("=== Alternative Channel Check ===");
    for (int i = 0; i < NUM_PWM; i++) {
        if (ESP32PWM::ChannelUsed[i] != NULL) {
            ESP32PWM* pwm = ESP32PWM::ChannelUsed[i];
            Serial.printf("Channel %d: pin=%d, timer=%d\n", 
                         i, pwm->getPin(), pwm->getTimer());
        }
    }*/
    
    Serial.println("Channels remaining: " + String(ESP32PWM::channelsRemaining()));
}
  void WriteConsoleLog(String StringLogMsg) {

    for (size_t i = 0; i < StringLogMsg.length(); i++) {
      if (StringLogMsg[i] == '"') StringLogMsg[i] = '\'';
      else if (StringLogMsg[i] == '\r') StringLogMsg[i] = '^';
      else if (StringLogMsg[i] == '\n') StringLogMsg[i] = ' ';
    }
    if (LogMsg.length() > 0) {
      LogMsg = LogMsg + "; " + StringLogMsg;
    } else LogMsg = StringLogMsg;

    if (LogMsg.length() > 10000) {
      LogMsg = StringLogMsg;
    }
        if (SamSetup.WSerial) {
            WebSerial.println(StringLogMsg);
            }
  }
  void DbgMsg(const String& Msg, bool ln=1) { 
    if (SamSetup.dbg) { 
      if (xSemaphoreTake(xSemaphoreSerial, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
        static uint32_t t = millis();
        Serial.print("Debug (dT:" + String(millis()-t) + "):" + Msg);
        if (ln) Serial.println();
        t = millis();
        xSemaphoreGive(xSemaphoreSerial);
      }
      WriteConsoleLog("(" + String(millis()) + "):" + Msg);
    }
  }
  void SerialMsg(const String& Msg, bool ln=1) {
    if (xSemaphoreTake(xSemaphoreSerial, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
      if (ln) Serial.println(Msg); else Serial.print(Msg);
      xSemaphoreGive(xSemaphoreSerial);
    }
    WriteConsoleLog(Msg);
  }
  void detailedHeapAnalysis() { //Анализ HEAP
   if (xSemaphoreTake(xSemaphoreSerial, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
    Serial.println("=== Heap Information (Internal RAM) ===");
    
    // Используем только внутреннюю память
    const uint32_t caps = MALLOC_CAP_INTERNAL;
    multi_heap_info_t info;
    heap_caps_get_info(&info, caps);
    
    Serial.printf("Total free bytes: %d\n", info.total_free_bytes);
    Serial.printf("Largest free block: %d bytes\n", info.largest_free_block);
    Serial.printf("Minimum free bytes: %d\n", info.minimum_free_bytes);
    Serial.printf("Allocated blocks: %d\n", info.allocated_blocks);
    Serial.printf("Free blocks: %d\n", info.free_blocks);
    
    // Расчет степени фрагментации
    /*if (info.total_free_bytes > 0) {
      float fragmentation = 100.0 - ((float)info.largest_free_block / info.total_free_bytes * 100.0);
      Serial.printf("Fragmentation: %.2f%%\n", fragmentation);
    }*/
    
    Serial.println(F("----------------Stack size------------------"));
    Serial.print(F("taskButton = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(SysTickerButton)));
    Serial.print(F("PowerStatus_Loop = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(PowerStatusTask)));
    Serial.print(F("Sensors_Loop = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(SysTickerTask1)));
    Serial.print(F("NET_Loop = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(GetClockTask1)));
    if (SamSetup.LUA) { 
    Serial.print(F("DoLuaScriptTask = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(DoLuaScriptTask)));
    }
    Serial.println(F("--------------------------------------------"));
      xSemaphoreGive(xSemaphoreSerial);
      }
  }
  void verbose_print_reset_reason() {
    esp_reset_reason_t reason = esp_reset_reason();
    
    if (reason == ESP_RST_UNKNOWN) {
      return; // Неизвестная причина - ничего не выводим
    }
    
    String reset_info = "🔄 Причина перезагрузки: ";
    
    switch (reason) {
      case ESP_RST_POWERON:    reset_info = "🔌 " + reset_info + "включение питания"; break;
      case ESP_RST_EXT:        reset_info = "🔘 " + reset_info + "кнопка RESET"; break;
      case ESP_RST_SW:         reset_info = "💻 " + reset_info + "программный сброс"; break;
      case ESP_RST_PANIC:      reset_info = "💥 " + reset_info + "авария (Guru Meditation)"; break;
      case ESP_RST_INT_WDT:    reset_info = "⏰ " + reset_info + "сторожевой таймер прерываний"; break;
      case ESP_RST_TASK_WDT:   reset_info = "⏰ " + reset_info + "сторожевой таймер задачи"; break;
      case ESP_RST_WDT:        reset_info = "⏰ " + reset_info + "сторожевой таймер"; break;
      case ESP_RST_DEEPSLEEP:  reset_info = "😴 " + reset_info + "пробуждение из глубокого сна"; break;
      case ESP_RST_BROWNOUT:   reset_info = "⚡ " + reset_info + "проблема с питанием (brownout)"; break;
      default:                 reset_info = "❓ " + reset_info + "неизвестно: " + String(reason);
    }
    
    SerialMsg(reset_info, 1);
    SendMsg(reset_info, NOTIFY_MSG);
  }
void recvMsg(uint8_t *data, size_t len) {
  WebSerial.println("Received Data...");
  String d = "";
  d = "";
  for (int i = 0; i < len; i++) {
    d += char(data[i]);
  }
  String Var, Val;
  Var = "";
  Val = "";
  Var = getValue(d, '=', 0);
  Val = getValue(d, '=', 1);
  Var.trim();
  Val.trim();
  if (Val.length() > 0) {
    WebSerial.print(Var);
    WebSerial.print(" = ");
    if (Var == "WFpulseCount") {
      WFpulseCount = (uint8_t)Val.toInt();
      WebSerial.println(WFpulseCount);
    } else if (Var.length() > 0) {
    }
  } else if (d == "print") {
    WebSerial.println("_______________________________________________");
    WebSerial.print("WFpulseCount = ");
    WebSerial.println(WFpulseCount);
    WebSerial.println("_______________________________________________");
  } else
    WebSerial.print("echo ");
  WebSerial.println(d);
}
//-------------------------------------bugtrace
    // Буфер в RTC памяти
    RTC_NOINIT_ATTR char rtc_crash_buffer[3072];
    RTC_NOINIT_ATTR size_t rtc_crash_index = 0;

    // Объявляем функции с extern "C" чтобы избежать конфликта
    extern "C" {
        int __real_putchar(int c);
        int __wrap_putchar(int c);
    }

    // Реализация перехватчика
    extern "C" int __wrap_putchar(int c) {
        // Сохраняем в RTC буфер
        if (rtc_crash_index < sizeof(rtc_crash_buffer) - 1) {
            rtc_crash_buffer[rtc_crash_index++] = (char)c;
        }
        
        // Вызываем оригинальную функцию
        return __real_putchar(c);
    }

    void setup_bugtrace() {
        Serial.println("=== Crash Logger Started ===");
            // Сброс некорректного индекса
        if (rtc_crash_index >= sizeof(rtc_crash_buffer)) {
            Serial.printf("Resetting invalid index: %d\n", rtc_crash_index);
            rtc_crash_index = 0;
        }
        // Проверяем RTC буфер
        if (rtc_crash_index > 0) {
            Serial.printf("Previous crash detected! Log size: %d bytes\n", rtc_crash_index);
            
            if (SPIFFS.begin(true)) {
                File file = SPIFFS.open("/bugtrace.txt", FILE_WRITE);
                if (file) {
                    file.write((uint8_t*)rtc_crash_buffer, rtc_crash_index);
                    file.close();
                    Serial.println("Crash log saved to /bugtrace.txt");
                    
                    // Показываем начало лога
                    Serial.println("=== Crash Log Preview ===");
                    for (size_t i = 0; i < (rtc_crash_index < 500 ? rtc_crash_index : 500); i++) {
                        Serial.print(rtc_crash_buffer[i]);
                    }
                    Serial.println("\n=== End Preview ===");
                }
                SPIFFS.end();
            }
            
            rtc_crash_index = 0;
        } else {
            Serial.println("No crash detected - system started normally");
        }
        
        Serial.println("System ready");
    }
