#ifndef MEMORY_H #define MEMORY_H #include #include #include "Config.h" // -------------------- کتابخانه EEPROM داخلی برای STM32 -------------------- #if defined(STM32F1xx) || defined(STM32F3xx) || defined(STM32F4xx) #include #endif // -------------------- متغیرهای خارجی -------------------- extern SPIClass flashSPI; extern DeviceConfig config; // -------------------- تنظیمات EEPROM داخلی -------------------- #define EEPROM_CONFIG_START 0 // آدرس شروع در EEPROM #define EEPROM_SIGNATURE_ADDR 0 // آدرس سیگنچور در EEPROM #define EEPROM_CONFIG_SIZE sizeof(DeviceConfig) // -------------------- توابع حافظه SPI Flash -------------------- inline void flashInit() { pinMode(FLASH_CS, OUTPUT); digitalWrite(FLASH_CS, HIGH); flashSPI.begin(); flashSPI.setClockDivider(SPI_CLOCK_DIV4); delay(100); } inline bool flashIsBusy() { digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x05); uint8_t status = flashSPI.transfer(0); digitalWrite(FLASH_CS, HIGH); return (status & 0x01); } inline void flashWaitForReady() { unsigned long start = millis(); while (flashIsBusy()) { if (millis() - start > 1000) { Serial1.println("⚠️ Flash wait timeout"); break; } delay(1); } } inline bool flashReadBytes(uint32_t addr, uint8_t *data, uint32_t len) { if (len == 0) return false; flashWaitForReady(); digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x03); // Read command flashSPI.transfer((addr >> 16) & 0xFF); flashSPI.transfer((addr >> 8) & 0xFF); flashSPI.transfer(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { data[i] = flashSPI.transfer(0); } digitalWrite(FLASH_CS, HIGH); return true; } inline bool flashWriteBytes(uint32_t addr, uint8_t *data, uint32_t len) { if (len == 0) return false; // Write Enable digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x06); digitalWrite(FLASH_CS, HIGH); delay(1); // Page Program digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x02); flashSPI.transfer((addr >> 16) & 0xFF); flashSPI.transfer((addr >> 8) & 0xFF); flashSPI.transfer(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { flashSPI.transfer(data[i]); } digitalWrite(FLASH_CS, HIGH); flashWaitForReady(); return true; } inline void flashSectorErase(uint32_t addr) { // Write Enable digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x06); digitalWrite(FLASH_CS, HIGH); delay(1); // Sector Erase digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x20); flashSPI.transfer((addr >> 16) & 0xFF); flashSPI.transfer((addr >> 8) & 0xFF); flashSPI.transfer(addr & 0xFF); digitalWrite(FLASH_CS, HIGH); flashWaitForReady(); } inline bool testFlashCommunication() { Serial1.print("Testing flash communication... "); // دستور خواندن JEDEC ID digitalWrite(FLASH_CS, LOW); flashSPI.transfer(0x9F); uint8_t manuf = flashSPI.transfer(0); uint8_t type = flashSPI.transfer(0); uint8_t capacity = flashSPI.transfer(0); digitalWrite(FLASH_CS, HIGH); if (manuf == 0xFF || manuf == 0x00) { Serial1.println("❌ FAILED (No response)"); return false; } Serial1.print("✅ OK (Manuf: 0x"); Serial1.print(manuf, HEX); Serial1.print(", Type: 0x"); Serial1.print(type, HEX); Serial1.print(", Capacity: 0x"); Serial1.print(capacity, HEX); Serial1.println(")"); return true; } // -------------------- توابع EEPROM داخلی STM32 -------------------- inline bool eepromIsAvailable() { #if defined(STM32F1xx) || defined(STM32F3xx) || defined(STM32F4xx) return true; #else return false; #endif } inline bool eepromWriteConfig(const DeviceConfig &cfg) { if (!eepromIsAvailable()) { Serial1.println("❌ EEPROM not available on this board"); return false; } Serial1.print("Writing config to EEPROM ("); Serial1.print(sizeof(cfg)); Serial1.println(" bytes)..."); uint8_t *data = (uint8_t*)&cfg; // نوشتن در EEPROM for (uint16_t i = 0; i < sizeof(cfg); i++) { EEPROM.write(EEPROM_CONFIG_START + i, data[i]); } // ذخیره تغییرات #if defined(EEPROM_commit) EEPROM.commit(); #endif Serial1.println("✅ Config written to EEPROM"); return true; } inline bool eepromReadConfig(DeviceConfig &cfg) { if (!eepromIsAvailable()) { return false; } uint8_t *data = (uint8_t*)&cfg; // خواندن از EEPROM for (uint16_t i = 0; i < sizeof(cfg); i++) { data[i] = EEPROM.read(EEPROM_CONFIG_START + i); } // بررسی سیگنچور if (strcmp(cfg.signature, "CFG") != 0) { Serial1.println("❌ Invalid signature in EEPROM"); return false; } Serial1.println("✅ Config read from EEPROM"); return true; } inline bool eepromClearConfig() { if (!eepromIsAvailable()) { return false; } // پاک کردن با نوشتن 0xFF for (uint16_t i = 0; i < EEPROM_CONFIG_SIZE; i++) { EEPROM.write(EEPROM_CONFIG_START + i, 0xFF); } #if defined(EEPROM_commit) EEPROM.commit(); #endif Serial1.println("✅ EEPROM cleared"); return true; } // -------------------- توابع مدیریت کانفیگ هوشمند -------------------- inline void saveConfig() { Serial1.println("\n💾 SAVING CONFIGURATION"); config.valid = true; uint8_t buffer[sizeof(DeviceConfig)]; memcpy(buffer, &config, sizeof(DeviceConfig)); bool flashSuccess = false; bool eepromSuccess = false; // 1. ذخیره در حافظه SPI Flash (اصلی) Serial1.println("1. Saving to SPI Flash..."); if (testFlashCommunication()) { flashSectorErase(CONFIG_ADDRESS); if (flashWriteBytes(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig))) { // تأیید uint8_t verifyBuffer[sizeof(DeviceConfig)]; if (flashReadBytes(CONFIG_ADDRESS, verifyBuffer, sizeof(DeviceConfig))) { if (memcmp(buffer, verifyBuffer, sizeof(DeviceConfig)) == 0) { flashSuccess = true; Serial1.println(" ✅ SPI Flash: Saved and verified"); } } } } if (!flashSuccess) { Serial1.println(" ❌ SPI Flash: Failed or not available"); } // 2. ذخیره در EEPROM داخلی (پشتیبان) Serial1.println("2. Saving to internal EEPROM..."); eepromSuccess = eepromWriteConfig(config); if (eepromSuccess) { Serial1.println(" ✅ EEPROM: Backup saved"); } else { Serial1.println(" ⚠️ EEPROM: Backup not available"); } // خلاصه Serial1.println("\n📊 SAVE SUMMARY:"); Serial1.print(" SPI Flash: "); Serial1.println(flashSuccess ? "✅" : "❌"); Serial1.print(" Internal EEPROM: "); Serial1.println(eepromSuccess ? "✅" : "⚠️"); if (flashSuccess || eepromSuccess) { Serial1.println("✅ Configuration saved successfully"); } else { Serial1.println("❌ CRITICAL: Could not save config anywhere!"); } } inline bool loadConfigFromFlash() { Serial1.print("Trying to load from SPI Flash... "); uint8_t buffer[sizeof(DeviceConfig)]; if (!testFlashCommunication()) { Serial1.println("❌ Flash not responding"); return false; } if (!flashReadBytes(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig))) { Serial1.println("❌ Failed to read from flash"); return false; } memcpy(&config, buffer, sizeof(DeviceConfig)); if (strcmp(config.signature, "CFG") != 0) { Serial1.println("❌ Invalid signature in flash"); return false; } config.valid = true; Serial1.println("✅ Success"); return true; } inline bool loadConfigFromEEPROM() { Serial1.print("Trying to load from internal EEPROM... "); if (!eepromIsAvailable()) { Serial1.println("❌ EEPROM not available"); return false; } if (!eepromReadConfig(config)) { Serial1.println("❌ Failed to read from EEPROM"); return false; } config.valid = true; Serial1.println("✅ Success"); return true; } inline void createDefaultConfig() { Serial1.println("Creating default configuration..."); strcpy(config.signature, "CFG"); strcpy(config.deviceId, ""); strcpy(config.serverPhoneNumber, ""); strcpy(config.serverUrl, "https://ghback.nabaksoft.ir"); config.uploadInterval = 5; config.smsInterval = 10; config.saveInterval = 60; config.simType = SIM_UNKNOWN; config.smsEnabled = false; config.verified = false; config.valid = false; } inline void readConfig() { Serial1.println("\n📖 LOADING CONFIGURATION"); bool configLoaded = false; String source = ""; // استراتژی: اول SPI Flash، اگر نشد EEPROM داخلی Serial1.println("Strategy: SPI Flash → Internal EEPROM"); // 1. اول از SPI Flash بخوان if (loadConfigFromFlash()) { configLoaded = true; source = "SPI Flash"; // همچنین در EEPROM هم بروزرسانی کن (sync) eepromWriteConfig(config); } // 2. اگر SPI Flash کار نکرد، از EEPROM داخلی بخوان else if (loadConfigFromEEPROM()) { configLoaded = true; source = "Internal EEPROM (backup)"; // سعی کن دوباره در SPI Flash ذخیره کنی (بازیابی) Serial1.println("Attempting to restore to SPI Flash..."); uint8_t buffer[sizeof(DeviceConfig)]; memcpy(buffer, &config, sizeof(DeviceConfig)); if (testFlashCommunication()) { flashSectorErase(CONFIG_ADDRESS); flashWriteBytes(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig)); Serial1.println("✅ Restored to SPI Flash"); } } // 3. اگر هیچ کدام کار نکرد، کانفیگ پیش‌فرض بساز if (!configLoaded) { Serial1.println("❌ No valid config found in any memory"); source = "Default"; createDefaultConfig(); saveConfig(); // ذخیره کانفیگ جدید configLoaded = true; } // نمایش نتیجه Serial1.println("\n📊 LOAD RESULT:"); Serial1.print(" Status: "); Serial1.println(configLoaded ? "✅ LOADED" : "❌ FAILED"); Serial1.print(" Source: "); Serial1.println(source); Serial1.print(" Device ID: "); Serial1.println(strlen(config.deviceId) > 0 ? config.deviceId : "[Empty]"); Serial1.print(" Verified: "); Serial1.println(config.verified ? "YES" : "NO"); if (configLoaded) { Serial1.println("✅ Configuration ready"); } } // تابع دیباگ برای نمایش وضعیت حافظه‌ها inline void memoryStatus() { Serial1.println("\n🔍 MEMORY STATUS"); Serial1.println("================"); // تست SPI Flash Serial1.print("SPI Flash: "); Serial1.println(testFlashCommunication() ? "✅ Available" : "❌ Not available"); // تست EEPROM داخلی Serial1.print("Internal EEPROM: "); Serial1.println(eepromIsAvailable() ? "✅ Available" : "❌ Not available"); // وضعیت کانفیگ فعلی Serial1.print("Config in RAM: "); Serial1.println(config.valid ? "✅ Valid" : "❌ Invalid"); Serial1.println("================\n"); } // تابع ریست کامل (برای تست) inline void resetAllConfig() { Serial1.println("\n⚠️ RESETTING ALL CONFIGURATION"); // پاک کردن SPI Flash if (testFlashCommunication()) { flashSectorErase(CONFIG_ADDRESS); Serial1.println("✅ SPI Flash erased"); } // پاک کردن EEPROM eepromClearConfig(); // ایجاد کانفیگ پیش‌فرض createDefaultConfig(); saveConfig(); Serial1.println("✅ All configuration reset to defaults\n"); } #endif // MEMORY_H