Spaces:
Running
Running
| // Класс для работы с SD картой | |
| class SDCardManager { | |
| public: | |
| bool init() { | |
| if (!SD.begin(SD_CS_PIN, SPI, SD_SPI_SPEED)) { | |
| Serial.println("Ошибка инициализации SD карты"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool saveData(const char* filename, const void* data, size_t size) { | |
| File file = SD.open(filename, FILE_WRITE); | |
| if (!file) { | |
| Serial.println("Ошибка открытия файла для записи"); | |
| return false; | |
| } | |
| size_t written = file.write((const uint8_t*)data, size); | |
| file.close(); | |
| return written == size; | |
| } | |
| bool loadData(const char* filename, void* data, size_t size) { | |
| File file = SD.open(filename, FILE_READ); | |
| if (!file) { | |
| Serial.println("Ошибка открытия файла для чтения"); | |
| return false; | |
| } | |
| size_t read = file.read((uint8_t*)data, size); | |
| file.close(); | |
| return read == size; | |
| } | |
| }; | |
| // Класс для работы с PSRAM | |
| class PSRAMManager { | |
| private: | |
| void* psramData; | |
| size_t dataSize; | |
| bool initialized; | |
| public: | |
| PSRAMManager() : psramData(nullptr), dataSize(0), initialized(false) {} | |
| bool init() { | |
| if (!psramInit()) { | |
| Serial.println("Ошибка инициализации PSRAM"); | |
| return false; | |
| } | |
| // Выделяем память для всех данных | |
| dataSize = sizeof(ConfigData) + sizeof(LearningData) + sizeof(RXXLearningData); | |
| psramData = ps_malloc(dataSize); | |
| if (!psramData) { | |
| Serial.println("Ошибка выделения памяти в PSRAM"); | |
| return false; | |
| } | |
| initialized = true; | |
| Serial.println("PSRAM инициализирован"); | |
| return true; | |
| } | |
| bool saveData(const void* data, size_t size, size_t offset) { | |
| if (!initialized) return false; | |
| if (offset + size > dataSize) { | |
| Serial.println("Ошибка: превышение размера PSRAM"); | |
| return false; | |
| } | |
| memcpy((uint8_t*)psramData + offset, data, size); | |
| return true; | |
| } | |
| bool loadData(void* data, size_t size, size_t offset) { | |
| if (!initialized) return false; | |
| if (offset + size > dataSize) { | |
| Serial.println("Ошибка: превышение размера PSRAM"); | |
| return false; | |
| } | |
| memcpy(data, (uint8_t*)psramData + offset, size); | |
| return true; | |
| } | |
| size_t getFreeMemory() { | |
| return ESP.getFreePsram(); | |
| } | |
| size_t getTotalMemory() { | |
| return ESP.getPsramSize(); | |
| } | |
| }; | |
| // Класс для работы с веб-сервером | |
| class WebServerManager { | |
| private: | |
| WebServer server; | |
| PSRAMManager psramManager; | |
| public: | |
| void init() { | |
| // Инициализация WiFi | |
| WiFi.mode(WIFI_AP_STA); | |
| WiFi.softAP(WEB_SSID, WEB_PASSWORD); | |
| // Настройка маршрутов | |
| server.on("/", HTTP_GET, [this]() { | |
| server.send(200, "text/html", htmlPage); | |
| }); | |
| server.on("/config", HTTP_GET, [this]() { | |
| StaticJsonDocument<1024> doc; | |
| ConfigData config; | |
| if (psramManager.loadData(&config, sizeof(ConfigData), 0)) { | |
| doc["injectionTimeMin"] = config.injectionTimeMin; | |
| doc["injectionTimeMax"] = config.injectionTimeMax; | |
| // ... добавьте остальные поля конфигурации | |
| } | |
| String response; | |
| serializeJson(doc, response); | |
| server.send(200, "application/json", response); | |
| }); | |
| server.on("/config", HTTP_POST, [this]() { | |
| StaticJsonDocument<1024> doc; | |
| DeserializationError error = deserializeJson(doc, server.arg("plain")); | |
| if (error) { | |
| server.send(400, "text/plain", "Ошибка разбора JSON"); | |
| return; | |
| } | |
| ConfigData config; | |
| // Обновление конфигурации | |
| config.injectionTimeMin = doc["injectionTimeMin"]; | |
| config.injectionTimeMax = doc["injectionTimeMax"]; | |
| // ... обновите остальные поля конфигурации | |
| if (psramManager.saveData(&config, sizeof(ConfigData), 0)) { | |
| server.send(200, "text/plain", "Конфигурация сохранена"); | |
| } else { | |
| server.send(500, "text/plain", "Ошибка сохранения конфигурации"); | |
| } | |
| }); | |
| server.begin(); | |
| } | |
| void handleClient() { | |
| server.handleClient(); | |
| } | |
| }; | |
| // Класс для управления двигателем | |
| class EngineManager { | |
| private: | |
| float currentRPM; | |
| float currentThrottle; | |
| float currentTemp; | |
| float currentLambda; | |
| public: | |
| void updateSensors(float rpm, float throttle, float temp, float lambda) { | |
| currentRPM = rpm; | |
| currentThrottle = throttle; | |
| currentTemp = temp; | |
| currentLambda = lambda; | |
| } | |
| float calculateInjectionTime() { | |
| // Базовая логика расчета времени впрыска | |
| float baseTime = map(currentRPM, 0, MAX_RPM, | |
| configData.injectionTimeMin, | |
| configData.injectionTimeMax); | |
| // Коррекция по температуре | |
| if (configData.tempCorrectionEnabled) { | |
| baseTime *= getTempCorrection(currentTemp); | |
| } | |
| // Коррекция по лямбде | |
| if (configData.lambdaCorrectionEnabled) { | |
| baseTime *= getLambdaCorrection(currentLambda); | |
| } | |
| return baseTime; | |
| } | |
| float calculateAdvance() { | |
| // Базовая логика расчета УОЗ | |
| float baseAdvance = map(currentRPM, 0, MAX_RPM, | |
| configData.minAdvance, | |
| configData.maxAdvance); | |
| // Коррекция для холостого хода | |
| if (isIdle()) { | |
| return getIdleAdvance(); | |
| } | |
| return baseAdvance; | |
| } | |
| private: | |
| bool isIdle() { | |
| return currentRPM >= configData.idleRpmMin && | |
| currentRPM <= configData.idleRpmMax && | |
| currentThrottle < THROTTLE_IDLE_THRESHOLD; | |
| } | |
| float getTempCorrection(float temp) { | |
| // Логика коррекции по температуре | |
| return 1.0; // Заглушка | |
| } | |
| float getLambdaCorrection(float lambda) { | |
| // Логика коррекции по лямбде | |
| return 1.0; // Заглушка | |
| } | |
| float getIdleAdvance() { | |
| // Логика расчета УОЗ на холостом ходу | |
| return map(currentRPM, configData.idleRpmMin, configData.idleRpmMax, | |
| configData.idleAdvanceMin, configData.idleAdvanceMax); | |
| } | |
| }; | |