From f69c0f2493b28f8007e747863d8d8fb00c09df8f Mon Sep 17 00:00:00 2001 From: rahimi rahimi Date: Tue, 23 Dec 2025 17:19:03 +0330 Subject: [PATCH] smses is ok --- stm32f103.ino | 987 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 697 insertions(+), 290 deletions(-) diff --git a/stm32f103.ino b/stm32f103.ino index bafe888..3e1ee91 100644 --- a/stm32f103.ino +++ b/stm32f103.ino @@ -1,154 +1,635 @@ #include #include +#include +#include +#include #include -#include "DHT.h" - -// --------------------------- تنظیمات --------------------------- -#define DHTPIN PA0 -#define DHTTYPE DHT11 -DHT dht(DHTPIN, DHTTYPE); +#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 SOIL_PIN PA1 -#define MQ7_PIN PA2 - +#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 SENSOR_READ_INTERVAL 60000 -#define UPLOAD_INTERVAL_MIN 0.2 -String networkTime = ""; -bool sslOK = false; +// -------------------- آدرس‌های حافظه -------------------- +#define CONFIG_ADDRESS 0x000000 +#define DATA_ADDRESS 0x010000 +#define MAX_STORED_DATA 100 -// تابع بهبود یافته برای ارسال AT -bool sendAT(String cmd, uint16_t wait = 2000) { - Serial1.print(">> "); +// -------------------- انوم‌ها -------------------- +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; + int soilMoisture; + float coPPM; + float lightLux; + unsigned long timestamp; + uint8_t sent; +}; + +// -------------------- متغیرهای سراسری -------------------- +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +BH1750 lightMeter; +Adafruit_SHT31 sht31; +HardwareSerial EC200U(USART3); +SPIClass flashSPI(FLASH_MOSI, FLASH_MISO, FLASH_SCK); + +DeviceConfig config; +SensorData currentData; +SensorData storedData[MAX_STORED_DATA]; + +// متغیرهای موقت برای پردازش پیامک‌ها +String tempPhoneNumber = ""; +String tempTokenCode = ""; +bool awaitingSMS2 = false; + +unsigned long lastSensorRead = 0; +unsigned long lastUpload = 0; +unsigned long lastSMS = 0; +unsigned long lastSave = 0; +unsigned long lastDisplayChange = 0; +unsigned long lastReconnectAttempt = 0; + +bool networkConnected = false; +int displayMode = 0; +int connectionAttempts = 0; +String lastError = ""; +String currentAPN = ""; +String deviceUID = ""; +String lastMessage = ""; +int storedDataCount = 0; + +// -------------------- توابع حافظه Flash -------------------- +void flashInit() { + pinMode(FLASH_CS, OUTPUT); + digitalWrite(FLASH_CS, HIGH); + flashSPI.begin(); + flashSPI.setClockDivider(SPI_CLOCK_DIV4); + delay(100); +} + +bool flashIsBusy() { + digitalWrite(FLASH_CS, LOW); + flashSPI.transfer(0x05); + uint8_t status = flashSPI.transfer(0); + digitalWrite(FLASH_CS, HIGH); + return (status & 0x01); +} + +void flashWaitForReady() { + while (flashIsBusy()) delay(1); +} + +void flashRead(uint32_t addr, uint8_t *data, uint32_t len) { + flashWaitForReady(); + digitalWrite(FLASH_CS, LOW); + flashSPI.transfer(0x03); + 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); +} + +void flashWrite(uint32_t addr, uint8_t *data, uint32_t len) { + uint32_t offset = 0; + while (offset < len) { + // Write Enable + digitalWrite(FLASH_CS, LOW); + flashSPI.transfer(0x06); + digitalWrite(FLASH_CS, HIGH); + + uint32_t pageOffset = addr % 256; + uint32_t remaining = 256 - pageOffset; + uint32_t writeSize = min(remaining, len - offset); + + 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 < writeSize; i++) { + flashSPI.transfer(data[offset + i]); + } + + digitalWrite(FLASH_CS, HIGH); + flashWaitForReady(); + + addr += writeSize; + offset += writeSize; + } +} + +void flashSectorErase(uint32_t addr) { + digitalWrite(FLASH_CS, LOW); + flashSPI.transfer(0x06); + digitalWrite(FLASH_CS, HIGH); + + 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(); +} + +// -------------------- توابع مدیریت داده -------------------- +void readConfig() { + uint8_t buffer[sizeof(DeviceConfig)]; + flashRead(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig)); + memcpy(&config, buffer, sizeof(DeviceConfig)); + + if (strcmp(config.signature, "CFG") != 0) { + 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; + + saveConfig(); + } +} + +void saveConfig() { + config.valid = true; + flashSectorErase(CONFIG_ADDRESS); + uint8_t buffer[sizeof(DeviceConfig)]; + memcpy(buffer, &config, sizeof(DeviceConfig)); + flashWrite(CONFIG_ADDRESS, buffer, sizeof(DeviceConfig)); + Serial1.println("✅ Config saved to Flash"); +} + +// -------------------- توابع کمکی -------------------- +String simTypeToString(SIMType type) { + switch(type) { + case SIM_HAMRAHE_AVAL: return "HA"; + case SIM_IRANCELL: return "IR"; + case SIM_RIGHTEL: return "RT"; + default: return "UK"; + } +} + +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"; + } +} + +String generateVerificationCode(String tokenCode) { +long token = tokenCode.toInt(); +long verification = (token * 7 + 12345) % 100000; + char buffer[6]; + sprintf(buffer, "%05ld", verification); + + Serial1.println("=== Code Calculation ==="); + Serial1.println("Token: " + tokenCode); + Serial1.println("Result: " + String(verification)); + Serial1.println("Formatted: " + String(buffer)); + Serial1.println("========================"); + + return String(buffer); +} + +// -------------------- پردازش SMS -------------------- +// استخراج همه اعداد از متن پیامک (برای SMS1 و SMS2) +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++; + } +} + +// تشخیص نوع پیامک (SMS1 یا SMS2) بر اساس تعداد اعداد +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++; + } + + // اگر حداقل ۵ تا عدد داشته باشیم، SMS2 فرض می‌کنیم + return (count >= 5) ? 2 : 1; +} + +// -------------------- توابع EC200U -------------------- +bool sendAT(String cmd, uint16_t wait = 2000, bool showResponse = true) { + 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; + if (showResponse) Serial1.write(c); } + if (response.indexOf("OK") != -1) return true; + if (response.indexOf("ERROR") != -1) return false; } - - 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; - } + return false; } -// تابع برای منتظر ماندن پاسخ خاص -bool waitForResponse(String expected, unsigned long timeout = 10000) { - unsigned long startTime = millis(); - String response = ""; +bool sendSMS(String number, String message) { + if (!sendAT("AT+CMGF=1", 3000, true)) { + Serial1.println("❌ Failed to set SMS mode"); + return false; + } - while (millis() - startTime < timeout) { + 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(expected) != -1) { - return true; - } - if (response.indexOf("ERROR") != -1) { - return false; + if (response.indexOf(">") != -1) { + gotPrompt = true; + break; } } + if (gotPrompt) break; } - Serial1.println("⏰ Timeout waiting for: " + expected); - return false; + + if (!gotPrompt) { + Serial1.println("❌ No prompt received"); + 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; + Serial1.write(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, true); + return true; } -void initEC200U() { - Serial1.println("🚀1 Starting EC200U..."); +// -------------------- پردازش پیامک‌ها -------------------- +void processSMS1(String message) { + Serial1.println("🔐 PROCESSING SMS1 (Verification)"); + + // فقط اعداد را به‌ترتیب از متن استخراج می‌کنیم + String numbers[10]; + int count = 0; + extractNumbersFromSMS2(message, numbers, count); + + String phone = ""; + String token = ""; + + if (count >= 2) { + phone = numbers[0]; // شماره تلفن (عدد اول) + token = numbers[1]; // کد ورود (عدد دوم) + } + + Serial1.println("Phone: " + phone); + Serial1.println("Token: " + token); + + if (phone.length() >= 10 && token.length() == 5) { + tempPhoneNumber = phone; + tempTokenCode = token; + + String verifyCode = generateVerificationCode(token); + String reply = "Code: " + verifyCode; + + if (sendSMS(phone, reply)) { + Serial1.println("✅ Verification code sent"); + awaitingSMS2 = true; + lastMessage = "Code Sent"; + + Serial1.println("⏳ Now awaiting SMS2..."); + Serial1.println("tempPhone: " + tempPhoneNumber); + Serial1.println("tempToken: " + tempTokenCode); + } + } +} + +void processSMS2(String message) { + Serial1.println("⚙️ PROCESSING SMS2 (Configuration)"); + + String numbers[10]; + int count = 0; + extractNumbersFromSMS2(message, numbers, count); + + Serial1.print("Numbers: "); + for (int i = 0; i < count; i++) { + Serial1.print(numbers[i] + " "); + } + Serial1.println("(Count: " + String(count) + ")"); + + // پیامک دوم معمولاً ۵ یا ۶ عدد دارد + // ترتیب: [0]=نابک, [1]=اینتروال, [2]=پیامک, [3]=سیستم(بی‌استفاده), [4]=سیم, [5]=لغو(بی‌استفاده) + if (count >= 5) { + // 1. Device ID (نابک) - عدد اول + String deviceId = numbers[0]; + if (deviceId.length() > 2) { + deviceId = deviceId.substring(0, deviceId.length() - 2); + } + deviceId.toCharArray(config.deviceId, 16); + Serial1.println("✓ Device ID: " + String(config.deviceId)); + + // 2. Upload Interval (اینتروال) - عدد دوم + if (numbers[1].length() > 1) { + config.uploadInterval = numbers[1].substring(1).toInt(); + } else { + config.uploadInterval = numbers[1].toInt(); + } + Serial1.println("✓ Upload Interval: " + String(config.uploadInterval)); + + // 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.println("✓ SMS: " + String(config.smsEnabled ? "ON" : "OFF") + ", Interval: " + String(config.smsInterval)); + + // 4. عدد چهارم (سیستم) - بی‌استفاده است، رد می‌شود + Serial1.println("✓ System ID (ignored): " + numbers[3]); + + // 5. SIM Type (سیم) - عدد پنجم (یا اگر ۶ تا بود، پنجم) + int simIndex = (count >= 6) ? 4 : (count - 1); // اگر ۶ تا بود index=4، وگرنه آخری + String simValue = numbers[simIndex]; + + // دو رقم آخر را بگیر + if (simValue.length() >= 2) { + String simCode = simValue.substring(simValue.length() - 2); + Serial1.println("✓ SIM Code: " + simCode); + + 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; + } + + // 6. شماره تلفن (از SMS1 یا اگر نبود، خالی می‌ماند) + if (tempPhoneNumber.length() > 0) { + tempPhoneNumber.toCharArray(config.serverPhoneNumber, 16); + Serial1.println("✓ Server Phone: " + tempPhoneNumber); + } else { + strcpy(config.serverPhoneNumber, ""); + Serial1.println("⚠️ No phone number (SMS1 was skipped)"); + } + + // 7. تایید نهایی + config.verified = true; + saveConfig(); + + currentAPN = simTypeToAPN(config.simType); + awaitingSMS2 = false; + + Serial1.println("\n✅✅✅ DEVICE FULLY CONFIGURED! ✅✅✅"); + Serial1.println("Device ID: " + String(config.deviceId)); + Serial1.println("Phone: " + String(config.serverPhoneNumber)); + Serial1.println("Upload Interval: " + String(config.uploadInterval) + " minutes"); + Serial1.println("SMS Enabled: " + String(config.smsEnabled ? "YES" : "NO")); + Serial1.println("SMS Interval: " + String(config.smsInterval) + " minutes"); + Serial1.println("SIM Type: " + simTypeToString(config.simType)); + Serial1.println("APN: " + currentAPN); + Serial1.println("✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅\n"); + + lastMessage = "Configured"; + + // اتصال به شبکه + initEC200U(); + } else { + Serial1.println("❌ Not enough numbers in SMS2 (need at least 5)"); + } +} + +void checkSMS() { + if (!sendAT("AT", 2000, false)) return; + + sendAT("AT+CMGF=1", 2000, true); + 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; + Serial1.write(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📱 NEW SMS: " + message); + Serial1.println("Verified: " + String(config.verified)); + Serial1.println("Awaiting SMS2: " + String(awaitingSMS2)); + + // استخراج اعداد برای دیباگ + String debugNumbers[10]; + int debugCount = 0; + extractNumbersFromSMS2(message, debugNumbers, debugCount); + Serial1.print("🔢 Numbers found: "); + for (int i = 0; i < debugCount; i++) { + Serial1.print(debugNumbers[i]); + if (i < debugCount - 1) Serial1.print(", "); + } + Serial1.println(" (Total: " + String(debugCount) + ")"); + + int smsType = detectSMSType(message); + Serial1.println("📋 SMS Type detected: " + String(smsType)); + + if (config.verified) { + Serial1.println("⚠️ Already verified, ignoring"); + } else if (smsType == 1 && !awaitingSMS2) { + processSMS1(message); + } else if (smsType == 2 && awaitingSMS2) { + // حالت استاندارد: پیامک اول آمده و الان پیامک دوم + processSMS2(message); + } else if (smsType == 2 && !awaitingSMS2 && !config.verified) { + // حالت مستقیم: پیامک دوم بدون پیامک اول (برای تست یا تنظیم سریع) + Serial1.println("⚡ Direct SMS2 (without SMS1) - Processing anyway"); + processSMS2(message); + } else { + Serial1.println("❌ Wrong SMS type or state"); + Serial1.println(" Expected: SMS type=" + String(awaitingSMS2 ? "2" : "1") + ", Got: " + String(smsType)); + } + + String indexStr = response.substring(msgIndex + 6, comma1); + indexStr.trim(); + sendAT("AT+CMGD=" + indexStr, 2000, true); + } + } +} + +// -------------------- اتصال شبکه -------------------- +void initEC200U() { + if (!config.verified) return; + + Serial1.println("📡 Connecting to network..."); + lastMessage = "Connecting"; - // روشن کردن ماژول digitalWrite(PWRKEY_PIN, HIGH); delay(2000); digitalWrite(PWRKEY_PIN, LOW); - delay(5000); // افزایش زمان انتظار - - // بررسی ارتباط + delay(5000); + if (!sendAT("AT", 3000)) { - Serial1.println("❌ EC200U not responding!"); + Serial1.println("❌ Modem error"); return; } - sendAT("ATI", 2000); - sendAT("AT+CPIN?", 2000); - sendAT("AT+CSQ", 2000); - sendAT("AT+CREG?", 2000); - sendAT("AT+CGREG?", 2000); + currentAPN = simTypeToAPN(config.simType); + String apnCmd = "AT+CGDCONT=1,\"IP\",\"" + currentAPN + "\""; + sendAT(apnCmd, 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"); + networkConnected = true; + lastMessage = "Connected"; + Serial1.println("✅ Network connected"); } 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"; + networkConnected = false; + Serial1.println("❌ Network failed"); } } -// --- MQ7 تابع مخصوص ماژول آماده --- -float MQ7_ReadPPM() { +// -------------------- سنسورها -------------------- +void readSensors() { + currentData.temperature = sht31.readTemperature(); + currentData.humidity = sht31.readHumidity(); + + // خاک + int adc = analogRead(SOIL_PIN); + float soil = (4095 - adc) / (4095.0 - 1200.0) * 100.0; + if (soil > 100) soil = 100; + if (soil < 0) soil = 0; + currentData.soilMoisture = (int)soil; + + // CO 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 voltage = (sum / 10.0 / 4095.0) * 3.3; float ppm; if (voltage < 0.1) ppm = 0; else if (voltage < 0.2) ppm = 10 * (voltage / 0.2); @@ -156,223 +637,149 @@ float MQ7_ReadPPM() { 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; + currentData.coPPM = ppm; + + // نور + currentData.lightLux = lightMeter.readLightLevel(); + if (isnan(currentData.lightLux)) currentData.lightLux = 0; + + lastSensorRead = millis(); } -// --- 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; +// -------------------- نمایشگر -------------------- +void updateDisplay() { + if (millis() - lastDisplayChange > 5000) { + displayMode = (displayMode + 1) % 3; + lastDisplayChange = millis(); } - - 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); + display.clearDisplay(); + display.setTextSize(1); - 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); + switch(displayMode) { + case 0: + display.setCursor(0, 0); + if (config.verified) { + display.println("ID: " + String(config.deviceId)); + display.println("SIM: " + simTypeToString(config.simType)); + display.println("NET: " + String(networkConnected ? "OK" : "NO")); + } else if (awaitingSMS2) { + display.println("AWAITING SMS2"); + display.println("Phone: " + tempPhoneNumber); + } else { + display.println("WAIT SMS1"); + display.println("Send: NUM#CODE"); } - 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"); + break; - // خواندن داده‌های پاسخ - 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; - } - } + case 1: + display.setCursor(0, 0); + display.print("T:"); + display.print(currentData.temperature, 1); + display.print("C H:"); + display.print(currentData.humidity, 1); + display.println("%"); + display.print("S:"); + display.print(currentData.soilMoisture); + display.print("% C:"); + display.print(currentData.coPPM, 0); + display.println("PPM"); + display.print("L:"); + display.print(currentData.lightLux, 0); + display.println("Lx"); + 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; + case 2: + display.setTextSize(2); + display.setCursor(0, 20); + display.print(currentData.temperature, 1); + display.println("C"); + break; } + + display.display(); } -// --------------------------- setup --------------------------- +// -------------------- Setup & Loop -------------------- void setup() { - pinMode(PWRKEY_PIN, OUTPUT); - digitalWrite(PWRKEY_PIN, LOW); - Serial1.begin(115200); - EC200U.begin(115200); - delay(3000); - - Serial1.println("🚀 System Starting..."); - - // مقداردهی سنسورها - dht.begin(); + delay(1000); + + Serial1.println("\n\n🚀 IoT Device 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 (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { - Serial1.println("✅ BH1750 OK"); - } else { - Serial1.println("❌ BH1750 Error"); + if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + Serial1.println("❌ OLED failed!"); } - - // مقداردهی پین‌های آنالوگ - pinMode(SOIL_PIN, INPUT); - pinMode(MQ7_PIN, INPUT); - - initEC200U(); - getNetworkTime(); - Serial1.println("✅ System Ready"); + sht31.begin(0x44); + lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); + + flashInit(); + readConfig(); + + // همیشه در شروع awaitingSMS2 = false + awaitingSMS2 = false; + tempPhoneNumber = ""; + tempTokenCode = ""; + + EC200U.begin(115200); + + Serial1.println("\n=== INITIAL STATUS ==="); + Serial1.println("Verified: " + String(config.verified)); + Serial1.println("Awaiting SMS2: " + String(awaitingSMS2)); + Serial1.println("====================="); + + if (config.verified) { + currentAPN = simTypeToAPN(config.simType); + initEC200U(); + } + + Serial1.println("✅ Ready"); } -// --------------------------- loop --------------------------- -unsigned long lastUpload = 0; - void loop() { - if (millis() - lastUpload > UPLOAD_INTERVAL_MIN * 60UL * 1000UL || lastUpload == 0) { - Serial1.println("=========================================="); + checkSMS(); + + if (millis() - lastSensorRead > SENSOR_READ_INTERVAL) { + readSensors(); - // خواندن سنسورها - 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!"); + if (config.verified && networkConnected) { + if (millis() - lastUpload > config.uploadInterval * 60000) { + // ارسال داده به API + String data = "deviceId=" + String(config.deviceId) + + "&temp=" + String(currentData.temperature, 1) + + "&hum=" + String(currentData.humidity, 1) + + "&soil=" + String(currentData.soilMoisture) + + "&co=" + String(currentData.coPPM, 0) + + "&lux=" + String(currentData.lightLux, 1); + + String url = String(config.serverUrl) + "/api/Telemetry/AddData?" + data; + + String cmd = "AT+QHTTPURL=" + String(url.length()) + ",80"; + if (sendAT(cmd, 5000)) { + delay(100); + EC200U.print(url); + if (sendAT("", 5000)) { + if (sendAT("AT+QHTTPGET=60", 10000)) { + lastUpload = millis(); + lastMessage = "Data Sent"; + } + } + } + } } - - lastUpload = millis(); - Serial1.println("=========================================="); } + updateDisplay(); delay(1000); } \ No newline at end of file