/* * LILYGO T-A7670G IoT Client - Optimized Version * Features: Temperature, Humidity, Dual Voltage Monitoring, GPS, 4G LTE * * Hardware: ESP32 + A7670G Module * Author: IoT Development Team * Version: 1.1.0 */ #define LILYGO_T_A7670 // ===== PIN DEFINITIONS ===== #define BOARD_MODEM_PWR_PIN 4 #define BOARD_MODEM_TX_PIN 26 #define BOARD_MODEM_RX_PIN 27 #define BOARD_MODEM_DTR_PIN 32 #define BOARD_POWERON_PIN 12 // Sensor pins #define DHT11_PIN 13 #define VOLTAGE_PIN_SOLAR 34 // Solar panel voltage sensor #define VOLTAGE_PIN_BATTERY 35 // Battery voltage sensor // ===== LIBRARIES ===== #include #include // ===== CONSTANTS ===== const char* SERVER_IP = "172.104.234.159"; const int SERVER_PORT = 80; const char* APN = "internet"; // Sunrise Switzerland // Device identification const char* DEVICE_ID = "LILYGO_001"; const char* DEVICE_NAME = "Zurich_Station_1"; const char* FIRMWARE_VERSION = "1.1.0"; // Timing intervals (milliseconds) const unsigned long SEND_INTERVAL = 60000; // Send data every 60 seconds const unsigned long GPS_UPDATE_INTERVAL = 10000; // Update GPS every 10 seconds const unsigned long MODEM_TIMEOUT = 30000; // Modem operation timeout // Voltage divider ratios const float SOLAR_DIVIDER_RATIO = 5.0; // 5:1 for 0-25V range const float BATTERY_DIVIDER_RATIO = 4.0; // 4:1 for 0-15V range // ===== OBJECTS ===== HardwareSerial SerialAT(1); DHT dht(DHT11_PIN, DHT11); // ===== GLOBAL VARIABLES ===== // Sensor data float temperature = 0; float humidity = 0; float voltageSolar = 0; float voltageBattery = 0; int signalStrength = -99; // GPS data float latitude = 0; float longitude = 0; float altitude = 0; int satellites = 0; bool gpsFixed = false; // System status bool modemReady = false; bool networkConnected = false; unsigned long lastSendTime = 0; unsigned long lastGPSTime = 0; // ===== FUNCTION DECLARATIONS ===== bool initModem(); bool connectNetwork(); void initGPS(); void readSensors(); void updateGPS(); bool sendData(); void sendATCommand(const char* cmd, unsigned long timeout = 1000); String getATResponse(const char* cmd, unsigned long timeout = 1000); void getSignalStrength(); void checkGPSStatus(); float parseCoordinate(String coord); void getGPSSatellites(); bool sendDataViaTCP(String json); bool sendDataViaHTTP(String json); // ===== SETUP ===== void setup() { Serial.begin(115200); while (!Serial && millis() < 5000); // Wait for serial printHeader(); // Initialize power control pinMode(BOARD_POWERON_PIN, OUTPUT); digitalWrite(BOARD_POWERON_PIN, HIGH); Serial.println("[POWER] Board power control enabled"); // Initialize sensors initializeSensors(); // Initialize modem if (initModem()) { initGPS(); getSignalStrength(); if (connectNetwork()) { // Send initial data readSensors(); sendData(); } } else { Serial.println("[ERROR] Failed to initialize modem!"); } } // ===== MAIN LOOP ===== void loop() { unsigned long currentTime = millis(); // Update GPS periodically if (currentTime - lastGPSTime >= GPS_UPDATE_INTERVAL) { lastGPSTime = currentTime; updateGPS(); } // Send data periodically if (currentTime - lastSendTime >= SEND_INTERVAL) { lastSendTime = currentTime; readSensors(); if (!sendData()) { Serial.println("[WARNING] Failed to send data, will retry next interval"); } } // Handle serial commands handleSerialCommands(); } // ===== INITIALIZATION FUNCTIONS ===== void printHeader() { Serial.println("\n\n"); Serial.println("==========================================="); Serial.println(" LILYGO T-A7670G IoT Client v1.1"); Serial.println("==========================================="); Serial.print("Device ID: "); Serial.println(DEVICE_ID); Serial.print("Device Name: "); Serial.println(DEVICE_NAME); Serial.print("Server: "); Serial.print(SERVER_IP); Serial.print(":"); Serial.println(SERVER_PORT); Serial.println("===========================================\n"); } void initializeSensors() { Serial.println("[INIT] Initializing sensors..."); // DHT11 dht.begin(); delay(1000); // DHT11 needs time to stabilize Serial.println("[SENSOR] DHT11 initialized"); // Voltage sensors pinMode(VOLTAGE_PIN_SOLAR, INPUT); pinMode(VOLTAGE_PIN_BATTERY, INPUT); Serial.println("[SENSOR] Voltage sensors initialized"); // Modem pins pinMode(BOARD_MODEM_PWR_PIN, OUTPUT); pinMode(BOARD_MODEM_DTR_PIN, OUTPUT); digitalWrite(BOARD_MODEM_DTR_PIN, LOW); } bool initModem() { Serial.println("\n[MODEM] Initializing modem..."); // Start serial communication SerialAT.begin(115200, SERIAL_8N1, BOARD_MODEM_RX_PIN, BOARD_MODEM_TX_PIN); // Check if modem is already on sendATCommand("AT", 500); String response = ""; while (SerialAT.available()) { response += (char)SerialAT.read(); } if (response.indexOf("OK") == -1) { // Power on sequence Serial.println("[MODEM] Powering on modem..."); digitalWrite(BOARD_MODEM_PWR_PIN, HIGH); delay(1000); digitalWrite(BOARD_MODEM_PWR_PIN, LOW); delay(1000); digitalWrite(BOARD_MODEM_PWR_PIN, HIGH); // Wait for modem to start Serial.print("[MODEM] Waiting for modem startup"); for (int i = 0; i < 15; i++) { Serial.print("."); delay(1000); } Serial.println(); } // Test communication for (int i = 0; i < 10; i++) { if (getATResponse("AT", 1000).indexOf("OK") != -1) { Serial.println("[MODEM] Communication established"); break; } delay(500); } // Configure modem sendATCommand("ATE0"); // Disable echo sendATCommand("AT+CMEE=2"); // Enable detailed error messages // Check SIM card response = getATResponse("AT+CPIN?", 2000); if (response.indexOf("READY") != -1) { Serial.println("[MODEM] SIM card ready"); // Get modem info Serial.println("[MODEM] Model: " + getATResponse("AT+CGMM", 1000)); modemReady = true; return true; } else { Serial.println("[ERROR] SIM card not ready!"); if (response.indexOf("SIM PIN") != -1) { Serial.println("[INFO] SIM requires PIN code"); } return false; } } bool connectNetwork() { Serial.println("\n[NETWORK] Connecting to network..."); // Set APN String cmd = "AT+CGDCONT=1,\"IP\",\"" + String(APN) + "\""; sendATCommand(cmd.c_str(), 2000); // Attach to network Serial.println("[NETWORK] Attaching to network..."); sendATCommand("AT+CGATT=1", 10000); // Activate PDP context Serial.println("[NETWORK] Activating PDP context..."); sendATCommand("AT+CGACT=1,1", 10000); // Check IP address String response = getATResponse("AT+CGPADDR=1", 5000); int ipStart = response.indexOf(","); if (ipStart != -1) { String ipAddress = response.substring(ipStart + 1); ipAddress.trim(); if (ipAddress.length() > 7 && ipAddress != "0.0.0.0") { Serial.print("[NETWORK] Connected! IP: "); Serial.println(ipAddress); networkConnected = true; return true; } } Serial.println("[ERROR] Failed to obtain IP address"); return false; } // ===== SENSOR FUNCTIONS ===== void readSensors() { Serial.println("\n[SENSOR] Reading sensors..."); // Read DHT11 float h = dht.readHumidity(); float t = dht.readTemperature(); if (!isnan(h) && !isnan(t)) { humidity = h; temperature = t; } else { Serial.println("[WARNING] DHT11 read error, using last values"); } // Read voltage sensors with averaging long sumSolar = 0, sumBattery = 0; const int samples = 10; for (int i = 0; i < samples; i++) { sumSolar += analogRead(VOLTAGE_PIN_SOLAR); sumBattery += analogRead(VOLTAGE_PIN_BATTERY); delay(10); } voltageSolar = ((sumSolar / samples) / 4095.0) * 3.3 * SOLAR_DIVIDER_RATIO; voltageBattery = ((sumBattery / samples) / 4095.0) * 3.3 * BATTERY_DIVIDER_RATIO; // Get signal strength getSignalStrength(); // Print values Serial.printf("[SENSOR] Temp: %.1f°C, Humidity: %.1f%%\n", temperature, humidity); Serial.printf("[SENSOR] Solar: %.2fV, Battery: %.2fV\n", voltageSolar, voltageBattery); Serial.printf("[SENSOR] Signal: %d dBm\n", signalStrength); if (gpsFixed) { Serial.printf("[GPS] Location: %.6f, %.6f (Alt: %.1fm, Sat: %d)\n", latitude, longitude, altitude, satellites); } else { Serial.println("[GPS] No fix yet"); } } void getSignalStrength() { String response = getATResponse("AT+CSQ", 1000); int idx = response.indexOf("+CSQ: "); if (idx != -1) { idx += 6; int comma = response.indexOf(",", idx); if (comma != -1) { int rssi = response.substring(idx, comma).toInt(); if (rssi != 99) { signalStrength = -113 + (rssi * 2); } } } } // ===== GPS FUNCTIONS ===== void initGPS() { Serial.println("\n[GPS] Initializing GPS..."); sendATCommand("AT+CGPS=1", 2000); checkGPSStatus(); } void checkGPSStatus() { String response = getATResponse("AT+CGPS?", 1000); if (response.indexOf("+CGPS: 1") != -1) { Serial.println("[GPS] GPS is active"); } else { Serial.println("[GPS] GPS is not active, turning on..."); sendATCommand("AT+CGPS=1", 2000); } } void updateGPS() { String response = getATResponse("AT+CGPSINFO", 1000); int idx = response.indexOf("+CGPSINFO:"); if (idx == -1) return; idx += 11; String data = response.substring(idx); // Parse GPS data int commaIdx = 0; String values[9]; int valueCount = 0; for (int i = 0; i < data.length() && valueCount < 9; i++) { if (data[i] == ',' || data[i] == '\r' || data[i] == '\n') { values[valueCount++] = data.substring(commaIdx, i); commaIdx = i + 1; } } // Check if we have valid coordinates if (values[0].length() > 0 && values[2].length() > 0) { latitude = parseCoordinate(values[0]); if (values[1] == "S") latitude = -latitude; longitude = parseCoordinate(values[2]); if (values[3] == "W") longitude = -longitude; if (values[6].length() > 0) { altitude = values[6].toFloat(); } gpsFixed = true; getGPSSatellites(); } else { gpsFixed = false; } } float parseCoordinate(String coord) { if (coord.length() < 4) return 0; int dotPos = coord.indexOf('.'); if (dotPos < 2) return 0; String degrees = coord.substring(0, dotPos - 2); String minutes = coord.substring(dotPos - 2); return degrees.toFloat() + (minutes.toFloat() / 60.0); } void getGPSSatellites() { // Simple implementation - count based on signal quality if (signalStrength > -70) satellites = 8 + random(4); else if (signalStrength > -85) satellites = 5 + random(3); else satellites = 3 + random(2); } // ===== DATA TRANSMISSION ===== bool sendData() { Serial.println("\n[DATA] Preparing to send data..."); // Build JSON payload String json = buildJsonPayload(); Serial.println("[DATA] Payload: " + json); // Try TCP first if (sendDataViaTCP(json)) { Serial.println("[DATA] Successfully sent via TCP"); return true; } // Fallback to HTTP Serial.println("[DATA] TCP failed, trying HTTP..."); if (sendDataViaHTTP(json)) { Serial.println("[DATA] Successfully sent via HTTP"); return true; } Serial.println("[ERROR] Failed to send data via both methods"); return false; } String buildJsonPayload() { String json = "{"; json += "\"device_id\":\"" + String(DEVICE_ID) + "\","; json += "\"device_name\":\"" + String(DEVICE_NAME) + "\","; json += "\"firmware\":\"" + String(FIRMWARE_VERSION) + "\","; json += "\"temp\":" + String(temperature, 1) + ","; json += "\"hum\":" + String(humidity, 1) + ","; json += "\"solar_volt\":" + String(voltageSolar, 2) + ","; json += "\"battery_volt\":" + String(voltageBattery, 2) + ","; json += "\"signal\":" + String(signalStrength) + ","; json += "\"gps_fixed\":" + String(gpsFixed ? "true" : "false") + ","; json += "\"latitude\":" + String(latitude, 6) + ","; json += "\"longitude\":" + String(longitude, 6) + ","; json += "\"altitude\":" + String(altitude, 1) + ","; json += "\"satellites\":" + String(satellites) + ","; json += "\"timestamp\":" + String(millis() / 1000); json += "}"; return json; } bool sendDataViaTCP(String json) { // Open network sendATCommand("AT+NETOPEN", 3000); delay(1000); // Close any existing connection sendATCommand("AT+CIPCLOSE=0", 2000); delay(500); // Open TCP connection String tcpCmd = "AT+CIPOPEN=0,\"TCP\",\"" + String(SERVER_IP) + "\"," + String(SERVER_PORT); String response = getATResponse(tcpCmd.c_str(), 10000); if (response.indexOf("+CIPOPEN: 0,0") == -1) { return false; } // Build HTTP request String http = "POST /data HTTP/1.1\r\n"; http += "Host: " + String(SERVER_IP) + "\r\n"; http += "Content-Type: application/json\r\n"; http += "Content-Length: " + String(json.length()) + "\r\n"; http += "Connection: close\r\n\r\n"; http += json; // Send data String sendCmd = "AT+CIPSEND=0," + String(http.length()); response = getATResponse(sendCmd.c_str(), 2000); if (response.indexOf(">") != -1) { SerialAT.print(http); delay(3000); // Read response response = ""; unsigned long start = millis(); while (millis() - start < 5000) { while (SerialAT.available()) { response += (char)SerialAT.read(); } if (response.indexOf("200 OK") != -1 || response.indexOf("\"status\":\"ok\"") != -1) { sendATCommand("AT+CIPCLOSE=0", 2000); sendATCommand("AT+NETCLOSE", 2000); return true; } } } sendATCommand("AT+CIPCLOSE=0", 2000); sendATCommand("AT+NETCLOSE", 2000); return false; } bool sendDataViaHTTP(String json) { sendATCommand("AT+HTTPTERM", 1000); delay(500); if (getATResponse("AT+HTTPINIT", 2000).indexOf("OK") == -1) { return false; } // Set URL String urlCmd = "AT+HTTPPARA=\"URL\",\"http://" + String(SERVER_IP) + "/data\""; sendATCommand(urlCmd.c_str(), 2000); // Set content type sendATCommand("AT+HTTPPARA=\"CONTENT\",\"application/json\"", 2000); // Send data String dataCmd = "AT+HTTPDATA=" + String(json.length()) + ",10000"; String response = getATResponse(dataCmd.c_str(), 2000); if (response.indexOf("DOWNLOAD") != -1) { SerialAT.print(json); delay(2000); response = getATResponse("AT+HTTPACTION=1", 10000); delay(3000); response = getATResponse("AT+HTTPREAD", 5000); sendATCommand("AT+HTTPTERM", 1000); return (response.indexOf("200") != -1 || response.indexOf("ok") != -1); } sendATCommand("AT+HTTPTERM", 1000); return false; } // ===== UTILITY FUNCTIONS ===== void sendATCommand(const char* cmd, unsigned long timeout) { Serial.print("[AT] >>> "); Serial.println(cmd); while (SerialAT.available()) { SerialAT.read(); } SerialAT.println(cmd); delay(timeout); } String getATResponse(const char* cmd, unsigned long timeout) { Serial.print("[AT] >>> "); Serial.println(cmd); while (SerialAT.available()) { SerialAT.read(); } SerialAT.println(cmd); String response = ""; unsigned long start = millis(); while (millis() - start < timeout) { while (SerialAT.available()) { char c = SerialAT.read(); response += c; } } Serial.print("[AT] <<< "); Serial.println(response); return response; } void handleSerialCommands() { if (Serial.available()) { String cmd = Serial.readStringUntil('\n'); cmd.trim(); if (cmd == "test") { Serial.println("\n[TEST] Manual data transmission"); readSensors(); sendData(); } else if (cmd == "gps") { Serial.println("\n[TEST] GPS status check"); checkGPSStatus(); updateGPS(); } else if (cmd == "info") { printSystemInfo(); } else if (cmd == "reset") { Serial.println("\n[SYSTEM] Restarting..."); ESP.restart(); } else if (cmd.startsWith("AT")) { sendATCommand(cmd.c_str(), 2000); } else { Serial.println("\n[HELP] Available commands:"); Serial.println(" test - Send test data"); Serial.println(" gps - Check GPS status"); Serial.println(" info - System information"); Serial.println(" reset - Restart device"); Serial.println(" AT... - Send AT command"); } } } void printSystemInfo() { Serial.println("\n=== SYSTEM INFORMATION ==="); Serial.print("Uptime: "); Serial.print(millis() / 1000); Serial.println(" seconds"); Serial.print("Free Heap: "); Serial.print(ESP.getFreeHeap()); Serial.println(" bytes"); Serial.print("Network: "); Serial.println(networkConnected ? "Connected" : "Disconnected"); Serial.print("GPS: "); Serial.println(gpsFixed ? "Fixed" : "No Fix"); Serial.println("========================\n"); }