#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