#include <Arduino.h>
+#include <iostream>
+#include <string>
+
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
+#include <WiFiUdp.h>
+#include <NTPClient.h>
+
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "main.h"
#include "secrets.h"
+bool safeToRun();
+bool runWater(String override);
+void stopWater();
+void handleWaterRunning();
+
void connectToWiFi();
void setupWebServer();
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)
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();
}
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!");
}