From: Noah Tomkins Date: Sun, 23 Aug 2026 22:56:12 +0000 (-0400) Subject: Working API to start water X-Git-Url: https://git.tnoah.ca/?a=commitdiff_plain;h=c723421a81319ed34261c00c8230a3a341dac535;p=garden.git Working API to start water --- diff --git a/platformio.ini b/platformio.ini index 2049f4b..bd72736 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,6 +12,7 @@ platform = espressif8266 board = d1_mini_lite framework = arduino +upload_speed = 115200 monitor_speed = 115200 lib_deps = bblanchon/ArduinoJson@^7.2.2 @@ -19,3 +20,4 @@ lib_deps = esp32async/ESPAsyncTCP@^2.0.0 adafruit/Adafruit BME280 Library@^2.3.0 adafruit/Adafruit Unified Sensor@^1.1.15 + arduino-libraries/NTPClient@^3.2.1 diff --git a/src/main.cpp b/src/main.cpp index 37f2a2e..ab39463 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,15 @@ #include +#include +#include + #include #include #include +#include +#include + #include #include #include @@ -14,6 +20,11 @@ #include "main.h" #include "secrets.h" +bool safeToRun(); +bool runWater(String override); +void stopWater(); +void handleWaterRunning(); + void connectToWiFi(); void setupWebServer(); void setupBME280(); @@ -21,10 +32,143 @@ void setupBME280(); WiFiClient wifiClient; AsyncWebServer server(80); +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP, "pool.ntp.org"); + Adafruit_BME280 bme; +// NTP Timer +unsigned long last_ran = 0; +unsigned long end_time = 0; + +// Fallback +unsigned long timer_setpoint = 0; +unsigned long timer_current = 0; + +bool waterState = false; +unsigned long loopTime = 10; // Seconds + +int maxNTPRetry = 5; +int currentNTPRetry = 0; + +unsigned long lastCheckedTime = 0; + +void setup() { + Serial.begin(115200); + Serial.println(); + + connectToWiFi(); + setupWebServer(); + setupBME280(); + + timeClient.begin(); + timeClient.update(); + last_ran = timeClient.getEpochTime(); + Serial.printf("\nCurrent Time (s): %ld", last_ran); + + pinMode(SOAKER, OUTPUT); + digitalWrite(SOAKER, LOW); +} + +void loop() { + + handleWaterRunning(); + + delay(loopTime * 1000); +} + +bool safeToRun() { + timeClient.update(); + unsigned long ctime = timeClient.getEpochTime(); + + if (ctime > last_ran + water_safe_time) + return true; + + return false; +} + +bool runWater(unsigned long runtime, String override = "") { + if (!safeToRun()) { + if (override != water_override) { + Serial.print("Water Not Safe to Run. Cannot Start Water."); + return false; + } + } + + // Run Water + Serial.printf("\n\nRunning Water for %lds (%ldm)", runtime, runtime / 60); + + // Set Endtime + timeClient.update(); + unsigned long cTime = timeClient.getEpochTime(); + end_time = cTime + runtime; + Serial.printf("\nCurrent Time: %ld. Run until: %ld.", cTime, end_time); + + // Reset Fallback + timer_current = 0; + timer_setpoint = runtime; + + waterState = true; + digitalWrite(SOAKER, HIGH); + + return true; +} + +void handleWaterRunning() { + timeClient.update(); + unsigned long cTime = timeClient.getEpochTime(); + + // STOP water if unable to connect to NTP Server + if (lastCheckedTime != cTime) + { + currentNTPRetry = 0; + lastCheckedTime = cTime; + } + else if (currentNTPRetry >= maxNTPRetry) + { + Serial.print("\nFailed to connect to NTP Server. Water turning off!"); + stopWater(); + } + else + { + currentNTPRetry++; + Serial.printf("\nRetying NTP %d/%d", currentNTPRetry, maxNTPRetry); + } + + if (waterState) { + Serial.printf("\nTime Remaining: %ld seconds.", end_time - cTime); + + if (cTime >= end_time) { + stopWater(); + Serial.print("Water Finished!"); + } + + // Fallback Timer + timer_current += loopTime; + if (timer_current > timer_setpoint) { + stopWater(); + Serial.print("\nFAILSAFE TRIGGERED!"); + } + } else { + stopWater(); + } +} + +void stopWater() { + waterState = false; + digitalWrite(SOAKER, LOW); + + last_ran = timeClient.getEpochTime(); + + timer_current = 0; + timer_setpoint = 0; + end_time = 0; + + Serial.print("\n\nWater Stopped."); +} + void connectToWiFi() { - WiFi.begin(ssid, pass); + WiFi.begin(ssid, pass); Serial.print("Connecting"); while (WiFi.status() != WL_CONNECTED) @@ -32,28 +176,79 @@ void connectToWiFi() { delay(500); Serial.print("."); } - Serial.println(); - - Serial.print("Connected, IP address: "); + Serial.print("\nConnected, IP address: "); Serial.println(WiFi.localIP()); } void setupWebServer() { - server.on("/api/sensors", HTTP_GET, [] (AsyncWebServerRequest *request) { + server.on("/status", HTTP_GET, [] (AsyncWebServerRequest *request) { + if (!request->authenticate(http_username, http_password)) + return request->requestAuthentication(); AsyncResponseStream *response = request->beginResponseStream("application/json"); JsonDocument doc; - doc["temperature"] = bme.readTemperature(); - doc["pressure"] = bme.readPressure() / 1000; - doc["humidity"] = bme.readHumidity(); - doc["altitude"] = bme.readAltitude(SEALEVELPRESSURE_HPA); + JsonDocument bmeDoc; + bmeDoc["temperature"] = bme.readTemperature(); + bmeDoc["pressure"] = bme.readPressure() / 1000; + bmeDoc["humidity"] = bme.readHumidity(); + doc["bme280"] = bmeDoc; serializeJson(doc, *response); request->send(response); }); + server.on("/water", HTTP_GET, [] (AsyncWebServerRequest *request) { + if (!request->authenticate(http_username, http_password)) + return request->requestAuthentication(); + + String time_str = ""; + String override_pass = ""; + + bool safe = safeToRun(); + + if (request->hasParam(PARAM_WATER_TIME)) { + time_str = request->getParam(PARAM_WATER_TIME)->value(); + + // If not safe, check override password. + if (!safe) { + if (request->hasParam(PARAM_WATER_OVERRIDE)) { + override_pass = request->getParam(PARAM_WATER_OVERRIDE)->value(); + + if (override_pass == water_override) + safe = true; + } + } + + // Fail If not Safe + if (!safe) { + request->send(400, "text/plain", "Not safe to run water or wrong password."); + return; + } + + // Convert Time String to Int + int time = -1; + if (!sscanf(time_str.c_str(), "%d", &time)) { + request->send(400, "text/plain", "Time unable to be parsed."); + return; + } + + // Check Time Within Safe Range + if (time < 1 || time > 25) { + request->send(400, "text/plain", "Time must be between 1 and 25 mins"); + return; + } + + Serial.printf("\nHTTP request to run water for %d mins.", time); + runWater(time * 60, override_pass); + + } else { + request->send(400, "text/plain", "No Time Provided."); + return; + } + }); + server.begin(); } @@ -64,25 +259,9 @@ void setupBME280() { for (int i = 10; i < 10; i++) { if (status) continue; delay(100); - Serial.print("Could not connect to BME, retrying"); + Serial.print("\nCould not connect to BME, retrying"); } - Serial.print("Connected to BME280!"); -} - -void setup() { - Serial.begin(115200); - Serial.println(); - - connectToWiFi(); - setupWebServer(); - setupBME280(); - - pinMode(SOAKER, OUTPUT); -} - -void loop() { - - delay(1000); + Serial.print("\nConnected to BME280!"); } diff --git a/src/main.h b/src/main.h index 84b49c5..f9e0023 100644 --- a/src/main.h +++ b/src/main.h @@ -1,8 +1,18 @@ -// Soaker Hose Solenoid -#define SOAKER D8 + +\// Soaker Hose Solenoid +#define SOAKER D3 + +// Buttons +#define BUTTON_A D0 +#define BUTTON_B D5 // BME280 I2C #define BME_SCL D1 #define BME_SDA D2 -#define SEALEVELPRESSURE_HPA (1013.25) \ No newline at end of file +#define SEALEVELPRESSURE_HPA (1013.25) + +const char* PARAM_WATER_TIME = "time"; +const char* PARAM_WATER_OVERRIDE = "override"; + +const unsigned long water_safe_time = 72000; // 20 hours \ No newline at end of file diff --git a/src/secrets.h.example b/src/secrets.h.example index 90a1225..d963848 100644 --- a/src/secrets.h.example +++ b/src/secrets.h.example @@ -1,3 +1,8 @@ // WiFi settings const char *ssid = "ssid"; -const char *pass = "password"; \ No newline at end of file +const char *pass = "password"; + +const char* http_username = "admin"; +const char* http_password = "admin"; + +const char* water_override = "override"; \ No newline at end of file