//-------------------------------------Системные и отладочные
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 DbgMsg(const String& Msg, bool ln) { 
    if (SamSetup.dbg) { 
      if (xSemaphoreTake(xSemaphoreSerial, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
        static uint32_t t = millis();
        const uint32_t caps = MALLOC_CAP_INTERNAL;
        multi_heap_info_t info;
        heap_caps_get_info(&info, caps);
        Serial.print("Debug: HEAP: " + String(info.total_free_bytes) + ", (dT:" + String(millis()-t) + "):" + Msg);
        if (ln) Serial.println();
        static File file;
        if(!file) {
          file = SPIFFS.open("/s_log.txt", FILE_APPEND);
        }
        if(file) {
          if (ln) file.print(" (" + String(millis()-t) + ")->");
          if (WiFi.status() == WL_CONNECTED) file.print(Crt+"->");
          file.print(Msg);
          if (ln) file.println();
          file.flush();
          file.close();
        }
        t = millis();
        xSemaphoreGive(xSemaphoreSerial);
      }
    }
  }
  void SerialMsg(const String& Msg, bool ln) {
    if (xSemaphoreTake(xSemaphoreSerial, (TickType_t)((200) / portTICK_RATE_MS)) == pdTRUE) {
      if (ln) Serial.println(Msg); else Serial.print(Msg);
      xSemaphoreGive(xSemaphoreSerial);
    }
    if (SamSetup.dbg) { 
      static File file;
      if(!file) {
        file = SPIFFS.open("/s_log.txt", FILE_APPEND);
      }
      if(file) {
        if (WiFi.status() == WL_CONNECTED) file.print(Crt+"->");
        file.print(Msg);
        if (ln) file.println();
        file.flush();
        file.close();
      }
    }
  }
  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("SysTickerButton = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(SysTickerButton)));
    Serial.print(F("PowerStatusTask = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(PowerStatusTask)));
    Serial.print(F("SysTickerTask1 = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(SysTickerTask1)));
    Serial.print(F("GetClockTask1 = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(GetClockTask1)));
    Serial.print(F("BuzzerTask = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(BuzzerTask)));
    #ifdef USE_LUA  
    Serial.print(F("DoLuaScriptTask = "));
    Serial.println(String(uxTaskGetStackHighWaterMark(DoLuaScriptTask)));
    #endif
    Serial.println(F("--------------------------------------------"));
      xSemaphoreGive(xSemaphoreSerial);
      }
  }
  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;
    }

    #ifdef USE_WEB_SERIAL
    WebSerial.println(StringLogMsg);
    #endif
    DbgMsg(StringLogMsg,1);
  }
  void print_wakeup_cause() {
    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    String wakeup_info = "   Причина пробуждения: ";
    
    switch(cause) {
      case ESP_SLEEP_WAKEUP_TIMER:     wakeup_info += "⏰ таймер"; break;
      case ESP_SLEEP_WAKEUP_EXT0:      wakeup_info += "📌 GPIO (EXT0)"; break;
      case ESP_SLEEP_WAKEUP_EXT1:      wakeup_info += "📌 GPIO (EXT1)"; break;
      case ESP_SLEEP_WAKEUP_TOUCHPAD:  wakeup_info += "👆 тачпад"; break;
      case ESP_SLEEP_WAKEUP_ULP:       wakeup_info += "🔋 ULP сопроцессор"; break;
      case ESP_SLEEP_WAKEUP_GPIO:      wakeup_info += "📌 GPIO"; break;
      case ESP_SLEEP_WAKEUP_UART:      wakeup_info += "🔌 UART"; break;
      case ESP_SLEEP_WAKEUP_WIFI:      wakeup_info += "📶 WiFi"; break;
      case ESP_SLEEP_WAKEUP_BT:        wakeup_info += "📱 Bluetooth"; break;
      case ESP_SLEEP_WAKEUP_UNDEFINED: wakeup_info += "❓ не определена"; break;
      default:                         wakeup_info += "❓ неизвестно: " + String(cause);
    }
    
    SerialMsg(wakeup_info, 1);
    SendMsg(wakeup_info, NOTIFY_MSG);
  }
  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);
    
    // Детали пробуждения из Deep Sleep
    if (reason == ESP_RST_DEEPSLEEP) {
      print_wakeup_cause();
    }
  }
//-------------------------------------coredump
    #include <esp_partition.h>
    #include <esp_ota_ops.h>

    // Функция для получения строкового описания причины ресета
    String getResetReasonString() {
        esp_reset_reason_t reason = esp_reset_reason();
        switch(reason) {
            case ESP_RST_UNKNOWN:    return "Unknown";
            case ESP_RST_POWERON:    return "Power On";
            case ESP_RST_EXT:        return "External Reset";
            case ESP_RST_SW:         return "Software Reset";
            case ESP_RST_PANIC:      return "Exception/Panic";
            case ESP_RST_INT_WDT:    return "Interrupt Watchdog";
            case ESP_RST_TASK_WDT:   return "Task Watchdog";
            case ESP_RST_WDT:        return "Other Watchdog";
            case ESP_RST_DEEPSLEEP:  return "Deep Sleep";
            case ESP_RST_BROWNOUT:   return "Brownout";
            case ESP_RST_SDIO:       return "SDIO Reset";
            default:                 return "Other";
        }
    }
    // Функция для сохранения информации о падении
    void saveCrashInfo(const String& dump_filename) {
        String info_filename = dump_filename + ".txt";
        File info_file = SPIFFS.open(info_filename, "w");
        
        if (!info_file) {
            Serial.println("Failed to create crash info file");
            return;
        }
        
        info_file.println("=== ESP32 CRASH REPORT ===");
        info_file.println("Dump File: " + dump_filename + ".bin");
        info_file.println("Timestamp: " + String(millis()));
        info_file.println("Reset Reason: " + getResetReasonString());
        info_file.println("Free Heap: " + String(ESP.getFreeHeap()));
        info_file.println("CPU Frequency: " + String(ESP.getCpuFreqMHz()) + " MHz");
        info_file.println("SDK Version: " + String(esp_get_idf_version()));
        
        // Информация о памяти
        info_file.println("=== MEMORY INFO ===");
        info_file.println("Free Heap: " + String(ESP.getFreeHeap()));
        info_file.println("Min Free Heap: " + String(ESP.getMinFreeHeap()));
        info_file.println("Max Alloc Heap: " + String(ESP.getMaxAllocHeap()));
        info_file.println("Heap Size: " + String(ESP.getHeapSize()));
        info_file.println("PSRAM Size: " + String(ESP.getPsramSize()));
        info_file.println("Free PSRAM: " + String(ESP.getFreePsram()));
        
        // Информация о прошивке
        info_file.println("=== FIRMWARE INFO ===");
        const esp_app_desc_t* app_desc = esp_ota_get_app_description();
        if (app_desc) {
            info_file.println("Project: " + String(app_desc->project_name));
            info_file.println("Version: " + String(app_desc->version));
            info_file.println("Compile Date: " + String(app_desc->date));
            info_file.println("Compile Time: " + String(app_desc->time));
        }
        
        info_file.close();
        Serial.println("Crash info saved: " + info_filename);
    }
    // Функция для проверки и сохранения Core Dump
    void handleCoreDumpOnBoot() {
        Serial.println("Checking for Core Dump...");
        
        // Ищем раздел coredump
        const esp_partition_t* core_partition = esp_partition_find_first(
            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
        
        if (!core_partition) {
            Serial.println("No Core Dump partition found");
            return;
        }
        
        // Проверяем размер данных в разделе
        size_t core_size = 0;
        esp_partition_read(core_partition, 0, &core_size, sizeof(core_size));
        
        if (core_size <= sizeof(size_t) || core_size > core_partition->size) {
            Serial.println("No valid Core Dump found");
            return;
        }
        
        Serial.println("Core Dump found! Size: " + String(core_size) + " bytes");
        
        // Сохраняем Core Dump в SPIFFS
        String filename = "/coredump";
        File dump_file = SPIFFS.open(filename + ".bin", "w");
        
        if (!dump_file) {
            Serial.println("Failed to create dump file");
            return;
        }
        
        // Читаем и сохраняем Core Dump
        uint8_t* buffer = (uint8_t*)malloc(512);
        if (!buffer) {
            Serial.println("Failed to allocate buffer");
            dump_file.close();
            return;
        }
        
        size_t bytes_read = 0;
        size_t offset = 0;
        
        while (offset < core_size) {
            // Простая замена min - используем тернарный оператор
            size_t to_read = (512 < (core_size - offset)) ? 512 : (core_size - offset);
            
            if (esp_partition_read(core_partition, offset, buffer, to_read) != ESP_OK) {
                Serial.println("Failed to read Core Dump at offset: " + String(offset));
                break;
            }
            
            if (dump_file.write(buffer, to_read) != to_read) {
                Serial.println("Failed to write Core Dump data");
                break;
            }
            
            offset += to_read;
            bytes_read += to_read;
        }
        
        free(buffer);
        dump_file.close();
        
        if (bytes_read == core_size) {
            Serial.println("Core Dump saved: " + filename + ".bin");
            
            // Сохраняем информацию о падении
            saveCrashInfo(filename);
            
            // Очищаем раздел
            if (esp_partition_erase_range(core_partition, 0, core_partition->size) == ESP_OK) {
                Serial.println("Core Dump partition cleared");
            }
        } else {
            Serial.println("Failed to save complete Core Dump");
            SPIFFS.remove(filename + ".bin");
        }
    }

//-------------------------------------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");
    }
//-------------------------------------Advanced_Stack_Monitor
 /*    class AdvancedStackMonitor {
    private:
        // Структура для хранения данных экземпляра
        struct InstanceData {
            UBaseType_t prevWatermark = 0;
            UBaseType_t min = 0xFFFF;
            UBaseType_t max = 0;
            UBaseType_t drops = 0;
            uint32_t totalChecks = 0;
            uint32_t lastReport = 0;
            bool initialized = false;
        };
        
        const char* taskName;
        UBaseType_t threshold;
        InstanceData data; 
        
    public:
        AdvancedStackMonitor(const char* name, UBaseType_t alertThreshold = 50) 
            : taskName(name), threshold(alertThreshold) {
            initSPIFFS();
        }
        
        void initSPIFFS() {

        }
        
        void logToFile(const String& message) {
            static File file;
            
            if(!file) {
                file = SPIFFS.open("/stack_m.csv", FILE_APPEND);
                if(!file) {
                    return;
                }
            }
            
            static char logBuffer[512];
            static char timestamp[16];
            
            snprintf(timestamp, sizeof(timestamp), "%lu", millis());
            snprintf(logBuffer, sizeof(logBuffer), "%s,%s\n", timestamp, message.c_str());
            
            file.print(logBuffer);
            file.flush();
        }
        
        void check(const char* location = "") {
            // Инициализация при первом вызове
            if(!data.initialized) {
                data.prevWatermark = uxTaskGetStackHighWaterMark(NULL);
                data.min = data.prevWatermark;
                data.max = data.prevWatermark;
                data.initialized = true;
                return; // Первый вызов только для инициализации
            }
            static bool spiffsInitialized = false;
            if(!spiffsInitialized) { // Второй вызов - Сначала заголовки
                spiffsInitialized = true;
                logToFile("Time,Task,Event,Location,Previous,Current,DropSize,Min,Max,TotalDrops");
            }
            
            UBaseType_t current = uxTaskGetStackHighWaterMark(NULL);
            data.totalChecks++;
            
            // Обновление статистики
            if(current < data.min) data.min = current;
            if(current > data.max) data.max = current;
            
            bool dropDetected = false;
            int dropSize = 0;
            
            // Детектор скачков
            if(data.prevWatermark > 0 && data.prevWatermark - current > threshold) {
                data.drops++;
                dropDetected = true;
                dropSize = data.prevWatermark - current;
                
                static char serialBuffer[256];
                static char fileBuffer[256];
                
                snprintf(serialBuffer, sizeof(serialBuffer), 
                        "🚨 STACK DROP #%d\nTask: %s\nLocation: %s\nDrop: %d->%d (-%d)\n\n", 
                        data.drops, taskName, location, data.prevWatermark, current, dropSize);
                Serial.print(serialBuffer);
                
                // ИЗМЕНЕНО: Сначала Event "DROP", потом Location
                snprintf(fileBuffer, sizeof(fileBuffer), 
                        "%s,DROP,%s,%d,%d,%d,%d,%d,%d", 
                        taskName, location, data.prevWatermark, current, dropSize, 
                        data.min, data.max, data.drops);
                logToFile(fileBuffer);
            }
            
            // Периодический отчет
             if(millis() - data.lastReport > 60000) {
                data.lastReport = millis();
                printStats();
                
                static char statsBuffer[256];
                snprintf(statsBuffer, sizeof(statsBuffer), 
                        "%s,STATS,Periodic,%d,%d,0,%d,%d,%d",
                        taskName, data.prevWatermark, current, data.min, data.max, data.drops);
                logToFile(statsBuffer);
            }
           
            // Редкий лог обычных проверок
            static uint32_t lastNormalLog = 0;
            if(!dropDetected && (millis() - lastNormalLog > 60000)) {
                lastNormalLog = millis();
                static char normalBuffer[256];
                snprintf(normalBuffer, sizeof(normalBuffer), 
                        "%s,CHECK,%s,%d,%d,0,%d,%d,%d",
                        taskName, location, data.prevWatermark, current, data.min, data.max, data.drops);
                logToFile(normalBuffer);
            }
            
            data.prevWatermark = current;
        }
        
        void printStats() {
            static char statsBuffer[128];
            snprintf(statsBuffer, sizeof(statsBuffer), 
                    "=== Stack Stats %s ===\nMin: %d, Max: %d, Drops: %d, Total Checks: %d\n======================\n", 
                    taskName, data.min, data.max, data.drops, data.totalChecks);
            Serial.print(statsBuffer);
        }
        
        void dumpLogToSerial() {
            static File file = SPIFFS.open("/stack_m.csv", FILE_READ);
            if(!file) {
                Serial.println("❌ Cannot open stack_m.csv for reading");
                return;
            }
            
            Serial.println("=== Stack Monitor Log ===");
            while(file.available()) {
                Serial.write(file.read());
            }
            Serial.println("=========================");
            
            file.seek(0);
        }
        
        void clearLog() {
            if(SPIFFS.exists("/stack_m.csv")) {
                SPIFFS.remove("/stack_m.csv");
            }
            
            logToFile("=== Stack Monitor Started (Cleared) ===");
            // ИЗМЕНЕНО: Event теперь перед Location
            logToFile("Time,Task,Event,Location,Previous,Current,DropSize,Min,Max,TotalDrops");
            
            Serial.println("✅ Stack log cleared");
        }
        
        // Метод для сброса статистики (опционально)
        void resetStats() {
            data.prevWatermark = 0;
            data.min = 0xFFFF;
            data.max = 0;
            data.drops = 0;
            data.totalChecks = 0;
            data.lastReport = 0;
            data.initialized = false;
        }
    };*/