diff --git a/stm32f103.ino b/stm32f103.ino new file mode 100644 index 0000000..3e1ee91 --- /dev/null +++ b/stm32f103.ino @@ -0,0 +1,785 @@ +#include +#include +#include +#include +#include +#include +#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 SDA_PIN PB9 +#define SCL_PIN PB8 +#define PWRKEY_PIN PB5 +#define SENSOR_READ_INTERVAL 60000 + +// -------------------- آدرس‌های حافظه -------------------- +#define CONFIG_ADDRESS 0x000000 +#define DATA_ADDRESS 0x010000 +#define MAX_STORED_DATA 100 + +// -------------------- انوم‌ها -------------------- +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; + if (showResponse) Serial1.write(c); + } + if (response.indexOf("OK") != -1) return true; + if (response.indexOf("ERROR") != -1) return false; + } + return false; +} + +bool sendSMS(String number, String message) { + if (!sendAT("AT+CMGF=1", 3000, true)) { + Serial1.println("❌ Failed to set SMS mode"); + 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) { + 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 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); + + if (!sendAT("AT", 3000)) { + Serial1.println("❌ Modem error"); + return; + } + + currentAPN = simTypeToAPN(config.simType); + String apnCmd = "AT+CGDCONT=1,\"IP\",\"" + currentAPN + "\""; + sendAT(apnCmd, 2000); + + if (sendAT("AT+QIACT=1", 15000)) { + networkConnected = true; + lastMessage = "Connected"; + Serial1.println("✅ Network connected"); + } else { + networkConnected = false; + Serial1.println("❌ Network failed"); + } +} + +// -------------------- سنسورها -------------------- +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 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); + else if (voltage < 0.5) ppm = 50 + (voltage - 0.2) * 300; + else if (voltage < 1.0) ppm = 140 + (voltage - 0.5) * 800; + else if (voltage < 2.0) ppm = 500 + (voltage - 1.0) * 1500; + else ppm = 2000 + (voltage - 2.0) * 2000; + if (ppm > 10000) ppm = 10000; + currentData.coPPM = ppm; + + // نور + currentData.lightLux = lightMeter.readLightLevel(); + if (isnan(currentData.lightLux)) currentData.lightLux = 0; + + lastSensorRead = millis(); +} + +// -------------------- نمایشگر -------------------- +void updateDisplay() { + if (millis() - lastDisplayChange > 5000) { + displayMode = (displayMode + 1) % 3; + lastDisplayChange = millis(); + } + + display.clearDisplay(); + display.setTextSize(1); + + 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"); + } + 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; + + case 2: + display.setTextSize(2); + display.setCursor(0, 20); + display.print(currentData.temperature, 1); + display.println("C"); + break; + } + + display.display(); +} + +// -------------------- Setup & Loop -------------------- +void setup() { + Serial1.begin(115200); + 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 (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + Serial1.println("❌ OLED failed!"); + } + + 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"); +} + +void loop() { + checkSMS(); + + if (millis() - lastSensorRead > SENSOR_READ_INTERVAL) { + readSensors(); + + 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"; + } + } + } + } + } + } + + updateDisplay(); + delay(1000); +} \ No newline at end of file