diff --git a/Config.h b/Config.h new file mode 100644 index 0000000..89a235f --- /dev/null +++ b/Config.h @@ -0,0 +1,172 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +// -------------------- پیکربندی -------------------- +#define FLASH_CS PA4 +#define FLASH_MOSI PA7 +#define FLASH_MISO PA6 +#define FLASH_SCK PA5 +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 +#define MQ7_PIN PA2 +#define SDA_PIN PB9 +#define SCL_PIN PB8 +#define PWRKEY_PIN PB5 +#define SENSOR_READ_INTERVAL 10000 + +#define POWER_PIN PA1 + + +#define VOLTAGE_DIVIDER_PIN PA0 // پین خواندن ولتاژ +#define VOLTAGE_DIVIDER_RATIO 2.0 // نسبت تقسیم (R1=R2=10k => نسبت = 2) +#define ADC_REF_VOLTAGE 3.3 // ولتاژ مرجع ADC در STM32 (معمولاً 3.3V) +#define ADC_RESOLUTION 4096 // رزولوشن ADC 12-bit = 4096 + + +// -------------------- آدرس‌های حافظه -------------------- +#define CONFIG_ADDRESS 0x000000 +#define DATA_ADDRESS 0x010000 + +// -------------------- آدرس حافظه داخلی STM32 -------------------- +#define INTERNAL_CONFIG_ADDR 0x8007800 // آخرین صفحه فلش در STM32F103C8 + +// -------------------- انوم‌ها -------------------- +enum SIMType { SIM_UNKNOWN = 0, SIM_HAMRAHE_AVAL = 1, SIM_IRANCELL = 2, SIM_RIGHTEL = 3 }; + +// -------------------- ساختارها -------------------- +struct DeviceConfig { + char signature[4]; + char deviceId[16]; + char serverPhoneNumber[16]; + char serverUrl[64]; + unsigned long uploadInterval; + unsigned long smsInterval; + unsigned long saveInterval; + SIMType simType; + bool smsEnabled; + bool verified; + bool valid; +}; + +struct SensorData { + float temperature; + float humidity; + float coPPM; + float lightLux; + unsigned long timestamp; + uint8_t sent; + float volage; + int power; +}; + +// -------------------- توابع کمکی -------------------- +inline String simTypeToString(SIMType type) { + switch(type) { + case SIM_HAMRAHE_AVAL: return "Hamrah Aval"; + case SIM_IRANCELL: return "Irancell"; + case SIM_RIGHTEL: return "Rightel"; + default: return "Unknown"; + } +} + +inline String simTypeToAPN(SIMType type) { + switch(type) { + case SIM_HAMRAHE_AVAL: return "mcinet"; + case SIM_IRANCELL: return "mtnirancell"; + case SIM_RIGHTEL: return "rightel"; + default: return "mcinet"; + } +} + +inline String generateVerificationCode(String tokenCode) { + long token = tokenCode.toInt(); + long verification = (token * 7 + 12345) % 100000; + char buffer[6]; + sprintf(buffer, "%05ld", verification); + return String(buffer); +} + +#endif // CONFIG_H + +// #ifndef CONFIG_H +// #define CONFIG_H + +// #include + +// // -------------------- پیکربندی -------------------- +// #define FLASH_CS PA4 +// #define FLASH_MOSI PA7 +// #define FLASH_MISO PA6 +// #define FLASH_SCK PA5 +// #define SCREEN_WIDTH 128 +// #define SCREEN_HEIGHT 64 +// #define OLED_RESET -1 +// #define MQ7_PIN PA2 +// #define SDA_PIN PB9 +// #define SCL_PIN PB8 +// #define PWRKEY_PIN PB5 +// #define SENSOR_READ_INTERVAL 60000 + +// // -------------------- آدرس‌های حافظه -------------------- +// #define CONFIG_ADDRESS 0x000000 +// #define DATA_ADDRESS 0x010000 + +// // -------------------- انوم‌ها -------------------- +// enum SIMType { SIM_UNKNOWN = 0, SIM_HAMRAHE_AVAL = 1, SIM_IRANCELL = 2, SIM_RIGHTEL = 3 }; + +// // -------------------- ساختارها -------------------- +// struct DeviceConfig { +// char signature[4]; +// char deviceId[16]; +// char serverPhoneNumber[16]; +// char serverUrl[64]; +// unsigned long uploadInterval; +// unsigned long smsInterval; +// unsigned long saveInterval; +// SIMType simType; +// bool smsEnabled; +// bool verified; +// bool valid; +// }; + +// struct SensorData { +// float temperature; +// float humidity; +// float coPPM; +// float lightLux; +// unsigned long timestamp; +// uint8_t sent; +// }; + +// // -------------------- توابع کمکی -------------------- +// inline String simTypeToString(SIMType type) { +// switch(type) { +// case SIM_HAMRAHE_AVAL: return "Hamrah Aval"; +// case SIM_IRANCELL: return "Irancell"; +// case SIM_RIGHTEL: return "Rightel"; +// default: return "Unknown"; +// } +// } + +// inline String simTypeToAPN(SIMType type) { +// switch(type) { +// case SIM_HAMRAHE_AVAL: return "mcinet"; +// case SIM_IRANCELL: return "mtnirancell"; +// case SIM_RIGHTEL: return "rightel"; +// default: return "mcinet"; +// } +// } + +// inline String generateVerificationCode(String tokenCode) { +// long token = tokenCode.toInt(); +// long verification = (token * 7 + 12345) % 100000; +// char buffer[6]; +// sprintf(buffer, "%05ld", verification); +// return String(buffer); +// } + +// #endif // CONFIG_H + diff --git a/Display.h b/Display.h new file mode 100644 index 0000000..074e1ee --- /dev/null +++ b/Display.h @@ -0,0 +1,320 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include +#include +#include +#include +#include +#include +#include "Config.h" + +// -------------------- متغیرهای خارجی -------------------- +extern Adafruit_SSD1306 display; +extern DeviceConfig config; +extern SensorData currentData; +extern bool sht31Connected; +extern bool lightSensorConnected; +extern bool coSensorConnected; +extern bool calibrationComplete; +extern bool awaitingSMS2; +extern int signalStrength; +extern String lastMessage; +extern int displayMode; +extern unsigned long lastDisplayChange; +extern unsigned long lastNetworkStatusUpdate; + +// Forward declaration +void updateNetworkStatus(); + +// -------------------- توابع نمایشگر -------------------- +inline void displayAllParameters() { + display.clearDisplay(); + //display.setTextColor(SSD1306_WHITE); + + // عنوان با فونت زیبا + display.setFont(&FreeSans9pt7b); + display.setCursor(8, 12); + display.print("All Parameters"); + + // خط جداکننده + display.drawLine(0, 15, 128, 15, SSD1306_WHITE); + + // ارتفاع 12-14 + // دما + display.setFont(&FreeSans9pt7b); + display.setCursor(1, 30); + display.print("T:");//16 پیکسل + display.setFont(&FreeSans9pt7b); + display.setCursor(18, 30); + display.print(currentData.temperature, 1);//34 پیکسل + display.setFont(&FreeSans9pt7b); + //display.setCursor(55, 30); + //display.print("-");//3 پیکسل + + // رطوبت + display.setCursor(65, 30); + display.print("H:");//16 پیکسل + display.setFont(&FreeSans9pt7b); + display.setCursor(83, 30); + display.print(currentData.humidity, 0);//34 پیکسل + display.setFont(&FreeSans9pt7b); + display.setCursor(110, 31); + display.print("%"); + + // نور + display.setCursor(1, 55); + display.print("L:");//15 پیکسل + display.setFont(&FreeSans9pt7b); + display.setCursor(20, 55); + display.print((int)currentData.lightLux);//40 پیکسل + + //گاز + display.setCursor(70, 55); + display.print("G:");//15 پیکسل + display.setFont(&FreeSans9pt7b); + display.setCursor(88, 55); + display.print((int)currentData.coPPM);//40 پیکسل + + // برگشت به فونت پیش‌فرض + display.setFont(); + display.display(); +} + +inline void displayTemperature() { + display.clearDisplay(); + + // عنوان + display.setFont(&FreeSans9pt7b); + display.setCursor(18, 14); + display.print("Temperature"); + + // خط جداکننده + display.drawLine(0, 18, 128, 18, SSD1306_WHITE); + + // مقدار دما - عدد بزرگ و نرم + display.setFont(&FreeSansBold18pt7b); + display.setCursor(10, 50); + display.print(currentData.temperature, 1); + + // واحد + display.setFont(&FreeSansBold12pt7b); + display.setCursor(90, 45); + display.print("C"); + + // علامت درجه + display.drawCircle(85, 30, 3, SSD1306_WHITE); + + // وضعیت سنسور + display.setFont(&FreeSans9pt7b); + display.setCursor(50, 63); + if (!sht31Connected) { + display.print("ERR"); + } + + display.setFont(); + display.display(); +} + +inline void displayHumidity() { + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + + // عنوان + display.setFont(&FreeSans9pt7b); + display.setCursor(30, 14); + display.print("Humidity"); + + // خط جداکننده + display.drawLine(0, 18, 128, 18, SSD1306_WHITE); + + // مقدار رطوبت - عدد بزرگ و نرم + display.setFont(&FreeSansBold18pt7b); + display.setCursor(25, 50); + display.print(currentData.humidity, 0); + + // واحد درصد + display.setFont(&FreeSansBold12pt7b); + display.setCursor(75, 48); + display.print("%"); + + // وضعیت سنسور + display.setFont(&FreeSans9pt7b); + display.setCursor(50, 63); + if (!sht31Connected) { + display.print("ERR"); + } + + display.setFont(); + display.display(); +} + +inline void displayCO() { + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + + // عنوان + display.setFont(&FreeSans9pt7b); + display.setCursor(35, 14); + display.print("CO Gas"); + + // خط جداکننده + display.drawLine(0, 18, 128, 18, SSD1306_WHITE); + + // مقدار CO (منفی = در حال کالیبره) - عدد بزرگ و نرم + display.setFont(&FreeSansBold18pt7b); + display.setCursor(8, 50); + display.print(currentData.coPPM, 0); + + // واحد + display.setFont(&FreeSans9pt7b); + display.setCursor(70, 48); + display.print("ppm"); + + // وضعیت سنسور و کالیبراسیون + display.setCursor(5, 63); + if (!calibrationComplete) { + display.print("Calibrating..."); + } else if (!coSensorConnected) { + display.print("Sensor ERR"); + } + + display.setFont(); + display.display(); +} + +inline void displayLight() { + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + + // عنوان + display.setFont(&FreeSans9pt7b); + display.setCursor(40, 14); + display.print("Light"); + + // خط جداکننده + display.drawLine(0, 18, 128, 18, SSD1306_WHITE); + + // مقدار نور - عدد بزرگ و نرم + display.setFont(&FreeSansBold18pt7b); + display.setCursor(8, 50); + display.print((int)currentData.lightLux); + + // واحد + display.setFont(&FreeSans9pt7b); + display.setCursor(75, 48); + display.print("lux"); + + // وضعیت سنسور + display.setCursor(50, 63); + if (!lightSensorConnected) { + display.print("ERR"); + } + + display.setFont(); + display.display(); +} + +inline void displaySIMStatus() { + display.clearDisplay(); + display.setTextColor(SSD1306_WHITE); + + // عنوان + display.setFont(&FreeSans9pt7b); + display.setCursor(25, 14); + display.print("SIM Status"); + + // خط جداکننده + display.drawLine(0, 18, 128, 18, SSD1306_WHITE); + + // بقیه اطلاعات با فونت پیش‌فرض برای جا شدن بیشتر + display.setFont(); + + if (!config.verified) { + // وضعیت تنظیم نشده + display.setTextSize(1); + display.setCursor(20, 28); + if (!awaitingSMS2) { + display.print("Waiting SMS1"); + display.setCursor(15, 42); + display.print("for activation"); + } else { + display.print("Waiting SMS2"); + display.setCursor(15, 42); + display.print("for config"); + } + } else { + // وضعیت تنظیم شده + display.setTextSize(1); + display.setCursor(5, 22); + display.print("ID:"); + display.setCursor(25, 22); + display.print(config.deviceId); + + display.setCursor(5, 32); + display.print("SIM:"); + display.setCursor(30, 32); + display.print(simTypeToString(config.simType)); + + display.setCursor(5, 42); + display.print("Upload:"); + display.setCursor(50, 42); + display.print(config.uploadInterval); + display.print("m"); + + // سیگنال با نشانگر گرافیکی + display.setCursor(80, 42); + display.print("Sig:"); + display.setCursor(105, 42); + display.print(signalStrength); + } + + // آخرین پیام + display.setFont(&FreeSans9pt7b); + display.setCursor(5, 62); + if (lastMessage.length() > 14) { + display.print(lastMessage.substring(0, 14)); + } else { + display.print(lastMessage); + } + + display.setFont(); + display.display(); +} + +inline void updateDisplay() { + // تغییر صفحه هر 3 ثانیه + if (millis() - lastDisplayChange > 3000) { + displayMode = (displayMode + 1) % 6; // 6 صفحه داریم + lastDisplayChange = millis(); + } + + // به‌روزرسانی وضعیت شبکه هر 30 ثانیه + if (millis() - lastNetworkStatusUpdate > 30000) { + updateNetworkStatus(); + lastNetworkStatusUpdate = millis(); + } + switch(displayMode) { + case 0: + displayAllParameters(); + break; + case 1: + displayTemperature(); + break; + case 2: + displayHumidity(); + break; + case 3: + displayCO(); + break; + case 4: + displayLight(); + break; + case 5: + displaySIMStatus(); + break; + } +} + +#endif // DISPLAY_H + diff --git a/EC200U.h b/EC200U.h new file mode 100644 index 0000000..a252ccd --- /dev/null +++ b/EC200U.h @@ -0,0 +1,1363 @@ +#ifndef EC200U_H +#define EC200U_H + +#include +#include "Config.h" +#include "Memory.h" + +// -------------------- متغیرهای خارجی -------------------- +extern HardwareSerial EC200U; +extern DeviceConfig config; +extern SensorData currentData; +extern bool networkConnected; +extern bool simConnected; +extern bool networkRegistered; +extern int signalStrength; +extern String lastMessage; +extern String currentAPN; +extern String tempPhoneNumber; +extern String tempTokenCode; +extern bool awaitingSMS2; +extern bool isInCall; +extern bool calibrationComplete; +extern unsigned long lastUpload; +// Forward declarations +void initEC200U(); +void updateNetworkStatus(); +// -------------------- توابع پایه EC200U -------------------- +inline bool sendAT(String cmd, uint16_t wait = 2000, bool showResponse = false) { + if (showResponse) { + Serial1.print(">> "); + Serial1.println(cmd); + } + EC200U.println(cmd); + + String response = ""; + unsigned long start = millis(); + + while (millis() - start < wait) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + if (showResponse) Serial1.write(c); + } + if (response.indexOf("OK") != -1) return true; + if (response.indexOf("ERROR") != -1) return false; + } + return false; +} + +inline bool sendSMS(String number, String message) { + if (!sendAT("AT+CMGF=1", 3000, false)) { + return false; + } + + delay(200); + + EC200U.print("AT+CMGS=\""); + EC200U.print(number); + EC200U.println("\""); + + String response = ""; + unsigned long start = millis(); + bool gotPrompt = false; + + while (millis() - start < 5000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + if (response.indexOf(">") != -1) { + gotPrompt = true; + break; + } + } + if (gotPrompt) break; + } + + if (!gotPrompt) { + return false; + } + + EC200U.print(message); + EC200U.write(26); + + response = ""; + start = millis(); + bool gotResponse = false; + + while (millis() - start < 15000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + if (response.indexOf("+CMGS:") != -1) { + gotResponse = true; + break; + } + if (response.indexOf("ERROR") != -1) return false; + } + if (gotResponse) break; + } + + if (!gotResponse) return false; + + delay(1000); + sendAT("AT+CMGD=1,4", 3000, false); + return true; +} + +// -------------------- پردازش SMS -------------------- +inline void extractNumbersFromSMS2(String message, String numbers[], int &count) { + count = 0; + String current = ""; + + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + if (c >= '0' && c <= '9') { + current += c; + } else { + if (current.length() > 0) { + if (count < 10) { + numbers[count] = current; + count++; + } + current = ""; + } + } + } + + if (current.length() > 0 && count < 10) { + numbers[count] = current; + count++; + } +} +inline void updateNetworkStatus() { + if (!config.verified) { + simConnected = false; + networkRegistered = false; + signalStrength = 0; + return; + } + + simConnected = sendAT("AT", 1000, false); + + EC200U.println("AT+CSQ"); + String response = ""; + unsigned long start = millis(); + while (millis() - start < 2000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + } + if (response.indexOf("OK") != -1 || response.indexOf("ERROR") != -1) break; + } + + if (response.indexOf("+CSQ:") != -1) { + int idx = response.indexOf("+CSQ:"); + int comma = response.indexOf(',', idx); + if (comma != -1) { + String rssiStr = response.substring(idx + 6, comma); + rssiStr.trim(); + signalStrength = rssiStr.toInt(); + if (signalStrength > 31) signalStrength = 0; + } + } + + EC200U.println("AT+CREG?"); + response = ""; + start = millis(); + while (millis() - start < 2000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + } + if (response.indexOf("OK") != -1 || response.indexOf("ERROR") != -1) break; + } + + networkRegistered = (response.indexOf("+CREG:") != -1 && + (response.indexOf(",1,") != -1 || response.indexOf(",5,") != -1)); +} +inline int detectSMSType(String message) { + int count = 0; + String current = ""; + + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + if (c >= '0' && c <= '9') { + current += c; + } else { + if (current.length() > 0) { + count++; + current = ""; + } + } + } + if (current.length() > 0) { + count++; + } + + return (count >= 5) ? 2 : 1; +} +inline void connectToNetwork() { + Serial1.println("\n======== NETWORK CONNECTION ========"); + lastMessage = "Connecting"; + + if (!config.verified) { + Serial1.println("❌ Cannot connect: Device not configured"); + return; + } + + currentAPN = simTypeToAPN(config.simType); + Serial1.print("Setting APN: "); + Serial1.println(currentAPN); + String apnCmd = "AT+CGDCONT=1,\"IP\",\"" + currentAPN + "\""; + sendAT(apnCmd, 2000, false); + + Serial1.println("Waiting for network registration..."); + bool registered = false; + for (int attempt = 1; attempt <= 30; attempt++) { + EC200U.println("AT+CREG?"); + String response = ""; + unsigned long start = millis(); + while (millis() - start < 2000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + } + if (response.indexOf("OK") != -1) break; + } + + if (response.indexOf(",1") != -1 || response.indexOf(",5") != -1) { + registered = true; + Serial1.print("✅ Network registered after "); + Serial1.print(attempt); + Serial1.println(" seconds"); + break; + } + + Serial1.print(" Attempt "); + Serial1.print(attempt); + Serial1.println("/30 - Searching..."); + delay(1000); + } + + if (!registered) { + Serial1.println("⚠️ Network registration timeout - continuing anyway"); + } + + Serial1.println("Deactivating old PDP context..."); + sendAT("AT+QIDEACT=1", 5000, false); + delay(1000); + + Serial1.println("Activating PDP context..."); + if (sendAT("AT+QIACT=1", 30000, false)) { + networkConnected = true; + lastMessage = "Online"; + Serial1.println("✅ Internet connection established!"); + } else { + Serial1.println("First attempt failed, retrying..."); + delay(2000); + if (sendAT("AT+QIACT=1", 30000, false)) { + networkConnected = true; + lastMessage = "Online"; + Serial1.println("✅ Internet connection established on retry!"); + } else { + networkConnected = false; + lastMessage = "Offline"; + Serial1.println("❌ Failed to connect to internet"); + } + } + + if (networkConnected) { + Serial1.println("Configuring SSL for HTTPS..."); + sendAT("AT+QSSLCFG=\"sslversion\",1,4", 2000, false); + sendAT("AT+QSSLCFG=\"ciphersuite\",1,0xFFFF", 2000, false); + sendAT("AT+QSSLCFG=\"seclevel\",1,0", 2000, false); + sendAT("AT+QHTTPCFG=\"sslctxid\",1", 2000, false); + Serial1.println("✅ SSL configured for HTTPS"); + } + + updateNetworkStatus(); + + Serial1.println("--- NETWORK STATUS ---"); + Serial1.print(" SIM: "); + Serial1.println(simConnected ? "Connected" : "Not detected"); + Serial1.print(" Network: "); + Serial1.println(networkRegistered ? "Registered" : "Searching"); + Serial1.print(" Signal: "); + Serial1.print(signalStrength); + Serial1.println("/31"); + Serial1.print(" Internet: "); + Serial1.println(networkConnected ? "Online" : "Offline"); + Serial1.println("=================================\n"); +} + +inline void processSMS1(String message) { + Serial1.println("\n======== SMS1 RECEIVED ========"); + Serial1.print("Raw Message: "); + Serial1.println(message); + + String numbers[10]; + int count = 0; + extractNumbersFromSMS2(message, numbers, count); + + Serial1.print("Numbers found: "); + Serial1.println(count); + for (int i = 0; i < count; i++) { + Serial1.print(" ["); + Serial1.print(i); + Serial1.print("]: "); + Serial1.println(numbers[i]); + } + + String phone = ""; + String token = ""; + + if (count >= 2) { + phone = numbers[0]; + token = numbers[1]; + } + + Serial1.print("Phone: "); + Serial1.println(phone); + Serial1.print("Token: "); + Serial1.println(token); + + if (phone.length() >= 10 && token.length() == 5) { + tempPhoneNumber = phone; + tempTokenCode = token; + + String verifyCode = generateVerificationCode(token); + String reply = "Code: " + verifyCode; + + Serial1.print("Generated Verification Code: "); + Serial1.println(verifyCode); + Serial1.print("Sending SMS to: "); + Serial1.println(phone); + + if (sendSMS(phone, reply)) { + Serial1.println("✅ SMS1: Verification code sent successfully!"); + awaitingSMS2 = true; + lastMessage = "Wait SMS2"; + } else { + Serial1.println("❌ SMS1: Failed to send verification code"); + lastMessage = "SMS1 Failed"; + } + } else { + Serial1.println("❌ SMS1: Invalid format - phone or token missing"); + Serial1.print(" Phone length: "); + Serial1.print(phone.length()); + Serial1.print(", Token length: "); + Serial1.println(token.length()); + lastMessage = "Invalid SMS1"; + } + Serial1.println("================================\n"); +} + +inline void processSMS2(String message) { + Serial1.println("\n======== SMS2 RECEIVED ========"); + Serial1.print("Raw Message: "); + Serial1.println(message); + + String numbers[10]; + int count = 0; + extractNumbersFromSMS2(message, numbers, count); + + Serial1.print("Numbers found: "); + Serial1.println(count); + for (int i = 0; i < count; i++) { + Serial1.print(" ["); + Serial1.print(i); + Serial1.print("]: "); + Serial1.println(numbers[i]); + } + + if (count >= 5) { + // 1. Device ID + String deviceId = numbers[0]; + Serial1.print("Original Device ID: "); + Serial1.println(deviceId); + if (deviceId.length() > 2) { + deviceId = deviceId.substring(0, deviceId.length() - 2); + } + deviceId.toCharArray(config.deviceId, 16); + Serial1.print("Parsed Device ID: "); + Serial1.println(config.deviceId); + + // 2. Upload Interval + if (numbers[1].length() > 1) { + config.uploadInterval = numbers[1].substring(1).toInt(); + } else { + config.uploadInterval = numbers[1].toInt(); + } + Serial1.print("Upload Interval: "); + Serial1.print(config.uploadInterval); + Serial1.println(" min"); + + // 3. SMS Settings + if (numbers[2].length() >= 2) { + config.smsEnabled = (numbers[2].charAt(0) == '1'); + config.smsInterval = numbers[2].substring(1).toInt(); + } else { + config.smsEnabled = false; + config.smsInterval = 0; + } + Serial1.print("SMS Enabled: "); + Serial1.println(config.smsEnabled ? "Yes" : "No"); + Serial1.print("SMS Interval: "); + Serial1.print(config.smsInterval); + Serial1.println(" min"); + + // 4. SIM Type (index 4 if 6 numbers, else last) + int simIndex = (count >= 6) ? 4 : (count - 1); + String simValue = numbers[simIndex]; + Serial1.print("SIM Value (raw): "); + Serial1.println(simValue); + + if (simValue.length() >= 2) { + String simCode = simValue.substring(simValue.length() - 2); + if (simCode == "21") config.simType = SIM_HAMRAHE_AVAL; + else if (simCode == "22") config.simType = SIM_IRANCELL; + else if (simCode == "23") config.simType = SIM_RIGHTEL; + else if (simCode == "71") config.simType = SIM_HAMRAHE_AVAL; + else config.simType = SIM_UNKNOWN; + } else { + int simInt = simValue.toInt(); + if (simInt == 21 || simInt == 71) config.simType = SIM_HAMRAHE_AVAL; + else if (simInt == 22) config.simType = SIM_IRANCELL; + else if (simInt == 23) config.simType = SIM_RIGHTEL; + else config.simType = SIM_UNKNOWN; + } + Serial1.print("SIM Type: "); + Serial1.println(simTypeToString(config.simType)); + + // 5. Phone number + if (tempPhoneNumber.length() > 0) { + tempPhoneNumber.toCharArray(config.serverPhoneNumber, 16); + } else { + strcpy(config.serverPhoneNumber, ""); + } + Serial1.print("Server Phone: "); + Serial1.println(config.serverPhoneNumber); + + // 6. Save and connect + config.verified = true; + saveConfig(); + + currentAPN = simTypeToAPN(config.simType); +awaitingSMS2 = false; + + Serial1.println("\n✅ SMS2: Configuration Complete!"); + Serial1.println("--- FINAL CONFIG ---"); + Serial1.print(" Device ID: "); + Serial1.println(config.deviceId); + Serial1.print(" SIM Type: "); + Serial1.println(simTypeToString(config.simType)); + Serial1.print(" APN: "); + Serial1.println(currentAPN); + Serial1.print(" Upload Int: "); + Serial1.print(config.uploadInterval); + Serial1.println(" min"); + Serial1.print(" SMS Enabled: "); + Serial1.println(config.smsEnabled ? "Yes" : "No"); + Serial1.print(" Server Phone: "); + Serial1.println(config.serverPhoneNumber); + Serial1.println("--------------------"); + + lastMessage = "Configured"; + Serial1.println("Connecting to network..."); + connectToNetwork(); // به جای initEC200U() از این تابع استفاده شود + } else { + Serial1.println("❌ SMS2: Invalid format - need at least 5 numbers"); + Serial1.print(" Found only: "); + Serial1.println(count); + lastMessage = "Invalid SMS2"; + } + Serial1.println("================================\n"); +} + +inline void checkSMS() { + if (!sendAT("AT", 2000, false)) return; + + sendAT("AT+CMGF=1", 2000, false); + delay(100); + + EC200U.println("AT+CMGL=\"REC UNREAD\""); + + String response = ""; + unsigned long start = millis(); + while (millis() - start < 5000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + } + if (response.indexOf("OK") != -1) break; + } + + if (response.indexOf("+CMGL:") != -1) { + int msgIndex = response.indexOf("+CMGL:"); + int comma1 = response.indexOf(',', msgIndex); + int comma2 = response.indexOf(',', comma1 + 1); + int quote1 = response.indexOf('\"', comma2 + 1); + int quote2 = response.indexOf('\"', quote1 + 1); + + if (quote1 != -1 && quote2 != -1) { + int msgStart = response.indexOf('\n', quote2) + 1; + int msgEnd = response.indexOf('\n', msgStart); + if (msgEnd == -1) msgEnd = response.length(); + + String message = response.substring(msgStart, msgEnd); + message.trim(); + + Serial1.println("\n📱 SMS received"); + + int smsType = detectSMSType(message); + + if (config.verified) { + Serial1.println("⚠️ Already configured - SMS ignored"); + lastMessage = "Already Configured"; + } else if (smsType == 1 && !awaitingSMS2) { + processSMS1(message); + } else if (smsType == 2 && awaitingSMS2) { + processSMS2(message); + } else if (smsType == 2 && !awaitingSMS2 && !config.verified) { + // این حالت برای زمانی است که مستقیم SMS2 دریافت شود + processSMS2(message); + } else { + Serial1.println("❌ Invalid SMS or wrong state"); + lastMessage = "Invalid SMS"; + } + + String indexStr = response.substring(msgIndex + 6, comma1); + indexStr.trim(); + sendAT("AT+CMGD=" + indexStr, 2000, false); + } + } +} + +// -------------------- توابع مدیریت EC200U -------------------- +inline void powerOffEC200U() { + Serial1.println("Powering off EC200U..."); + + sendAT("AT+QPOWD", 3000, true); + delay(3000); + + digitalWrite(PWRKEY_PIN, HIGH); + delay(1500); + digitalWrite(PWRKEY_PIN, LOW); + + Serial1.println("EC200U powered off"); + networkConnected = false; +} + + +inline void initEC200U() { + // حذف شرط config.verified - همیشه اجرا شود + Serial1.println("\n======== MODEM INITIALIZATION ========"); + lastMessage = "Modem Initializing"; + + Serial1.println("Checking if modem is already running..."); + bool modemAlreadyOn = sendAT("AT", 2000, false); + + if (modemAlreadyOn) { + Serial1.println("✅ Modem is already ON"); + lastMessage = "Modem Ready"; + } else { + Serial1.println("Modem not responding, powering on..."); + + Serial1.println(" PWRKEY -> HIGH (MOSFET ON, PWRKEY to GND)"); + digitalWrite(PWRKEY_PIN, HIGH); + delay(1500); + Serial1.println(" PWRKEY -> LOW (MOSFET OFF, PWRKEY floating)"); + digitalWrite(PWRKEY_PIN, LOW); + + Serial1.println("Waiting for modem to boot (8s)..."); + delay(8000); + + if (!sendAT("AT", 5000, false)) { + Serial1.println("⚠️ First attempt failed, trying again with longer pulse..."); + digitalWrite(PWRKEY_PIN, HIGH); + delay(2000); + digitalWrite(PWRKEY_PIN, LOW); + delay(10000); + + if (!sendAT("AT", 5000, false)) { + Serial1.println("❌ Modem not responding after power on!"); + lastMessage = "Modem Err"; + Serial1.println("=================================\n"); + return; + } + } + Serial1.println("✅ Modem powered on successfully"); + lastMessage = "Modem Ready"; + } + + digitalWrite(PWRKEY_PIN, LOW); + + // تنظیمات اولیه + sendAT("AT+CMGF=1", 2000, false); // تنظیم فرمت SMS + sendAT("AT+CNMI=2,1,0,0,0", 2000, false); // اعلان SMS جدید + + // فقط اگر config.verified بود به شبکه وصل شود + if (config.verified) { + Serial1.println("Device is configured, connecting to network..."); + connectToNetwork(); + } else { + Serial1.println("⏳ Device not configured. Waiting for configuration SMS..."); + Serial1.println("📱 Send SMS1 format: PHONENUMBER TOKENCODE"); + lastMessage = "Wait SMS1"; + } + + updateNetworkStatus(); + Serial1.println("=================================\n"); +} + +// -------------------- توابع SSL و HTTP -------------------- +inline void configureSSL() { + Serial1.println("Configuring SSL for HTTPS..."); + + sendAT("AT+QSSLCFG=\"sslversion\",1,4", 2000, true); + sendAT("AT+QSSLCFG=\"ciphersuite\",1,0xFFFF", 2000, true); + sendAT("AT+QSSLCFG=\"seclevel\",1,0", 2000, true); + sendAT("AT+QHTTPCFG=\"sslctxid\",1", 2000, true); + + Serial1.println("✅ SSL configured"); +} + +inline String readHttpResponse() { + Serial1.println("Reading HTTP response body..."); + + while (EC200U.available()) { + EC200U.read(); + } + + EC200U.println("AT+QHTTPREAD=80"); + + char buffer[600]; + int bufIndex = 0; + unsigned long start = millis(); + bool gotConnect = false; + int bodyStart = -1; + + while (millis() - start < 30000 && bufIndex < 599) { + if (EC200U.available()) { + char c = EC200U.read(); + buffer[bufIndex++] = c; + Serial1.write(c); + + if (!gotConnect && bufIndex >= 7) { + if (strncmp(&buffer[bufIndex-7], "CONNECT", 7) == 0) { + gotConnect = true; + } + } + } + + if (bufIndex >= 12) { + buffer[bufIndex] = '\0'; + if (strstr(buffer, "+QHTTPREAD: 0") != NULL) { + delay(100); + while (EC200U.available() && bufIndex < 599) { + buffer[bufIndex++] = EC200U.read(); + } + break; + } + if (strstr(buffer, "ERROR") != NULL) { + Serial1.println("\n❌ Error reading HTTP response"); + return ""; + } + } + } + + buffer[bufIndex] = '\0'; + + Serial1.println("\n--- Raw buffer ---"); + Serial1.println(buffer); + Serial1.println("--- End raw ---"); + + String body = ""; + + char* connectPtr = strstr(buffer, "CONNECT"); + if (connectPtr != NULL) { + char* bodyPtr = strchr(connectPtr, '\n'); + if (bodyPtr != NULL) { + bodyPtr++; + + char* endPtr = strstr(bodyPtr, "\r\nOK"); + if (endPtr == NULL) endPtr = strstr(bodyPtr, "\nOK"); + if (endPtr == NULL) endPtr = strstr(bodyPtr, "+QHTTPREAD"); + + if (endPtr != NULL) { + int len = endPtr - bodyPtr; + body.reserve(len + 1); + for (int i = 0; i < len; i++) { + if (bodyPtr[i] != '\r' && bodyPtr[i] != '\n') { + body += bodyPtr[i]; + } + } + } else { + body = String(bodyPtr); + } + } + } + + body.trim(); + + Serial1.print("\n--- Extracted Body ("); + Serial1.print(body.length()); + Serial1.print(" chars) ---\n["); + Serial1.print(body); + Serial1.println("]"); + Serial1.println("--- End Body ---"); + + return body; +} + +// -------------------- توابع دانلود و تماس صوتی -------------------- +inline bool downloadAMRFile(String url) { + Serial1.println("\n--- Downloading AMR File ---"); + Serial1.print("URL ("); + Serial1.print(url.length()); + Serial1.print(" chars): ["); + Serial1.print(url); + Serial1.println("]"); + + if (url.length() < 10 || !url.startsWith("http")) { + Serial1.println("❌ Invalid URL!"); + return false; + } + + sendAT("AT+QFDEL=\"UFS:audio.amr\"", 2000, false); + delay(100); + + int urlLen = url.length(); + Serial1.print("Setting URL with length: "); + Serial1.println(urlLen); + + String cmd = "AT+QHTTPURL=" + String(urlLen) + ",80"; + Serial1.print("Command: "); + Serial1.println(cmd); + EC200U.println(cmd); + + String response = ""; + unsigned long start = millis(); + bool gotConnect = false; + + while (millis() - start < 5000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + } + if (response.indexOf("CONNECT") != -1) { + gotConnect = true; + break; + } + if (response.indexOf("ERROR") != -1) { + Serial1.println("❌ QHTTPURL error"); + return false; + } + } + + if (!gotConnect) { + Serial1.println("❌ No CONNECT for URL"); + return false; + } + + EC200U.print(url); + delay(1000); + + response = ""; + start = millis(); + while (millis() - start < 3000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + } + if (response.indexOf("OK") != -1) break; + } + + if (response.indexOf("OK") == -1) { + Serial1.println("❌ URL not accepted"); + return false; + } + + Serial1.println("✅ URL set successfully"); + delay(200); + + Serial1.println("Step 1: Sending HTTP GET request..."); + EC200U.println("AT+QHTTPGET=60"); + + response = ""; + start = millis(); + bool httpSuccess = false; + + while (millis() - start < 65000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + + int qhttpIdx = response.indexOf("+QHTTPGET:"); + if (qhttpIdx != -1) { + String afterQhttp = response.substring(qhttpIdx); + int firstComma = afterQhttp.indexOf(','); + int secondComma = afterQhttp.indexOf(',', firstComma + 1); + + if (firstComma != -1 && secondComma != -1) { + int errorCode = afterQhttp.substring(11, firstComma).toInt(); + int httpCode = afterQhttp.substring(firstComma + 1, secondComma).toInt(); + + Serial1.print("\n Error code: "); + Serial1.print(errorCode); + Serial1.print(", HTTP code: "); + Serial1.println(httpCode); + + if (errorCode == 0 && httpCode >= 200 && httpCode < 300) { + httpSuccess = true; + } + break; + } + } + + if (response.indexOf("ERROR") != -1) { + Serial1.println("\n❌ HTTP GET failed"); + return false; + } + } + + if (!httpSuccess) { + Serial1.println("\n❌ HTTP GET timeout or failed"); + return false; + } + + Serial1.println("✅ HTTP GET successful"); + + delay(500); + while (EC200U.available()) EC200U.read(); + + Serial1.println("Step 2: Saving response to file..."); + EC200U.println("AT+QHTTPREADFILE=\"UFS:audio.amr\",80"); + + response = ""; + start = millis(); + bool success = false; + + while (millis() - start < 60000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + + if (response.indexOf("+QHTTPREADFILE: 0") != -1) { + success = true; + break; + } + if (response.indexOf("ERROR") != -1) { + break; + } + } + + if (success) { + Serial1.println("\n✅ AMR file downloaded to RAM"); + return true; + } else { + Serial1.println("\n❌ Failed to save AMR file"); + return false; + } +} + +inline int getCallState(String clccResponse) { + int clccIdx = clccResponse.indexOf("+CLCC:"); + if (clccIdx == -1) return -1; + + String afterClcc = clccResponse.substring(clccIdx + 7); + int firstComma = afterClcc.indexOf(','); + if (firstComma == -1) return -1; + int secondComma = afterClcc.indexOf(',', firstComma + 1); + if (secondComma == -1) return -1; + int thirdComma = afterClcc.indexOf(',', secondComma + 1); + if (thirdComma == -1) return -1; + + String statStr = afterClcc.substring(secondComma + 1, thirdComma); + statStr.trim(); + return statStr.toInt(); +} + +inline bool makeVoiceCallWithAudio(String phoneNumber) { + isInCall = true; + Serial1.println("\n--- Making Voice Call ---"); + Serial1.print("Phone: "); + Serial1.println(phoneNumber); + + //digitalWrite(PWRKEY_PIN, LOW); + + // مکث کوتاه بعد از ترافیک HTTP برای پایدار شدن مودم + delay(1000); + + String dialCmd = "ATD" + phoneNumber + ";"; + EC200U.println(dialCmd); + + String response = ""; + unsigned long start = millis(); + bool callAnswered = false; + + while (millis() - start < 10000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + if (response.indexOf("OK") != -1) break; + if (response.indexOf("ERROR") != -1 || + response.indexOf("NO CARRIER") != -1 || + response.indexOf("BUSY") != -1) { + Serial1.println("❌ Call initiation failed"); + isInCall = false; + return false; + } + } + + Serial1.println("📞 Call initiated, waiting for answer..."); + + start = millis(); + while (millis() - start < 60000) { + while (EC200U.available()) { + char c = EC200U.read(); + Serial1.write(c); + } + + EC200U.println("AT+CLCC"); + delay(500); + + String clccResp = ""; + unsigned long clccStart = millis(); + while (millis() - clccStart < 2000) { + while (EC200U.available()) { + char c = EC200U.read(); + clccResp += c; + Serial1.write(c); + } + if (clccResp.indexOf("OK") != -1) break; + } + + int callState = getCallState(clccResp); + Serial1.print(" Call state: "); + Serial1.println(callState); + + if (callState == 0) { + callAnswered = true; + Serial1.println("✅ Call answered!"); + break; + } else if (callState == 2) { + Serial1.println(" ⏳ Dialing..."); + } else if (callState == 3) { + Serial1.println(" 🔔 Ringing..."); + } else if (callState == -1) { + if (clccResp.indexOf("NO CARRIER") != -1 || + clccResp.indexOf("+CLCC:") == -1) { + Serial1.println("❌ Call ended or no answer"); + break; + } + } + + delay(2000); + } + + if (!callAnswered) { + Serial1.println("❌ Call not answered"); + sendAT("ATH", 2000, false); + isInCall = false; + return false; + } + + Serial1.println("✅ Call connected, playing audio..."); + + sendAT("AT+QAUDMOD=0", 1000, false); + + EC200U.println("AT+QPSND=1,\"UFS:audio.amr\""); + + response = ""; + start = millis(); + bool playStarted = false; + + while (millis() - start < 5000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + if (response.indexOf("OK") != -1) { + playStarted = true; + break; + } + if (response.indexOf("ERROR") != -1) { + Serial1.println("❌ Failed to play audio"); + break; + } + } + + if (playStarted) { + Serial1.println("✅ Audio playing..."); + + bool playEnded = false; + start = millis(); + + while (millis() - start < 120000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + + if (response.indexOf("+QPSND: 0") != -1 || + response.indexOf("QPSND: END") != -1) { + playEnded = true; + break; + } + + if (response.indexOf("NO CARRIER") != -1) { + Serial1.println("Call ended by other party"); + break; + } + + delay(500); + } + + if (playEnded) { + Serial1.println("✅ Audio playback completed"); + } + } + + delay(500); + Serial1.println("Hanging up..."); + sendAT("ATH", 3000, true); + + delay(2000); + + Serial1.println("✅ Call ended"); + isInCall = false; + return true; +} + +inline void deleteAMRFile() { + Serial1.println("Deleting AMR file from RAM..."); + sendAT("AT+QFDEL=\"UFS:audio.amr\"", 2000, false); + Serial1.println("✅ File deleted"); +} + +// -------------------- پردازش پاسخ سرور -------------------- +inline void processServerResponse(String response) { + if (response.length() == 0) { + Serial1.println("Empty response, no action needed"); + return; + } + + Serial1.println("\n======== PROCESSING SERVER RESPONSE ========"); + Serial1.print("Response: "); + Serial1.println(response); + + if (response.startsWith("TT")) { + Serial1.println("Response Type: TT (Send SMS)"); + + String data = response.substring(2); + + int hashIdx = data.indexOf('#'); + if (hashIdx != -1) { + String phoneNumber = data.substring(0, hashIdx); + String message = data.substring(hashIdx + 1); + + phoneNumber.trim(); + message.trim(); + + Serial1.print(" Phone: "); + Serial1.println(phoneNumber); + Serial1.print(" Message: "); + Serial1.println(message); + + if (phoneNumber.length() >= 10 && message.length() > 0) { + if (sendSMS(phoneNumber, message)) { + Serial1.println("✅ SMS sent successfully"); + lastMessage = "TT SMS Sent"; + } else { + Serial1.println("❌ Failed to send SMS"); + lastMessage = "TT SMS Fail"; + } + } else { + Serial1.println("❌ Invalid phone or message"); + lastMessage = "TT Invalid"; + } + } else { + Serial1.println("❌ Invalid TT format (no #)"); + lastMessage = "TT Format Err"; + } + + } else if (response.startsWith("TY")) { + Serial1.println("Response Type: TY (Voice Call with Audio)"); + + String data = response.substring(2); + + Serial1.print("Data after TY removal ("); + Serial1.print(data.length()); + Serial1.print(" chars): "); + Serial1.println(data); + + int hashIdx = data.indexOf('#'); + Serial1.print("Hash index: "); + Serial1.println(hashIdx); + + if (hashIdx != -1) { + String phoneNumber = data.substring(0, hashIdx); + String audioUrl = data.substring(hashIdx + 1); + + phoneNumber.trim(); + audioUrl.trim(); + + Serial1.print("Phone length: "); + Serial1.println(phoneNumber.length()); + Serial1.print("URL length: "); + Serial1.println(audioUrl.length()); + + Serial1.print(" Phone: "); + Serial1.println(phoneNumber); + Serial1.print(" Audio URL: "); + Serial1.println(audioUrl); + + if (phoneNumber.length() >= 10 && audioUrl.length() > 0) { + if (downloadAMRFile(audioUrl)) { + if (makeVoiceCallWithAudio(phoneNumber)) { + Serial1.println("✅ Voice call completed"); + lastMessage = "TY Call Done"; + } else { + Serial1.println("❌ Voice call failed"); + lastMessage = "TY Call Fail"; + } + + deleteAMRFile(); + } else { + Serial1.println("❌ Failed to download audio"); + lastMessage = "TY DL Fail"; + } + } else { + Serial1.println("❌ Invalid phone or URL"); + lastMessage = "TY Invalid"; + } + } else { + Serial1.println("❌ Invalid TY format (no #)"); + lastMessage = "TY Format Err"; + } + + } else { + Serial1.println("Response Type: Numeric or unknown - No action needed"); + } + + Serial1.println("=============================================\n"); +} + +// -------------------- ارسال داده به سرور -------------------- +inline void uploadData() { + Serial1.println("\n======== DATA UPLOAD ========"); + + if (!config.verified) { + Serial1.println("❌ Upload skipped: Device not verified"); + Serial1.println("==============================\n"); + return; + } + if (!networkConnected && config.verified) { + Serial1.println("⚠️ Not connected, trying to reconnect..."); + connectToNetwork(); +} + if (!networkConnected) { + Serial1.println("⚠️ Not connected, trying to reconnect..."); + initEC200U(); + if (!networkConnected) { + Serial1.println("❌ Upload skipped: No network connection"); + Serial1.println("==============================\n"); + return; + } + } + + float coToSend = currentData.coPPM; + + String data = "deviceId=" + String(config.deviceId) + + "&temperatureC=" + String(currentData.temperature, 1) + + "&humidityPercent=" + String(currentData.humidity, 1) + + "&gasPPM=" + String(coToSend, 0) + + "&lux=" + String(currentData.lightLux, 1) + + "&voltage=" + String(currentData.volage, 1) + + "&power=" + String(currentData.power); + data.replace(" ", ""); + String url = String(config.serverUrl) + "/api/Telemetry/AddData?" + data; + //for test call + //url="https://ghback.nabaksoft.ir/My_StaticFiles/calltest.html"; + Serial1.println("--- REQUEST INFO ---"); + Serial1.print(" Server: "); + Serial1.println(config.serverUrl); + Serial1.print(" Device ID: "); + Serial1.println(config.deviceId); + Serial1.print(" Temp: "); + Serial1.print(currentData.temperature, 1); + Serial1.println(" C"); + Serial1.print(" Hum: "); + Serial1.print(currentData.humidity, 1); + Serial1.println(" %"); + Serial1.print(" CO (local): "); + Serial1.print(currentData.coPPM, 0); + Serial1.println(" ppm"); + Serial1.print(" CO (to server): "); + Serial1.print(coToSend, 0); + Serial1.print(" ppm"); + if (!calibrationComplete) { + Serial1.print(" [CALIBRATING]"); + } + Serial1.println(); + Serial1.print(" Light: "); + Serial1.print(currentData.lightLux, 1); + Serial1.println(" lux"); + Serial1.println("--- FULL URL ---"); + Serial1.println(url); + Serial1.print(" URL Length: "); + Serial1.println(url.length()); + Serial1.println("--------------------"); + + configureSSL(); + + Serial1.println("Step 1: Setting URL..."); + String cmd = "AT+QHTTPURL=" + String(url.length()) + ",80"; + EC200U.println(cmd); + + String response = ""; + unsigned long start = millis(); + bool gotConnect = false; + + while (millis() - start < 5000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + if (response.indexOf("CONNECT") != -1) { + gotConnect = true; + break; + } + if (response.indexOf("ERROR") != -1) { + Serial1.println("\n❌ Step 1 failed: QHTTPURL error"); + lastMessage = "URL Err"; + Serial1.println("==============================\n"); + return; + } + } + + if (!gotConnect) { + Serial1.println("❌ Step 1 failed: No CONNECT response"); + lastMessage = "URL Err"; + Serial1.println("==============================\n"); + return; + } + + Serial1.println("\n✅ Step 1: Got CONNECT, sending URL..."); + + EC200U.print(url); + delay(1000); + + response = ""; + start = millis(); + while (millis() - start < 5000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + if (response.indexOf("OK") != -1) break; + } + + if (response.indexOf("OK") == -1) { + Serial1.println("\n❌ Step 1 failed: URL not accepted"); + lastMessage = "URL Err"; + Serial1.println("==============================\n"); + return; + } + + Serial1.println("\n✅ Step 1: URL set successfully"); + delay(500); + + Serial1.println("Step 2: Sending HTTP GET (waiting up to 60 sec)..."); + EC200U.println("AT+QHTTPGET=60"); + + response = ""; + start = millis(); + bool success = false; + bool gotResponse = false; + + while (millis() - start < 65000) { + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + + if (response.indexOf("+QHTTPGET:") != -1) { + delay(1000); + while (EC200U.available()) { + char c = EC200U.read(); + response += c; + Serial1.write(c); + } + gotResponse = true; + + int idx = response.indexOf("+QHTTPGET:"); + String httpResult = response.substring(idx); + Serial1.print("\nHTTP Result: "); + Serial1.println(httpResult); + + if (httpResult.indexOf(" 0,200") != -1 || httpResult.indexOf(" 0,201") != -1 || + httpResult.indexOf(":0,200") != -1 || httpResult.indexOf(":0,201") != -1) { + success = true; + } else if (httpResult.indexOf(",200,") != -1 || httpResult.indexOf(",201,") != -1) { + success = true; + } + break; + } + + if (response.indexOf("ERROR") != -1) { + Serial1.println("\n❌ HTTP GET command error"); + break; + } + } + + if (success) { + lastUpload = millis(); + lastMessage = "Uploaded"; + Serial1.println("\n✅ Step 2: Data uploaded successfully!"); + Serial1.println("--- UPLOAD SUCCESS ---"); + Serial1.print(" Time: "); + Serial1.print(millis() / 1000); + Serial1.println(" sec since boot"); + Serial1.println("----------------------"); + + delay(500); + String serverResponse = readHttpResponse(); + if (serverResponse.length() > 0) { + processServerResponse(serverResponse); + } + } else { + lastMessage = "Upload Err"; + if (gotResponse) { + Serial1.println("\n❌ Step 2: Server returned error"); + } else { + Serial1.println("\n❌ Step 2: Timeout waiting for response"); + } + + Serial1.println("--- FULL RESPONSE ---"); + Serial1.println(response); + Serial1.println("---------------------"); + + Serial1.println("Will retry on next interval..."); + networkConnected = false; + } + + Serial1.println("==============================\n"); +} + +#endif // EC200U_H + diff --git a/Memory.h b/Memory.h new file mode 100644 index 0000000..e67ce61 --- /dev/null +++ b/Memory.h @@ -0,0 +1,429 @@ +#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 \ No newline at end of file diff --git a/STM32_InternalFlash.h.h b/STM32_InternalFlash.h.h new file mode 100644 index 0000000..256f418 --- /dev/null +++ b/STM32_InternalFlash.h.h @@ -0,0 +1,14 @@ +// STM32_InternalFlash.h +#ifdef __cplusplus +extern "C" { +#endif + +#include "stm32f1xx_hal.h" + +bool STM32_InternalFlash_Write(uint32_t address, const uint8_t* data, uint32_t size); +bool STM32_InternalFlash_Read(uint32_t address, uint8_t* data, uint32_t size); +bool STM32_InternalFlash_Erase(uint32_t address); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Sensors.h b/Sensors.h new file mode 100644 index 0000000..eccf36b --- /dev/null +++ b/Sensors.h @@ -0,0 +1,199 @@ +#ifndef SENSORS_H +#define SENSORS_H + +#include +#include +#include +#include "Config.h" + +// -------------------- متغیرهای خارجی -------------------- +extern Adafruit_SHT31 sht31; +extern BH1750 lightMeter; +extern SensorData currentData; +extern bool sht31Connected; +extern bool lightSensorConnected; +extern bool coSensorConnected; +extern bool calibrationComplete; +extern unsigned long systemStartTime; +extern unsigned long lastSensorRead; +extern float MQ7_R0; + +#define VCC 4.63 // ولتاژ واقعی سنسور +#define RL 10000.0 // مقاومت بار RL +#define VDIV_RATIO 0.681 + +// ثابت‌های کالیبراسیون +extern const long calibrationPeriod; + +// -------------------- توابع CO -------------------- +inline void calibrateMQ7() { + Serial1.println("Calibrating MQ7..."); + + float sumRS = 0; + for(int i = 0; i < 100; i++) { + int adc = analogRead(MQ7_PIN); + float v_adc = adc * (5.0 / 4095.0); // ولتاژ ADC 12 بیتی، 5V + float v_ao = v_adc / VDIV_RATIO; // اصلاح نسبت تقسیم ولتاژ + + if (v_ao < 0.01) v_ao = 0.01; + + float RS = RL * (VCC / v_ao - 1.0); // محاسبه مقاومت سنسور + sumRS += RS; + + delay(50); + } + + // نسبت RS/R0 در هوای پاک ≈ 9.8 + MQ7_R0 = (sumRS / 100.0) / 9.8; + calibrationComplete = true; + + Serial1.print("MQ7 R0 calibrated: "); + Serial1.println(MQ7_R0); +} + + +// ------------------------------------------------------------------- + +inline float readCOImproved() { + float sumVoltage = 0; + for (int i = 0; i < 20; i++) { + int adc = analogRead(MQ7_PIN); + float v_adc = adc * (5.0 / 4095.0); // ولتاژ ADC 12 بیتی، 5V + sumVoltage += v_adc; + delay(2); + } + + float v_avg = sumVoltage / 20.0; + if (v_avg < 0.01) v_avg = 0.01; + if (v_avg > 4.99) v_avg = 4.99; + + float v_ao = v_avg / VDIV_RATIO; // اصلاح تقسیم ولتاژ + float RS = RL * (VCC / v_ao - 1.0); // مقاومت سنسور + + float ratio = RS / MQ7_R0; + float ppm = 0; + + if (ratio > 0) { + // رابطه لگاریتمی تقریبی دیتاشیت MQ-7 + ppm = 100.0 * pow(ratio, -1.53); + + if (ppm < 0) ppm = 0; + if (ppm > 5000) ppm = 5000; // MQ-7 تا 5k ppm + } + + if (!calibrationComplete) { + ppm = -ppm; + // Serial1.println("MQ7 not calibrated!"); + } + + return ppm; +} + +// -------------------- بررسی وضعیت سنسورها -------------------- +inline void checkSensorStatus() { + float temp = sht31.readTemperature(); + float hum = sht31.readHumidity(); + sht31Connected = !isnan(temp) && !isnan(hum) && temp > -40 && temp < 125 && hum >= 0 && hum <= 100; + + float lux = lightMeter.readLightLevel(); + lightSensorConnected = !isnan(lux) && lux >= 0; + + int adc = analogRead(MQ7_PIN); + coSensorConnected = (adc > 0 && adc < 4095); +} + +// -------------------- لاگ کامل وضعیت سیستم -------------------- +inline void printFullStatus() { + extern bool simConnected; + extern bool networkRegistered; + extern bool networkConnected; + extern int signalStrength; + extern bool awaitingSMS2; + extern String lastMessage; + extern DeviceConfig config; + + Serial1.println("\n========== SYSTEM STATUS =========="); + + Serial1.println("--- SENSORS ---"); + Serial1.print(" Temperature: "); + Serial1.print(currentData.temperature, 1); + Serial1.print(" C ["); + Serial1.print(sht31Connected ? "OK" : "ERR"); + Serial1.println("]"); + + Serial1.print(" Humidity: "); + Serial1.print(currentData.humidity, 1); + Serial1.print(" % ["); + Serial1.print(sht31Connected ? "OK" : "ERR"); + Serial1.println("]"); + + Serial1.print(" Light: "); + Serial1.print(currentData.lightLux, 0); + Serial1.print(" lux ["); + Serial1.print(lightSensorConnected ? "OK" : "ERR"); + Serial1.println("]"); + + Serial1.print(" CO: "); + Serial1.print(currentData.coPPM, 1); + Serial1.print(" ppm ["); + Serial1.print(coSensorConnected ? "OK" : "ERR"); + Serial1.print("] Cal:"); + Serial1.println(calibrationComplete ? "Complete" : "In Progress"); + + Serial1.println("--- NETWORK ---"); + Serial1.print(" SIM Connected: "); + Serial1.println(simConnected ? "Yes" : "No"); + Serial1.print(" Network Registered:"); + Serial1.println(networkRegistered ? "Yes" : "No"); + Serial1.print(" Signal Strength: "); + Serial1.print(signalStrength); + Serial1.println("/31"); + Serial1.print(" Internet: "); + Serial1.println(networkConnected ? "Online" : "Offline"); + + Serial1.println("--- CONFIG ---"); + Serial1.print(" Verified: "); + Serial1.println(config.verified ? "Yes" : "No"); + if (config.verified) { + Serial1.print(" Device ID: "); + Serial1.println(config.deviceId); + Serial1.print(" SIM Type: "); + Serial1.println(simTypeToString(config.simType)); + Serial1.print(" Upload Int: "); + Serial1.print(config.uploadInterval); + Serial1.println(" min"); + } + + Serial1.println("--- SMS STATUS ---"); + Serial1.print(" Awaiting SMS2: "); + Serial1.println(awaitingSMS2 ? "Yes" : "No"); + Serial1.print(" Last Message: "); + Serial1.println(lastMessage); + + Serial1.println("====================================\n"); +} + +// -------------------- خواندن سنسورها -------------------- +inline void readSensors() { + currentData.temperature = sht31.readTemperature(); + currentData.humidity = sht31.readHumidity(); + + if (!calibrationComplete && (millis() - systemStartTime >= calibrationPeriod)) { + calibrationComplete = true; + Serial1.println("✅ Calibration period completed!"); + } + + currentData.coPPM = readCOImproved(); + + currentData.lightLux = lightMeter.readLightLevel(); + if (isnan(currentData.lightLux)) currentData.lightLux = 0; + + checkSensorStatus(); + + printFullStatus(); + + lastSensorRead = millis(); +} + +#endif // SENSORS_H + diff --git a/TestLCD.ino b/TestLCD.ino index ab8f2ee..9e39bd5 100644 --- a/TestLCD.ino +++ b/TestLCD.ino @@ -1,265 +1,196 @@ +#include #include -#include #include #include +#include +#include +#include +#include #include +#include -// تعریف پین‌ها -#define ADC_PIN PA2 // یا A2 اگر تعریف شده -#define LED_PIN PC13 -#define OLED_ADDR 0x3C +// -------------------- فایل‌های پروژه -------------------- +#include "Config.h" +#include "Memory.h" +#include "Sensors.h" +#include "EC200U.h" +#include "Display.h" +#include "Voltage_Reader.h" -// پین‌های I2C -#define I2C_SDA PB9 -#define I2C_SCL PB8 - -// آبجکت‌ها -Adafruit_SHT31 sht31; +// -------------------- متغیرهای سراسری -------------------- +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); BH1750 lightMeter; -Adafruit_SSD1306 display(128, 64, &Wire, -1); +Adafruit_SHT31 sht31; +HardwareSerial EC200U(USART3); +SPIClass flashSPI(FLASH_MOSI, FLASH_MISO, FLASH_SCK); -// متغیرها -float temperature, humidity, light; -int raw_adc = 0; -float voltage = 0; -float co_ppm = 0; +DeviceConfig config; +SensorData currentData; -// تابع دیباگ برای تست ADC -void testADC() { - Serial1.print("ADC Pin: PA2 (Pin "); - Serial1.print(ADC_PIN); - Serial1.println(")"); - - // خواندن 10 نمونه - Serial1.println("Reading ADC values:"); - for (int i = 0; i < 10; i++) { - int val = analogRead(ADC_PIN); - Serial1.print("Sample "); - Serial1.print(i); - Serial1.print(": "); - Serial1.print(val); - Serial1.print(" ("); - Serial1.print(val * 3.3 / 4095.0, 3); - Serial1.println("V)"); - delay(200); - } -} +// متغیرهای موقت برای پردازش پیامک‌ها +String tempPhoneNumber = ""; +String tempTokenCode = ""; +bool awaitingSMS2 = false; -// تابع برای بررسی اتصال MQ-7 -void checkMQ7Connection() { - Serial1.println("\n=== Testing MQ-7 Connection ==="); - - // حالت 1: اتصال باز (هوای آزاد) - Serial1.println("1. Disconnect MQ-7 (open circuit):"); - delay(3000); - raw_adc = analogRead(ADC_PIN); - voltage = raw_adc * 3.3 / 4095.0; - Serial1.print(" ADC: "); - Serial1.print(raw_adc); - Serial1.print(" -> Voltage: "); - Serial1.print(voltage, 3); - Serial1.println("V"); - - // حالت 2: اتصال به GND - Serial1.println("2. Short MQ-7 output to GND:"); - Serial1.println(" (Connect AOUT to GND temporarily)"); - delay(5000); - raw_adc = analogRead(ADC_PIN); - voltage = raw_adc * 3.3 / 4095.0; - Serial1.print(" ADC: "); - Serial1.print(raw_adc); - Serial1.print(" -> Voltage: "); - Serial1.print(voltage, 3); - Serial1.println("V"); - - // حالت 3: اتصال به 3.3V - Serial1.println("3. Short MQ-7 output to 3.3V:"); - Serial1.println(" (Connect AOUT to 3.3V temporarily)"); - delay(5000); - raw_adc = analogRead(ADC_PIN); - voltage = raw_adc * 3.3 / 4095.0; - Serial1.print(" ADC: "); - Serial1.print(raw_adc); - Serial1.print(" -> Voltage: "); - Serial1.print(voltage, 3); - Serial1.println("V"); - - Serial1.println("=== End Test ==="); -} +unsigned long lastSensorRead = 0; +unsigned long lastUpload = 0; +unsigned long lastDisplayChange = 0; +unsigned long lastNetworkStatusUpdate = 0; -float readCO() { - // خواندن مستقیم ADC - raw_adc = analogRead(ADC_PIN); - voltage = raw_adc * 3.3 / 4095.0; - - // اگر ولتاژ خیلی کم یا زیاد باشد - if (voltage < 0.1) { - return 0.1; // حداقل مقدار - } - if (voltage > 4.9) { - return 1000; // حداکثر مقدار - } - - // محاسبه Rs (مقاومت سنسور) - float Rs = 10000.0 * (5.0 - voltage) / voltage; - - // فرض R0 ثابت (می‌توانید کالیبره کنید) - float R0 = 10000.0; - float ratio = Rs / R0; - - // محاسبه PPM با فرمول MQ-7 - float ppm = 99.042 * pow(ratio, -1.518); - - // محدود کردن خروجی - if (ppm < 0.5) ppm = 0.5; - if (ppm > 1000) ppm = 1000; - - return ppm; -} +bool networkConnected = false; +int displayMode = 0; +String lastError = ""; +String currentAPN = ""; +String lastMessage = ""; +// وضعیت سنسورها و اتصالات +bool sht31Connected = false; +bool lightSensorConnected = false; +bool coSensorConnected = false; +bool simConnected = false; +bool networkRegistered = false; +int signalStrength = 0; + +// متغیرهای CO کالیبراسیون +unsigned long systemStartTime = 0; +const long calibrationPeriod = 600000; // 10 دقیقه +bool calibrationComplete = false; +float MQ7_R0 = 10000.0; + +bool isInCall = false; + + +bool powerState = false; + +// -------------------- Setup -------------------- void setup() { Serial1.begin(115200); - Serial1.println("\n=== STM32 4-Sensor Monitor ==="); - // تنظیم پین‌ها - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, HIGH); // روشن برای نشان دادن فعالیت + pinMode(PWRKEY_PIN, OUTPUT); + digitalWrite(PWRKEY_PIN, LOW); + + pinMode(POWER_PIN, INPUT); + + powerState = digitalRead(POWER_PIN) == HIGH; + + delay(1000); - // تنظیم ADC - analogReadResolution(12); // برای STM32 مهم است! + Serial1.println("\n\n🚀 IoT Device Starting..."); - // تنظیم I2C - Wire.setSDA(I2C_SDA); - Wire.setSCL(I2C_SCL); + systemStartTime = millis(); + + pinMode(MQ7_PIN, INPUT); + + Wire.setSDA(SDA_PIN); + Wire.setSCL(SCL_PIN); Wire.begin(); - // OLED - if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { - Serial1.println("OLED not found!"); - while(1); + // 1. ولتاژ را تست کن + bool voltageOK = voltageReader.testCircuit(); + + if (!voltageOK) { + Serial1.println("⚠️ Voltage issue detected!"); } - // SHT31 - if (!sht31.begin(0x44)) { - Serial1.println("SHT31 not found!"); + // 2. اطلاعات دیباگ ولتاژ + voltageReader.debugInfo(); + + // 3. خواندن اولیه ولتاژ + float initialVoltage = voltageReader.readVoltage(); + Serial1.print("Initial voltage: "); + Serial1.print(initialVoltage, 2); + Serial1.println("V"); + + if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + Serial1.println("❌ OLED failed!"); } - // BH1750 - if (!lightMeter.begin()) { - Serial1.println("BH1750 not found!"); + sht31.begin(0x44); + lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); + + flashInit(); + readConfig(); + + calibrateMQ7(); + + awaitingSMS2 = false; + tempPhoneNumber = ""; + tempTokenCode = ""; + + EC200U.begin(115200); + initEC200U(); + if (config.verified) { + Serial1.println("Device ID: " + String(config.deviceId)); + currentAPN = simTypeToAPN(config.simType); + //initEC200U(); } else { - lightMeter.configure(BH1750::CONTINUOUS_HIGH_RES_MODE); + Serial1.println("⚠️ Not configured - Waiting for SMS"); } - // تست اولیه ADC - testADC(); + readSensors(); - // نمایش صفحه شروع - display.clearDisplay(); - display.setTextSize(1); - display.setTextColor(SSD1306_WHITE); - display.setCursor(20, 20); - display.println("ADC Test Mode"); - display.setCursor(30, 35); - display.println("Check Serial"); - display.display(); + updateNetworkStatus(); + lastNetworkStatusUpdate = millis(); - Serial1.println("\nSend 't' to test MQ-7 connection"); - Serial1.println("Send 'r' to read normal values"); + Serial1.println("✅ Ready - CO calibration: " + String(calibrationComplete ? "Complete" : "In progress")); + + if (config.verified && networkConnected) { + Serial1.println("\n🚀 First upload - sending data immediately..."); + uploadData(); + } } +// -------------------- Loop -------------------- void loop() { - // بررسی دستورات سریال - if (Serial1.available()) { - char cmd = Serial1.read(); + if (!isInCall) { + digitalWrite(PWRKEY_PIN, LOW); + } + + bool currentPower = digitalRead(POWER_PIN) == HIGH; + + if (currentPower != powerState) { - if (cmd == 't' || cmd == 'T') { - checkMQ7Connection(); + powerState = currentPower; + + if (powerState) { + Serial.println("5V Connected"); + } else { + Serial.println("5V Disconnected"); } - if (cmd == 'r' || cmd == 'R') { - // خواندن حالت عادی - temperature = sht31.readTemperature(); - humidity = sht31.readHumidity(); - light = lightMeter.readLightLevel(); - co_ppm = readCO(); - - Serial1.print("\nTemp: "); - Serial1.print(temperature, 1); - Serial1.print("C, Hum: "); - Serial1.print(humidity, 1); - Serial1.print("%, Light: "); - Serial1.print(light, 0); - Serial1.print("lx, CO: "); - Serial1.print(co_ppm, 1); - Serial1.print("ppm, ADC: "); - Serial1.print(raw_adc); - Serial1.print(" ("); - Serial1.print(voltage, 3); - Serial1.println("V)"); + lastUpload -= 86400000L; + } + + checkSMS(); + + if (!calibrationComplete && (millis() - systemStartTime >= calibrationPeriod)) { + calibrationComplete = true; + Serial1.println("✅ CO calibration period completed!"); + } + + if (millis() - lastSensorRead > SENSOR_READ_INTERVAL) { + readSensors(); + + float voltage = voltageReader.readVoltage(); + Serial1.print("📊 System Voltage: "); + Serial1.print(voltage, 2); + Serial1.println("V"); + + currentData.volage=voltage; + + if(currentPower) + currentData.power=1; + else + currentData.power=0; + + unsigned long uploadIntervalMs = config.uploadInterval * 60000UL; + if (millis() - lastUpload > uploadIntervalMs) { + Serial1.print("Upload interval reached ("); + Serial1.print(config.uploadInterval); + Serial1.println(" min), starting upload..."); + uploadData(); } } - // خواندن و نمایش معمول - temperature = sht31.readTemperature(); - humidity = sht31.readHumidity(); - light = lightMeter.readLightLevel(); - co_ppm = readCO(); - - // نمایش روی OLED - display.clearDisplay(); - display.setTextSize(1); - display.setTextColor(SSD1306_WHITE); - - // عنوان - display.setCursor(40, 0); - display.println("SENSORS"); - display.drawLine(0, 10, 128, 10, SSD1306_WHITE); - - // دما - display.setCursor(0, 15); - display.print("Temp: "); - display.print(temperature, 1); - display.print("C"); - - // رطوبت - display.setCursor(0, 25); - display.print("Hum: "); - display.print(humidity, 1); - display.print("%"); - - // نور - display.setCursor(0, 35); - display.print("Light:"); - if (light < 1000) { - display.print(light, 0); - } else { - display.print(light / 1000, 1); - display.print("k"); - } - display.print("lx"); - - // CO با نمایش ولتاژ - display.setCursor(0, 45); - display.print("CO: "); - display.print(co_ppm, 1); - display.print("ppm"); - - // نمایش ADC و ولتاژ در پایین - display.drawLine(0, 55, 128, 55, SSD1306_WHITE); - display.setCursor(0, 58); - display.print("ADC:"); - display.print(raw_adc); - display.print(" V:"); - display.print(voltage, 2); - display.print("V"); - - // چشمک زدن LED برای نشان دادن فعالیت - static unsigned long lastBlink = 0; - if (millis() - lastBlink > 1000) { - digitalWrite(LED_PIN, !digitalRead(LED_PIN)); - lastBlink = millis(); - } - - display.display(); - delay(500); -} \ No newline at end of file + updateDisplay(); + delay(100); +} diff --git a/Voltage_Reader.h b/Voltage_Reader.h new file mode 100644 index 0000000..30b2e63 --- /dev/null +++ b/Voltage_Reader.h @@ -0,0 +1,142 @@ +#ifndef VOLTAGE_READER_H +#define VOLTAGE_READER_H + +#include +#include "Config.h" + +class VoltageReader { +private: + float filteredVoltage = 0.0; + bool firstReading = true; // فلگ برای اولین خواندن + const float alpha = 0.3; // ضریب فیلتر را کمی بیشتر کردم + +public: + VoltageReader() { + init(); + } + + void init() { + pinMode(VOLTAGE_DIVIDER_PIN, INPUT_ANALOG); + analogReadResolution(12); // تنظیم رزولوشن ADC به 12-bit + delay(100); // کمی بیشتر صبر کن + + // چند بار خواندن برای تخلیه خازن‌های داخلی + for (int i = 0; i < 10; i++) { + analogRead(VOLTAGE_DIVIDER_PIN); + delay(1); + } + } + + // خواندن ولتاژ بدون فیلتر + float readRawVoltage() { + int samples = 32; // تعداد نمونه‌ها را بیشتر کردم + long sum = 0; + + for (int i = 0; i < samples; i++) { + sum += analogRead(VOLTAGE_DIVIDER_PIN); + delayMicroseconds(20); + } + + int rawValue = sum / samples; + + // تبدیل به ولتاژ + float voltageAtPin = (rawValue * ADC_REF_VOLTAGE) / ADC_RESOLUTION; + + // محاسبه ولتاژ اصلی + float actualVoltage = voltageAtPin * VOLTAGE_DIVIDER_RATIO; + + return actualVoltage; + } + + // خواندن با فیلتر + float readVoltage() { + float currentVoltage = readRawVoltage(); + + // اگر اولین بار است، فیلتر را با مقدار فعلی مقداردهی کن + if (firstReading) { + filteredVoltage = currentVoltage; + firstReading = false; + } else { + // اعمال فیلتر Low-pass + filteredVoltage = (alpha * currentVoltage) + ((1 - alpha) * filteredVoltage); + } + + return currentVoltage; // actual voltage برمی‌گردانیم + } + + // دریافت ولتاژ فیلتر شده + float getFilteredVoltage() { + return filteredVoltage; + } + + // تست سلامت مدار + bool testCircuit() { + Serial1.println("\n🔌 Testing voltage divider circuit..."); + + // چند بار خواندن برای اطمینان + float sum = 0; + int readings = 10; + + for (int i = 0; i < readings; i++) { + sum += readRawVoltage(); + delay(50); + } + + float avgVoltage = sum / readings; + + Serial1.print("Average voltage: "); + Serial1.print(avgVoltage, 2); + Serial1.println("V"); + + Serial1.print("Raw ADC value: "); + Serial1.println(analogRead(VOLTAGE_DIVIDER_PIN)); + + // ولتاژ در پین + float pinVoltage = avgVoltage / VOLTAGE_DIVIDER_RATIO; + Serial1.print("Voltage at PA0 pin: "); + Serial1.print(pinVoltage, 2); + Serial1.println("V"); + + if (avgVoltage > 4.5 && avgVoltage < 5.5) { + Serial1.println("✅ Circuit OK (within expected 5V ±10%)"); + return true; + } else { + Serial1.print("❌ Expected ~5V, got "); + Serial1.print(avgVoltage, 2); + Serial1.println("V"); + return false; + } + } + + // نمایش اطلاعات دیباگ + void debugInfo() { + float rawVoltage = readRawVoltage(); + int rawADC = analogRead(VOLTAGE_DIVIDER_PIN); + float pinVoltage = rawVoltage / VOLTAGE_DIVIDER_RATIO; + + Serial1.println("\n📊 Voltage Debug Info:"); + Serial1.print("Raw ADC value: "); + Serial1.println(rawADC); + Serial1.print("Voltage at PA0 pin: "); + Serial1.print(pinVoltage, 3); + Serial1.println("V"); + Serial1.print("Actual voltage (raw): "); + Serial1.print(rawVoltage, 3); + Serial1.println("V"); + Serial1.print("Filtered voltage: "); + Serial1.print(filteredVoltage, 3); + Serial1.println("V"); + } + + // ریست فیلتر + void resetFilter() { + filteredVoltage = 0.0; + firstReading = true; + Serial1.println("✅ Voltage filter reset"); + } +}; + +// ایجاد نمونه سراسری +VoltageReader voltageReader; + +#endif // VOLTAGE_READER_H \ No newline at end of file diff --git a/old/STM32-03/STM32-03.ino b/old/STM32-03/STM32-03.ino deleted file mode 100644 index 04bed3a..0000000 --- a/old/STM32-03/STM32-03.ino +++ /dev/null @@ -1,519 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -// -------------------- پیکربندی حافظه SPI Flash -------------------- -#define FLASH_CS PA4 -#define FLASH_MOSI PA7 -#define FLASH_MISO PA6 -#define FLASH_SCK PA5 - -// کتابخانه جایگزین برای حافظه SPI -#include - -// -------------------- پیکربندی OLED -------------------- -#define SCREEN_WIDTH 128 -#define SCREEN_HEIGHT 64 -#define OLED_RESET -1 -Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); - -// -------------------- پیکربندی سنسورها -------------------- -#define SOIL_PIN PA1 -#define MQ7_PIN PA2 -#define SDA_PIN PB9 -#define SCL_PIN PB8 - -BH1750 lightMeter; -Adafruit_SHT31 sht31 = Adafruit_SHT31(); - -// -------------------- پیکربندی EC200U -------------------- -#define PWRKEY_PIN PB5 -HardwareSerial EC200U(USART3); - -// -------------------- پارامترهای قابل تنظیم -------------------- -#define SENSOR_READ_INTERVAL 60000 // خواندن سنسورها هر 1 دقیقه - -// -------------------- ساختار تنظیمات -------------------- -struct DeviceConfig { - char signature[4]; - char apn[32]; - char serverUrl[64]; - char deviceName[16]; - char smsNumber[16]; - unsigned long webInterval; // دقیقه - unsigned long smsInterval; // دقیقه - unsigned long saveInterval; // ثانیه - فاصله ذخیره در قطعی - bool smsEnabled; - bool valid; -}; - -// -------------------- ساختار داده سنسورها -------------------- -struct SensorData { - float temperature; - float humidity; - int soilMoisture; - float coPPM; - float lightLux; - unsigned long timestamp; -}; - -// -------------------- متغیرهای سراسری -------------------- -DeviceConfig config; -SensorData currentData; -SensorData storedData; - -unsigned long lastSensorRead = 0; -unsigned long lastUpload = 0; -unsigned long lastSMS = 0; -unsigned long lastSave = 0; -bool networkConnected = false; -bool dataStored = false; -int displayMode = 0; -unsigned long lastDisplayChange = 0; - -// -------------------- توابع حافظه SPI Flash -------------------- -void initFlash() { - SPI.setMOSI(FLASH_MOSI); - SPI.setMISO(FLASH_MISO); - SPI.setSCLK(FLASH_SCK); - SPI.begin(); - - pinMode(FLASH_CS, OUTPUT); - digitalWrite(FLASH_CS, HIGH); - - delay(100); - - // مقداردهی اولیه SerialFlash - if (!SerialFlash.begin(FLASH_CS)) { - Serial1.println("❌ Flash init failed!"); - return; - } - - Serial1.println("✅ Flash OK"); - - // نمایش اطلاعات حافظه - SerialFlash::ID id; - SerialFlash.readID(&id); - Serial1.print("Flash ID: "); - Serial1.print(id.id, HEX); - Serial1.print(" Size: "); - Serial1.print(SerialFlash.capacity(id) / 1024); - Serial1.println("KB"); -} - -void readConfig() { - // ابتدا از EEPROM داخلی بخوان - EEPROM.get(0, config); - - // اگر تنظیمات معتبر نیست، از حافظه SPI بخوان - if (strcmp(config.signature, "CFG") != 0) { - if (SerialFlash.exists("config.bin")) { - SerialFlashFile file = SerialFlash.open("config.bin"); - if (file) { - file.read(&config, sizeof(DeviceConfig)); - file.close(); - Serial1.println("✅ Config read from SPI Flash"); - } - } - } - - // اگر هنوز تنظیمات معتبر نیست، مقادیر پیش‌فرض - if (strcmp(config.signature, "CFG") != 0) { - strcpy(config.signature, "CFG"); - strcpy(config.apn, "mcinet"); - strcpy(config.serverUrl, "https://ghback.nabaksoft.ir/api/Telemetry/AddData"); - strcpy(config.smsNumber, "+989120000000"); - strcpy(config.deviceName, "ST-"); - config.webInterval = 1; - config.smsInterval = 5; - config.saveInterval = 60; // پیش‌فرض 60 ثانیه - config.smsEnabled = false; - config.valid = false; - } -} - -void saveConfig() { - config.valid = true; - - // در EEPROM داخلی ذخیره کن - EEPROM.put(0, config); - EEPROM.commit(); - - // در حافظه SPI هم ذخیره کن - if (SerialFlash.exists("config.bin")) { - SerialFlash.remove("config.bin"); - } - - SerialFlashFile file = SerialFlash.create("config.bin", sizeof(DeviceConfig)); - if (file) { - file.write((byte*)&config, sizeof(DeviceConfig)); - file.close(); - Serial1.println("✅ Config saved to SPI Flash"); - } -} - -void saveDataToFlash() { - if (!dataStored) { - if (SerialFlash.exists("data.bin")) { - SerialFlash.remove("data.bin"); - } - dataStored = true; - } - - currentData.timestamp = millis(); - - SerialFlashFile file = SerialFlash.open("data.bin"); - if (!file) { - file = SerialFlash.create("data.bin", sizeof(SensorData)); - } - - if (file) { - file.seek(0); - file.write((byte*)¤tData, sizeof(SensorData)); - file.close(); - Serial1.println("💾 Data saved to flash"); - } -} - -void readDataFromFlash() { - if (SerialFlash.exists("data.bin")) { - SerialFlashFile file = SerialFlash.open("data.bin"); - if (file) { - file.read(&storedData, sizeof(SensorData)); - file.close(); - - // بررسی اعتبار داده - if (storedData.timestamp > 0) { - Serial1.println("📖 Stored data found"); - } - } - } -} - -void clearStoredData() { - if (SerialFlash.exists("data.bin")) { - SerialFlash.remove("data.bin"); - storedData.timestamp = 0; - dataStored = false; - } -} - -// -------------------- توابع EC200U -------------------- -bool sendAT(String cmd, uint16_t wait = 2000) { - Serial1.print(">> "); - Serial1.println(cmd); - EC200U.println(cmd); - - String response = ""; - unsigned long start = millis(); - - while (millis() - start < wait) { - while (EC200U.available()) { - char c = EC200U.read(); - response += c; - Serial1.write(c); - } - if (response.indexOf("OK") != -1) return true; - if (response.indexOf("ERROR") != -1) return false; - } - return false; -} - -bool waitForResponse(String expected, unsigned long timeout = 10000) { - unsigned long startTime = millis(); - String response = ""; - - while (millis() - startTime < timeout) { - while (EC200U.available()) { - char c = EC200U.read(); - response += c; - Serial1.write(c); - if (response.indexOf(expected) != -1) return true; - if (response.indexOf("ERROR") != -1) return false; - } - } - return false; -} - -bool sendSMS(String number, String message) { - if (!sendAT("AT+CMGF=1", 2000)) return false; - - String cmd = "AT+CMGS=\"" + number + "\""; - if (!sendAT(cmd, 2000)) return false; - - delay(100); - EC200U.print(message); - EC200U.write(26); - return waitForResponse("+CMGS:", 10000); -} - -void initEC200U() { - Serial1.println("🚀 Starting EC200U..."); - - digitalWrite(PWRKEY_PIN, HIGH); - delay(2000); - digitalWrite(PWRKEY_PIN, LOW); - delay(5000); - - if (!sendAT("AT", 3000)) { - Serial1.println("❌ EC200U not responding!"); - return; - } - - String apnCmd = "AT+CGDCONT=1,\"IP\",\"" + String(config.apn) + "\""; - sendAT(apnCmd, 2000); - - if (sendAT("AT+QIACT=1", 15000)) { - networkConnected = true; - Serial1.println("✅ Network connected"); - - // اگر داده ذخیره شده وجود دارد، ارسال کن - if (storedData.timestamp > 0) { - Serial1.println("📤 Sending stored data..."); - sendStoredData(); - } - } else { - networkConnected = false; - Serial1.println("❌ Network failed"); - } -} - -// -------------------- توابع سنسورها -------------------- -void readSensors() { - currentData.temperature = sht31.readTemperature(); - currentData.humidity = sht31.readHumidity(); - currentData.soilMoisture = readSoil(); - currentData.coPPM = MQ7_ReadPPM(); - currentData.lightLux = lightMeter.readLightLevel(); - - if (isnan(currentData.temperature)) currentData.temperature = -999; - if (isnan(currentData.humidity)) currentData.humidity = -999; - if (isnan(currentData.lightLux)) currentData.lightLux = 0; - - lastSensorRead = millis(); -} - -int readSoil() { - int adcValue = analogRead(SOIL_PIN); - const int adcDry = 4095; - const int adcWet = 1200; - - float soilPercent = (float)(adcDry - adcValue) / (adcDry - adcWet) * 100.0; - if (soilPercent > 100.0) soilPercent = 100.0; - if (soilPercent < 0.0) soilPercent = 0.0; - - return (int)soilPercent; -} - -float MQ7_ReadPPM() { - long sum = 0; - for (int i = 0; i < 10; i++) { - sum += analogRead(MQ7_PIN); - delay(5); - } - float adcValue = sum / 10.0; - float voltage = (adcValue / 4095.0) * 3.3; - - float ppm; - if (voltage < 0.1) ppm = 0; - else if (voltage < 0.2) ppm = 10 * (voltage / 0.2); - else if (voltage < 0.5) ppm = 50 + (voltage - 0.2) * 300; - else if (voltage < 1.0) ppm = 140 + (voltage - 0.5) * 800; - else if (voltage < 2.0) ppm = 500 + (voltage - 1.0) * 1500; - else ppm = 2000 + (voltage - 2.0) * 2000; - - if (ppm < 0) ppm = 0; - if (ppm > 10000) ppm = 10000; - - return ppm; -} - -// -------------------- توابع ارسال داده -------------------- -bool sendData(SensorData data) { - if (data.temperature == -999 || data.humidity == -999) return false; - - String dataStr = "temperatureC=" + String(data.temperature, 1) + - "&humidityPercent=" + String(data.humidity, 1) + - "&soilPercent=" + String(data.soilMoisture) + - "&gasPPM=" + String(data.coPPM, 0) + - "&lux=" + String(data.lightLux, 1); - - String url = String(config.serverUrl) + "?deviceName=" + String(config.deviceName) + "&" + dataStr; - - String httpCmd = "AT+QHTTPURL=" + String(url.length()) + ",80"; - if (!sendAT(httpCmd, 5000)) return false; - - delay(100); - EC200U.print(url); - - if (!waitForResponse("OK", 5000)) return false; - - return sendAT("AT+QHTTPGET=60", 15000); -} - -void sendStoredData() { - if (sendData(storedData)) { - Serial1.println("✅ Stored data sent successfully"); - clearStoredData(); - } -} - -// -------------------- توابع نمایش OLED -------------------- -void updateDisplay() { - if (millis() - lastDisplayChange > 5000) { - displayMode = (displayMode + 1) % 3; - lastDisplayChange = millis(); - } - - display.clearDisplay(); - display.setTextSize(1); - - switch(displayMode) { - case 0: - display.setTextSize(2); - display.setCursor(20, 10); - display.println("DEVICE"); - display.setCursor(30, 35); - display.println(config.deviceName); - break; - - case 1: - display.setCursor(0, 0); - display.print("T:"); - display.print(currentData.temperature, 1); - display.print("C "); - display.print("H:"); - display.print(currentData.humidity, 1); - display.println("%"); - - display.setCursor(0, 16); - display.print("S:"); - display.print(currentData.soilMoisture); - display.print("% "); - display.print("C:"); - display.print(currentData.coPPM, 0); - display.println("P"); - - display.setCursor(0, 32); - display.print("L:"); - display.print(currentData.lightLux, 0); - display.println("Lx"); - - display.setCursor(0, 48); - display.print(networkConnected ? "ONLINE" : "OFFLINE"); - if (dataStored) display.print(" *"); - break; - - case 2: - display.setTextSize(3); - display.setCursor(10, 25); - display.print(currentData.temperature, 1); - display.setTextSize(2); - display.setCursor(100, 30); - display.print("C"); - break; - } - display.display(); -} - -// -------------------- setup -------------------- -void setup() { - Serial1.begin(115200); - delay(1000); - - Serial1.println("\n\n🚀 System Starting..."); - - pinMode(PWRKEY_PIN, OUTPUT); - pinMode(SOIL_PIN, INPUT); - pinMode(MQ7_PIN, INPUT); - digitalWrite(PWRKEY_PIN, LOW); - - Wire.setSDA(SDA_PIN); - Wire.setSCL(SCL_PIN); - Wire.begin(); - - if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { - Serial1.println("❌ OLED failed!"); - } - - sht31.begin(0x44); - lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); - - // مقداردهی حافظه - initFlash(); - readConfig(); - readDataFromFlash(); - - // اگر نام دستگاه تنظیم نشده، از UID استفاده کن - if (strlen(config.deviceName) < 3) { - uint32_t uid0 = *(uint32_t*)0x1FFFF7E8; - uint32_t uid1 = *(uint32_t*)0x1FFFF7EC; - uint32_t uid2 = *(uint32_t*)0x1FFFF7F0; - - sprintf(config.deviceName, "ST-%08X", (unsigned int)(uid0 ^ uid1 ^ uid2)); - saveConfig(); - } - - EC200U.begin(115200); - if (config.valid) { - initEC200U(); - } - - Serial1.println("✅ System Ready"); -} - -// -------------------- loop -------------------- -void loop() { - // خواندن سنسورها در بازه زمانی مشخص - if (millis() - lastSensorRead > SENSOR_READ_INTERVAL) { - readSensors(); - Serial1.println("📊 Sensors read"); - - // اگر شبکه وصل است، داده را ارسال کن - if (networkConnected) { - if (millis() - lastUpload > config.webInterval * 60000) { - if (sendData(currentData)) { - Serial1.println("✅ Web data sent"); - lastUpload = millis(); - } - } - } - // اگر شبکه قطع است، داده را ذخیره کن - else { - if (config.saveInterval > 0) { - if (millis() - lastSave > config.saveInterval * 1000) { - saveDataToFlash(); - lastSave = millis(); - } - } - - // ارسال SMS در صورت فعال بودن - if (config.smsEnabled && millis() - lastSMS > config.smsInterval * 60000) { - String smsMsg = String(config.deviceName) + - " T:" + String(currentData.temperature, 1) + - " H:" + String(currentData.humidity, 1) + - " S:" + String(currentData.soilMoisture) + "%"; - - if (sendSMS(config.smsNumber, smsMsg)) { - Serial1.println("✅ SMS sent"); - lastSMS = millis(); - } - } - } - } - - updateDisplay(); - - // تلاش برای اتصال مجدد شبکه - static unsigned long lastReconnect = 0; - if (!networkConnected && millis() - lastReconnect > 60000) { - initEC200U(); - lastReconnect = millis(); - } - - delay(1000); -} \ No newline at end of file diff --git a/old/Stm32Test1/Stm32Test1.ino b/old/Stm32Test1/Stm32Test1.ino deleted file mode 100644 index 98044f1..0000000 --- a/old/Stm32Test1/Stm32Test1.ino +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include -#include -#include "DHT.h" - -// --------------------------- تنظیمات --------------------------- -#define DHTPIN A0 -#define DHTTYPE DHT22 -DHT dht(DHTPIN, DHTTYPE); - -#define SOIL_PIN A1 - -#define MQ7_PIN A2 -#define VREF 5.0 // ولتاژ مرجع ADC -#define ADC_MAX 4095.0 // برای STM32 (۱۲ بیتی) - -#define SDA_PIN PB9 -#define SCL_PIN PB8 -BH1750 lightMeter; - -// UART EC200U -HardwareSerial EC200U(1); -#define PWRKEY_PIN PB5 - -// ارسال هر n دقیقه -#define UPLOAD_INTERVAL_MIN 1 - -// ذخیره ساعت شبکه -String networkTime = ""; - -// --------------------------- توابع --------------------------- - -// روشن کردن ماژول EC200U -void powerEC200U() { - pinMode(PWRKEY_PIN, OUTPUT); - digitalWrite(PWRKEY_PIN, HIGH); - delay(1000); // نگه داشتن HIGH حدود 1 ثانیه برای روشن کردن - digitalWrite(PWRKEY_PIN, LOW); - delay(2000); // کمی صبر قبل از AT -} - -// ارسال دستور AT و نمایش پاسخ -void sendAT(String cmd, uint16_t wait = 1000) { - Serial.print(">> "); Serial.println(cmd); - EC200U.println(cmd); - unsigned long start = millis(); - while (millis() - start < wait) { - while (EC200U.available()) Serial.write(EC200U.read()); - } - Serial.println(); -} - -// آماده‌سازی ماژول و اینترنت -void initEC200U() { - Serial.println("🚀 Initializing EC200U..."); - sendAT("AT",1000); - sendAT("ATI",1000); - sendAT("AT+CPIN?",1000); - sendAT("AT+CSQ",1000); - sendAT("AT+CREG?",1000); - sendAT("AT+CGATT=1",2000); - sendAT("AT+CGDCONT=1,\"IP\",\"mcinet\"",1000); - sendAT("AT+QIACT=1",5000); - sendAT("AT+QIACT?",2000); - Serial.println("✅ EC200U Ready"); -} - -// خواندن ساعت شبکه -void getNetworkTime() { - sendAT("AT+QLTS=2", 3000); - networkTime = ""; - unsigned long t = millis(); - while (millis()-t < 3000) { - while(EC200U.available()) { - char c = EC200U.read(); - Serial.write(c); - networkTime += c; - } - } -} - -// خواندن سنسورها -float readDHT22() { return dht.readTemperature(); } -float readHumidity() { return dht.readHumidity(); } - -// رطوبت خاک به درصد -int readSoilPercent() { - int val = analogRead(SOIL_PIN); // 0-4095 - int perc = map(val, 3000, 1500, 0, 100); // خشک: 3000, خیس:1500 (تنظیم شود) - if(perc>100) perc=100; - if(perc<0) perc=0; - return perc; -} - -float readMQ7ppm() { - // --- 1. میانگین‌گیری چند قرائت برای پایداری --- - long sum = 0; - for (int i = 0; i < 10; i++) { - sum += analogRead(MQ7_PIN); - delay(5); - } - float adcValue = sum / 10.0; - - // --- 2. محاسبه ولتاژ خروجی --- - float voltage = (adcValue / ADC_MAX) * VREF; - - // --- 3. تبدیل تقریبی به ppm --- - // منحنی تجربی بر اساس تست ماژول‌های MQ7 آماده (ولتاژ به ppm CO) - // این ضرایب با ولتاژ 0.1V ~ 1V برای ppm=50~5000 کالیبره شده‌اند. - // اگر مقدار خروجی بیش از 4V باشد، سنسور اشباع است. - float ppm; - - if (voltage < 0.1) ppm = 0; // خیلی پاک - else if (voltage < 0.2) ppm = 10 * (voltage / 0.2); // محدوده پایین - else if (voltage < 0.5) ppm = 50 + (voltage - 0.2) * 300; // 50–140ppm - else if (voltage < 1.0) ppm = 140 + (voltage - 0.5) * 800; // تا حدود 500ppm - else if (voltage < 2.0) ppm = 500 + (voltage - 1.0) * 1500; // تا حدود 2000ppm - else ppm = 2000 + (voltage - 2.0) * 2000; // تا ~4000ppm - - // --- 4. محدود کردن مقدار --- - if (ppm < 0) ppm = 0; - if (ppm > 10000) ppm = 10000; - - // --- 5. نمایش برای دیباگ --- - Serial.print("MQ7 voltage: "); - Serial.print(voltage, 3); - Serial.print(" V, CO ≈ "); - Serial.print(ppm, 1); - Serial.println(" ppm"); - - return ppm; -} - - -float readLight() { return lightMeter.readLightLevel(); } - -// ارسال داده HTTP GET -void sendData(float temp, float hum, int soilPerc, float mq7ppm, float lux, String timeStr) { - String dataStr = String(temp,1) + "," + String(hum,1) + "," + String(soilPerc) + "," + String(mq7ppm,0) + "," + String(lux,1) + "," + timeStr; - // 'http://localhost:5064/api/Telemetry/AddData?deviceName=dr110&temperatureC=24.5&humidityPercent=26.3&soilPercent=12&gasPPM=2&lux=103' - String url = "http://nabaksoft.ir/api/Telemetry/AddData?deviceName=dr110&temperatureC="+String(temp,1) - +"&humidityPercent="+String(hum,1)+"&soilPercent="+String(soilPerc)+"&gasPPM="+String(mq7ppm,0)+"&lux="+String(lux,1); - - Serial.println("🌐 Sending data: " + dataStr); - - sendAT("AT+QHTTPURL=" + String(url.length()) + ",80", 500); - delay(500); - EC200U.print(url); - delay(1000); - sendAT("AT+QHTTPGET=80", 5000); - sendAT("AT+QHTTPREAD=80", 5000); -} - -// --------------------------- راه‌اندازی --------------------------- -void setup() { - Serial.begin(115200); - delay(1000); - Serial.println("🚀 STM32 + EC200U + Sensors"); - - // روشن کردن ماژول EC200U - powerEC200U(); - - // UART EC200U - EC200U.setRx(PA10); - EC200U.setTx(PA9); - EC200U.begin(115200); - delay(1000); - - // سنسورها - dht.begin(); - Wire.setSDA(SDA_PIN); - Wire.setSCL(SCL_PIN); - Wire.begin(); - if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { - Serial.println("BH1750 Initialized successfully!"); - } else { - Serial.println("Error initializing BH1750!"); - } - - // آماده سازی ماژول و اینترنت - initEC200U(); - - // خواندن ساعت شبکه - getNetworkTime(); -} - -// --------------------------- حلقه اصلی --------------------------- -unsigned long lastUpload = 0; - -void loop() { - unsigned long now = millis(); - if (now - lastUpload > UPLOAD_INTERVAL_MIN*60UL*1000UL || lastUpload==0) { - // خواندن سنسورها - float temp = readDHT22(); - float hum = readHumidity(); - int soil = readSoilPercent(); - float mq7 = readMQ7ppm(); - float lux = readLight(); - - // دریافت ساعت شبکه - getNetworkTime(); - - // ارسال به سرور - sendData(temp, hum, soil, mq7, lux, networkTime); - - lastUpload = now; - } - - delay(500); -} diff --git a/old/newWifi/newWifi.ino b/old/newWifi/newWifi.ino deleted file mode 100644 index 521c20f..0000000 --- a/old/newWifi/newWifi.ino +++ /dev/null @@ -1,156 +0,0 @@ -#include - -SoftwareSerial esp8266(4, 5); // RX, TX - -// صفحه پیش‌فرض HTML -const char* HTML_PAGE = "

Hello from ESP8266

"; - -void setup() { - Serial.begin(9600); - esp8266.begin(9600); - - delay(2000); // آماده‌سازی ESP - - // تنظیمات اولیه ESP8266 - sendCommand("AT", "OK", 2000); - sendCommand("ATE0", "OK", 2000); - sendCommand("AT+CWMODE=2", "OK", 2000); - sendCommand("AT+CWSAP=\"ESP8266_AP\",\"12345678\",1,0", "OK", 5000); - sendCommand("AT+CIPMUX=1", "OK", 3000); - sendCommand("AT+CIPSERVER=1,80", "OK", 3000); - - Serial.println(F("ESP8266 Ready. Connect to SSID: ESP8266_AP, Pass: 12345678")); - Serial.println(F("Then open http://192.168.4.1/ in your browser")); -} - -void loop() { - if (esp8266.available()) { - String line = esp8266.readStringUntil('\n'); - line.trim(); - if (line.length() == 0) return; - - Serial.println("[ESP8266] " + line); - - if (line.startsWith("+IPD,")) { - int len = 0; - int linkId = parseIPDLine(line, len); - if (linkId < 0) { - Serial.println("Failed to parse IPD line."); - return; - } - - drainPayload(len); - - // فراخوانی تابع پردازش و ارسال پاسخ - processAndSend(linkId, line); - } - } -} - -// =================== توابع کمکی =================== - -// تابع پردازش داده و ارسال پاسخ به کلاینت -void processAndSend(int linkId, const String &requestLine) { - String html; - - // بررسی پارامتر صفحه در URL - if (requestLine.indexOf("GET /?page=") != -1) { - int start = requestLine.indexOf("GET /?page=") + 10; - int end = requestLine.indexOf(" ", start); - int pageNum = requestLine.substring(start, end).toInt(); - - switch (pageNum) { - case 1: - html = "

صفحه ۱

"; - break; - case 2: - html = "

صفحه ۲

"; - break; - case 3: - html = "

صفحه ۳

"; - break; - default: - html = "

صفحه پیش‌فرض

"; - break; - } - } else { - html = HTML_PAGE; // صفحه پیش‌فرض - } - - // ساخت پاسخ HTTP - String response = "HTTP/1.1 200 OK\r\n"; - response += "Content-Type: text/html\r\n"; - response += "Content-Length: " + String(html.length()) + "\r\n"; - response += "Connection: close\r\n\r\n"; - response += html; - - // ارسال پاسخ - String cmd = "AT+CIPSEND=" + String(linkId) + "," + String(response.length()); - if (sendCommand(cmd, ">", 5000)) { - esp8266.print(response); - - if (waitFor("SEND OK", 5000)) { - Serial.println("Response sent to client."); - } else { - Serial.println("SEND OK not received (timeout)."); - } - } else { - Serial.println("CIPSEND prompt not received (timeout)."); - } - - // بستن کانکشن - String closeCmd = "AT+CIPCLOSE=" + String(linkId); - sendCommand(closeCmd, "OK", 2000); -} - -// استخراج linkId و len از خط +IPD -int parseIPDLine(const String& line, int &lenOut) { - int ipdPos = line.indexOf("+IPD,"); - if (ipdPos == -1) return -1; - - int firstComma = line.indexOf(',', ipdPos + 4); - int secondComma = line.indexOf(',', firstComma + 1); - if (firstComma == -1 || secondComma == -1) return -1; - - int colon = line.indexOf(':', secondComma + 1); - if (colon == -1) { - lenOut = line.substring(secondComma + 1).toInt(); - return line.substring(ipdPos + 4, firstComma).toInt(); - } else { - lenOut = line.substring(firstComma + 1, secondComma).toInt(); - return line.substring(ipdPos + 4, firstComma).toInt(); - } -} - -// پاک‌سازی payload بعد از +IPD -void drainPayload(int len) { - unsigned long t0 = millis(); - int remaining = len; - while (millis() - t0 < 1000 && remaining > 0) { - if (esp8266.available()) { - esp8266.read(); - remaining--; - } - } -} - -// ارسال فرمان AT و انتظار ACK -bool sendCommand(const String& cmd, const char* ack, unsigned long timeout) { - esp8266.println(cmd); - return waitFor(ack, timeout); -} - -// منتظر بودن برای وجود یک ACK/کلمه کلیدی تا مهلت معین -bool waitFor(const char* target, unsigned long timeout) { - unsigned long start = millis(); - String buffer = ""; - while (millis() - start < timeout) { - while (esp8266.available()) { - char c = esp8266.read(); - buffer += c; - if (buffer.indexOf(target) != -1) return true; - } - } - Serial.print("Timeout waiting for: "); Serial.println(target); - return false; -} diff --git a/old/nodemcu3.ino b/old/nodemcu3.ino deleted file mode 100644 index 2c75cdb..0000000 --- a/old/nodemcu3.ino +++ /dev/null @@ -1,759 +0,0 @@ -// Clean, full sketch for ESP8266 (NodeMCU) -// - RCSwitch receiver on D5 -// - RTC DS3231 on I2C (SDA=D2, SCL=D1) -// - Optional SD on CS = D8 (GPIO15) -// - Always prints received RF data to Serial -// - Saves to SD every N minutes if SD present -// - Cleans files older than M days every 12 hours -// - getPageID(...) returns numeric page id; switch-case used -// - STA + periodic sending to server -// - API to set STA & send interval -#include -#include -#include -#include "RTClib.h" -//#include -#include -#include -#include -#include -#include - - - -// ---------------- CONFIG ---------------- -String apSSID = "ESP8266_AP"; -String apPassword = "12345678"; -WiFiServer server(80); - -// ---------------- HW ---------------- -RTC_DS3231 rtc; -bool rtcReady = false; - -//RCSwitch rx; -//const uint8_t RX_PIN = D1; -#define RF_RECEIVE_PIN D1 -RH_ASK driver(1200, RF_RECEIVE_PIN, -1, -1); - -#define LED_PIN 1 // TX = GPIO1 -#define NUM_LEDS 8 - -//Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); - - -#define SD_CS_PIN D8 -bool sdReady = false; -const char* CONFIG_FILE = "config.txt"; - -const int buttonPin = 16; // D0 -bool wifiEnabled = true; -bool ConnectToModem = false; -bool buttonPressed = false; -int lastButtonState = HIGH; -unsigned long lastDebounceTime = 0; -const unsigned long debounceDelay = 50; - -// ---------------- Protocol & storage ---------------- -const String PRIVATE_KEY = "as23f"; -const String ALLOWED_DEVICES[] = {"dr142","abcde","fghij"}; -const int NUM_ALLOWED = 3; - -#define MAX_DEVICES 4 -struct RemoteData { - String deviceId; - int soil; - int gas; - int temp; - int hum; - unsigned long timestamp; -}; - -RemoteData lastRemoteData[MAX_DEVICES]; -/*struct DeviceData { - String deviceId; - int soil; - int gas; - float temp; - float hum; - String timestamp; -}; -DeviceData lastRemoteData[MAX_DEVICES];*/ -int deviceCount = 0; - -// RX frame parsing -String receivedKey = ""; -String receivedHex = ""; -bool receivingPublicKey = true; -int receivedChars = 0; -unsigned long lastReceiveTime = 0; -const unsigned long FRAME_TIMEOUT_MS = 500; -const int EXPECTED_PLAIN_LEN = 25; - -// ---------------- Settings (default) ---------------- -unsigned long saveIntervalMinutes = 5; -int retentionDays = 90; -unsigned long lastSaveMillis = 0; -unsigned long lastCleanupMillis = 0; - -// ---------------- WiFi client (STA) ---------------- -String staSSID = ""; -String staPassword = ""; -unsigned long lastSendMillis = 0; -unsigned long sendIntervalMinutes = 1; // پیش فرض - -// ---------------- Helpers ---------------- -bool isHexChar(char c) { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); -} - -bool isDeviceAllowed(const String &id) { - for (int i=0;i= 8 && isDigit(name[0])) { - int y = name.substring(0,4).toInt(); - int m = name.substring(4,6).toInt(); - int d = name.substring(6,8).toInt(); - if (y > 2000 && m>=1 && m<=12 && d>=1 && d<=31) { - DateTime fileDate(y,m,d,0,0,0); - TimeSpan diff = now - fileDate; - if (diff.days() > retentionDays) SD.remove(name); - } - } - } - entry.close(); - entry = root.openNextFile(); - } - root.close(); -} - -// ---------------- Remote receiver ---------------- -void processRemote() { - uint8_t buf[32]; // حداکثر طول بسته - uint8_t buflen = sizeof(buf); - - if (driver.recv(buf, &buflen)) { - buf[buflen] = '\0'; // تبدیل به رشته - String plain = String((char*)buf); - - Serial.print("[RX] Received: "); - Serial.println(plain); - - // 📌 فرض: فرمت مثل "dr142S0345G0123T0254H0456" - if (plain.length() >= 25) { - RemoteData _remoteData; - _remoteData.soil = plain.substring(6, 10).toInt(); - _remoteData.gas = plain.substring(11, 15).toInt(); - _remoteData.deviceId = plain.substring(0, 5); - _remoteData.temp = plain.substring(16, 20).toInt(); - _remoteData.hum = plain.substring(21, 25).toInt(); - _remoteData.timestamp = millis(); -bool updated=false; - for (int i=0;i= saveIntervalMinutes*60000UL) { - if (sdReady) { - String j="{\"device\":\""+_remoteData.deviceId+"\",\"soil\":"+String(_remoteData.soil)+",\"gas\":"+String(_remoteData.gas)+ - ",\"temp\":"+String(_remoteData.temp)+",\"hum\":"+String(_remoteData.hum)+",\"time\":\""+_remoteData.timestamp+"\"}"; - appendDataToSD(j); - Serial.println("[INFO] appended to SD"); - } - lastSaveMillis=millis(); - } - - - - // String logLine = plain + "," + String(millis()); - // saveToSD(logLine); - - //digitalWrite(LED_GREEN, HIGH); - delay(50); - //digitalWrite(LED_GREEN, LOW); - } - } -} - -// ---------------- Page mapping ---------------- -int getPageID(const String &page) { - if (page == "info") return 1; - if (page == "sensor") return 2; - if (page == "time") return 3; - if (page == "settime") return 4; - if (page == "lastremote") return 5; - if (page == "readfile") return 6; - if (page == "files") return 7; - if (page == "format") return 8; - if (page == "set_save_interval") return 9; - if (page == "set_retention_days") return 10; - if (page == "sd_status") return 11; - return 0; -} - -String urlDecode(const String &input) { - String s = input; - s.replace("+", " "); - for (int i = 0; i + 2 < s.length(); ++i) { - if (s[i] == '%') { - String hx = s.substring(i+1, i+3); - char c = (char) strtol(hx.c_str(), NULL, 16); - s = s.substring(0,i) + String(c) + s.substring(i+3); - } - } - return s; -} - -String getParamFromPath(const String &path, const String &key) { - int q = path.indexOf('?'); - if (q == -1) return ""; - String qstr = path.substring(q+1); - int start = 0; - while (start < qstr.length()) { - int amp = qstr.indexOf('&', start); - if (amp == -1) amp = qstr.length(); - int eq = qstr.indexOf('=', start); - if (eq != -1 && eq < amp) { - String k = qstr.substring(start, eq); - String v = qstr.substring(eq+1, amp); - if (k == key) return urlDecode(v); - } - start = amp + 1; - } - return ""; -} - -// ---------------- HTTP handler ---------------- -void handleClient(WiFiClient &client) { - String req = ""; - unsigned long start = millis(); - while (client.connected() && millis() - start < 1500) { - while (client.available()) { - char c = client.read(); - req += c; - if (req.endsWith("\r\n\r\n")) break; - } - if (req.endsWith("\r\n\r\n")) break; - } - if (req.length() == 0) return; - - int lineEnd = req.indexOf("\r\n"); - String firstLine = (lineEnd == -1) ? req : req.substring(0, lineEnd); - Serial.print("[HTTP] "); Serial.println(firstLine); - - String path = ""; - int sp1 = firstLine.indexOf(' '); - int sp2 = firstLine.indexOf(" HTTP/"); - if (sp1 != -1 && sp2 != -1) path = firstLine.substring(sp1+1, sp2); - - String page = getParamFromPath(path,"page"); - String response=""; - - // --- API: set STA --- - if(page=="set_sta") { - String ssid = getParamFromPath(path,"ssid"); - String pass = getParamFromPath(path,"pass"); - if(ssid.length()>0) { - staSSID=ssid; staPassword=pass; - ConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); - if(sdReady) saveConfigToSD(); - response="{\"status\":\"ok\",\"message\":\"STA updated, connecting...\"}"; - } else response="{\"status\":\"error\",\"message\":\"SSID missing\"}"; - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); client.println(); - client.println(response); - client.stop(); - return; - } - - // --- API: set send interval --- - if(page=="set_send_interval") { - String v = getParamFromPath(path,"minutes"); - int m=v.toInt(); - if(m>0) { - sendIntervalMinutes=m; - if(sdReady) saveConfigToSD(); - response="{\"status\":\"ok\",\"send_interval_minutes\":"+String(m)+"}"; - } else response="{\"status\":\"error\",\"message\":\"invalid minutes\"}"; - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); client.println(); - client.println(response); - client.stop(); - return; - } - - // --- old page switch --- - int pid = getPageID(page); - switch(pid) { - case 1: response="{\"status\":\"ok\",\"data\":\"ESP ready\"}"; break; - case 2: response="{\"status\":\"ok\",\"sensor\":"+String(analogRead(A0))+"}"; break; - case 3: response=rtcReady?"{\"status\":\"ok\",\"datetime\":\""+dateTimeToISO(rtc.now())+"\"}":"{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; - case 4: { - String iso=getParamFromPath(path,"iso"); - if(!rtcReady){ response="{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; } - if(iso.length()>=19){ - int y=iso.substring(0,4).toInt(), m=iso.substring(5,7).toInt(), d=iso.substring(8,10).toInt(); - int hh=iso.substring(11,13).toInt(), mm=iso.substring(14,16).toInt(), ss=iso.substring(17,19).toInt(); - rtc.adjust(DateTime(y,m,d,hh,mm,ss)); - response="{\"status\":\"ok\",\"datetime\":\""+dateTimeToISO(rtc.now())+"\"}"; - } else response="{\"status\":\"error\",\"message\":\"invalid iso\"}"; - break; - } - case 5: { - String arr="["; - for(int i=0;i 0) { - content.remove(content.length() - 3); - } - f.close(); - response = "{\"status\":\"ok\",\"content\":[" + content + "]}"; - - break; - } - case 7: { // files - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - String arr = "["; - bool first = true; - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (!first) arr += ","; - arr += "\"" + name + "\""; - first = false; - entry.close(); - entry = root.openNextFile(); - } - root.close(); - arr += "]"; - response = "{\"status\":\"ok\",\"files\":" + arr + "}"; - break; - } - case 8: { // format (remove all files except config) - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (name != String(CONFIG_FILE) && !entry.isDirectory()) SD.remove(name); - entry.close(); - entry = root.openNextFile(); - } - root.close(); - response = "{\"status\":\"ok\",\"message\":\"formatted (config preserved)\"}"; - break; - } - case 9: { // set_save_interval?min=NUM - String v = getParamFromPath(path, "min"); - int m = v.toInt(); - if (m <= 0) response = "{\"status\":\"error\",\"message\":\"invalid min\"}"; - else { - saveIntervalMinutes = (unsigned long)m; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"save_interval_minutes\":" + String(m) + "}"; - } - break; - } - case 10: { // set_retention_days?days=NUM - String v = getParamFromPath(path, "days"); - int d = v.toInt(); - if (d <= 0) response = "{\"status\":\"error\",\"message\":\"invalid days\"}"; - else { - retentionDays = d; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"retention_days\":" + String(d) + "}"; - } - break; - } - case 11: { // sd_status - if (!sdReady) response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; - else - { - uint64_t used = computeSDUsedBytes(); - //total/free not reliably available via SD.h on ESP8266 - response = "{\"status\":\"ok\",\"used_bytes\":" + String((unsigned long)used) + "}"; - } - break; - } - default: response = page.length()==0?"{\"status\":\"error\",\"message\":\"missing page\"}":"{\"status\":\"error\",\"message\":\"unknown page\"}"; break; - } - - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); - client.println(); - client.println(response); - delay(5); - client.stop(); - Serial.println("[HTTP] client disconnected"); -} -void setLed(uint8_t idx, bool condition, uint32_t colorTrue, uint32_t colorFalse) { - // if (condition) { - // strip.setPixelColor(idx, colorTrue); - // } else { - // strip.setPixelColor(idx, colorFalse); - // } -} -// ---------------- Setup & Loop ---------------- -void setup() { - Serial.begin(115200); delay(2000); - -//led - // strip.begin(); - // strip.show(); - - //چراغ اول روشن شود. یعنی دستگاه روشن است - // setLed(0, true, strip.Color(0,255,0), strip.Color(0,0,0)); - // strip.show(); - - pinMode(buttonPin, INPUT_PULLUP); - Wire.begin(D2,D3); - rtcReady=rtc.begin(); - if(rtcReady && rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__),F(__TIME__))); - - if (!driver.init()) { - Serial.println("[ERR] RH_ASK init failed!"); - } else { - Serial.println("[INFO] RH_ASK ready"); - } - - // rx.enableReceive(digitalPinToInterrupt(RX_PIN)); - // rx.setProtocol(1); - // rx.setPulseLength(300); - Serial.println("[INFO] RCSwitch enabled on D5"); - - sdReady=SD.begin(SD_CS_PIN); - if(sdReady){ Serial.println("[INFO] SD ready"); loadConfigFromSD(); } - else Serial.println("[WARN] SD init failed"); - - if(wifiEnabled){ WiFi.softAP(apSSID,apPassword); server.begin(); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); } - - if(staSSID.length()>0){ ConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); } - - lastSaveMillis=millis(); lastCleanupMillis=millis(); lastSendMillis=millis(); -} - -void loop() { - // ---------------- Button handling (toggle AP) ---------------- - int reading = digitalRead(buttonPin); - if (reading != lastButtonState) lastDebounceTime = millis(); - - if ((millis() - lastDebounceTime) > debounceDelay) { - if (reading == LOW && !buttonPressed) { - buttonPressed = true; - wifiEnabled = !wifiEnabled; - - if (wifiEnabled) { - WiFi.mode(WIFI_AP_STA); - WiFi.softAP(apSSID, apPassword); - server.begin(); - Serial.println("✅ AP enabled"); - Serial.print("IP: "); Serial.println(WiFi.softAPIP()); - } else { - WiFi.softAPdisconnect(true); - WiFi.mode(WIFI_STA); - Serial.println("❌ AP disabled"); - } - } - - if (reading == HIGH) buttonPressed = false; - } - lastButtonState = reading; - - // ---------------- HTTP server handling ---------------- - if (wifiEnabled) { - WiFiClient client = server.available(); - if (client) handleClient(client); - } - - // ---------------- Process RF data ---------------- - processRemote(); - - // ---------------- Periodic cleanup every 12 hours ---------------- - if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) { - cleanupOldFilesOnSD(); - lastCleanupMillis = millis(); - } - - // ---------------- Debug info before sending ---------------- - - - // ---------------- Periodic send to server ---------------- - // ---------------- Periodic send to server ---------------- -if (deviceCount > 0 && sendIntervalMinutes > 0) { - if (lastSendMillis == 0) lastSendMillis = millis(); - - if ((millis() - lastSendMillis) >= sendIntervalMinutes * 60000UL) { - Serial.println("[DEBUG] Entering send block"); - Serial.print("[DEBUG] WiFi.status: "); Serial.println(WiFi.status()); - - if (WiFi.status() == WL_CONNECTED) { - // Prepare JSON payload - String payload = "["; - for (int i = 0; i < deviceCount; i++) { - if (i) payload += ","; - RemoteData d = lastRemoteData[i]; - payload += "{\"device\":\"" + d.deviceId + "\",\"soil\":" + String(d.soil) + - ",\"gas\":" + String(d.gas) + ",\"temp\":" + String(d.temp) + - ",\"hum\":" + String(d.hum) + ",\"time\":\"" + d.timestamp + "\"}"; - } - payload += "]"; - - // Use WiFiClientSecure for HTTPS - WiFiClientSecure client; - client.setInsecure(); // SSL certificate not verified - HTTPClient http; - - // Use URL with www if nabaksoft.ir ریدایرکت می‌کند - String url = "https://www.nabaksoft.ir/greenhome/mygreenhome.php?aid="+PRIVATE_KEY+"&data=" + payload; - - http.begin(client, url); - http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // Follow redirects automatically - - Serial.println("[HTTP] Sending data to server..."); - int httpCode = http.GET(); - - if (httpCode > 0) { - Serial.printf("[HTTP] Response code: %d\n", httpCode); - String resp = http.getString(); - Serial.printf("[HTTP] Server response: %s\n", resp.c_str()); - } else { - Serial.printf("[HTTP] Send failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } else { - Serial.println("[HTTP] WiFi not connected, skipping send"); - } - - lastSendMillis = millis(); - } -} - //دریافت کننده فعال است؟ - // setLed(1, rtcReady, strip.Color(0,255,0), strip.Color(255,0,0)); - // //کارتخوان فعال است؟ - // setLed(2, sdReady, strip.Color(0,255,0), strip.Color(255,0,0)); - // //wifi فعال است؟ - // setLed(3, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0)); - // //مودم فعال است یا خیر و اگر فعال است متصل است؟ - // if(staSSID.length()>0) - // setLed(4, ConnectToModem, strip.Color(0,255,0), strip.Color(255,0,0)); - // else - // setLed(4, true, strip.Color(0,0,0), strip.Color(0,0,0)); - - // strip.show(); -} - diff --git a/old/nodemcu3/nodemcu3.ino b/old/nodemcu3/nodemcu3.ino deleted file mode 100644 index 2c75cdb..0000000 --- a/old/nodemcu3/nodemcu3.ino +++ /dev/null @@ -1,759 +0,0 @@ -// Clean, full sketch for ESP8266 (NodeMCU) -// - RCSwitch receiver on D5 -// - RTC DS3231 on I2C (SDA=D2, SCL=D1) -// - Optional SD on CS = D8 (GPIO15) -// - Always prints received RF data to Serial -// - Saves to SD every N minutes if SD present -// - Cleans files older than M days every 12 hours -// - getPageID(...) returns numeric page id; switch-case used -// - STA + periodic sending to server -// - API to set STA & send interval -#include -#include -#include -#include "RTClib.h" -//#include -#include -#include -#include -#include -#include - - - -// ---------------- CONFIG ---------------- -String apSSID = "ESP8266_AP"; -String apPassword = "12345678"; -WiFiServer server(80); - -// ---------------- HW ---------------- -RTC_DS3231 rtc; -bool rtcReady = false; - -//RCSwitch rx; -//const uint8_t RX_PIN = D1; -#define RF_RECEIVE_PIN D1 -RH_ASK driver(1200, RF_RECEIVE_PIN, -1, -1); - -#define LED_PIN 1 // TX = GPIO1 -#define NUM_LEDS 8 - -//Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); - - -#define SD_CS_PIN D8 -bool sdReady = false; -const char* CONFIG_FILE = "config.txt"; - -const int buttonPin = 16; // D0 -bool wifiEnabled = true; -bool ConnectToModem = false; -bool buttonPressed = false; -int lastButtonState = HIGH; -unsigned long lastDebounceTime = 0; -const unsigned long debounceDelay = 50; - -// ---------------- Protocol & storage ---------------- -const String PRIVATE_KEY = "as23f"; -const String ALLOWED_DEVICES[] = {"dr142","abcde","fghij"}; -const int NUM_ALLOWED = 3; - -#define MAX_DEVICES 4 -struct RemoteData { - String deviceId; - int soil; - int gas; - int temp; - int hum; - unsigned long timestamp; -}; - -RemoteData lastRemoteData[MAX_DEVICES]; -/*struct DeviceData { - String deviceId; - int soil; - int gas; - float temp; - float hum; - String timestamp; -}; -DeviceData lastRemoteData[MAX_DEVICES];*/ -int deviceCount = 0; - -// RX frame parsing -String receivedKey = ""; -String receivedHex = ""; -bool receivingPublicKey = true; -int receivedChars = 0; -unsigned long lastReceiveTime = 0; -const unsigned long FRAME_TIMEOUT_MS = 500; -const int EXPECTED_PLAIN_LEN = 25; - -// ---------------- Settings (default) ---------------- -unsigned long saveIntervalMinutes = 5; -int retentionDays = 90; -unsigned long lastSaveMillis = 0; -unsigned long lastCleanupMillis = 0; - -// ---------------- WiFi client (STA) ---------------- -String staSSID = ""; -String staPassword = ""; -unsigned long lastSendMillis = 0; -unsigned long sendIntervalMinutes = 1; // پیش فرض - -// ---------------- Helpers ---------------- -bool isHexChar(char c) { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); -} - -bool isDeviceAllowed(const String &id) { - for (int i=0;i= 8 && isDigit(name[0])) { - int y = name.substring(0,4).toInt(); - int m = name.substring(4,6).toInt(); - int d = name.substring(6,8).toInt(); - if (y > 2000 && m>=1 && m<=12 && d>=1 && d<=31) { - DateTime fileDate(y,m,d,0,0,0); - TimeSpan diff = now - fileDate; - if (diff.days() > retentionDays) SD.remove(name); - } - } - } - entry.close(); - entry = root.openNextFile(); - } - root.close(); -} - -// ---------------- Remote receiver ---------------- -void processRemote() { - uint8_t buf[32]; // حداکثر طول بسته - uint8_t buflen = sizeof(buf); - - if (driver.recv(buf, &buflen)) { - buf[buflen] = '\0'; // تبدیل به رشته - String plain = String((char*)buf); - - Serial.print("[RX] Received: "); - Serial.println(plain); - - // 📌 فرض: فرمت مثل "dr142S0345G0123T0254H0456" - if (plain.length() >= 25) { - RemoteData _remoteData; - _remoteData.soil = plain.substring(6, 10).toInt(); - _remoteData.gas = plain.substring(11, 15).toInt(); - _remoteData.deviceId = plain.substring(0, 5); - _remoteData.temp = plain.substring(16, 20).toInt(); - _remoteData.hum = plain.substring(21, 25).toInt(); - _remoteData.timestamp = millis(); -bool updated=false; - for (int i=0;i= saveIntervalMinutes*60000UL) { - if (sdReady) { - String j="{\"device\":\""+_remoteData.deviceId+"\",\"soil\":"+String(_remoteData.soil)+",\"gas\":"+String(_remoteData.gas)+ - ",\"temp\":"+String(_remoteData.temp)+",\"hum\":"+String(_remoteData.hum)+",\"time\":\""+_remoteData.timestamp+"\"}"; - appendDataToSD(j); - Serial.println("[INFO] appended to SD"); - } - lastSaveMillis=millis(); - } - - - - // String logLine = plain + "," + String(millis()); - // saveToSD(logLine); - - //digitalWrite(LED_GREEN, HIGH); - delay(50); - //digitalWrite(LED_GREEN, LOW); - } - } -} - -// ---------------- Page mapping ---------------- -int getPageID(const String &page) { - if (page == "info") return 1; - if (page == "sensor") return 2; - if (page == "time") return 3; - if (page == "settime") return 4; - if (page == "lastremote") return 5; - if (page == "readfile") return 6; - if (page == "files") return 7; - if (page == "format") return 8; - if (page == "set_save_interval") return 9; - if (page == "set_retention_days") return 10; - if (page == "sd_status") return 11; - return 0; -} - -String urlDecode(const String &input) { - String s = input; - s.replace("+", " "); - for (int i = 0; i + 2 < s.length(); ++i) { - if (s[i] == '%') { - String hx = s.substring(i+1, i+3); - char c = (char) strtol(hx.c_str(), NULL, 16); - s = s.substring(0,i) + String(c) + s.substring(i+3); - } - } - return s; -} - -String getParamFromPath(const String &path, const String &key) { - int q = path.indexOf('?'); - if (q == -1) return ""; - String qstr = path.substring(q+1); - int start = 0; - while (start < qstr.length()) { - int amp = qstr.indexOf('&', start); - if (amp == -1) amp = qstr.length(); - int eq = qstr.indexOf('=', start); - if (eq != -1 && eq < amp) { - String k = qstr.substring(start, eq); - String v = qstr.substring(eq+1, amp); - if (k == key) return urlDecode(v); - } - start = amp + 1; - } - return ""; -} - -// ---------------- HTTP handler ---------------- -void handleClient(WiFiClient &client) { - String req = ""; - unsigned long start = millis(); - while (client.connected() && millis() - start < 1500) { - while (client.available()) { - char c = client.read(); - req += c; - if (req.endsWith("\r\n\r\n")) break; - } - if (req.endsWith("\r\n\r\n")) break; - } - if (req.length() == 0) return; - - int lineEnd = req.indexOf("\r\n"); - String firstLine = (lineEnd == -1) ? req : req.substring(0, lineEnd); - Serial.print("[HTTP] "); Serial.println(firstLine); - - String path = ""; - int sp1 = firstLine.indexOf(' '); - int sp2 = firstLine.indexOf(" HTTP/"); - if (sp1 != -1 && sp2 != -1) path = firstLine.substring(sp1+1, sp2); - - String page = getParamFromPath(path,"page"); - String response=""; - - // --- API: set STA --- - if(page=="set_sta") { - String ssid = getParamFromPath(path,"ssid"); - String pass = getParamFromPath(path,"pass"); - if(ssid.length()>0) { - staSSID=ssid; staPassword=pass; - ConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); - if(sdReady) saveConfigToSD(); - response="{\"status\":\"ok\",\"message\":\"STA updated, connecting...\"}"; - } else response="{\"status\":\"error\",\"message\":\"SSID missing\"}"; - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); client.println(); - client.println(response); - client.stop(); - return; - } - - // --- API: set send interval --- - if(page=="set_send_interval") { - String v = getParamFromPath(path,"minutes"); - int m=v.toInt(); - if(m>0) { - sendIntervalMinutes=m; - if(sdReady) saveConfigToSD(); - response="{\"status\":\"ok\",\"send_interval_minutes\":"+String(m)+"}"; - } else response="{\"status\":\"error\",\"message\":\"invalid minutes\"}"; - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); client.println(); - client.println(response); - client.stop(); - return; - } - - // --- old page switch --- - int pid = getPageID(page); - switch(pid) { - case 1: response="{\"status\":\"ok\",\"data\":\"ESP ready\"}"; break; - case 2: response="{\"status\":\"ok\",\"sensor\":"+String(analogRead(A0))+"}"; break; - case 3: response=rtcReady?"{\"status\":\"ok\",\"datetime\":\""+dateTimeToISO(rtc.now())+"\"}":"{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; - case 4: { - String iso=getParamFromPath(path,"iso"); - if(!rtcReady){ response="{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; } - if(iso.length()>=19){ - int y=iso.substring(0,4).toInt(), m=iso.substring(5,7).toInt(), d=iso.substring(8,10).toInt(); - int hh=iso.substring(11,13).toInt(), mm=iso.substring(14,16).toInt(), ss=iso.substring(17,19).toInt(); - rtc.adjust(DateTime(y,m,d,hh,mm,ss)); - response="{\"status\":\"ok\",\"datetime\":\""+dateTimeToISO(rtc.now())+"\"}"; - } else response="{\"status\":\"error\",\"message\":\"invalid iso\"}"; - break; - } - case 5: { - String arr="["; - for(int i=0;i 0) { - content.remove(content.length() - 3); - } - f.close(); - response = "{\"status\":\"ok\",\"content\":[" + content + "]}"; - - break; - } - case 7: { // files - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - String arr = "["; - bool first = true; - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (!first) arr += ","; - arr += "\"" + name + "\""; - first = false; - entry.close(); - entry = root.openNextFile(); - } - root.close(); - arr += "]"; - response = "{\"status\":\"ok\",\"files\":" + arr + "}"; - break; - } - case 8: { // format (remove all files except config) - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (name != String(CONFIG_FILE) && !entry.isDirectory()) SD.remove(name); - entry.close(); - entry = root.openNextFile(); - } - root.close(); - response = "{\"status\":\"ok\",\"message\":\"formatted (config preserved)\"}"; - break; - } - case 9: { // set_save_interval?min=NUM - String v = getParamFromPath(path, "min"); - int m = v.toInt(); - if (m <= 0) response = "{\"status\":\"error\",\"message\":\"invalid min\"}"; - else { - saveIntervalMinutes = (unsigned long)m; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"save_interval_minutes\":" + String(m) + "}"; - } - break; - } - case 10: { // set_retention_days?days=NUM - String v = getParamFromPath(path, "days"); - int d = v.toInt(); - if (d <= 0) response = "{\"status\":\"error\",\"message\":\"invalid days\"}"; - else { - retentionDays = d; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"retention_days\":" + String(d) + "}"; - } - break; - } - case 11: { // sd_status - if (!sdReady) response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; - else - { - uint64_t used = computeSDUsedBytes(); - //total/free not reliably available via SD.h on ESP8266 - response = "{\"status\":\"ok\",\"used_bytes\":" + String((unsigned long)used) + "}"; - } - break; - } - default: response = page.length()==0?"{\"status\":\"error\",\"message\":\"missing page\"}":"{\"status\":\"error\",\"message\":\"unknown page\"}"; break; - } - - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); - client.println(); - client.println(response); - delay(5); - client.stop(); - Serial.println("[HTTP] client disconnected"); -} -void setLed(uint8_t idx, bool condition, uint32_t colorTrue, uint32_t colorFalse) { - // if (condition) { - // strip.setPixelColor(idx, colorTrue); - // } else { - // strip.setPixelColor(idx, colorFalse); - // } -} -// ---------------- Setup & Loop ---------------- -void setup() { - Serial.begin(115200); delay(2000); - -//led - // strip.begin(); - // strip.show(); - - //چراغ اول روشن شود. یعنی دستگاه روشن است - // setLed(0, true, strip.Color(0,255,0), strip.Color(0,0,0)); - // strip.show(); - - pinMode(buttonPin, INPUT_PULLUP); - Wire.begin(D2,D3); - rtcReady=rtc.begin(); - if(rtcReady && rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__),F(__TIME__))); - - if (!driver.init()) { - Serial.println("[ERR] RH_ASK init failed!"); - } else { - Serial.println("[INFO] RH_ASK ready"); - } - - // rx.enableReceive(digitalPinToInterrupt(RX_PIN)); - // rx.setProtocol(1); - // rx.setPulseLength(300); - Serial.println("[INFO] RCSwitch enabled on D5"); - - sdReady=SD.begin(SD_CS_PIN); - if(sdReady){ Serial.println("[INFO] SD ready"); loadConfigFromSD(); } - else Serial.println("[WARN] SD init failed"); - - if(wifiEnabled){ WiFi.softAP(apSSID,apPassword); server.begin(); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); } - - if(staSSID.length()>0){ ConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); } - - lastSaveMillis=millis(); lastCleanupMillis=millis(); lastSendMillis=millis(); -} - -void loop() { - // ---------------- Button handling (toggle AP) ---------------- - int reading = digitalRead(buttonPin); - if (reading != lastButtonState) lastDebounceTime = millis(); - - if ((millis() - lastDebounceTime) > debounceDelay) { - if (reading == LOW && !buttonPressed) { - buttonPressed = true; - wifiEnabled = !wifiEnabled; - - if (wifiEnabled) { - WiFi.mode(WIFI_AP_STA); - WiFi.softAP(apSSID, apPassword); - server.begin(); - Serial.println("✅ AP enabled"); - Serial.print("IP: "); Serial.println(WiFi.softAPIP()); - } else { - WiFi.softAPdisconnect(true); - WiFi.mode(WIFI_STA); - Serial.println("❌ AP disabled"); - } - } - - if (reading == HIGH) buttonPressed = false; - } - lastButtonState = reading; - - // ---------------- HTTP server handling ---------------- - if (wifiEnabled) { - WiFiClient client = server.available(); - if (client) handleClient(client); - } - - // ---------------- Process RF data ---------------- - processRemote(); - - // ---------------- Periodic cleanup every 12 hours ---------------- - if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) { - cleanupOldFilesOnSD(); - lastCleanupMillis = millis(); - } - - // ---------------- Debug info before sending ---------------- - - - // ---------------- Periodic send to server ---------------- - // ---------------- Periodic send to server ---------------- -if (deviceCount > 0 && sendIntervalMinutes > 0) { - if (lastSendMillis == 0) lastSendMillis = millis(); - - if ((millis() - lastSendMillis) >= sendIntervalMinutes * 60000UL) { - Serial.println("[DEBUG] Entering send block"); - Serial.print("[DEBUG] WiFi.status: "); Serial.println(WiFi.status()); - - if (WiFi.status() == WL_CONNECTED) { - // Prepare JSON payload - String payload = "["; - for (int i = 0; i < deviceCount; i++) { - if (i) payload += ","; - RemoteData d = lastRemoteData[i]; - payload += "{\"device\":\"" + d.deviceId + "\",\"soil\":" + String(d.soil) + - ",\"gas\":" + String(d.gas) + ",\"temp\":" + String(d.temp) + - ",\"hum\":" + String(d.hum) + ",\"time\":\"" + d.timestamp + "\"}"; - } - payload += "]"; - - // Use WiFiClientSecure for HTTPS - WiFiClientSecure client; - client.setInsecure(); // SSL certificate not verified - HTTPClient http; - - // Use URL with www if nabaksoft.ir ریدایرکت می‌کند - String url = "https://www.nabaksoft.ir/greenhome/mygreenhome.php?aid="+PRIVATE_KEY+"&data=" + payload; - - http.begin(client, url); - http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // Follow redirects automatically - - Serial.println("[HTTP] Sending data to server..."); - int httpCode = http.GET(); - - if (httpCode > 0) { - Serial.printf("[HTTP] Response code: %d\n", httpCode); - String resp = http.getString(); - Serial.printf("[HTTP] Server response: %s\n", resp.c_str()); - } else { - Serial.printf("[HTTP] Send failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } else { - Serial.println("[HTTP] WiFi not connected, skipping send"); - } - - lastSendMillis = millis(); - } -} - //دریافت کننده فعال است؟ - // setLed(1, rtcReady, strip.Color(0,255,0), strip.Color(255,0,0)); - // //کارتخوان فعال است؟ - // setLed(2, sdReady, strip.Color(0,255,0), strip.Color(255,0,0)); - // //wifi فعال است؟ - // setLed(3, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0)); - // //مودم فعال است یا خیر و اگر فعال است متصل است؟ - // if(staSSID.length()>0) - // setLed(4, ConnectToModem, strip.Color(0,255,0), strip.Color(255,0,0)); - // else - // setLed(4, true, strip.Color(0,0,0), strip.Color(0,0,0)); - - // strip.show(); -} - diff --git a/old/nodmcu1/nodmcu1.ino b/old/nodmcu1/nodmcu1.ino deleted file mode 100644 index 921744d..0000000 --- a/old/nodmcu1/nodmcu1.ino +++ /dev/null @@ -1,716 +0,0 @@ -// Clean, full sketch for ESP8266 (NodeMCU) -// - RCSwitch receiver on D5 -// - RTC DS3231 on I2C (SDA=D2, SCL=D1) -// - Optional SD on CS = D8 (GPIO15) -// - Always prints received RF data to Serial -// - Saves to SD every N minutes if SD present -// - Cleans files older than M days every 12 hours -// - getPageID(...) returns numeric page id; switch-case used - -#include -#include -#include "RTClib.h" -#include -#include -#include -#include -// ---------------- CONFIG ---------------- -const char* ssid = "ESP8266_AP"; -const char* password = "12345678"; -WiFiServer server(80); - -// ---------------- HW ---------------- -#define LED_PIN 1 // TX = GPIO1 -#define NUM_LEDS 8 - -Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); - -RTC_DS3231 rtc; -bool rtcReady = false; - -RCSwitch rx; // RCSwitch for RXB45 -const uint8_t RX_PIN = D1; - -#define SD_CS_PIN D8 // safe for boot (GPIO15) - -const int buttonPin = 16; // D0 - - - -bool sdReady = false; -const char* CONFIG_FILE = "config.txt"; -bool wifiEnabled = true; // وضعیت WiFi -bool buttonPressed = false; // وضعیت فشار داده شده -int lastButtonState = HIGH; -unsigned long lastDebounceTime = 0; -const unsigned long debounceDelay = 50; // 200ms برای حذف لرزش -// ---------------- Protocol & storage ---------------- -const String PRIVATE_KEY = "as23f"; // shared key -const String ALLOWED_DEVICES[] = {"dr142","abcde","fghij"}; -const int NUM_ALLOWED = 3; - -#define MAX_DEVICES 16 -struct DeviceData { - String deviceId; - int soil; - int gas; - float temp; - float hum; - String timestamp; -}; -DeviceData lastRemoteData[MAX_DEVICES]; -int deviceCount = 0; - -// RX frame parsing (same logic as earlier RCSwitch-based receiver) -String receivedKey = ""; -String receivedHex = ""; -bool receivingPublicKey = true; -int receivedChars = 0; -unsigned long lastReceiveTime = 0; -const unsigned long FRAME_TIMEOUT_MS = 500; -const int EXPECTED_PLAIN_LEN = 25; - -// ---------------- Settings (default) ---------------- -unsigned long saveIntervalMinutes = 5; // minutes (configurable) -int retentionDays = 90; // days (configurable) - -unsigned long lastSaveMillis = 0; -unsigned long lastCleanupMillis = 0; - -// ---------------- Helpers ---------------- -bool isHexChar(char c) { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); -} - -bool isDeviceAllowed(const String &id) { - for (int i=0;i= 8 && isDigit(name[0])) { - int y = name.substring(0,4).toInt(); - int m = name.substring(4,6).toInt(); - int d = name.substring(6,8).toInt(); - if (y > 2000 && m>=1 && m<=12 && d>=1 && d<=31) { - DateTime fileDate(y,m,d,0,0,0); - TimeSpan diff = now - fileDate; - if (diff.days() > retentionDays) { - SD.remove(name); - } - } - } - } - entry.close(); - entry = root.openNextFile(); - } - root.close(); -} - -// ---------------- Remote receiver (RCSwitch pulses) ---------------- -void processRemote() { - bool rxa=rx.available(); - //Serial.println("rxa:"+rxa); - if (rxa) { - unsigned long value = rx.getReceivedValue(); - unsigned int bitlen = rx.getReceivedBitlength(); - - // debug to ensure we see raw values - // Serial.print(F("[DEBUG] got value=")); - // Serial.print(value); - // Serial.print(F(" bits=")); - // Serial.println(bitlen); - - if (bitlen == 8) { - char c = (char)value; - if (receivingPublicKey) { - if (receivedKey.length() < 5) receivedKey += c; - if (receivedKey.length() == 5) { - receivingPublicKey = false; - Serial.println(F("[DEBUG] public key received -> switching to HEX")); - } - } else { - if (isHexChar(c)) receivedHex += c; - else { - // noise - Serial.print(F("[DEBUG] ignored non-hex char: 0x")); - Serial.println((int)(uint8_t)c, HEX); - } - } - receivedChars++; - lastReceiveTime = millis(); - } - - rx.resetAvailable(); - } - - // when frame timeout -> process - if (receivedChars >= 5 && (millis() - lastReceiveTime) > FRAME_TIMEOUT_MS) { - Serial.println(F("[DEBUG] frame timeout -> processing...")); - - if (receivedKey != PRIVATE_KEY) { - Serial.println(F("[WARN] key mismatch")); - resetFrame(); return; - } - if ((receivedHex.length() % 2) != 0) { - Serial.println(F("[WARN] odd hex length")); - resetFrame(); return; - } - - // hex -> bytes - String decoded; decoded.reserve(receivedHex.length()/2); - for (int i=0; i+1 < receivedHex.length(); i+=2) { - char hex2[3] = { receivedHex[i], receivedHex[i+1], '\0' }; - char b = (char) strtol(hex2, NULL, 16); - decoded += b; - } - - if (decoded.length() != EXPECTED_PLAIN_LEN) { - Serial.print(F("[WARN] decoded length != expected: ")); - Serial.println(decoded.length()); - resetFrame(); return; - } - - String plain = decryptData(decoded, PRIVATE_KEY); - // check format markers (positions 5,10,15,20 should be S,G,T,H) - if (plain.length() != EXPECTED_PLAIN_LEN || - plain[5] != 'S' || plain[10] != 'G' || plain[15] != 'T' || plain[20] != 'H') { - Serial.println(F("[WARN] format check failed")); - resetFrame(); return; - } - - String deviceId = plain.substring(0,5); - if (!isDeviceAllowed(deviceId)) { - Serial.print(F("[WARN] device not allowed: ")); Serial.println(deviceId); - resetFrame(); return; - } - - // parse values - int soil = plain.substring(6,10).toInt(); - int gas = plain.substring(11,15).toInt(); - float temp = plain.substring(16,20).toInt() / 10.0; - float hum = plain.substring(21,25).toInt() / 10.0; - - String ts; - if (rtcReady) ts = dateTimeToISO(rtc.now()); - else ts = String(millis()); - - // PRINT ALWAYS - Serial.print(F("[RX] device=")); Serial.print(deviceId); - Serial.print(F(" soil=")); Serial.print(soil); - Serial.print(F(" gas=")); Serial.print(gas); - Serial.print(F(" temp=")); Serial.print(temp); - Serial.print(F(" hum=")); Serial.print(hum); - Serial.print(F(" time=")); Serial.println(ts); - - // update array (replace if exists else append) - bool updated = false; - for (int i=0;i= (saveIntervalMinutes * 60000UL)) { - if (sdReady) { - // prepare JSON line - String j = "{\"device\":\"" + deviceId + "\",\"soil\":" + String(soil) + - ",\"gas\":" + String(gas) + ",\"temp\":" + String(temp) + - ",\"hum\":" + String(hum) + ",\"time\":\"" + ts + "\"}"; - appendDataToSD(j); - Serial.println(F("[INFO] appended to SD")); - } else { - Serial.println(F("[INFO] SD not ready, skipped save")); - } - lastSaveMillis = millis(); - } - - resetFrame(); - } -} - -// ---------------- Page mapping to integer (user requested) ---------------- -int getPageID(const String &page) { - if (page == "info") return 1; - if (page == "sensor") return 2; - if (page == "time") return 3; - if (page == "settime") return 4; - if (page == "lastremote") return 5; - if (page == "readfile") return 6; - if (page == "files") return 7; - if (page == "format") return 8; - if (page == "set_save_interval") return 9; - if (page == "set_retention_days") return 10; - if (page == "sd_status") return 11; - return 0; -} - -// URL-decode (simple) -String urlDecode(const String &input) { - String s = input; - s.replace("+", " "); - for (int i = 0; i + 2 < s.length(); ++i) { - if (s[i] == '%') { - String hx = s.substring(i+1, i+3); - char c = (char) strtol(hx.c_str(), NULL, 16); - s = s.substring(0,i) + String(c) + s.substring(i+3); - } - } - return s; -} - -String getParamFromPath(const String &path, const String &key) { - int q = path.indexOf('?'); - if (q == -1) return ""; - String qstr = path.substring(q+1); - int start = 0; - while (start < qstr.length()) { - int amp = qstr.indexOf('&', start); - if (amp == -1) amp = qstr.length(); - int eq = qstr.indexOf('=', start); - if (eq != -1 && eq < amp) { - String k = qstr.substring(start, eq); - String v = qstr.substring(eq+1, amp); - if (k == key) return urlDecode(v); - } - start = amp + 1; - } - return ""; -} - -// ---------------- HTTP handler ---------------- -void handleClient(WiFiClient &client) { - String req = ""; - unsigned long start = millis(); - while (client.connected() && millis() - start < 1500) { - while (client.available()) { - char c = client.read(); - req += c; - if (req.endsWith("\r\n\r\n")) break; - } - if (req.endsWith("\r\n\r\n")) break; - } - if (req.length() == 0) return; - - int lineEnd = req.indexOf("\r\n"); - String firstLine = (lineEnd == -1) ? req : req.substring(0, lineEnd); - Serial.print(F("[HTTP] ")); Serial.println(firstLine); - - // extract path, e.g. GET /?page=info HTTP/1.1 - String path = ""; - int sp1 = firstLine.indexOf(' '); - int sp2 = firstLine.indexOf(" HTTP/"); - if (sp1 != -1 && sp2 != -1) path = firstLine.substring(sp1+1, sp2); - - String page = getParamFromPath(path, "page"); - int pid = getPageID(page); - String response = ""; - - switch (pid) { - case 1: // info - response = "{\"status\":\"ok\",\"data\":\"ESP ready\"}"; - break; - case 2: // sensor (A0) - response = "{\"status\":\"ok\",\"sensor\":" + String(analogRead(A0)) + "}"; - break; - case 3: // time - if (!rtcReady) response = "{\"status\":\"error\",\"message\":\"RTC not ready\"}"; - else response = "{\"status\":\"ok\",\"datetime\":\"" + dateTimeToISO(rtc.now()) + "\"}"; - break; - case 4: { // settime (iso) - String iso = getParamFromPath(path, "iso"); - if (!rtcReady) { response = "{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; } - if (iso.length() >= 19) { - int y=iso.substring(0,4).toInt(), m=iso.substring(5,7).toInt(), d=iso.substring(8,10).toInt(); - int hh=iso.substring(11,13).toInt(), mm=iso.substring(14,16).toInt(), ss=iso.substring(17,19).toInt(); - rtc.adjust(DateTime(y,m,d,hh,mm,ss)); - response = "{\"status\":\"ok\",\"datetime\":\"" + dateTimeToISO(rtc.now()) + "\"}"; - } else response = "{\"status\":\"error\",\"message\":\"invalid iso\"}"; - break; - } - case 5: { // lastremote - String arr = "["; - for (int i=0;i 0) { - content.remove(content.length() - 3); - } - f.close(); - response = "{\"status\":\"ok\",\"content\":[" + content + "]}"; - - break; - } - case 7: { // files - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - String arr = "["; - bool first = true; - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (!first) arr += ","; - arr += "\"" + name + "\""; - first = false; - entry.close(); - entry = root.openNextFile(); - } - root.close(); - arr += "]"; - response = "{\"status\":\"ok\",\"files\":" + arr + "}"; - break; - } - case 8: { // format (remove all files except config) - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (name != String(CONFIG_FILE) && !entry.isDirectory()) SD.remove(name); - entry.close(); - entry = root.openNextFile(); - } - root.close(); - response = "{\"status\":\"ok\",\"message\":\"formatted (config preserved)\"}"; - break; - } - case 9: { // set_save_interval?min=NUM - String v = getParamFromPath(path, "min"); - int m = v.toInt(); - if (m <= 0) response = "{\"status\":\"error\",\"message\":\"invalid min\"}"; - else { - saveIntervalMinutes = (unsigned long)m; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"save_interval_minutes\":" + String(m) + "}"; - } - break; - } - case 10: { // set_retention_days?days=NUM - String v = getParamFromPath(path, "days"); - int d = v.toInt(); - if (d <= 0) response = "{\"status\":\"error\",\"message\":\"invalid days\"}"; - else { - retentionDays = d; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"retention_days\":" + String(d) + "}"; - } - break; - } - case 11: { // sd_status - if (!sdReady) response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; - else - { - uint64_t used = computeSDUsedBytes(); - //total/free not reliably available via SD.h on ESP8266 - response = "{\"status\":\"ok\",\"used_bytes\":" + String((unsigned long)used) + "}"; - } - break; - } - default: - if (page.length() == 0) response = "{\"status\":\"error\",\"message\":\"missing page\"}"; - else response = "{\"status\":\"error\",\"message\":\"unknown page\"}"; - break; - } - - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); - client.println(response.length()); - client.println(F("Connection: close")); - client.println(); - client.println(response); - delay(5); - client.stop(); - Serial.println(F("[HTTP] client disconnected")); -} - -// ---------------- Setup & Loop ---------------- -void setup() { - Serial.begin(115200); - delay(2000); - - //led - strip.begin(); - strip.show(); - - //چراغ اول روشن شود. یعنی دستگاه روشن است - setLed(0, true, strip.Color(0,255,0), strip.Color(0,0,0)); - - - pinMode(buttonPin, INPUT_PULLUP); // کلید به GND وصل میشه - - Wire.begin(D2, D3); - rtcReady = rtc.begin(); - if (!rtcReady) Serial.println(F("RTC not found")); - else if (rtc.lostPower()) { - Serial.println(F("RTC lost power — setting to compile time")); - rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); - } - - // RCSwitch init - rx.enableReceive(digitalPinToInterrupt(RX_PIN)); // use interrupt on D1 - rx.setProtocol(1); - rx.setPulseLength(300); - Serial.println(F("[INFO] RCSwitch receiver enabled on D5")); - - // SD init - sdReady = SD.begin(SD_CS_PIN); - if (sdReady) { - Serial.println("[INFO] SD ready"); - //loadConfigFromSD(); - } else { - Serial.println("[WARN] SD init failed — will continue without SD"); - } - - // WiFi AP + server - //WiFi.softAP(ssid, password); - //server.begin(); - if (wifiEnabled) { - WiFi.softAP(ssid, password); - server.begin(); - Serial.print(F("AP: ")); Serial.println(ssid); - Serial.print(F("IP: ")); Serial.println(WiFi.softAPIP()); - } - lastSaveMillis = millis(); - lastCleanupMillis = millis(); -} - -void loop() { - int reading = digitalRead(buttonPin); - - if (reading != lastButtonState) { - lastDebounceTime = millis(); - } - - if ((millis() - lastDebounceTime) > debounceDelay) { - // تشخیص لبه فشار واقعی (HIGH → LOW) - if (reading == LOW && !buttonPressed) { - buttonPressed = true; // علامتگذاری فشار داده شده - wifiEnabled = !wifiEnabled; // تغییر وضعیت AP - - if (wifiEnabled) { - WiFi.mode(WIFI_AP_STA); - WiFi.softAP("ESP-Server", "12345678"); - Serial.println("✅ سرور (AP) فعال شد"); - Serial.print("IP سرور: "); - Serial.println(WiFi.softAPIP()); - } else { - WiFi.softAPdisconnect(true); - WiFi.mode(WIFI_STA); - Serial.println("❌ سرور (AP) غیرفعال شد"); - } - } - - // وقتی دکمه رها شد، آماده فشار بعدی - if (reading == HIGH) { - buttonPressed = false; - } - } - - lastButtonState = reading; -// int buttonState = digitalRead(buttonPin); -// if (buttonState == LOW && (millis() - lastDebounceTime) > debounceDelay) { -// lastDebounceTime = millis(); - -// wifiEnabled = !wifiEnabled; // تغییر وضعیت سرور - -// if (wifiEnabled) { -// WiFi.mode(WIFI_AP_STA); // مودم + AP -// WiFi.softAP("ESP-Server", "12345678"); // روشن کردن سرور -// Serial.println("✅ سرور (AP) فعال شد"); -// Serial.print("IP سرور: "); -// Serial.println(WiFi.softAPIP()); -// } else { -// WiFi.softAPdisconnect(true); // خاموش کردن AP -// WiFi.mode(WIFI_STA); // فقط مودم -// Serial.println("❌ سرور (AP) غیرفعال شد"); -// } -// } - - if (wifiEnabled) { - WiFiClient client = server.available(); - if (client) handleClient(client); - } - - processRemote(); - - // WiFiClient client = server.available(); - //if (client) handleClient(client); - - // periodic cleanup every 12 hours - if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) { - //cleanupOldFilesOnSD(); - lastCleanupMillis = millis(); - } - //---------------leds--------------------------------- - //دریافت کننده فعال است؟ - setLed(0, rtcReady, strip.Color(0,255,0), strip.Color(255,0,0)); - //کارتخوان فعال است؟ - setLed(0, sdReady, strip.Color(0,255,0), strip.Color(255,0,0)); - //wifi فعال است؟ - setLed(0, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0)); - //مودم فعال است یا خیر و اگر فعال است متصل است؟ - setLed(0, wifiEnabled, strip.Color(0,255,0), strip.Color(0,0,0)); - - //---------------leds--------------------------------- -} \ No newline at end of file diff --git a/old/nodmcu2/nodmcu2.ino b/old/nodmcu2/nodmcu2.ino deleted file mode 100644 index 2ba5197..0000000 --- a/old/nodmcu2/nodmcu2.ino +++ /dev/null @@ -1,928 +0,0 @@ -// Clean, full sketch for ESP8266 (NodeMCU) -// - RCSwitch receiver on D5 -// - RTC DS3231 on I2C (SDA=D2, SCL=D1) -// - Optional SD on CS = D8 (GPIO15) -// - Always prints received RF data to Serial -// - Saves to SD every N minutes if SD present -// - Cleans files older than M days every 12 hours -// - getPageID(...) returns numeric page id; switch-case used -// - STA + periodic sending to server -// - API to set STA & send interval - -#include -#include -#include "RTClib.h" -#include -#include -#include -#include -#include -#include -#include - - -// ---------------- CONFIG ---------------- -String apSSID = "ESP8266_AP"; -String apPassword = "12345678"; -WiFiServer server(80); - -// ---------------- HW ---------------- -RTC_DS3231 rtc; -bool rtcReady = false; - -RCSwitch rx; -const uint8_t RX_PIN = D1; - -//-------------------------------------led---------------------------------- -#define LED_PIN 2 -#define NUM_LEDS 8 -uint32_t lastLedState[NUM_LEDS] = {0}; -// uint8_t currentLedIndex = 0; // LED فعلی برای ارسال -// bool ledUpdateNeeded = false; -uint32_t targetColors[NUM_LEDS]; // رنگ‌های هدف -Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); -uint32_t On_color = strip.Color(0,255,127); -uint32_t off_color = strip.Color(0, 0, 0); -//uint32_t off_color = strip.Color(210, 105, 30); -uint32_t false_color = strip.Color(255, 255, 0); -uint32_t Ok_color = strip.Color(0,255,127); -uint32_t Red_color = strip.Color(255,0,0); -//----------------------------------------------------------------------- - - -#define SD_CS_PIN D8 -bool sdReady = false; -const char* CONFIG_FILE = "config.txt"; - -const int buttonPin = 16; // D0 -bool wifiEnabled = true; -bool StartConnectToModem = false; -bool buttonPressed = false; -int lastButtonState = HIGH; -unsigned long lastDebounceTime = 0; -const unsigned long debounceDelay = 50; - -// ---------------- Protocol & storage ---------------- -const String PRIVATE_KEY = "as23f"; -const String device_1 = "dr142"; -const String device_2 = "dv154"; - -uint8_t device1_recived=0; -uint8_t device2_recived=0; -//bool device2_recived=false; - -const String ALLOWED_DEVICES[] = {device_1,device_2}; -const int NUM_ALLOWED = 2; - -#define MAX_DEVICES 16 -struct DeviceData { - String deviceId; - int soil; - int gas; - float temp; - float hum; - String timestamp; -}; -DeviceData lastRemoteData[MAX_DEVICES]; -int deviceCount = 0; - -// RX frame parsing -String receivedKey = ""; -String receivedHex = ""; -bool receivingPublicKey = true; -int receivedChars = 0; -unsigned long lastReceiveTime = 0; -const unsigned long FRAME_TIMEOUT_MS = 500; -const int EXPECTED_PLAIN_LEN = 25; - -// ---------------- Settings (default) ---------------- -unsigned long saveIntervalMinutes = 5; -int retentionDays = 90; -unsigned long lastSaveMillis = 0; -unsigned long lastCleanupMillis = 0; -unsigned long lastCheckDevice = 0; - - -// ---------------- WiFi client (STA) ---------------- -String staSSID = ""; -String staPassword = ""; -unsigned long lastSendMillis = 0; -unsigned long sendIntervalMinutes = 1; // پیش فرض - -// ---------------- Helpers ---------------- -bool isHexChar(char c) { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); -} - -bool isDeviceAllowed(const String &id) { - for (int i=0;i= 8 && isDigit(name[0])) { - int y = name.substring(0,4).toInt(); - int m = name.substring(4,6).toInt(); - int d = name.substring(6,8).toInt(); - if (y > 2000 && m>=1 && m<=12 && d>=1 && d<=31) { - DateTime fileDate(y,m,d,0,0,0); - TimeSpan diff = now - fileDate; - if (diff.days() > retentionDays) SD.remove(name); - } - } - } - entry.close(); - entry = root.openNextFile(); - } - root.close(); -} - -// ---------------- Remote receiver ---------------- -void processRemote() { - if (rx.available()) { - unsigned long value = rx.getReceivedValue(); - unsigned int bitlen = rx.getReceivedBitlength(); - if (bitlen == 8) { - char c = (char)value; - if (receivingPublicKey) { - if (receivedKey.length() < 5) receivedKey += c; - if (receivedKey.length() == 5) receivingPublicKey = false; - } else { - if (isHexChar(c)) receivedHex += c; - } - receivedChars++; - lastReceiveTime = millis(); - } - rx.resetAvailable(); - } - - if (receivedChars >= 5 && (millis() - lastReceiveTime) > FRAME_TIMEOUT_MS) { - if (receivedKey != PRIVATE_KEY || (receivedHex.length() % 2) != 0) { resetFrame(); return; } - - String decoded; decoded.reserve(receivedHex.length()/2); - for (int i=0; i+1= saveIntervalMinutes*60000UL) { - if (sdReady) { - String j="{\"device\":\""+deviceId+"\",\"soil\":"+String(soil)+",\"gas\":"+String(gas)+ - ",\"temp\":"+String(temp)+",\"hum\":"+String(hum)+",\"time\":\""+ts+"\"}"; - appendDataToSD(j); - Serial.println("[INFO] appended to SD"); - } - lastSaveMillis=millis(); - } - - resetFrame(); - } -} - -// ---------------- Page mapping ---------------- -int getPageID(const String &page) { - if (page == "info") return 1; - if (page == "sensor") return 2; - if (page == "time") return 3; - if (page == "settime") return 4; - if (page == "lastremote") return 5; - if (page == "readfile") return 6; - if (page == "files") return 7; - if (page == "format") return 8; - if (page == "set_save_interval") return 9; - if (page == "set_retention_days") return 10; - if (page == "sd_status") return 11; - return 0; -} - -String urlDecode(const String &input) { - String s = input; - s.replace("+", " "); - for (int i = 0; i + 2 < s.length(); ++i) { - if (s[i] == '%') { - String hx = s.substring(i+1, i+3); - char c = (char) strtol(hx.c_str(), NULL, 16); - s = s.substring(0,i) + String(c) + s.substring(i+3); - } - } - return s; -} - -String getParamFromPath(const String &path, const String &key) { - int q = path.indexOf('?'); - if (q == -1) return ""; - String qstr = path.substring(q+1); - int start = 0; - while (start < qstr.length()) { - int amp = qstr.indexOf('&', start); - if (amp == -1) amp = qstr.length(); - int eq = qstr.indexOf('=', start); - if (eq != -1 && eq < amp) { - String k = qstr.substring(start, eq); - String v = qstr.substring(eq+1, amp); - if (k == key) return urlDecode(v); - } - start = amp + 1; - } - return ""; -} - -// ---------------- HTTP handler ---------------- -void handleClient() { - if (!wifiEnabled) - return; - WiFiClient client = server.available(); - if (!client) - return; - String req = ""; - unsigned long start = millis(); - while (client.connected() && millis() - start < 1500) { - while (client.available()) { - char c = client.read(); - req += c; - if (req.endsWith("\r\n\r\n")) break; - } - if (req.endsWith("\r\n\r\n")) break; - } - if (req.length() == 0) return; - - int lineEnd = req.indexOf("\r\n"); - String firstLine = (lineEnd == -1) ? req : req.substring(0, lineEnd); - Serial.print("[HTTP] "); Serial.println(firstLine); - - String path = ""; - int sp1 = firstLine.indexOf(' '); - int sp2 = firstLine.indexOf(" HTTP/"); - if (sp1 != -1 && sp2 != -1) path = firstLine.substring(sp1+1, sp2); - - String page = getParamFromPath(path,"page"); - String response=""; - - // --- API: set STA --- - if(page=="set_sta") { - String ssid = getParamFromPath(path,"ssid"); - String pass = getParamFromPath(path,"pass"); - if(ssid.length()>0) { - staSSID=ssid; staPassword=pass; - StartConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); - if(sdReady) saveConfigToSD(); - response="{\"status\":\"ok\",\"message\":\"STA updated, connecting...\"}"; - } else response="{\"status\":\"error\",\"message\":\"SSID missing\"}"; - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); client.println(); - client.println(response); - client.stop(); - return; - } - - // --- API: set send interval --- - if(page=="set_send_interval") { - String v = getParamFromPath(path,"minutes"); - int m=v.toInt(); - if(m>0) { - sendIntervalMinutes=m; - if(sdReady) saveConfigToSD(); - response="{\"status\":\"ok\",\"send_interval_minutes\":"+String(m)+"}"; - } else response="{\"status\":\"error\",\"message\":\"invalid minutes\"}"; - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); client.println(); - client.println(response); - client.stop(); - return; - } - - // --- old page switch --- - int pid = getPageID(page); - switch(pid) { - case 1: - response="{\"status\":\"ok\",\"data\":\"ESP ready,StartConnectToModem:"+String(StartConnectToModem)+",wifiEnabled:"+String(wifiEnabled)+"\"}"; break; - case 2: response="{\"status\":\"ok\",\"sensor\":"+String(analogRead(A0))+"}"; break; - case 3: response=rtcReady?"{\"status\":\"ok\",\"data\":\""+dateTimeToISO(rtc.now())+"\"}":"{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; - case 4: { - String iso=getParamFromPath(path,"iso"); - if(!rtcReady){ response="{\"status\":\"error\",\"message\":\"RTC not ready\"}"; break; } - if(iso.length()>=19){ - int y=iso.substring(0,4).toInt(), m=iso.substring(5,7).toInt(), d=iso.substring(8,10).toInt(); - int hh=iso.substring(11,13).toInt(), mm=iso.substring(14,16).toInt(), ss=iso.substring(17,19).toInt(); - rtc.adjust(DateTime(y,m,d,hh,mm,ss)); - response="{\"status\":\"ok\",\"data\":\""+dateTimeToISO(rtc.now())+"\"}"; - } else response="{\"status\":\"error\",\"message\":\"invalid iso\"}"; - break; - } - case 5: { - String arr="["; - for(int i=0;i 0) { - content.remove(content.length() - 3); - } - f.close(); - response = "{\"status\":\"ok\",\"content\":[" + content + "]}"; - - break; - } - case 7: { // files - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - String arr = "["; - bool first = true; - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (!first) arr += ","; - arr += "\"" + name + "\""; - first = false; - entry.close(); - entry = root.openNextFile(); - } - root.close(); - arr += "]"; - response = "{\"status\":\"ok\",\"files\":" + arr + "}"; - break; - } - case 8: { // format (remove all files except config) - if (!sdReady) { response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; break; } - File root = SD.open("/"); - File entry = root.openNextFile(); - while (entry) { - String name = entry.name(); - if (name.length() && name[0] == '/') name = name.substring(1); - if (name != String(CONFIG_FILE) && !entry.isDirectory()) SD.remove(name); - entry.close(); - entry = root.openNextFile(); - } - root.close(); - response = "{\"status\":\"ok\",\"message\":\"formatted (config preserved)\"}"; - break; - } - case 9: { // set_save_interval?min=NUM - String v = getParamFromPath(path, "min"); - int m = v.toInt(); - if (m <= 0) response = "{\"status\":\"error\",\"message\":\"invalid min\"}"; - else { - saveIntervalMinutes = (unsigned long)m; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"save_interval_minutes\":" + String(m) + "}"; - } - break; - } - case 10: { // set_retention_days?days=NUM - String v = getParamFromPath(path, "days"); - int d = v.toInt(); - if (d <= 0) response = "{\"status\":\"error\",\"message\":\"invalid days\"}"; - else { - retentionDays = d; - if (sdReady) saveConfigToSD(); - response = "{\"status\":\"ok\",\"retention_days\":" + String(d) + "}"; - } - break; - } - case 11: { // sd_status - if (!sdReady) response = "{\"status\":\"error\",\"message\":\"SD not ready\"}"; - else - { - uint64_t used = computeSDUsedBytes(); - //total/free not reliably available via SD.h on ESP8266 - response = "{\"status\":\"ok\",\"used_bytes\":" + String((unsigned long)used) + "}"; - } - break; - } - default: response = page.length()==0?"{\"status\":\"error\",\"message\":\"missing page\"}":"{\"status\":\"error\",\"message\":\"unknown page\"}"; break; - } - - client.println(F("HTTP/1.1 200 OK")); - client.println(F("Content-Type: application/json")); - client.print(F("Content-Length: ")); client.println(response.length()); - client.println(F("Connection: close")); - client.println(); - client.println(response); - delay(5); - client.stop(); - Serial.println("[HTTP] client disconnected"); -} - -time_t parseTimestamp(String ts) { - int year = ts.substring(0, 4).toInt(); - int month = ts.substring(5, 7).toInt(); - int day = ts.substring(8, 10).toInt(); - int hour = ts.substring(11, 13).toInt(); - int min = ts.substring(14, 16).toInt(); - int sec = ts.substring(17, 19).toInt(); - - tmElements_t tm; - tm.Year = year - 1970; - tm.Month = month; - tm.Day = day; - tm.Hour = hour; - tm.Minute = min; - tm.Second = sec; - - return makeTime(tm); -} -void checkDeviceData() { - time_t now = rtc.now().unixtime(); - int device1Index=-1; - int device2Index=-1; - for (int i = 0; i < MAX_DEVICES; i++) - { - if(i 0) { - time_t t = parseTimestamp(lastRemoteData[i].timestamp); - long diff = now - t; // اختلاف زمان به ثانیه - - if (diff < 60) { - device1_recived=2; - Serial.println(lastRemoteData[i].deviceId + " updated less than 60 seconds ago"); - } else { - device1_recived=1; - Serial.println(lastRemoteData[i].deviceId + "--- updated more than 60 seconds ago"); - } - } - else{ - device1_recived=1; - Serial.println(lastRemoteData[i].deviceId + "--- noDataRecive updated more than 60 seconds ago"); - } - } - if(device2Index==-1) - { - device2_recived=0;//دریافتی از دستگاه 2 نداشتیم - Serial.println(device_2 + " not exist"); - } - else - { - int i=device2Index; - if (lastRemoteData[i].timestamp.length() > 0) { - time_t t = parseTimestamp(lastRemoteData[i].timestamp); - long diff = now - t; // اختلاف زمان به ثانیه - - if (diff < 60) { - device2_recived=2; - Serial.println(lastRemoteData[i].deviceId + " updated less than 60 seconds ago"); - } else { - device2_recived=1; - Serial.println(lastRemoteData[i].deviceId + "--- updated more than 60 seconds ago"); - } - } - else{ - device1_recived=1; - Serial.println(lastRemoteData[i].deviceId + "--- noDataRecive updated more than 60 seconds ago"); - } - } -} -void setLed(uint8_t idx, bool condition, uint32_t colorTrue, uint32_t colorFalse) { - if (condition) { - strip.setPixelColor(idx, colorTrue); - } else { - strip.setPixelColor(idx, colorFalse); - } -} -// ---------------- Setup & Loop ---------------- -void startEsp() -{ - -} -void setup() { - - Serial.begin(115200); delay(100); - -//--------------------------------Start Led--------------------------------------------------------- - strip.begin(); - strip.show(); - - //چراغ اول روشن شود. یعنی دستگاه روشن است - //setLed(0, true, On_color, On_color); - //strip.show(); -//----------------------------------------------------------------------------------------- -//-------------------------------------Button Config---------------------------------------------------- - pinMode(buttonPin, INPUT_PULLUP); -//----------------------------------------------------------------------------------------- -//------------------------------------Start Clock----------------------------------------------------- - Wire.begin(D2,D3); - rtcReady=rtc.begin(); - if(rtcReady && rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__),F(__TIME__))); -//----------------------------------------------------------------------------------------- - -//--------------------------------------Start Sdk--------------------------------------------------- - rx.enableReceive(digitalPinToInterrupt(RX_PIN)); - rx.setProtocol(1); - rx.setPulseLength(300); - Serial.println("[INFO] RCSwitch enabled on D1"); -//----------------------------------------------------------------------------------------- -//-----------------------------------------Start SD Card------------------------------------------------ - sdReady=SD.begin(SD_CS_PIN); - if(sdReady){ Serial.println("[INFO] SD ready"); loadConfigFromSD(); } - else Serial.println("[WARN] SD init failed"); -//----------------------------------------------------------------------------------------- -//-----------------------------------Start ESP------------------------------------------------------ - if(wifiEnabled){ WiFi.softAP(apSSID,apPassword,6); server.begin(); Serial.print("AP IP: "); Serial.println(WiFi.softAPIP()); } - - if(staSSID.length()>0){ StartConnectToModem=WiFi.begin(staSSID.c_str(),staPassword.c_str()); } -//----------------------------------------------------------------------------------------- - - lastSaveMillis=millis(); lastCleanupMillis=millis(); lastSendMillis=millis();lastCheckDevice=millis(); -} -void CheckWifiBtn() -{ -// ---------------- Button handling (toggle AP) ---------------- - int reading = digitalRead(buttonPin); - if (reading != lastButtonState) lastDebounceTime = millis(); - - if ((millis() - lastDebounceTime) > debounceDelay) { - if (reading == LOW && !buttonPressed) { - buttonPressed = true; - wifiEnabled = !wifiEnabled; - - if (wifiEnabled) { - WiFi.mode(WIFI_AP_STA); - WiFi.softAP(apSSID, apPassword,6); - server.begin(); - Serial.println("✅ AP enabled"); - Serial.print("IP: "); Serial.println(WiFi.softAPIP()); - } else { - WiFi.softAPdisconnect(true); - WiFi.mode(WIFI_STA); - Serial.println("❌ AP disabled"); - } - } - - if (reading == HIGH) buttonPressed = false; - } - lastButtonState = reading; -} -void SendDataToServer() -{ - if (deviceCount > 0 && sendIntervalMinutes > 0) { - if (lastSendMillis == 0) lastSendMillis = millis(); - - if ((millis() - lastSendMillis) >= sendIntervalMinutes * 60000UL) { - Serial.println("[DEBUG] Entering send block"); - Serial.print("[DEBUG] WiFi.status: "); Serial.println(WiFi.status()); - if (WiFi.status() == WL_CONNECTED) { - // Prepare JSON payload - String payload = "["; - for (int i = 0; i < deviceCount; i++) { - if (i) payload += ","; - DeviceData &d = lastRemoteData[i]; - payload += "{\"device\":\"" + d.deviceId + "\",\"soil\":" + String(d.soil) + - ",\"gas\":" + String(d.gas) + ",\"temp\":" + String(d.temp) + - ",\"hum\":" + String(d.hum) + ",\"time\":\"" + d.timestamp + "\"}"; - } - payload += "]"; - - // Use WiFiClientSecure for HTTPS - WiFiClientSecure client; - client.setInsecure(); // SSL certificate not verified - HTTPClient http; - - // Use URL with www if nabaksoft.ir ریدایرکت می‌کند - String url = "https://www.nabaksoft.ir/greenhome/mygreenhome.php?aid="+PRIVATE_KEY+"&data=" + payload; - - http.begin(client, url); - http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // Follow redirects automatically - - Serial.println("[HTTP] Sending data to server..."); - int httpCode = http.GET(); - - if (httpCode > 0) { - Serial.printf("[HTTP] Response code: %d\n", httpCode); - String resp = http.getString(); - Serial.printf("[HTTP] Server response: %s\n", resp.c_str()); - } else { - Serial.printf("[HTTP] Send failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } else { - Serial.println("[HTTP] WiFi not connected, skipping send"); - } - lastSendMillis = millis(); - } - } -} -void SetLedStates() -{ - //دریافت کننده فعال است؟ - /*setLed(1, rtcReady, Ok_color, off_color); - //کارتخوان فعال است؟ - setLed(2, sdReady, Ok_color, off_color); - //wifi فعال است؟ - setLed(3, wifiEnabled, Ok_color, off_color); - //مودم فعال است یا خیر و اگر فعال است متصل است؟ - if(staSSID.length()>0) - { - //اگر رمز دارد پس باید متصل بشه.اگر شده که سبز. اگر نشده قرمز - setLed(4, WiFi.status()==WL_CONNECTED, Ok_color, false_color); - } - else - setLed(4, true, off_color, off_color); - - setLed(5, true, off_color, off_color); - setLed(6, true, off_color, off_color); - setLed(7, true, off_color, off_color); - //شدت روشنایی - strip.setBrightness(20); - strip.show();*/ - int32_t colors[NUM_LEDS]; - colors[0] = On_color; // دستگاه روشن - colors[1] = rtcReady ? Ok_color : off_color; // وضعیت RTC - colors[2] = sdReady ? Ok_color : off_color; // وضعیت SD - colors[3] = wifiEnabled ? Ok_color : off_color; // وضعیت AP - colors[4] = (staSSID.length() > 0 && WiFi.status() == WL_CONNECTED) ? Ok_color : false_color; // STA - - colors[5] = (device1_recived==2)?Ok_color:(device1_recived==1?false_color:Red_color); - colors[6] = (device2_recived == 2)?Ok_color:(device2_recived==1?false_color:Red_color); - colors[7] = off_color; - - // بررسی اینکه آیا تغییری نسبت به وضعیت قبلی وجود دارد - bool changed = false; - for (int i = 0; i < NUM_LEDS; i++) { - if (colors[i] != lastLedState[i]) { - lastLedState[i] = colors[i]; - strip.setPixelColor(i, colors[i]); - changed = true; - } - } - - // فقط وقتی تغییر بوده، strip.show() اجرا شود - if (changed) { - strip.setBrightness(20); - strip.show(); // این تابع اکنون به حداقل فراخوانی کاهش یافته - } -} -void loop() { - - CheckWifiBtn(); - - // ---------------- HTTP server handling ---------------- - handleClient(); - - // ---------------- Process RF data ---------------- - processRemote(); - - //---------------- Periodic cleanup every 12 hours ---------------- - if ((millis() - lastCleanupMillis) >= (12UL * 60UL * 60UL * 1000UL)) { - cleanupOldFilesOnSD(); - lastCleanupMillis = millis(); - } -//هر یک دقیقه چک کنه وضعیت دو فرستنده چطور هست -if ((millis() - lastCheckDevice) >= (60UL * 1000UL)) { - checkDeviceData(); - lastCheckDevice = millis(); - } - - - // ---------------- Periodic send to server ---------------- - SendDataToServer(); - - SetLedStates(); -} - - -// if (wifiEnabled) { -// WiFiClient client = server.available(); -// if (client) handleClient(client); -// } \ No newline at end of file diff --git a/old/reciver01/reciver01.ino b/old/reciver01/reciver01.ino deleted file mode 100644 index f4e72a0..0000000 --- a/old/reciver01/reciver01.ino +++ /dev/null @@ -1,196 +0,0 @@ -#include - -RCSwitch rx = RCSwitch(); - -// باید با فرستنده یکسان باشد -const String PRIVATE_KEY = "as23f"; - -// دستگاه‌های مجاز -const String ALLOWED_DEVICES[] = {"dr142", "abcde", "fghij"}; -const int NUM_ALLOWED_DEVICES = 3; - -// بافرهای دریافت -String receivedKey = ""; -String receivedHex = ""; - -// کنترل فریم -bool receivingPublicKey = true; -int receivedChars = 0; -unsigned long lastReceiveTime = 0; -const unsigned long TIMEOUT_MS = 500; - -// طول متن اصلی انتظار می‌رود 25 باشد -const int EXPECTED_PLAIN_LEN = 25; - -// XOR ساده -String decryptData(const String &data, const String &key) { - String out; - out.reserve(data.length()); - for (int i = 0; i < data.length(); i++) { - out += (char)(data[i] ^ key[i % key.length()]); - } - return out; -} - -bool isDeviceAllowed(const String &deviceId) { - for (int i = 0; i < NUM_ALLOWED_DEVICES; i++) { - if (deviceId == ALLOWED_DEVICES[i]) return true; - } - return false; -} - -static inline bool isHexChar(char c) { - return (c >= '0' && c <= '9') || - (c >= 'A' && c <= 'F') || - (c >= 'a' && c <= 'f'); -} - -void resetFrame() { - receivedKey = ""; - receivedHex = ""; - receivingPublicKey = true; - receivedChars = 0; -} - -void setup() { - Serial.begin(9600); - - rx.enableReceive(0); // interrupt 0 → پایه 2 در آردوینو UNO - rx.setProtocol(1); - rx.setPulseLength(300); - - resetFrame(); - - Serial.println(F("=== Receiver Started ===")); - Serial.print(F("Private Key: ")); Serial.println(PRIVATE_KEY); - Serial.print(F("Allowed IDs: ")); - for (int i = 0; i < NUM_ALLOWED_DEVICES; i++) { Serial.print(ALLOWED_DEVICES[i]); Serial.print(' '); } - Serial.println(); -} - -void loop() { - if (rx.available()) { - unsigned long value = rx.getReceivedValue(); - unsigned int bitlen = rx.getReceivedBitlength(); - - if (bitlen == 8) { - char c = (char)value; - - if (receivingPublicKey) { - if (receivedKey.length() < 5) { - receivedKey += c; - //Serial.print(F("Received key char: ")); Serial.println(c); - receivedChars++; - if (receivedKey.length() == 5) { - receivingPublicKey = false; - Serial.println(F("Now receiving encrypted HEX data...")); - } - } - } else { - // فقط کاراکتر HEX معتبر ذخیره شود - if (isHexChar(c)) { - receivedHex += c; - //Serial.print(F("Received HEX char: ")); Serial.println(c); - receivedChars++; - } else { - // نویز را رد کن - Serial.print(F("Ignored non-HEX char: 0x")); - Serial.println((int)(uint8_t)c, HEX); - } - } - - lastReceiveTime = millis(); - } - - rx.resetAvailable(); - } - - // اگر وقفه افتاد، پردازش کن - if (receivedChars >= 5 && (millis() - lastReceiveTime) > TIMEOUT_MS) { - Serial.println(F("=== Processing Data ===")); - Serial.print(F("Received Public Key: ")); Serial.println(receivedKey); - - if (receivedKey != PRIVATE_KEY) { - Serial.println(F("Key verification: FAILED")); - resetFrame(); - return; - } - Serial.println(F("Key verification: SUCCESS")); - - // طول HEX باید زوج باشد (هر دو کاراکتر → یک بایت) - if ((receivedHex.length() % 2) != 0) { - Serial.print(F("Odd HEX length: ")); Serial.println(receivedHex.length()); - resetFrame(); - return; - } - - // تبدیل HEX → بایت‌ها - String decoded; decoded.reserve(receivedHex.length() / 2); - for (int i = 0; i + 1 < receivedHex.length(); i += 2) { - char hex2[3] = { receivedHex[i], receivedHex[i+1], '\0' }; - char b = (char) strtol(hex2, NULL, 16); - decoded += b; - } - - Serial.print(F("Decoded length: ")); Serial.println(decoded.length()); - Serial.print(F("Decoded (raw XORed) bytes: ")); - for (int i = 0; i < decoded.length(); i++) { - Serial.print((int)(uint8_t)decoded[i]); Serial.print(' '); - } - Serial.println(); - - // باید دقیقاً به طول 25 بایت باشد - if (decoded.length() != EXPECTED_PLAIN_LEN) { - Serial.println(F("Unexpected decoded length (should be 25).")); - resetFrame(); - return; - } - - // رمزگشایی - String plain = decryptData(decoded, PRIVATE_KEY); - Serial.print(F("Decrypted Data: ")); Serial.println(plain); - - // اعتبارسنجی قالب - if (plain.length() != EXPECTED_PLAIN_LEN || - plain[5] != 'S' || plain[10] != 'G' || - plain[15] != 'T' || plain[20] != 'H') { - Serial.println(F("Format check failed.")); - resetFrame(); - return; - } - - String deviceId = plain.substring(0, 5); - Serial.print(F("Device ID: ")); Serial.println(deviceId); - - if (!isDeviceAllowed(deviceId)) { - Serial.println(F("Device authorization: FAILED")); - resetFrame(); - return; - } - - Serial.println(F("Device authorization: SUCCESS")); - - // پارس مقادیر - String sSoil = plain.substring(6, 10); // بین S و G - String sGas = plain.substring(11, 15); // بین G و T - String sTemp = plain.substring(16, 20); // بین T و H - String sHum = plain.substring(21, 25); // بعد از H - - int soilMoisture = sSoil.toInt(); - int gasValue = sGas.toInt(); - float temperature = sTemp.toInt() / 10.0; - float humidity = sHum.toInt() / 10.0; - - Serial.println(F("=== Authorized Data ===")); - Serial.print(F("Soil Moisture: ")); Serial.print(soilMoisture); - Serial.print(F(" - Gas Value : ")); Serial.print(gasValue); - Serial.print(F(" - Temperature : ")); Serial.print(temperature); Serial.print(F(" C")); - Serial.print(F(" - Humidity : ")); Serial.print(humidity); Serial.println(F(" %")); - Serial.println(F("=======================")); - - // آماده دریافت بعدی - resetFrame(); - } - - delay(5); -} diff --git a/old/sender01/sender01.ino b/old/sender01/sender01.ino deleted file mode 100644 index cb0edb3..0000000 --- a/old/sender01/sender01.ino +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include - -// پین‌ها -#define DHTPIN 2 -#define DHTTYPE DHT11 -#define SOIL_MOISTURE_PIN A3 -#define GAS_SENSOR_PIN A2 -#define RF_TRANSMIT_PIN 9 - -const float RL = 10.0; // مقاومت Load روی ماژول (کیلو اهم) -float R0 = 50; // بعد از کالیبراسیون مقدار واقعی جایگزین کنید -// متن ثابت و کلیدها - -const String PUBLIC_KEY = "as23f"; -const String DEVICE_ID = "dr142"; - -// اشیاء -DHT dht(DHTPIN, DHTTYPE); -RCSwitch tx = RCSwitch(); - -// زمان‌بندی -unsigned long lastSendTime = 0; -const unsigned long sendInterval = 10000; - -// تاخیر بین ارسال هر کاراکتر (ms) -const unsigned int INTER_CHAR_DELAY_MS = 1; - -// XOR ساده -String encryptData(const String &data, const String &key) { - String out; - out.reserve(data.length()); - for (int i = 0; i < data.length(); i++) { - out += (char)(data[i] ^ key[i % key.length()]); - } - return out; -} - -static inline void sendChar(uint8_t c) { - tx.send((unsigned long)c, 8); - delay(INTER_CHAR_DELAY_MS); -} - -static inline void sendHexByte(uint8_t b) { - char hex[3]; - sprintf(hex, "%02X", b); // حروف بزرگ - sendChar(hex[0]); - sendChar(hex[1]); -} - -void setup() { - Serial.begin(9600); - dht.begin(); - - tx.enableTransmit(RF_TRANSMIT_PIN); - tx.setProtocol(1); - tx.setPulseLength(300); - tx.setRepeatTransmit(3); - - Serial.println(F("=== Transmitter Started ===")); - Serial.print(F("Public Key: ")); Serial.println(PUBLIC_KEY); - Serial.print(F("Device ID : ")); Serial.println(DEVICE_ID); -} -int getSoilMoistureInt() { - int adcValue = analogRead(SOIL_MOISTURE_PIN); - // مقادیر کالیبراسیون: خاک خشک و خاک خیس - const int adcDry = 1023; // خاک کاملاً خشک - const int adcWet = 400; // خاک کاملاً خیس - - // محاسبه درصد رطوبت با یک رقم اعشار - float soilPercent = (float)(adcDry - adcValue) / (adcDry - adcWet) * 100.0; - - // محدود کردن بین 0 تا 100 - if (soilPercent > 100.0) soilPercent = 100.0; - if (soilPercent < 0.0) soilPercent = 0.0; - - // ضرب در 10 و تبدیل به عدد صحیح - int soilInt = (int)(soilPercent * 10 + 0.5); // +0.5 برای گرد کردن صحیح - return soilInt; -} - -int readGas() { - long sum = 0; - for (int i = 0; i < 5; i++) { - sum += analogRead(GAS_SENSOR_PIN); - delay(10); - } - float adcValue = sum / 5.0; // میانگین ADC - - float vrl = (adcValue / 1023.0) * 5.0; // ولتاژ خروجی - if (vrl < 0.001) vrl = 0.001; // جلوگیری از تقسیم بر صفر - - float Rs = (5.0 - vrl) * RL / vrl; // مقاومت سنسور (kΩ) - float ratio = Rs / R0; // نسبت Rs/R0 - - // ثابت‌های تقریبی از دیتاشیت MQ7 (CO curve) - float m = -0.77; - float b = 0.36; - - float ppm_log = (log10(ratio) - b) / m; - int ppm = round(pow(10, ppm_log)); - - return ppm; // خروجی اعشاری -} -void loop() { - // خواندن سنسورها - float temperature = dht.readTemperature(); - float humidity = dht.readHumidity(); - int soilMoisture = getSoilMoistureInt(); - int gasValue = readGas(); //analogRead(GAS_SENSOR_PIN); - - if (isnan(temperature) || isnan(humidity)) { - temperature = 0; humidity = 0; - } - - soilMoisture = constrain(soilMoisture, 0, 9999); - gasValue = constrain(gasValue, 0, 9999);//1023 - int tempInt = constrain((int)(temperature * 10), 0, 9999); // چهار رقمی - int humidityInt = constrain((int)(humidity * 10), 0, 9999); // چهار رقمی - - // ⚠️ خیلی مهم: ۲۶ بایت برای نَل ترمینیتور - char dataString[26]; - // قالب دقیقاً 25 کاراکتر تولید می‌کند: 5 + (S####) + (G####) + (T####) + (H####) - // 5 + 1+4 + 1+4 + 1+4 + 1+4 = 25 - int n = snprintf( - dataString, sizeof(dataString), - "%sS%04dG%04dT%04dH%04d", - DEVICE_ID.c_str(), soilMoisture, gasValue, tempInt, humidityInt - ); - - // اگر بنا به هر دلیلی طول غیرمنتظره شد، همین‌جا خروج - if (n != 25) { - Serial.print(F("Format length unexpected: ")); Serial.println(n); - delay(500); - return; - } - - String plain = String(dataString); - String enc = encryptData(plain, PUBLIC_KEY); - - if (millis() - lastSendTime >= sendInterval) { - Serial.println(F("=== Data to Send ===")); - Serial.print(F("Original: ")); Serial.println(plain); - Serial.print(F("Length : ")); Serial.println(plain.length()); - - // 1) ارسال 5 کاراکتر کلید عمومی - for (int i = 0; i < PUBLIC_KEY.length(); i++) { - sendChar((uint8_t)PUBLIC_KEY[i]); - } - - // 2) ارسال دیتای رمز شده به صورت HEX (هر بایت → دو کاراکتر) - Serial.print(F("Encrypted HEX: ")); - for (int i = 0; i < enc.length(); i++) { - uint8_t b = (uint8_t)enc[i]; - char hex[3]; sprintf(hex, "%02X", b); - Serial.print(hex); Serial.print(' '); - sendHexByte(b); - } - Serial.println(); - - lastSendTime = millis(); - } - - delay(100); -} diff --git a/old/sender02/sender02.ino b/old/sender02/sender02.ino deleted file mode 100644 index 5b208ac..0000000 --- a/old/sender02/sender02.ino +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -// پین‌ها -#define DHTPIN 2 -#define DHTTYPE DHT11 -#define SOIL_MOISTURE_PIN A3 -#define GAS_SENSOR_PIN A2 -#define RF_TRANSMIT_PIN 9 - -// متن ثابت و کلیدها -const String PUBLIC_KEY = "as23f"; -const String DEVICE_ID = "dr142"; - -// اشیاء -DHT dht(DHTPIN, DHTTYPE); -RCSwitch tx = RCSwitch(); - -// زمان‌بندی -unsigned long lastSendTime = 0; -const unsigned long sendInterval = 5000; - -// تاخیر بین ارسال هر کاراکتر (ms) -const unsigned int INTER_CHAR_DELAY_MS = 15; - -// شمارهٔ بسته (در صورت نیاز بعداً) -uint8_t seqNumber = 0; - -// XOR ساده -String encryptData(const String &data, const String &key) { - String out; - out.reserve(data.length()); - for (int i = 0; i < data.length(); i++) { - out += (char)(data[i] ^ key[i % key.length()]); - } - return out; -} - -static inline void sendChar(uint8_t c) { - tx.send((unsigned long)c, 8); - delay(INTER_CHAR_DELAY_MS); -} - -void setup() { - Serial.begin(9600); - dht.begin(); - - tx.enableTransmit(RF_TRANSMIT_PIN); - tx.setProtocol(1); - tx.setPulseLength(300); - tx.setRepeatTransmit(3); // کتابخانه هر بایت را 3 بار تکرار می‌کند - - Serial.println(F("=== Transmitter Started (with 1-byte checksum) ===")); - Serial.print(F("Public Key: ")); Serial.println(PUBLIC_KEY); - Serial.print(F("Device ID : ")); Serial.println(DEVICE_ID); - - randomSeed(analogRead(0)); // برای ایجاد تاخیر تصادفی احتمالی -} - -void loop() { - // خواندن سنسورها - float temperature = dht.readTemperature(); - float humidity = dht.readHumidity(); - int soilMoisture = analogRead(SOIL_MOISTURE_PIN); - int gasValue = analogRead(GAS_SENSOR_PIN); - - if (isnan(temperature) || isnan(humidity)) { - temperature = 0; humidity = 0; - } - - soilMoisture = constrain(soilMoisture, 0, 1023); - gasValue = constrain(gasValue, 0, 1023); - int tempInt = constrain((int)(temperature * 10), 0, 9999); // چهار رقمی - int humidityInt = constrain((int)(humidity * 10), 0, 9999); // چهار رقمی - - // قالب دقیقاً 25 کاراکتر تولید می‌کند: 5 + (S####) + (G####) + (T####) + (H####) - char dataString[26]; - int n = snprintf( - dataString, sizeof(dataString), - "%sS%04dG%04dT%04dH%04d", - DEVICE_ID.c_str(), soilMoisture, gasValue, tempInt, humidityInt - ); - - if (n != 25) { - Serial.print(F("Format length unexpected: ")); Serial.println(n); - delay(500); - return; - } - - String plain = String(dataString); - String enc = encryptData(plain, PUBLIC_KEY); // length 25, may contain non-printables - - if (millis() - lastSendTime >= sendInterval) { - Serial.println(F("=== Data to Send ===")); - Serial.print(F("Plain : ")); Serial.println(plain); - Serial.print(F("Enc : ")); - for (int i = 0; i < enc.length(); i++) { - uint8_t b = (uint8_t)enc[i]; - if (b < 0x10) Serial.print('0'); - Serial.print(b, HEX); Serial.print(' '); - } - Serial.println(); - - // ساخت packet: [PUBLIC_KEY(5)] + [ENC(25)] + [CHK(1)] => مجموع 31 بایت - const int PACKET_LEN = 31; - uint8_t packet[PACKET_LEN]; - - // PUBLIC_KEY (5 bytes) - for (int i = 0; i < 5; i++) packet[i] = (uint8_t)PUBLIC_KEY[i]; - - // ENC (25 bytes) - for (int i = 0; i < 25; i++) packet[5 + i] = (uint8_t)enc[i]; - - // محاسبه چک‌سام ساده (sum of bytes 0..29) & 0xFF - uint16_t sum = 0; - for (int i = 0; i < 30; i++) sum += packet[i]; - packet[30] = (uint8_t)(sum & 0xFF); - - // ارسال تمام بایت‌ها متوالی (فقط یک بار، بدون حلقهٔ 3تایی دستی) - for (int i = 0; i < PACKET_LEN; i++) { - sendChar(packet[i]); - } - - Serial.print(F("Checksum sent: 0x")); - if (packet[30] < 0x10) Serial.print('0'); - Serial.println(packet[30], HEX); - - lastSendTime = millis(); - } - - delay(100); -} diff --git a/old/stm32f103_2/stm32f103_2.ino b/old/stm32f103_2/stm32f103_2.ino deleted file mode 100644 index 3dcdfa4..0000000 --- a/old/stm32f103_2/stm32f103_2.ino +++ /dev/null @@ -1,307 +0,0 @@ -#include -#include -#include -#include -#include - -// ==================== پین‌ها ==================== -#define SOIL_MOISTURE_PIN PA1 -#define DHT11_PIN PA0 -#define SHT31_SCL PB6 -#define SHT31_SDA PB7 -#define OLED_SCL PB10 -#define OLED_SDA PB11 - -// ==================== OLED ==================== -#define SCREEN_WIDTH 128 -#define SCREEN_HEIGHT 64 -#define OLED_ADDRESS 0x3C - -TwoWire Wire2(OLED_SDA, OLED_SCL); -Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire2, -1); - -// ==================== سنسورها ==================== -#define SHT31_ADDRESS 0x44 -Adafruit_SHT31 sht31(&Wire); -#define DHTTYPE DHT11 -DHT dht(DHT11_PIN, DHTTYPE); - -// ==================== کالیبراسیون ==================== -#define SOIL_WET_VALUE 520 -#define SOIL_DRY_VALUE 800 - -// ==================== متغیرها ==================== -int soilMoisturePercent = 0; -float dht11_temp = 0, dht11_humidity = 0; -float sht31_temp = 0, sht31_humidity = 0; -bool sht31Found = false; -unsigned long lastRead = 0; - -// ==================== تنظیمات نمایش ==================== -#define LARGE_FONT_SIZE 2 // فونت بزرگ برای اعداد -#define SMALL_FONT_SIZE 1 // فونت کوچک برای متن - -void setup() { - Serial1.begin(115200); - delay(100); - - Serial1.println("========================================"); - Serial1.println("STM32F103 - Three Sensor System"); - Serial1.println("========================================"); - - // I2C1 برای SHT31 - Wire.setSDA(SHT31_SDA); - Wire.setSCL(SHT31_SCL); - Wire.begin(); - - // I2C2 برای OLED - Wire2.begin(); - Wire2.setClock(400000); - - // سنسورها - pinMode(SOIL_MOISTURE_PIN, INPUT_ANALOG); - dht.begin(); - - // OLED - if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) { - Serial1.println("OLED initialization failed!"); - while (1); - } - - Serial1.println("OLED initialized successfully"); - - // SHT31 - sht31Found = sht31.begin(SHT31_ADDRESS); - if (!sht31Found) { - sht31Found = sht31.begin(0x45); // تست آدرس دیگر - } - - if (sht31Found) { - Serial1.println("SHT31 found!"); - } else { - Serial1.println("SHT31 not found!"); - } - - displayStartup(); -} - -void loop() { - if (millis() - lastRead >= 2000) { - lastRead = millis(); - readSensors(); - updateDisplay(); - printSerial(); - } - delay(100); -} - -void readSensors() { - // خاک - میانگین‌گیری برای دقت بیشتر - long soilSum = 0; - for (int i = 0; i < 5; i++) { - soilSum += analogRead(SOIL_MOISTURE_PIN); - delay(1); - } - int soilRaw = soilSum / 5; - - if (soilRaw <= SOIL_WET_VALUE) { - soilMoisturePercent = 100; - } else if (soilRaw >= SOIL_DRY_VALUE) { - soilMoisturePercent = 0; - } else { - soilMoisturePercent = map(soilRaw, SOIL_DRY_VALUE, SOIL_WET_VALUE, 0, 100); - } - soilMoisturePercent = constrain(soilMoisturePercent, 0, 100); - - // DHT11 - dht11_temp = dht.readTemperature(); - dht11_humidity = dht.readHumidity(); - if (isnan(dht11_temp) || isnan(dht11_humidity)) { - dht11_temp = dht11_humidity = -999; - } - - // SHT31 - if (sht31Found) { - sht31_temp = sht31.readTemperature(); - sht31_humidity = sht31.readHumidity(); - if (isnan(sht31_temp) || isnan(sht31_humidity)) { - sht31_temp = sht31_humidity = -999; - } - } -} - -void updateDisplay() { - display.clearDisplay(); - display.setTextColor(SSD1306_WHITE); - - // ==================== خط اول: خاک ==================== - // عنوان خاک - display.setTextSize(SMALL_FONT_SIZE); - display.setCursor(0, 0); - display.print("Soil:"); - - // مقدار خاک (بزرگ) - display.setTextSize(LARGE_FONT_SIZE); - if (soilMoisturePercent < 10) { - display.setCursor(35, 0); - } else if (soilMoisturePercent < 100) { - display.setCursor(25, 0); - } else { - display.setCursor(15, 0); - } - display.print(soilMoisturePercent); - - // علامت درصد - display.setTextSize(SMALL_FONT_SIZE); - display.print("%"); - - // ==================== خط دوم: DHT11 ==================== - // عنوان DHT11 - display.setTextSize(SMALL_FONT_SIZE); - display.setCursor(0, 20); - display.print("DHT:"); - - // دما DHT (بزرگ) - display.setTextSize(LARGE_FONT_SIZE); - if (dht11_temp != -999) { - if (dht11_temp >= 0 && dht11_temp < 10) { - display.setCursor(40, 20); - } else if (dht11_temp >= 10 && dht11_temp < 100) { - display.setCursor(30, 20); - } else { - display.setCursor(20, 20); - } - display.print(dht11_temp, 1); - } else { - display.setCursor(40, 20); - display.print("---"); - } - - // درجه سانتیگراد - display.setTextSize(SMALL_FONT_SIZE); - display.print("C"); - - // رطوبت DHT (بزرگ) - display.setTextSize(LARGE_FONT_SIZE); - display.setCursor(80, 20); - if (dht11_humidity != -999) { - if (dht11_humidity < 10) { - display.setCursor(85, 20); - } else if (dht11_humidity < 100) { - display.setCursor(80, 20); - } else { - display.setCursor(75, 20); - } - display.print(dht11_humidity, 0); - } else { - display.print("---"); - } - - // علامت درصد - display.setTextSize(SMALL_FONT_SIZE); - display.print("%"); - - // ==================== خط سوم: SHT31 ==================== - // عنوان SHT31 - display.setTextSize(SMALL_FONT_SIZE); - display.setCursor(0, 40); - display.print("SHT:"); - - // دما SHT (بزرگ) - display.setTextSize(LARGE_FONT_SIZE); - if (sht31Found && sht31_temp != -999) { - if (sht31_temp >= 0 && sht31_temp < 10) { - display.setCursor(40, 40); - } else if (sht31_temp >= 10 && sht31_temp < 100) { - display.setCursor(30, 40); - } else { - display.setCursor(20, 40); - } - display.print(sht31_temp, 1); - } else { - display.setCursor(40, 40); - display.print("---"); - } - - // درجه سانتیگراد - display.setTextSize(SMALL_FONT_SIZE); - display.print("C"); - - // رطوبت SHT (بزرگ) - display.setTextSize(LARGE_FONT_SIZE); - if (sht31Found && sht31_humidity != -999) { - if (sht31_humidity < 10) { - display.setCursor(85, 40); - } else if (sht31_humidity < 100) { - display.setCursor(80, 40); - } else { - display.setCursor(75, 40); - } - display.print(sht31_humidity, 0); - } else { - display.setCursor(80, 40); - display.print("---"); - } - - // علامت درصد - display.setTextSize(SMALL_FONT_SIZE); - display.print("%"); - - // ==================== خط جداساز ==================== - display.drawFastHLine(0, 15, 128, SSD1306_WHITE); // بین خاک و DHT - display.drawFastHLine(0, 35, 128, SSD1306_WHITE); // بین DHT و SHT - - display.display(); -} - -void printSerial() { - Serial1.print("Time: "); - Serial1.print(millis() / 1000); - Serial1.print("s | "); - - Serial1.print("Soil: "); - Serial1.print(soilMoisturePercent); - Serial1.print("% | "); - - Serial1.print("DHT: "); - if (dht11_temp != -999) { - Serial1.print(dht11_temp, 1); - Serial1.print("C "); - Serial1.print(dht11_humidity, 0); - Serial1.print("%"); - } else { - Serial1.print("---"); - } - - Serial1.print(" | "); - - Serial1.print("SHT: "); - if (sht31Found && sht31_temp != -999) { - Serial1.print(sht31_temp, 1); - Serial1.print("C "); - Serial1.print(sht31_humidity, 0); - Serial1.print("%"); - } else { - Serial1.print("---"); - } - - Serial1.println(); -} - -void displayStartup() { - display.clearDisplay(); - - // نمایش عنوان - display.setTextSize(LARGE_FONT_SIZE); - display.setCursor(15, 10); - display.println("SENSOR"); - display.setCursor(20, 30); - display.println("SYSTEM"); - - display.setTextSize(SMALL_FONT_SIZE); - display.setCursor(40, 50); - display.println("READY"); - - display.display(); - delay(1500); -} \ No newline at end of file diff --git a/old/test1/test1.ino b/old/test1/test1.ino deleted file mode 100644 index 93a651c..0000000 --- a/old/test1/test1.ino +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include - -// پین‌ها -#define DHTPIN 2 -#define DHTTYPE DHT11 -#define SOIL_MOISTURE_PIN A0 -#define GAS_SENSOR_PIN A1 -#define RF_TRANSMIT_PIN 9 - -// متن ثابت و کلیدها - -const String PUBLIC_KEY = "as23f"; -const String DEVICE_ID = "dr142"; - -// اشیاء -DHT dht(DHTPIN, DHTTYPE); -RCSwitch tx = RCSwitch(); - -// زمان‌بندی -unsigned long lastSendTime = 0; -const unsigned long sendInterval = 5000; - -// تاخیر بین ارسال هر کاراکتر (ms) -const unsigned int INTER_CHAR_DELAY_MS = 15; - -// XOR ساده -String encryptData(const String &data, const String &key) { - String out; - out.reserve(data.length()); - for (int i = 0; i < data.length(); i++) { - out += (char)(data[i] ^ key[i % key.length()]); - } - return out; -} - -static inline void sendChar(uint8_t c) { - tx.send((unsigned long)c, 8); - delay(INTER_CHAR_DELAY_MS); -} - -static inline void sendHexByte(uint8_t b) { - char hex[3]; - sprintf(hex, "%02X", b); // حروف بزرگ - sendChar(hex[0]); - sendChar(hex[1]); -} - -void setup() { - Serial.begin(9600); - dht.begin(); - - tx.enableTransmit(RF_TRANSMIT_PIN); - tx.setProtocol(1); - tx.setPulseLength(300); - tx.setRepeatTransmit(3); - - Serial.println(F("=== Transmitter Started ===")); - Serial.print(F("Public Key: ")); Serial.println(PUBLIC_KEY); - Serial.print(F("Device ID : ")); Serial.println(DEVICE_ID); -} - -void loop() { - // خواندن سنسورها - float temperature = dht.readTemperature(); - float humidity = dht.readHumidity(); - int soilMoisture = analogRead(SOIL_MOISTURE_PIN); - int gasValue = analogRead(GAS_SENSOR_PIN); - - if (isnan(temperature) || isnan(humidity)) { - temperature = 0; humidity = 0; - } - - soilMoisture = constrain(soilMoisture, 0, 1023); - gasValue = constrain(gasValue, 0, 1023); - int tempInt = constrain((int)(temperature * 10), 0, 9999); // چهار رقمی - int humidityInt = constrain((int)(humidity * 10), 0, 9999); // چهار رقمی - - // ⚠️ خیلی مهم: ۲۶ بایت برای نَل ترمینیتور - char dataString[26]; - // قالب دقیقاً 25 کاراکتر تولید می‌کند: 5 + (S####) + (G####) + (T####) + (H####) - // 5 + 1+4 + 1+4 + 1+4 + 1+4 = 25 - int n = snprintf( - dataString, sizeof(dataString), - "%sS%04dG%04dT%04dH%04d", - DEVICE_ID.c_str(), soilMoisture, gasValue, tempInt, humidityInt - ); - - // اگر بنا به هر دلیلی طول غیرمنتظره شد، همین‌جا خروج - if (n != 25) { - Serial.print(F("Format length unexpected: ")); Serial.println(n); - delay(500); - return; - } - - String plain = String(dataString); - String enc = encryptData(plain, PUBLIC_KEY); - - if (millis() - lastSendTime >= sendInterval) { - Serial.println(F("=== Data to Send ===")); - Serial.print(F("Original: ")); Serial.println(plain); - Serial.print(F("Length : ")); Serial.println(plain.length()); - - // 1) ارسال 5 کاراکتر کلید عمومی - for (int i = 0; i < PUBLIC_KEY.length(); i++) { - sendChar((uint8_t)PUBLIC_KEY[i]); - } - - // 2) ارسال دیتای رمز شده به صورت HEX (هر بایت → دو کاراکتر) - Serial.print(F("Encrypted HEX: ")); - for (int i = 0; i < enc.length(); i++) { - uint8_t b = (uint8_t)enc[i]; - char hex[3]; sprintf(hex, "%02X", b); - Serial.print(hex); Serial.print(' '); - sendHexByte(b); - } - Serial.println(); - - lastSendTime = millis(); - } - - delay(100); -} diff --git a/old/test2/test2.ino b/old/test2/test2.ino deleted file mode 100644 index 66a6d77..0000000 --- a/old/test2/test2.ino +++ /dev/null @@ -1,148 +0,0 @@ -#include -#define RX_PIN D3 // RX NodeMCU -> TX M66 -#define TX_PIN D4 // TX NodeMCU -> RX M66 - -SoftwareSerial M66(RX_PIN, TX_PIN); - -const String url = "http://amlakmodaberan.ir/1.html"; -// ========================= -// Timeout ها -// ========================= -const unsigned long AT_TIMEOUT = 15000; -const unsigned long HTTP_TIMEOUT = 30000; - -// ========================= -// ارسال دستور AT با انتظار پاسخ -// ========================= -bool sendAT(String cmd, String expected = "OK", unsigned long timeout = AT_TIMEOUT) { - Serial.print("[TX] "); Serial.println(cmd); - - while (M66.available()) M66.read(); // پاکسازی بافر - - M66.println(cmd); - - unsigned long start = millis(); - String response = ""; - - while (millis() - start < timeout) { - yield(); - while (M66.available()) { - char c = M66.read(); - response += c; - Serial.write(c); - - if (response.indexOf(expected) != -1) return true; - if (response.indexOf("ERROR") != -1 || response.indexOf("+CME ERROR") != -1) { - Serial.println("❌ Command failed"); - return false; - } - } - } - - Serial.println("⏱ Timeout waiting for response"); - return false; -} - -// ========================= -// راه‌اندازی GPRS و HTTP با QIREGAPP -// ========================= -bool initGPRS() { - Serial.println("=== Initializing GPRS ==="); - delay(2000); - - sendAT("ATE0"); - sendAT("AT+CMEE=2"); - sendAT("AT+CFUN=1"); - delay(2000); - - if (!sendAT("AT+CPIN?", "READY")) return false; - - // شبکه ثبت شده - for (int i = 0; i < 10; i++) { - if (sendAT("AT+CREG?", "+CREG: 0,1")) break; - delay(2000); - } - - if (!sendAT("AT+CGATT=1")) return false; - delay(5000); - - // reset context - sendAT("AT+QIFGCNT=0"); - - if (!sendAT("AT+QICSGP=1,\"mcinet\",\"\",\"\"")) return false; - - // فعال کردن QIREGAPP به جای QIACT - if (!sendAT("AT+QIREGAPP", "OK", 30000)) return false; - - // بررسی IP state - sendAT("AT+QISTAT", "STATE: IP GPRSACT"); - - // پیکربندی HTTP - sendAT("AT+QHTTPCFG=\"contextid\",1"); - sendAT("AT+QHTTPCFG=\"responseheader\",1"); - sendAT("AT+QHTTPCFG=\"timeout\",30000"); - sendAT("AT+QHTTPCFG=\"requestheader\",1"); - - Serial.println("✅ GPRS ready!"); - return true; -} - -// ========================= -// ارسال HTTP GET -// ========================= -bool sendHTTPRequest() { - Serial.println("=== Sending HTTP Request ==="); - - int len = url.length(); - - if (!sendAT("AT+QHTTPURL=" + String(len) + ",60", "CONNECT", 15000)) { - Serial.println("❌ URL setup failed"); - return false; - } - - M66.print(url); - delay(200); - M66.write(0x1A); // Ctrl+Z - delay(1500); - - if (!sendAT("AT+QHTTPGET=80", "OK", HTTP_TIMEOUT)) { - Serial.println("❌ HTTP GET failed"); - return false; - } - - if (!sendAT("AT+QHTTPREAD", "+QHTTPREAD:", HTTP_TIMEOUT)) { - Serial.println("⚠️ No HTTP response body"); - return false; - } - - Serial.println("✅ HTTP request done!"); - return true; -} - -// ========================= -// setup -// ========================= -void setup() { - Serial.begin(115200); - M66.begin(9600); - delay(2000); - - int retry = 0; - while (retry < 3) { - if (initGPRS()) { - if (sendHTTPRequest()) break; - else Serial.println("⚠️ HTTP failed, retrying..."); - } else { - Serial.println("❌ GPRS init failed, retrying..."); - } - retry++; - delay(5000); - } -} - -// ========================= -// loop -// ========================= -void loop() { - yield(); // جلوگیری از WDT -} diff --git a/old/teststm03/teststm03.ino b/old/teststm03/teststm03.ino deleted file mode 100644 index accc4f0..0000000 --- a/old/teststm03/teststm03.ino +++ /dev/null @@ -1,116 +0,0 @@ -/* - MQ-7 Module - با ضرایب صحیح -*/ - -#include - -const uint8_t ADC_PIN = PA2; -const uint8_t LED_PIN = PC13; - -const float ADC_REF = 3.3; -const uint32_t ADC_MAX = 4095; -const float RL = 10000.0; -float R0 = -1.0; -const float CO_THRESHOLD = 50.0; - -// ضرایب صحیح برای MQ-7 و گاز CO -const float CO_A = 99.042; -const float CO_B = -1.518; - -float readVoltage() { - const int N = 10; - uint32_t sum = 0; - for (int i = 0; i < N; i++) { - sum += analogRead(ADC_PIN); - delay(3); - } - float adc_avg = sum / float(N); - return adc_avg * (ADC_REF / ADC_MAX); -} - -float calcRs(float Vout) { - if (Vout <= 0.001) return 1e6; - - // فرمول اصلاح شده: ما Vout را از سنسور می‌خوانیم که نسبت به 5V داخلی ماژول است - // بنابراین باید از 5.0 در فرمول استفاده کنیم، نه ADC_REF - return RL * (5.0 - Vout) / Vout; // این فرمول صحیح است -} - -float toRealPPM(float Rs) { - if (R0 <= 0) return -1; - float ratio = Rs / R0; - - // فرمول صحیح براساس دیتاشیت MQ-7 - float ppm = CO_A * pow(ratio, CO_B); - - return ppm; -} - -void calibrateR0() { - Serial1.println("=== Calibrating R0 in clean air ==="); - const int N = 50; - float sum_R0 = 0; - - for (int i = 0; i < N; i++) { - float V = readVoltage(); - float Rs = calcRs(V); - - // در هوای پاک: نسبت Rs/R0 ≈ 27.5 (طبق دیتاشیت) - // بنابراین: R0 = Rs / 27.5 - sum_R0 += Rs / 27.5; - - delay(100); - } - - R0 = sum_R0 / N; - Serial1.print("Calibration complete. R0 = "); - Serial1.println(R0, 2); -} - -void setup() { - Serial1.begin(115200); - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, LOW); - - Serial1.println("MQ-7 CO Monitor - Waiting for warm up..."); - delay(30000); // 30 ثانیه پیش‌گرمایش - Serial1.println("Ready. Send 'c' to calibrate."); - calibrateR0(); -} - -void loop() { - if (Serial1.available()) { - char c = Serial1.read(); - if (c == 'c' || c == 'C') calibrateR0(); - } - - float V = readVoltage(); - float Rs = calcRs(V); - float ppm = toRealPPM(Rs); - - Serial1.print("Vout="); - Serial1.print(V, 3); - Serial1.print("V, Rs="); - Serial1.print(Rs, 1); - Serial1.print("Ω"); - - if (R0 > 0) { - Serial1.print(", Rs/R0="); - Serial1.print(Rs/R0, 3); - Serial1.print(", CO="); - Serial1.print(ppm, 1); - Serial1.println("ppm"); - - // هشدار - if (ppm > CO_THRESHOLD) { - digitalWrite(LED_PIN, HIGH); - Serial1.println("⚠️ WARNING: High CO level!"); - } else { - digitalWrite(LED_PIN, LOW); - } - } else { - Serial1.println(" [Not calibrated]"); - } - - delay(2000); -} \ No newline at end of file diff --git a/old/wifi1/wifi1.ino b/old/wifi1/wifi1.ino deleted file mode 100644 index 3f9c065..0000000 --- a/old/wifi1/wifi1.ino +++ /dev/null @@ -1,212 +0,0 @@ -#include -#include -#include "RTClib.h" -#include -#include - -// تعریف نام مستعار برای حل مشکل تداخل -using SDLibFile = File; - -SoftwareSerial espSerial(4, 5); -const int chipSelect = 10; -RTC_DS3231 rtc; - -// کاهش مصرف حافظه با استفاده از PROGMEM -const char daysOfTheWeek[7][4] PROGMEM = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - -// پیش‌تعریف توابع -bool saveDataToFile(const char* filename, const char* data, bool append = true); -bool formatSDCard(); -void sendCommand(const char* cmd, int delayTime); -void sendHTTPResponse(int connectionId, const char* content); -const char* readSensorData(); -const char* handleSaveData(const char* query); -const char* handleReadData(const char* query); -const char* handleFormatCard(); - -void setup() { - Serial.begin(9600); - - // راه اندازی کارت SD - if (!SD.begin(chipSelect)) { - Serial.println(F("SD Card Error!")); - } else { - Serial.println(F("SD Card OK")); - } - - espSerial.begin(9600); - - // راه اندازی RTC - if (!rtc.begin()) { - Serial.println(F("RTC Error!")); - while (1); - } - - if (rtc.lostPower()) { - rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); - } - - delay(1000); - Serial.println(F("Initializing ESP...")); - - // کاهش دستورات ESP - const char* commands[] = { - "AT", "AT+RST", "AT+CWMODE=2", - "AT+CWSAP=\"ESP_CTRL\",\"12345678\",1,3", - "AT+CIPMUX=1", "AT+CIPSERVER=1,80" - }; - - for (int i = 0; i < 6; i++) { - sendCommand(commands[i], 1000); - } - - Serial.println(F("AP Ready!")); -} - -void loop() { - checkESP(); - delay(1000); -} - -void sendCommand(const char* cmd, int delayTime) { - Serial.print(F("Sending: ")); - Serial.println(cmd); - espSerial.println(cmd); - - delay(delayTime); -} - -void sendHTTPResponse(int connectionId, const char* content) { - char response[512]; - snprintf(response, sizeof(response), - "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n%s", - content); - - char cmd[50]; - snprintf(cmd, sizeof(cmd), "AT+CIPSEND=%d,%d", connectionId, strlen(response)); - - sendCommand(cmd, 50); - sendCommand(response, 100); - - snprintf(cmd, sizeof(cmd), "AT+CIPCLOSE=%d", connectionId); - sendCommand(cmd, 50); -} - -void checkESP() { - static char buffer[128]; - static int index = 0; - - while (espSerial.available()) { - char c = espSerial.read(); - if (index < sizeof(buffer) - 1) { - buffer[index++] = c; - } - - if (c == '\n') { - buffer[index] = '\0'; - - if (strstr(buffer, "+IPD") != NULL) { - int connId = atoi(&buffer[5]); - handleRequest(connId, buffer); - } - - index = 0; - } - } -} - -void handleRequest(int connectionId, char* request) { - char* getStart = strstr(request, "GET /"); - if (!getStart) return; - - char* pathStart = getStart + 5; - char* pathEnd = strchr(pathStart, ' '); - if (!pathEnd) return; - - char path[32] = {0}; - strncpy(path, pathStart, min((int)(pathEnd - pathStart), 31)); - - char* query = strchr(path, '?'); - if (query) { - *query = '\0'; - query++; - } - - Serial.print(F("Path: ")); - Serial.println(path); - - const char* response; - - if (strcmp(path, "se") == 0) { - response = readSensorData(); - } - else if (strcmp(path, "save") == 0) { - response = handleSaveData(query); - } - else if (strcmp(path, "format") == 0) { - response = handleFormatCard(); - } - else { - response = "Available endpoints: /se, /save, /format"; - } - - sendHTTPResponse(connectionId, response); -} - -const char* readSensorData() { - static char buffer[64]; - DateTime now = rtc.now(); - - snprintf(buffer, sizeof(buffer), "Temp: %.1fC, Time: %02d:%02d:%02d", - rtc.getTemperature(), now.hour(), now.minute(), now.second()); - - return buffer; -} - -const char* handleSaveData(const char* query) { - if (query && strlen(query) > 0) { - if (saveDataToFile("data.txt", query, true)) { - return "Data saved"; - } - return "Save error"; - } - return "No data"; -} - -const char* handleFormatCard() { - return formatSDCard() ? "Formatted" : "Format error"; -} - -bool saveDataToFile(const char* filename, const char* data, bool append) { - SDLibFile dataFile; - - if (append) { - dataFile = SD.open(filename, FILE_WRITE); - } else { - if (SD.exists(filename)) { - SD.remove(filename); - } - dataFile = SD.open(filename, FILE_WRITE); - } - - if (!dataFile) return false; - - bool result = dataFile.println(data); - dataFile.close(); - - return result; -} - -bool formatSDCard() { - // فقط حذف فایل‌های اصلی - const char* files[] = {"data.txt", "test.txt"}; - - for (int i = 0; i < 2; i++) { - if (SD.exists(files[i])) { - SD.remove(files[i]); - } - } - - // تست نوشتن - return saveDataToFile("test.txt", "Formatted", false); -} \ No newline at end of file diff --git a/stm32f103.ino b/stm32f103.ino deleted file mode 100644 index bafe888..0000000 --- a/stm32f103.ino +++ /dev/null @@ -1,378 +0,0 @@ -#include -#include -#include -#include "DHT.h" - -// --------------------------- تنظیمات --------------------------- -#define DHTPIN PA0 -#define DHTTYPE DHT11 -DHT dht(DHTPIN, DHTTYPE); - -#define SOIL_PIN PA1 -#define MQ7_PIN PA2 - -#define SDA_PIN PB9 -#define SCL_PIN PB8 -BH1750 lightMeter; - -#define EC200U_TX PB10 -#define EC200U_RX PB11 - -HardwareSerial EC200U(USART3); - -#define PWRKEY_PIN PB5 - -#define UPLOAD_INTERVAL_MIN 0.2 -String networkTime = ""; -bool sslOK = false; - -// تابع بهبود یافته برای ارسال AT -bool sendAT(String cmd, uint16_t wait = 2000) { - Serial1.print(">> "); - Serial1.println(cmd); - - EC200U.println(cmd); - - String response = ""; - unsigned long start = millis(); - - while (millis() - start < wait) { - while (EC200U.available()) { - char c = EC200U.read(); - response += c; - Serial1.write(c); - } - // اگر پاسخ کامل دریافت شد، خارج شو - if (response.indexOf("OK") != -1 || response.indexOf("ERROR") != -1) { - break; - } - } - - Serial1.println(); - - // بررسی موفقیت یا خطا - if (response.indexOf("OK") != -1) { - return true; - } else if (response.indexOf("ERROR") != -1 || response.indexOf("FAIL") != -1) { - return false; - } else { - Serial1.println("⚠️ No response11"); - return false; - } -} - -// تابع برای منتظر ماندن پاسخ خاص -bool waitForResponse(String expected, unsigned long timeout = 10000) { - unsigned long startTime = millis(); - String response = ""; - - while (millis() - startTime < timeout) { - while (EC200U.available()) { - char c = EC200U.read(); - response += c; - Serial1.write(c); - - if (response.indexOf(expected) != -1) { - return true; - } - if (response.indexOf("ERROR") != -1) { - return false; - } - } - } - Serial1.println("⏰ Timeout waiting for: " + expected); - return false; -} - -void initEC200U() { - Serial1.println("🚀1 Starting EC200U..."); - - // روشن کردن ماژول - digitalWrite(PWRKEY_PIN, HIGH); - delay(2000); - digitalWrite(PWRKEY_PIN, LOW); - delay(5000); // افزایش زمان انتظار - - // بررسی ارتباط - if (!sendAT("AT", 3000)) { - Serial1.println("❌ EC200U not responding!"); - return; - } - - sendAT("ATI", 2000); - sendAT("AT+CPIN?", 2000); - sendAT("AT+CSQ", 2000); - sendAT("AT+CREG?", 2000); - sendAT("AT+CGREG?", 2000); - - // فعال کردن GPRS - sendAT("AT+CGATT=1", 5000); - sendAT("AT+CGDCONT=1,\"IP\",\"mcinet\"", 2000); - - // فعال کردن PDP context - if (sendAT("AT+QIACT=1", 15000)) { - Serial1.println("✅ PDP context activated"); - } else { - Serial1.println("❌ Failed to activate PDP context"); - sendAT("AT+QIACT?", 2000); - } - - // تنظیمات HTTP - sendAT("AT+QHTTPCFG=\"contextid\",1", 2000); - sendAT("AT+QHTTPCFG=\"requestheader\",1", 2000); - sendAT("AT+QHTTPCFG=\"responseheader\",1", 2000); - - // تنظیمات SSL - sendAT("AT+QSSLCFG=\"sslversion\",1,4", 2000); - sendAT("AT+QSSLCFG=\"ciphersuite\",1,0XFFFF", 2000); - sendAT("AT+QSSLCFG=\"seclevel\",1,0", 2000); - sendAT("AT+QSSLCFG=\"ignorelocaltime\",1,1", 2000); - - Serial1.println("✅ EC200U Initialized"); -} - -void getNetworkTime() { - if (sendAT("AT+QLTS=2", 5000)) { - networkTime = "Time received"; - } else { - networkTime = "No time"; - } -} - -// --- MQ7 تابع مخصوص ماژول آماده --- -float MQ7_ReadPPM() { - long sum = 0; - for (int i = 0; i < 10; i++) { - sum += analogRead(MQ7_PIN); - delay(5); - } - float adcValue = sum / 10.0; - float voltage = (adcValue / 4095.0) * 3.3; // اصلاح به 3.3V برای STM32 - - float ppm; - if (voltage < 0.1) ppm = 0; - else if (voltage < 0.2) ppm = 10 * (voltage / 0.2); - else if (voltage < 0.5) ppm = 50 + (voltage - 0.2) * 300; - else if (voltage < 1.0) ppm = 140 + (voltage - 0.5) * 800; - else if (voltage < 2.0) ppm = 500 + (voltage - 1.0) * 1500; - else ppm = 2000 + (voltage - 2.0) * 2000; - - if (ppm < 0) ppm = 0; - if (ppm > 10000) ppm = 10000; - - return ppm; -} - -// --- Soil Sensor (تبدیل به درصد) --- -int readSoil() { - int adcValue = analogRead(SOIL_PIN); - const int adcDry = 4095; // خاک کاملاً خشک (12-bit ADC) - const int adcWet = 1200; // خاک کاملاً خیس - - // محاسبه درصد رطوبت - float soilPercent = (float)(adcDry - adcValue) / (adcDry - adcWet) * 100.0; - - // محدود کردن بین 0 تا 100 - if (soilPercent > 100.0) soilPercent = 100.0; - if (soilPercent < 0.0) soilPercent = 0.0; - - return (int)soilPercent; -} - -// --- سایر سنسورها --- -float readLight() { - float lux = lightMeter.readLightLevel(); - if (isnan(lux)) return 0; - return lux; -} - -float readTemp() { - float temp = dht.readTemperature(); - if (isnan(temp)) return -999; - return temp; -} - -float readHum() { - float hum = dht.readHumidity(); - if (isnan(hum)) return -999; - return hum; -} - -bool sendData(float temp, float hum, int soil, float gas, float lux, String timeStr) { - // بررسی مقادیر سنسورها - if (temp == -999 || hum == -999) { - Serial1.println("❌ DHT Sensor error!"); - return false; - } - - String gazStr = String(gas, 0); - gazStr.trim(); - String dataStr = "temperatureC=" + String(temp, 1) + - "&humidityPercent=" + String(hum, 1) + - "&soilPercent=" + String(soil) + - "&gasPPM=" + gazStr + - "&lux=" + String(lux, 1); - - String url = "https://ghback.nabaksoft.ir/api/Telemetry/AddData?deviceName=dr110&" + dataStr; - url.replace(" ", ""); - - Serial1.println("📤 Sending data to server..."); - Serial1.println("URL: " + url); - Serial1.println("Length: " + String(url.length())); - - // تنظیمات SSL برای HTTPS - Serial1.println("🔒 Configuring SSL..."); - if (!sendAT("AT+QHTTPCFG=\"sslctxid\",1", 2000)) { - Serial1.println("❌ SSL configuration failed"); - return false; - } - - // تنظیم URL - String urlCmd = "AT+QHTTPURL=" + String(url.length()) + ",443"; - Serial1.println(">> " + urlCmd); - - EC200U.println(urlCmd); - - // منتظر CONNECT - if (waitForResponse("CONNECT", 10000)) { - Serial1.println("✅ CONNECT received, sending URL..."); - - // ارسال URL - EC200U.print(url); - - // منتظر OK - if (waitForResponse("OK", 5000)) { - Serial1.println("✅ URL set successfully"); - } else { - Serial1.println("❌ Failed to set URL"); - return false; - } - } else { - Serial1.println("❌ CONNECT timeout"); - return false; - } - - // اجرای درخواست GET - Serial1.println("🚀 Sending GET request..."); - EC200U.println("AT+QHTTPGET=120"); - - if (waitForResponse("+QHTTPGET:", 30000)) { - Serial1.println("✅ GET request sent"); - - // بررسی کد پاسخ - String response = ""; - unsigned long start = millis(); - while (millis() - start < 5000) { - while (EC200U.available()) { - char c = EC200U.read(); - response += c; - Serial1.write(c); - } - if (response.indexOf("OK") != -1 || response.indexOf("ERROR") != -1) { - break; - } - } - - // خواندن پاسخ سرور - Serial1.println("📖 Reading server response..."); - EC200U.println("AT+QHTTPREAD=80"); - - if (waitForResponse("+QHTTPREAD:", 15000)) { - Serial1.println("✅ Response received"); - - // خواندن داده‌های پاسخ - String serverResponse = ""; - start = millis(); - while (millis() - start < 10000) { - while (EC200U.available()) { - char c = EC200U.read(); - serverResponse += c; - Serial1.write(c); - } - if (serverResponse.indexOf("OK") != -1 || serverResponse.indexOf("ERROR") != -1) { - break; - } - } - - Serial1.println("✅ Data sent successfully"); - return true; - } else { - Serial1.println("❌ Failed to read response"); - return false; - } - } else { - Serial1.println("❌ GET request failed"); - return false; - } -} - -// --------------------------- setup --------------------------- -void setup() { - pinMode(PWRKEY_PIN, OUTPUT); - digitalWrite(PWRKEY_PIN, LOW); - - Serial1.begin(115200); - EC200U.begin(115200); - delay(3000); - - Serial1.println("🚀 System Starting..."); - - // مقداردهی سنسورها - dht.begin(); - - Wire.setSDA(SDA_PIN); - Wire.setSCL(SCL_PIN); - Wire.begin(); - - if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { - Serial1.println("✅ BH1750 OK"); - } else { - Serial1.println("❌ BH1750 Error"); - } - - // مقداردهی پین‌های آنالوگ - pinMode(SOIL_PIN, INPUT); - pinMode(MQ7_PIN, INPUT); - - initEC200U(); - getNetworkTime(); - - Serial1.println("✅ System Ready"); -} - -// --------------------------- loop --------------------------- -unsigned long lastUpload = 0; - -void loop() { - if (millis() - lastUpload > UPLOAD_INTERVAL_MIN * 60UL * 1000UL || lastUpload == 0) { - Serial1.println("=========================================="); - - // خواندن سنسورها - float temp = readTemp(); - float hum = readHum(); - int soil = readSoil(); - float gas = MQ7_ReadPPM(); - float lux = readLight(); - - // نمایش مقادیر - Serial1.println("📊 Sensor Readings:"); - Serial1.println("Temperature: " + String(temp, 1) + "°C"); - Serial1.println("Humidity: " + String(hum, 1) + "%"); - Serial1.println("Soil: " + String(soil) + "%"); - Serial1.println("Gas: " + String(gas, 0) + " ppm"); - Serial1.println("Light: " + String(lux, 1) + " lux"); - Serial1.println("------------------------------------------"); - - // ارسال داده - if (sendData(temp, hum, soil, gas, lux, networkTime)) { - Serial1.println("🎉 Data upload successful!"); - } else { - Serial1.println("❌ Data upload failed!"); - } - - lastUpload = millis(); - Serial1.println("=========================================="); - } - - delay(1000); -} \ No newline at end of file