Files
Arduino/EC200U.h
2026-01-06 19:06:03 +03:30

1364 lines
37 KiB
C

#ifndef EC200U_H
#define EC200U_H
#include <Arduino.h>
#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