//Library Inclusion

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include<ADS1115_WE.h>
#include "ThingSpeak.h" // Include thingspeak header file after other header files

//Constants and Network credentials

#ifndef STASSID
#define STASSID "YOUR_WIFI_SSID"
#define STAPSK  "YOUR_WIFI_PASS"
#endif

#define I2C_ADDRESS 0x48
#define SDA_PIN 4
#define SCL_PIN 5
#define VALVE_PIN 14
#define VALVE2_PIN 13
#define VALVE3_PIN 12
#define SNSe_PIN 15
#define LEDPIN 2
#define SleepInverval 1000000*60*15  // How much to sleep between each read (in microseconds), in this case, 15 minutes
#define IRRIGATION_STATION_CH_ID 123456   // Place your Thingspeak CH ID here
#define IRRIG_DURATION 60   // Duration of each irrigation cycle (in seconds)
#define IRRIG_THRESHOLD 30 

//Library entities

Adafruit_BME280 bme;
WiFiClient client;
ADS1115_WE adc(I2C_ADDRESS);

//Global Variables

const char* server = "api.thingspeak.com";
bool sleep = 1;
const char* ssid = STASSID;
const char* pass = STAPSK;
bool LED = 1;
unsigned long blinkEntry;
unsigned long millisON;
long secON = 0;
unsigned long WIFIwait = 0;
unsigned long sleepUS = 1000000 * 60 * 15;
int Vbat = 0;
int Mode = 0;
int RemainingCycles = 0;
String apiKey = "Your_WRITE_API_Key";   //Place your Write API Key here
const char * readKey = "Your_READ_API_Key";    //Place your Read API Key here
unsigned long IrrigationStationChannelNumber = IRRIGATION_STATION_CH_ID;
int statusCode = 0;
unsigned long myChannelNumber = IRRIGATION_STATION_CH_ID;
const char * myWriteAPIKey = "Your_WRITE_API_Key";   //Place your Write API Key here too


void setup() {  // All main procesess occur in Setup()
  pinMode(VALVE_PIN, OUTPUT);
  digitalWrite(VALVE_PIN, LOW);
  pinMode(VALVE2_PIN, OUTPUT);
  digitalWrite(VALVE2_PIN, LOW);
  pinMode(VALVE3_PIN, OUTPUT);
  digitalWrite(VALVE3_PIN, LOW);
  pinMode(LEDPIN, OUTPUT);
  digitalWrite(LEDPIN, LOW);
  delay(50);
  pinMode(SNSe_PIN, OUTPUT);
  digitalWrite(SNSe_PIN, HIGH);

  Serial.begin(115200);
  delay(2);
  Serial.println(" ");
  Serial.println(" ");
  Serial.println("Awake!");
  Serial.print("LED ON. ");
  Serial.println("Sensors ON and valve OFF");
  Serial.println(" ");
  delay(200);

  Wire.begin();  //Initialize I2C sensors
  unsigned status;
  status = bme.begin(0x76);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
    Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(), 16);
    Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
    Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
    Serial.print("        ID of 0x60 represents a BME 280.\n");
    Serial.print("        ID of 0x61 represents a BME 680.\n");
    digitalWrite(VALVE_PIN, LOW);
    digitalWrite(VALVE2_PIN, LOW);
    digitalWrite(VALVE3_PIN, LOW);
    Serial.print("VALVE OFF.  ");
    digitalWrite(LEDPIN, HIGH);
    Serial.println("LED OFF.  ");
    pinMode(D7, INPUT_PULLUP);
    delay(2);
    sleepUS = SleepInverval - (millis() * 1000);
    Serial.print("Going to sleep for ");
    Serial.print(sleepUS);
    Serial.println(" microseconds... ");
    digitalWrite(VALVE_PIN, LOW);
    digitalWrite(VALVE2_PIN, LOW);
    digitalWrite(VALVE3_PIN, LOW);
    digitalWrite(SNSe_PIN, LOW);
    WiFi.mode(WIFI_OFF);
    ESP.deepSleep(sleepUS);
  }
  if (!adc.init()) {
    Serial.println("ADS1115 not connected!");
    digitalWrite(VALVE_PIN, LOW);
    Serial.print("VALVE OFF.  ");
    digitalWrite(LEDPIN, HIGH);
    Serial.println("LED OFF.  ");
    pinMode(D7, INPUT_PULLUP);
    delay(2);
    sleepUS = SleepInverval - (millis() * 1000);
    Serial.print("Going to sleep for ");
    Serial.print(sleepUS);
    Serial.println(" microseconds... ");
    digitalWrite(VALVE_PIN, LOW);
    digitalWrite(VALVE2_PIN, LOW);
    digitalWrite(VALVE3_PIN, LOW);
    digitalWrite(SNSe_PIN, LOW);
    WiFi.mode(WIFI_OFF);
    ESP.deepSleep(sleepUS);
  }

  //On wakeup, read all sensors and send the values through serial monitor

  delay(300);
  adc.setVoltageRange_mV(ADS1115_RANGE_6144);
  float h = bme.readHumidity();
  float t = bme.readTemperature();
  float p = bme.readPressure() / 100.0F;
  Vbat = readChannel(ADS1115_COMP_0_GND);
  float s1 = readChannel(ADS1115_COMP_1_GND);
  float s2 = readChannel(ADS1115_COMP_2_GND);
  float s3 = readChannel(ADS1115_COMP_3_GND);
//  s1 = 100 - ((s1 - 1980) / 8); //map the soil moisture to 0-100%
//  s2 = 100 - ((s2 - 1980) / 8);
//  s3 = 100 - ((s3 - 1980) / 8);

  s1 = 100 - ((s1 - 1150) / 18); //map the soil moisture to 0-100%
  s2 = 100 - ((s2 - 1150) / 18);
  s3 = 100 - ((s3 - 1150) / 18);

  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from BME sensor!");
    digitalWrite(VALVE_PIN, LOW);
    Serial.print("VALVE OFF.  ");
    digitalWrite(LEDPIN, HIGH);
    Serial.println("LED OFF.  ");
    pinMode(D7, INPUT_PULLUP);
    delay(2);
    sleepUS = SleepInverval - (millis() * 1000);
    Serial.print("Going to sleep for ");
    Serial.print(sleepUS);
    Serial.println(" microseconds... ");
    digitalWrite(VALVE_PIN, LOW);
    digitalWrite(VALVE2_PIN, LOW);
    digitalWrite(VALVE3_PIN, LOW);
    digitalWrite(SNSe_PIN, LOW);
    WiFi.mode(WIFI_OFF);
    ESP.deepSleep(sleepUS);
    return;
  }

  Serial.println("Successfully read from sensors!");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" degrees Celcius, Humidity: ");
  Serial.print(h);
  Serial.print("%, Pressure: ");
  Serial.print(p);
  Serial.print("hPa, Battery Voltage: ");
  Serial.print(Vbat);
  Serial.println(" mV, ");
  Serial.print("Pot #1 Soil Moisture: ");
  Serial.print(s1);
  Serial.print("%, Pot #2 Soil Moisture: ");
  Serial.print(s2);
  Serial.print("%, Pot #3 Soil Moisture: ");
  Serial.print(s3);
  Serial.println("%.");
  Serial.println(" ");

  digitalWrite(VALVE_PIN, LOW);
  digitalWrite(SNSe_PIN, LOW);

  //Connect to WiFi

  Serial.println("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    WIFIwait++;
    delay(500);
    Serial.print(".");
    Serial.print(WIFIwait);
    if (WIFIwait > 60) {
      WiFi.mode(WIFI_OFF);
      ESP.deepSleep(sleepUS - 30400000);
    }
  }
  Serial.println(" ");
  Serial.println("WiFi connected");
  Serial.println(" ");

  //Read the latest data from Thingspeak to check if there are any pending irrigation cycles

  Serial.println("Reading data from Thingspeak...");
  Serial.println(" ");
  ThingSpeak.begin(client);  // Initialize ThingSpeak
  statusCode = ThingSpeak.readMultipleFields(IrrigationStationChannelNumber, readKey);

  if (statusCode == 200) {

    float AmbienTemp = ThingSpeak.getFieldAsFloat(1); // Field 1
    float AmbientHum = ThingSpeak.getFieldAsFloat(2); // Field 2
    float AmbientPressure = ThingSpeak.getFieldAsFloat(3); // Field 3
    float BatV = ThingSpeak.getFieldAsFloat(4); // Field 4
    int SoilMoistA = ThingSpeak.getFieldAsInt(5); // Field 5
    int SoilMoistB = ThingSpeak.getFieldAsInt(6); // Field 6
    int SoilMoistC = ThingSpeak.getFieldAsInt(7); // Field 7
    int SoilMoistD = ThingSpeak.getFieldAsInt(8); // Field 8
    String statusMessage = ThingSpeak.getStatus(); // Status message
    String createdAt = ThingSpeak.getCreatedAt(); // Created-at timestamp

    Serial.println("Latest data from Thingspeak:  ");
    Serial.print("Air temperature: " + String(AmbienTemp) + " °C.   ");
    Serial.print("Air Humidity: " + String(AmbientHum) + " %.   ");
    Serial.println("Atmospheric pressure: " + String(AmbientPressure) + " minibar.   ");
    Serial.print("Battery Voltage: " + String(BatV) + " mV.   ");
    Serial.println("Auto Irrigation: " + String(SoilMoistA) + " cycles pending.   ");
    Serial.print("Pot A Soil Moisture: " + String(SoilMoistB) + " %.   ");
    Serial.print("Pot B Soil Moisture: " + String(SoilMoistC) + " %.   ");
    Serial.println("Pot C Soil Moisture: " + String(SoilMoistD) + " %.   ");
    Serial.print("Created at (Y-M-D h:m:s):  " + createdAt);
    Serial.println("    Status Message: " + statusMessage);

    RemainingCycles = SoilMoistA;

    if (RemainingCycles >= 1) {
      Mode = 1;
    }
    RemainingCycles = RemainingCycles - 1;
    if (0 >= RemainingCycles) RemainingCycles = 0;
    if (IRRIG_THRESHOLD >= s1) RemainingCycles = 3;
    if (IRRIG_THRESHOLD >= s2) RemainingCycles = 3;
//  if (IRRIG_THRESHOLD >= s3) RemainingCycles = 3;
  } else {
    Serial.println("Problem reading channel. Error code " + String(statusCode));
  }

  //Send Data to THINGSPEAK
  delay(100);

  ThingSpeak.setField(1, t);
  ThingSpeak.setField(2, h);
  ThingSpeak.setField(3, p);
  ThingSpeak.setField(4, Vbat);
  ThingSpeak.setField(5, RemainingCycles);
  ThingSpeak.setField(6, s1);
  ThingSpeak.setField(7, s2);
  ThingSpeak.setField(8, s3);
  int x = ThingSpeak.writeFields(IrrigationStationChannelNumber, myWriteAPIKey);
  if (x == 200) {
    Serial.println("Channel update successful.");
  } else {
    Serial.println("Problem updating channel. HTTP error code " + String(x));
  }
  Serial.print("Data sent to Thingspeak. ");

  if (Mode == 0) {  //Go into DeepSleep mode
    client.stop();
    digitalWrite(LEDPIN, HIGH);
    Serial.println("LED OFF");
    pinMode(D7, INPUT_PULLUP);
    delay(2);
    sleepUS = SleepInverval - (millis() * 1000);
    Serial.print("Going to sleep for ");
    Serial.print(sleepUS);
    Serial.println(" microseconds... ");
    digitalWrite(VALVE_PIN, LOW);
    digitalWrite(SNSe_PIN, LOW);
    WiFi.mode(WIFI_OFF);
    ESP.deepSleep(sleepUS);
  }

  if (Mode == 1) {
    Serial.println("  ");
    Serial.println(String(RemainingCycles) + " Irrigation cycles pending. Beginning 30 second IRRIGATION cycle.  ");
    digitalWrite(SNSe_PIN, HIGH);
    delay(15);
    digitalWrite(VALVE_PIN, HIGH);
    digitalWrite(VALVE2_PIN, HIGH);
    digitalWrite(VALVE3_PIN, HIGH);
    Serial.println("VALVE ON.  ");
  }
}

void loop() { //the loop only executes if there are remaining irrigation cycles.

  if (Mode == 1) {
    if (millis() - millisON > 1000) {
      secON++;
      digitalWrite(LEDPIN, LED);
      LED = 1 - LED;
      Serial.print("Seconds ON: ");
      Serial.println(secON);
      millisON = millis();
    }
    if (secON >= IRRIG_DURATION) {
      Mode = 0;
    }
  }

  if (Mode == 0) {
    client.stop();
    digitalWrite(LEDPIN, HIGH);
    Serial.println("LED OFF, VALVE OFF. ");
    pinMode(D7, INPUT_PULLUP);
    delay(2);
    sleepUS = SleepInverval - (millis() * 1000);
    Serial.print("Going to sleep for ");
    Serial.print(sleepUS);
    Serial.println(" microseconds... ");
    digitalWrite(VALVE_PIN, LOW);
    digitalWrite(VALVE2_PIN, LOW);
    digitalWrite(VALVE3_PIN, LOW);
    digitalWrite(SNSe_PIN, LOW);
    WiFi.mode(WIFI_OFF);
    ESP.deepSleep(sleepUS);
  }
}

float readChannel(ADS1115_MUX channel) {  //Secondary function
  float voltage = 0.0000;
  adc.setCompareChannels(channel);
  adc.startSingleMeasurement();
  while (adc.isBusy()) {}
  voltage = adc.getResult_mV();
  return voltage;
}
