#include #include #include // For making HTTP POST requests #include // Required for HTTPClient on ESP32 #include // For parsing JSON #define RX_PIN D7 #define TX_PIN D6 // WiFi network credentials const char* ssid = "MIT"; const char* password = "FsPju]6HbE"; // --- FastAPI Server Details (via SSH Tunnel) --- const char* serverIp = "efpi-10.mit.edu"; // Public server address hosting the tunnel entry const uint16_t serverPort = 80; // Port specified in your SSH -R command (remote port) const char* serverPath = "/tunnel_durra/latest_readings_all_sensors"; const char* serverPathGetCommand = "/tunnel_durra/get_latest_pump_command/"; // --- Timing for Polling Server --- unsigned long previousMillisGetData = 0; const long getDataInterval = 20000; // Poll server every 20 seconds (20000 milliseconds) // --- Timing for Polling Server for Pump Commands --- unsigned long previousMillisPumpCommand = 0; const long pumpCommandInterval = 5000; // Poll for pump command every 5 seconds // --- Pump Control Parameters --- // Using Serial1 for communication with P-ESP32 #define PUMP_SERIAL Serial1 const long PUMP_SERIAL_BAUD = 115200; void fetchMoistureData(); void connectToWiFi(); void sendPumpCommand(const String& command); void checkAndExecutePumpCommand(); void setup() { // put your setup code here, to run once: Serial.begin(115200); // while (!Serial){ // delay(10); // } Serial.println("Central Control ESP32 (CC-ESP32)"); PUMP_SERIAL.begin(PUMP_SERIAL_BAUD, SERIAL_8N1, RX_PIN, TX_PIN); Serial.println("Serial2 initialized for P-ESP32 communication."); connectToWiFi(); Serial.println("Sending test command to P-ESP32..."); // Example command: "steps " // This command will make the pump run 16000 microsteps with a 1000us period per step. sendPumpCommand("steps 16000 1600"); Serial.println("Setup complete. Starting main loop."); } int counter = 0; void loop() { unsigned long currentMillis = millis(); if (WiFi.status() != WL_CONNECTED) { connectToWiFi(); } if (currentMillis - previousMillisGetData >= getDataInterval) { previousMillisGetData = currentMillis; if (WiFi.status() == WL_CONNECTED) { fetchMoistureData(); String message = "HELLO from CC-ESP32, count: " + String(counter); if (PUMP_SERIAL.availableForWrite()) { size_t bytesSent = PUMP_SERIAL.println(message); // Send with a newline // Debug output to CC-ESP32's USB monitor Serial.print("Sent via Serial2: '"); Serial.print(message); Serial.print("' ("); Serial.print(bytesSent); Serial.println(" bytes)"); } else { Serial.println("Serial2 buffer full or not available for writing."); } counter++; } else { Serial.println("WiFi not connected. Skipping data fetch."); } } if (currentMillis - previousMillisPumpCommand >= pumpCommandInterval) { previousMillisPumpCommand = currentMillis; if (WiFi.status() == WL_CONNECTED) { checkAndExecutePumpCommand(); } else { Serial.println("WiFi not connected. Skipping pump command check."); } } } void connectToWiFi() { if (WiFi.status() == WL_CONNECTED) { Serial.println("Already connected to WiFi."); return; } WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP()); } void fetchMoistureData() { WiFiClient client; // WiFiClient object HTTPClient http; String fullServerURL = "http://" + String(serverIp) + ":" + String(serverPort) + String(serverPath); Serial.print("[HTTP] Attempting to GET from: "); Serial.println(fullServerURL); // Begin HTTP connection (pass client object for ESP32) if (http.begin(client, fullServerURL)) { int httpResponseCode = http.GET(); Serial.print("[HTTP] Response code: "); Serial.println(httpResponseCode); if (httpResponseCode == HTTP_CODE_OK) { String payload = http.getString(); Serial.println("[HTTP] Received payload:"); Serial.println(payload); // Parse JSON // Calculate necessary capacity: https://arduinojson.org/v6/assistant/ // Example for 3 sensors: [{"sensor_id":"1","raw_value":100,"percent":50}, ..., ...] // For an array of 3 objects, each with 3 key-value pairs: // JSON_ARRAY_SIZE(3) + 3 * JSON_OBJECT_SIZE(3) + some buffer // For ESP32, DynamicJsonDocument is often easier for prototyping. DynamicJsonDocument doc(2048); // Adjust size as needed based on max number of sensors expected DeserializationError error = deserializeJson(doc, payload); if (error) { Serial.print("[JSON] Deserialization failed: "); Serial.println(error.f_str()); http.end(); return; } // Assuming the payload is a JSON array JsonArray array = doc.as(); if (array.isNull()) { Serial.println("[JSON] Payload is not a JSON array or parsing failed to produce one."); http.end(); return; } Serial.println("[JSON] Parsed Data:"); for (JsonObject sensorData : array) { const char* sensor_id = sensorData["sensor_id"]; int raw_value = sensorData["raw_moisture_value"]; // Key from FastAPI int moisture_percent = sensorData["moisture_percent"]; // Key from FastAPI // long timestamp_from_db = sensorData["timestamp"]; // If you decide to include it in this endpoint Serial.print(" Sensor ID: "); Serial.print(sensor_id); Serial.print(", Raw Value: "); Serial.print(raw_value); Serial.print(", Moisture Percent: "); Serial.print(moisture_percent); Serial.println("%"); } } else { Serial.print("[HTTP] GET request failed, error: "); Serial.println(http.errorToString(httpResponseCode).c_str()); } http.end(); // Free resources } else { Serial.println("[HTTP] Unable to connect to server."); } Serial.println("------------------------------------"); } // Function to send a command to the P-ESP32 void sendPumpCommand(const String& command) { if (PUMP_SERIAL.availableForWrite()) { PUMP_SERIAL.println(command); // println adds a newline, which P-ESP32 expects Serial.print("Sent to P-ESP32: '"); Serial.print(command); Serial.println("'"); } else { Serial.println("Error: Serial buffer to P-ESP32 is full or not available."); } } void checkAndExecutePumpCommand() { WiFiClient client; HTTPClient http; String fullServerURL = "http://" + String(serverIp) + ":" + String(serverPort) + String(serverPathGetCommand); Serial.print("[HTTP] Checking for pump command from: "); Serial.println(fullServerURL); if (http.begin(client, fullServerURL)) { int httpResponseCode = http.GET(); if (httpResponseCode == HTTP_CODE_OK) { String payload = http.getString(); Serial.print("[HTTP] Received pump command response: "); Serial.println(payload); DynamicJsonDocument doc(256); DeserializationError error = deserializeJson(doc, payload); if (error) { Serial.print("[JSON] Deserialization failed: "); Serial.println(error.f_str()); } else { if (doc.containsKey("command")) { String command = doc["command"].as(); if (command.length() > 0) { Serial.print("Received pump command: '"); Serial.print(command); Serial.println("' - Sending to P-ESP32."); sendPumpCommand(command); // Optionally, you might want to send a confirmation back to the FastAPI server } else { Serial.println("No pump command received."); } } } } else { Serial.print("[HTTP] Error getting pump command: "); Serial.println(http.errorToString(httpResponseCode).c_str()); } http.end(); } else { Serial.println("[HTTP] Unable to connect to server to get pump command."); } }