BankliPlus/Client.txt
2025-08-13 18:05:26 +02:00

631 lines
18 KiB
Plaintext

/*
* 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 <Arduino.h>
#include <DHT.h>
// ===== 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");
}