429 lines
11 KiB
C
429 lines
11 KiB
C
#ifndef MEMORY_H
|
|
#define MEMORY_H
|
|
|
|
#include <Arduino.h>
|
|
#include <SPI.h>
|
|
#include "Config.h"
|
|
|
|
// -------------------- کتابخانه EEPROM داخلی برای STM32 --------------------
|
|
#if defined(STM32F1xx) || defined(STM32F3xx) || defined(STM32F4xx)
|
|
#include <EEPROM.h>
|
|
#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
|