#include <ArduinoOTA.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <math.h>
// For display
#include <Adafruit_SSD1306.h>
// For WebSerial Lite (https://github.com/asjdf/WebSerialLite)
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <WebSerialLite.h>
#define pump_pin 5 // Output pin 5 for water pump
#define manual_pin 14 // Input pin 14 for manually starting irrigation
#define pot_pin 34 // Pin for potentiometer input
#define SCREEN_ADDRESS 0x3C // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32. GPIO21 for SDA and GPIO22 for SCL in ESP32.
Adafruit_SSD1306 display(128, 64, &Wire, -1);
// HTTP variables
HTTPClient http;
AsyncWebServer server(80);
// Declaring variables and settings
int use_geo = 0; // Toggle from 0 to 1 to use geolocation based on public IP
int use_webserial = 1; // Toggle from 0 to 1 to use WebSerial printouts too
int high_level_relay = 1; // Toggle from 0 to 1 to use high level relay instead of low level
int button;
int isPressed;
int webPressed;
int sunup;
int sundown;
int now_day;
int last_day;
int isDay;
int now_hour;
int last_hour = -1;
int now_minute;
int last_minute;
int now_second;
int last_second;
int cloud;
int pres;
int humi;
int symb;
int maxtime = 60; // Maximum seconds of manual watering per start
int has_watered;
int is_watering;
int water_mid;
int water_end;
int water_tot;
int manual_start;
int basetime = 10;
int cloud_modifier = 5;
int rain_modifier = 100;
int wind_modifier = 3;
float temp;
float wind;
float rain;
float rain_tot;
float rain_reduction;
float watertime;
float man_counter;
float passed;
float lat = 59.319435;
float lon = 18.147673;
String publicIP = "192.168.0.1";
String wlabel = "weather";
// Wifi connection settings
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// NTP time synchronization settings
const char* ntpServer = "europe.pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
// Loop time counter
long loopTime = millis()-1001;
// Watering timer
long startTime = millis();
// System uptime counter
long sysStart = millis();
void setup() {
// Starting sequence
Serial.begin(115200); // For ESP32
pinMode(pump_pin, OUTPUT);
pinMode(manual_pin, INPUT_PULLUP);
pinMode(pot_pin, INPUT);
if (high_level_relay == 0) {
digitalWrite(pump_pin, HIGH);
}
else {
digitalWrite(pump_pin, LOW);
}
delay(50);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("Couldn't find display."));
}
else {
Serial.println(F("Found display."));
display.clearDisplay();
}
Serial.println(F("."));
Serial.println(F(".."));
Serial.println(F("..."));
Serial.println(F("...."));
Serial.println(F("....."));
Serial.println(F("......System starting..."));
Serial.println(F("."));
// Mapping potentiometer reading to temperature
int pot_modifier = map(analogRead(pot_pin), 0, 4095, 0, 2); // Range normally from 0-4095
pot_modifier = 1; // Manually overriding potentiometer reading
if (pot_modifier < 1) {
basetime = basetime*0.8;
Serial.println(F("Basetime modifier 0.8"));
}
else if (pot_modifier < 2) {
basetime = basetime*1;
Serial.println(F("Basetime modifier 1"));
}
else if (pot_modifier < 3) {
basetime = basetime*1.2;
Serial.println(F("Basetime modifier 1.2"));
}
Serial.println(F(".")); // Adding empty serial row
delay(500);
// Connecting to Wifi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print(F("Connecting to Wifi.."));
while(WiFi.status() != WL_CONNECTED) {
delay(10);
Serial.print('.');
}
delay(50);
// Checking internet connection and selecting time setting option
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println(F("Wifi connection failed! Rebooting.."));
delay(5000);
ESP.restart();
}
else {
Serial.println(F("Wifi connection successful!"));
// Initializing NTP time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
fetchLocalTime();
last_day = now_day;
last_minute = now_minute-1;
}
// WebSerial is accessible at "<IP Address>/webserial" in browser
WebSerial.begin(&server);
WebSerial.onMessage(webMsg);
server.begin();
// OTA settings
// Hostname defaults to esp3232-[MAC]
ArduinoOTA.setHostname("HOSTNAME");
// No authentication by default
ArduinoOTA.setPassword("PASSWORD"); // OTA password
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println(F("\nEnd"));
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println(F("Auth Failed"));
else if (error == OTA_BEGIN_ERROR) Serial.println(F("Begin Failed"));
else if (error == OTA_CONNECT_ERROR) Serial.println(F("Connect Failed"));
else if (error == OTA_RECEIVE_ERROR) Serial.println(F("Receive Failed"));
else if (error == OTA_END_ERROR) Serial.println(F("End Failed"));
});
ArduinoOTA.begin();
Serial.println(F("Network Ready"));
// Pausing to give the browser time to connect
delay(10000);
// Get public IP, longitude and latitude coordinates
geoLocation();
// Get sunrise and sunset variables
delay(500);
sunTimes();
Serial.print(F("Local IP address: "));
Serial.println(WiFi.localIP());
Serial.print(F("Public IP address: "));
Serial.println(String(publicIP));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Public IP address: "+String(publicIP));
}
// Pause
delay(500);
// Start time for the first loop
last_second = now_second;
loopTime = millis();
}
// ------------------------------------------------ EOS ------------------------------------------------------ //
void geoLocation() {
// Function for retrieving geopositional longitude and latitude from ip-api API
int tries;
int httpCode;
int nap;
const char apiName[] = "ip-api.com";
const unsigned apiPort = 80;
String apiPath = "json/?fields=lat,lon,query";
// Make HTTP request to GEO location API
do {
if (tries < 5) {
nap = (1000+(1000*tries));
}
else {
nap = 1000;
}
delay(nap);
http.begin(apiName, apiPort, apiPath);
httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Deserialize JSON response
StaticJsonDocument<80> doc;
deserializeJson(doc, payload);
payload = "";
// Extract data from JSON if use_geo = 1
if (use_geo == 1) {
lat = doc["lat"].as<float>();
lon = doc["lon"].as<float>();
}
publicIP = doc["query"].as<String>();
Serial.println("Latitude: " + String(lat));
Serial.println("Longitude: " + String(lon));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Latitude: " + String(lat));
WebSerial.println("Longitude: " + String(lon));
}
}
else {
Serial.println("GEO Location HTTP Error: " + String(httpCode));
if (use_webserial == 1) {
delay(50);
WebSerial.println("GEO Location HTTP Error: " + String(httpCode));
}
}
tries++;
if (tries > 5) {
http.end();
break;
}
http.end();
}
while (httpCode != HTTP_CODE_OK);
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void sunTimes() {
// Function for retrieving sunset and sunrise data from Sunrise-Sunset API
WiFiClientSecure wifiClient;
wifiClient.setInsecure();
int tries;
int httpCode;
int nap;
const char apiName[] = "api.sunrise-sunset.org";
const unsigned apiPort = 443;
String apiPath = "/json?lat="+String(lat)+"&lng="+String(lon);
String apiPathLong = "https://api.sunrise-sunset.org/json?lat="+String(lat)+"&lng="+String(lon);
// Make HTTP request to Sunrise-Sunset API
do {
if (tries < 5) {
nap = (1000+(1000*tries));
}
else {
nap = 1000;
}
delay(nap);
http.begin(wifiClient, apiName, apiPort, apiPath, true);
// http.begin(apiName, apiPort, apiPath);
// http.begin(apiPathLong);
httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Deserialize JSON response
StaticJsonDocument<400> doc;
deserializeJson(doc, payload);
payload = "";
// Extract data from JSON
String uptime = doc["results"]["sunrise"].as<String>();
String downtime = doc["results"]["sunset"].as<String>();
String upsec = uptime;
String downsec = downtime;
upsec.remove(2,upsec.length()-2);
upsec.remove(0,1);
if (upsec == ":") {
uptime.remove(1,uptime.length()-1);
}
else {
uptime.remove(2,uptime.length()-2);
}
downsec.remove(2,downsec.length()-2);
downsec.remove(0,1);
if (downsec == ":") {
downtime.remove(1,downtime.length()-1);
}
else {
downtime.remove(2,downtime.length()-2);
}
if ((uptime.toInt() == 0) && (downtime.toInt() == 0)) {
sunup = 4;
sundown = 21;
Serial.println("sunTimes function returned zeros for sunrise and sunset..");
if (use_webserial == 1) {
delay(50);
WebSerial.println("sunTimes function returned zeros for sunrise and sunset..");
}
}
else {
sunup = uptime.toInt()+2;
sundown = downtime.toInt()+14;
}
Serial.println("Sunrise hour: " + String(sunup));
Serial.println("Sunset hour: " + String(sundown));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Sunrise hour: " + String(sunup));
WebSerial.println("Sunset hour: " + String(sundown));
}
}
else {
Serial.println("SunTimes HTTP Error: " + String(httpCode));
if (use_webserial == 1) {
delay(50);
WebSerial.println("SunTimes HTTP Error: " + String(httpCode));
}
}
tries++;
if (tries > 5) {
http.end();
break;
}
http.end();
}
while (httpCode != HTTP_CODE_OK);
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
float daypass() {
// This function returns the percentage of the day that has passed between sunup and sundown
float percentage = (float(now_hour-sunup)/(sundown-sunup));
int passprint = percentage*100;
if (now_hour < sunup) {
percentage = 0;
Serial.println("It is night time");
if (use_webserial == 1) {
delay(50);
WebSerial.println("It is night time");
}
isDay = 0;
}
else if (percentage > 1) {
percentage = 1;
Serial.println("It is night time");
if (use_webserial == 1) {
delay(50);
WebSerial.println("It is night time");
}
isDay = 0;
}
else {
Serial.println(String(passprint)+"% of the day has passed");
if (use_webserial == 1) {
delay(50);
WebSerial.println(String(passprint)+"% of the day has passed");
}
isDay = 1;
}
return percentage;
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
double tempCalc(double x) {
// This function returns a basetime modifier for the tempMod function
if (x >= 14 && x < 28) {
// For the range 14 <= x < 28
double result = 0.4 + (1.5 - 0.4) * (log(x) - log(14)) / (log(28) - log(14));
return result;
}
else if (x >= 28 && x <= 40) {
// For the range 28 <= x <= 40
double result = 1.5 + (4 - 1.5) * (log(x) - log(28)) / (log(40) - log(28));
return result;
}
else {
// Handle the case if x is outside the range [14, 40]
return 0.0; // For example, return 0.0 if x is outside the specified range
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void tempMod() {
// Returning watertime based on temperature
// If temperature is below 14
if (temp < 14) {
watertime = watertime;
}
// If temperature is between 14 and 40
else {
watertime = watertime+(basetime*tempCalc(temp));
// Printing out watertime after modifications
Serial.println("Watertime after tempMod: "+String(watertime));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Watertime after tempMod: "+String(watertime));
}
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void cloudMod() {
// Function for modifying watering time depending on clouds
int c_modifier;
// Adjusting cloud_modifier depending on sun strength
if ((passed > 0.45) && (passed < 0.55)) {
c_modifier = cloud_modifier*1.5;
}
else if ((passed > 0.35) && (passed < 0.65)) {
c_modifier = cloud_modifier*1.2;
}
else {
c_modifier = cloud_modifier;
}
// If cloud is 0
if (cloud < 1) {
watertime = watertime+(c_modifier*1.3);
Serial.println(F("Modifying watertime based on clouds = 0"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on clouds = 0");
}
}
// If cloud is 1
else if (cloud < 2) {
watertime = watertime+(c_modifier*1.2);
Serial.println(F("Modifying watertime based on clouds = 1"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on clouds = 1");
}
}
// If cloud is 2
else if (cloud < 3) {
watertime = watertime+(c_modifier*1.1);
Serial.println(F("Modifying watertime based on clouds = 2"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on clouds = 2");
}
}
// If cloud is 3
else if (cloud < 4) {
watertime = watertime+(c_modifier*1.05);
Serial.println(F("Modifying watertime based on clouds = 3"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on clouds = 3");
}
}
// If cloud is 4 or more
else {
watertime = watertime;
}
// Printout if watertime is modified
if (cloud < 4) {
Serial.println("Watertime after cloudMod: "+String(watertime));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Watertime after cloudMod: "+String(watertime));
}
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void rainMod() {
// Function for modifying watering time depending on rain and updating the rain counter
if (rain == 0) {
rain_reduction = rain_reduction;
}
else {
rain_reduction = rain_reduction+(rain*rain_modifier);
Serial.println(F("Modifying rain_reduction since rain > 0"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying rain_reduction since rain > 0");
}
}
// Adding rain to daily rain counter
rain_tot = rain_tot + rain;
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void windMod() {
// Function for modifying watering time depending on wind
// If wind is less than 3
if (wind < 3) {
watertime = watertime;
}
// If wind is between 3 and 5
else if (wind < 5) {
watertime = watertime-(1*wind_modifier);
Serial.println(F("Modifying watertime based on wind < 5"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on wind < 5");
}
}
// If wind is between 5 and 7
else if (wind < 7) {
watertime = watertime-(2*wind_modifier);
Serial.println(F("Modifying watertime based on wind < 7"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on wind < 7");
}
}
// If wind is more than 7
else {
watertime = watertime-(3*wind_modifier);
Serial.println(F("Modifying watertime based on wind >= 7"));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Modifying watertime based on wind >= 7");
}
}
// Printout if modified
if (wind >= 3) {
Serial.println("Watertime after windMod: "+String(watertime));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Watertime after windMod: "+String(watertime));
}
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void setWaterTime() {
// Function for calculating watering time for the day
// Printing out starting value for watertime
Serial.println("Starting watertime: "+String(watertime));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Starting watertime: "+String(watertime));
}
// Modifying watertime if temperature is more than 14 degrees
tempMod();
// Modifying watertime if the sun is up, temp is over 20 and clouds are less than 4 (since less clouds will result in stronger sun)
if ((isDay == 1) && (temp > 20)) {
cloudMod();
}
// Adding rain to rain_reduction variable which is subtracted from watertime upon automatical watering
rainMod();
// Modifying watertime if the sun is up wind is 3 or more (because of the cooling effect)
if ((now_hour > sunup) && (now_hour < sundown)) {
windMod();
}
// Making sure watertime is not negative
if (watertime < 0) {
watertime = 0;
}
// Printing out ending value for watertime
Serial.println("Watertime after adjustments: "+String(watertime));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Watertime after adjustments: "+String(watertime));
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void doWatering() {
// Function for performing watering
// Saving the time for when watering started
startTime = millis();
// Starting watering
if (high_level_relay == 0) {
digitalWrite(pump_pin, LOW);
}
else {
digitalWrite(pump_pin, HIGH);
}
is_watering = 1;
// If watering was started manually
if (manual_start == 1) {
Serial.println(F("Manually starting watering"));
if (use_webserial == 1) {
WebSerial.println("Manually starting watering");
WebSerial.println("");
}
}
// If watering was started automatically
else {
// Reducing watertime according to the accumulated rain and resetting rain_reduction variable
if (rain_reduction != 0) {
if (rain_reduction >= watertime) {
rain_reduction = rain_reduction - watertime;
watertime = 0;
}
else {
watertime = watertime - rain_reduction;
rain_reduction = 0;
}
Serial.println("Watertime after rain_reduction: "+String(watertime));
if (use_webserial == 1) {
delay(50);
WebSerial.println("Watertime after rain_reduction: "+String(watertime));
}
}
// If there has been more manual watering than the calculated amount that should be watered
if (man_counter >= watertime) {
if (now_hour == 13) {
water_mid = watertime;
}
else if (now_hour == 23) {
water_end = watertime;
}
man_counter = man_counter - watertime;
watertime = 0;
if (high_level_relay == 0) {
digitalWrite(pump_pin, HIGH);
}
else {
digitalWrite(pump_pin, LOW);
}
is_watering = 0;
}
// If there has been less manual watering than the calculated amount that should be watered
else {
watertime = watertime - man_counter;
man_counter = 0;
if (now_hour == 13) {
water_mid = watertime;
}
else if (now_hour == 23) {
water_end = watertime;
}
Serial.println(F("Starting watering"));
if (use_webserial == 1) {
WebSerial.println("Starting watering");
WebSerial.println("");
}
}
Serial.println("Water_mid value: "+String(water_mid));
Serial.println("Water_end value: "+String(water_end));
if (use_webserial == 1) {
WebSerial.println("Water_mid value: "+String(water_mid));
WebSerial.println("Water_end value: "+String(water_end));
}
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void stopWatering() {
// Function for stopping watering
// Stopping watering that happens at 13:00 or 23:00
if (((millis()-startTime) > (watertime*1000)) && (manual_start == 0) && (is_watering == 1) && (watertime != 0)) {
if (high_level_relay == 0) {
digitalWrite(pump_pin, HIGH);
}
else {
digitalWrite(pump_pin, LOW);
}
is_watering = 0;
Serial.println("Stopping watering after "+String(watertime)+" seconds because watertime is finished");
if (use_webserial == 1) {
WebSerial.println("Stopping watering after "+String(watertime)+" seconds because watertime is finished");
WebSerial.println("");
}
watertime = 0;
}
// Stopping manual watering that has been been going on longer than the maximum allowed
else if (((millis() - startTime) > (1000*maxtime)) && ((millis() - startTime) < 90000000) && (is_watering == 1) && (manual_start == 1)) {
if (high_level_relay == 0) {
digitalWrite(pump_pin, HIGH);
}
else {
digitalWrite(pump_pin, LOW);
}
is_watering = 0;
man_counter = man_counter+((millis()-startTime)/1000);
Serial.println(F("Stopping watering because time exceeded maximum"));
Serial.println("Current manual watering in total: "+String(man_counter));
if (use_webserial == 1) {
WebSerial.println("Stopping watering because time exceeded maximum");
WebSerial.println("Current manual watering in total: "+String(man_counter));
WebSerial.println("");
}
manual_start = 0;
}
// Stopping manual watering with button press
else if ((isPressed == 1) && (is_watering == 1) && (manual_start == 1)) {
if (high_level_relay == 0) {
digitalWrite(pump_pin, HIGH);
}
else {
digitalWrite(pump_pin, LOW);
}
is_watering = 0;
man_counter = man_counter+((millis()-startTime)/1000);
Serial.println(F("Stopping watering manually"));
Serial.println("Current manual watering in total: "+String(man_counter));
if (use_webserial == 1) {
WebSerial.println("Stopping watering manually");
WebSerial.println("Current manual watering in total: "+String(man_counter));
WebSerial.println("");
}
manual_start = 0;
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void fetchLocalTime() {
// Function for obtaining current time
// Saving time to variable
struct tm timeinfo;
// Printing out information if time wasn't received
if (!getLocalTime(&timeinfo)) {
Serial.println(F("Failed to obtain time"));
}
// Updating global time variables
now_hour = timeinfo.tm_hour;
now_minute = timeinfo.tm_min;
now_second = timeinfo.tm_sec;
now_day = timeinfo.tm_mday;
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void fetchWeather() {
// Function for retrieving weather data from SMHI API
// Setting the function variables
WiFiClientSecure wifiClient;
wifiClient.setInsecure();
int tries;
int httpCode;
int nap;
const char apiName[] = "opendata-download-metanalys.smhi.se";
const unsigned apiPort = 443;
String apiPath = "/api/category/mesan1g/version/2/geotype/point/lon/"+String(lon)+"/lat/"+String(lat)+"/data.json";
// Make HTTP request to SMHI API
// Do this if the connection to the SMHI API yet hasn't been successful
do {
// Increase the length of pause between tries to connect to API
if (tries < 5) {
nap = (1000+(1000*tries));
}
else {
nap = 1000;
}
// Pause for a bit
delay(nap);
// Begin HTTP request and get http status code
http.begin(wifiClient, apiName, apiPort, apiPath, true);
httpCode = http.GET();
// If connection successful
if (httpCode == HTTP_CODE_OK) {
// Parse JSON response
String payload = http.getString();
// Filter out the neccessary fields from response
static StaticJsonDocument<150> filter;
filter["timeSeries"][0]["parameters"][0]["name"] = true;
filter["timeSeries"][0]["parameters"][0]["values"] = true;
static StaticJsonDocument<50000> json_document;
deserializeJson( json_document, payload, DeserializationOption::Filter( filter ) );
Serial.println();
JsonArray parameters_array = json_document["timeSeries"][0]["parameters"].as<JsonArray>();
// Update weather variables with new data received from SMHI
for ( JsonObject parameter : parameters_array ) {
if ( !strcmp( parameter["name"].as<const char*>(), "t" ) )
{
temp = parameter["values"][0].as<float>();
}
else if ( !strcmp( parameter["name"].as<const char*>(), "prec1h" ) )
{
rain = parameter["values"][0].as<float>();
}
else if ( !strcmp( parameter["name"].as<const char*>(), "tcc" ) )
{
cloud = parameter["values"][0].as<int>();
}
else if ( !strcmp( parameter["name"].as<const char*>(), "ws" ) )
{
wind = parameter["values"][0].as<float>();
}
else if ( !strcmp( parameter["name"].as<const char*>(), "msl" ) )
{
pres = parameter["values"][0].as<int>();
}
else if ( !strcmp( parameter["name"].as<const char*>(), "r" ) )
{
humi = parameter["values"][0].as<int>();
}
else if ( !strcmp( parameter["name"].as<const char*>(), "Wsymb2" ) )
{
symb = parameter["values"][0].as<int>();
}
}
// Translating Wsymb2 integer to weather string
weatherSymbol();
// Printing out the received information
Serial.println(F("."));
Serial.println("Time: "+String(now_hour)+":"+String(now_minute)+":"+String(now_second));
Serial.println("Temp: "+String(temp)+" C");
Serial.println("Rain: "+String(rain)+" mm");
Serial.println("Clouds: "+String(cloud)+" of 8");
Serial.println("Wind: "+String(wind)+" m/s");
Serial.println("Pressure: "+String(pres)+" hPa");
Serial.println("Humidity: "+String(humi)+" % RH");
Serial.println("Weather: "+String(wlabel));
if (use_webserial == 1) {
delay(50);
WebSerial.println("");
WebSerial.println("Time: "+String(now_hour)+":"+String(now_minute)+":"+String(now_second));
WebSerial.println("Temp: "+String(temp)+" C");
WebSerial.println("Rain: "+String(rain)+" mm");
WebSerial.println("Clouds: "+String(cloud)+" of 8");
WebSerial.println("Wind: "+String(wind)+" m/s");
WebSerial.println("Pressure: "+String(pres)+" hPa");
WebSerial.println("Humidity: "+String(humi)+" % RH");
WebSerial.println("Weather: "+String(wlabel));
}
}
// Do this if the connection to the SMHI API failed
else {
// Printing out information
Serial.println("SMHI HTTP Error: " + String(httpCode));
if (use_webserial == 1) {
delay(50);
WebSerial.println("SMHI HTTP Error: " + String(httpCode));
}
}
// Increase the counter for how many connections has been tried
tries++;
if (tries > 5) {
// Close the http request and break the loop if 5 connection attempts has failed
http.end();
break;
}
// Close the http request before trying again
http.end();
}
// Condition for when to continue running the do/while loop
while (httpCode != HTTP_CODE_OK);
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void webMsg(uint8_t *data, size_t len) {
// Function for retrieving on/off messages send from WebSerial as manual button clicks
// Printing out information
WebSerial.print("Received input: ");
String inc_mess = "";
// Adding sent characters to string
for(int i=0; i < len; i++){
inc_mess += char(data[i]);
}
// Printing out received string
WebSerial.println(inc_mess);
// Switching webPressed variable to 1 if the string received was "ON" or "OFF"
if ((inc_mess == "ON") || (inc_mess =="OFF")) {
webPressed = 1;
}
// Rebooting the system if this was the received string
else if ((inc_mess == "REBOOT") || (inc_mess =="reboot") || (inc_mess =="RESTART") || (inc_mess =="restart")) {
// Printing out information
WebSerial.println("Rebooting system...");
WebSerial.println("");
delay(2500);
// Rebooting
ESP.restart();
}
// Setting the webPressed variable to 0
else {
webPressed = 0;
}
// Clearing the string again
inc_mess = "";
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void wTot() {
// Function for updating the watering total variable
// Setting water_tot variable
if ((now_hour < 13) && ((watertime-rain_reduction) > man_counter)) {
water_tot = watertime-rain_reduction;
}
else if ((now_hour < 13) && ((watertime-rain_reduction) <= man_counter)) {
water_tot = man_counter;
}
else if ((now_hour < 23) && ((watertime-rain_reduction) > man_counter)) {
water_tot = water_mid+watertime-rain_reduction;
}
else if ((now_hour < 23) && ((watertime-rain_reduction) <= man_counter)) {
water_tot = water_mid+man_counter;
}
else {
water_tot = water_mid+water_end+man_counter;
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void weatherSymbol() {
// Function for translating Wsymb2 to weather label (and possibly symbol)
// Setting wlabel variable
if (symb == 1) {
wlabel = "Clear sky";
}
else if (symb == 2) {
wlabel = "Nearly clear sky";
}
else if (symb == 3) {
wlabel = "Variable cloudiness";
}
else if (symb == 4) {
wlabel = "Halfclear sky";
}
else if (symb == 5) {
wlabel = "Cloudy sky";
}
else if (symb == 6) {
wlabel = "Overcast";
}
else if (symb == 7) {
wlabel = "Fog";
}
else if (symb == 8) {
wlabel = "Light rain showers";
}
else if (symb == 9) {
wlabel = "Moderate rain showers";
}
else if (symb == 10) {
wlabel = "Heavy rain showers";
}
else if (symb == 11) {
wlabel = "Thunderstorm";
}
else if (symb == 12) {
wlabel = "Light sleet showers";
}
else if (symb == 13) {
wlabel = "Moderate sleet showers";
}
else if (symb == 14) {
wlabel = "Heavy sleet showers";
}
else if (symb == 15) {
wlabel = "Light snow showers";
}
else if (symb == 16) {
wlabel = "Moderate snow showers";
}
else if (symb == 17) {
wlabel = "Heavy snow showers";
}
else if (symb == 18) {
wlabel = "Light rain";
}
else if (symb == 19) {
wlabel = "Moderate rain";
}
else if (symb == 20) {
wlabel = "Heavy rain";
}
else if (symb == 21) {
wlabel = "Thunder";
}
else if (symb == 22) {
wlabel = "Light sleet";
}
else if (symb == 23) {
wlabel = "Moderate sleet";
}
else if (symb == 24) {
wlabel = "Heavy sleet";
}
else if (symb == 25) {
wlabel = "Light snowfall";
}
else if (symb == 26) {
wlabel = "Moderate snowfall";
}
else if (symb == 27) {
wlabel = "Heavy snowfall";
}
else {
wlabel = "Unknown";
}
}
// ------------------------------------------------ EOF ------------------------------------------------------ //
void loop() {
// OTA wireless update handle
ArduinoOTA.handle();
// Retrieving time for each new second the loop has run
if (millis() - loopTime >= 1000) {
// Fetching the weather from SMHI API
fetchLocalTime();
}
// Retrieving weather and setting watertime variable once each hour, unless it's watering hours
if ((now_hour != last_hour) && (now_hour != 0) && (now_hour != 23)) {
// Fetching the weather from SMHI API// Fetching the weather from SMHI API
fetchWeather();
// Calculating watering time
setWaterTime();
}
// Retrieving weather only, since it's watering hours
else if (now_hour != last_hour) {
// Fetching the weather from SMHI API
fetchWeather();
}
// Starting watering since it's time for it
if ((now_hour == 13) || (now_hour == 23)) {
if (has_watered == 0) {
// Running the watering function
doWatering();
// Setting the manual_start and has_watered variables
manual_start = 0;
has_watered = 1;
}
}
// Making sure watering only occurs once per intended hour
else if (has_watered == 1) {
// Setting the has_watered variable
has_watered = 0;
}
// Reading button state for manual irrigation
button = digitalRead(manual_pin);
// If the button has been pressed or if an equivalent command has been received from WebSerial:
if (((button == HIGH) && (isPressed == 0)) || (webPressed == 1)) {
// Printing out information
Serial.println(F("Button pressed"));
if (use_webserial == 1) {
WebSerial.println("Button pressed");
}
// Setting the isPressed and webPressed variables
isPressed = 1;
webPressed = 0;
// Starting watering manually
if (is_watering == 0) {
// Setting the manual_start variable
manual_start = 1;
// Updating display
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(12,15);
display.print(F("Starting"));
display.setCursor(12,35);
display.print(F("watering"));
display.display();
delay(50);
doWatering();
}
// Stopping watering manually
else if (is_watering == 1) {
// Updating display
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(12,15);
display.print(F("Stopping"));
display.setCursor(12,35);
display.print(F("watering"));
display.display();
delay(50);
stopWatering();
}
// Pausing a little to allow information to show on the screen for the user
delay(1500);
}
// Making sure the isPressed variable is correctly set if the button is not pressed
else if (button == LOW) {
// Setting the isPressed variable
isPressed = 0;
}
// Making sure watering gets stopped if it should as well as updating the second counter
if (now_second != last_second) {
// Updating second counter
last_second = now_second;
loopTime = millis();
// Setting the water_tot variable
wTot();
// Running the function for stopping watering
stopWatering();
}
// Updating the minute counter
if (now_minute != last_minute) {
// Updating minute counter
last_minute = now_minute;
}
// Printing out watering and rain information in the end of the loop, calculating how much of the day has passed, and then updating the hour counter
if (now_hour != last_hour) {
// Calculating how much of the day that has passed
passed = daypass();
// Printing out information
if (use_webserial == 1) {
delay(50);
WebSerial.println("Watering today: "+String(water_tot));
WebSerial.println("Rain today: "+String(rain_tot));
WebSerial.println("");
}
// Updating hour counter
last_hour = now_hour;
}
// Resetting variables and carrying over rain and manual watering after every midnight
if (now_day != last_day) {
// Printing out information
Serial.println(F("New day = Daily reset."));
if (use_webserial == 1) {
WebSerial.println("New day = Daily reset.");
}
// Clearing the display
display.clearDisplay();
display.display();
delay(5000);
// Resetting last day variable to start new day
last_day = now_day;
// Resetting variables for new day
water_mid = 0;
water_end = 0;
rain_reduction = rain_reduction/2; // Carrying over a fraction of the rain reduction to the new day
rain_reduction = rain_reduction+(man_counter/2); // Carrying over some of the manual watering to the new day
// Making sure we don't carry over more than 5 minutes to the next day
if (rain_reduction > 300) {
rain_reduction = 300;
}
man_counter = 0;
rain_tot = 0;
watertime = 0;
// Printing out information about uptime and rain reduction variable
Serial.println("Rain reduction variable: "+String(rain_reduction));
Serial.println("Uptime: "+String((millis()-sysStart)/3600000)+" hours");
Serial.println(F(" "));
if (use_webserial == 1) {
WebSerial.println("Rain reduction variable: "+String(rain_reduction));
WebSerial.println("Uptime: "+String((millis()-sysStart)/3600000)+" hours");
WebSerial.println(" ");
}
// Option for Rebooting on new day start, loosing rain reduction and manual watering counters
// ESP.restart();
}
// Updating the display
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,5);
display.print(F("Temperature: "));
display.print(temp);
display.println(F(" C"));
display.println(F(""));
display.print(F("Cloudiness: "));
display.print(cloud);
display.println(F(" of 8"));
display.println(F(""));
display.print(F("Watering today: "));
display.print(water_tot);
display.println(F("s"));
display.println(F(""));
display.print(F("Rain today: "));
display.print(rain_tot);
display.println(F("mm"));
display.display();
}