 }
 
 void stateSetup()
-{   
+{
   displaySetup();
 }
 

#include <SoftwareSerial.h>
#include <LiquidCrystal595.h>
#include <Dht11.h>
#include <Servo.h>


int8_t maxC=0, minC=100;
const byte buttonPin = 5;
int8_t tempSensorValue = 0;
uint8_t tempLimitPotValue = 0;
uint8_t humSensorValue = 0;
uint8_t humLimitPotValue = 0;
uint8_t buttonState = LOW;
uint8_t alertLevel  = 30;
uint8_t humLevel = 80;
uint8_t wifiCheckCntr = 0;
boolean hatchOpen = false;

//state handling
const byte STATE_INITIAL = 0;
const byte STATE_WORKING = 1;
const byte STATE_SETUP   = 2;
byte state = STATE_INITIAL;

//analog pin assignments
const byte tempPotPin    = 0;
const byte humPotPin     = 1;

// The data I/O pin connected to the DHT11 sensor
const byte DHT_DATA_PIN = 6;
//digital pin assignments
const byte servo1Pin     = 10;
const byte wifiResetPin  = 13;

// The baud rate of the serial interface
const uint16_t SERIAL_BAUD  = 9600;
const uint16_t ESP8266_BAUD = 9600;


// Initialize the library with the numbers of the interface pins
LiquidCrystal595 lcd(2,3,4);
Servo servo1; // Create a servo object
Dht11 sensor(DHT_DATA_PIN); //Create 

//wifi esp8266
SoftwareSerial espSerial(12, 11); // TX, RX

#define BUFFER_SIZE 50
char buffer[BUFFER_SIZE];

uint16_t staticContLen = 0;
#define HOME_PAGE_STATIC_ARR_SZ  9
const uint8_t CONTENT_LEN_PLACE = 2; 
String homePageArray[HOME_PAGE_STATIC_ARR_SZ]={
  "HTTP/1.1 200 OK\r\n",
  "Connection: close\r\n",
//  "Content-Length:",
  "Content-Type: text/html; charset=UTF-8\r\n\r\n",
  "<!DOCTYPE html PUBLIC>\r\n",
//  "\"-//W3C//DTD XHTML 1.0 Strict//EN\"\r\n",
//  "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n",
  "<html>\r\n",
//  "<head>\r\n",
//  "<title>The Greenhouse</title>\r\n",
//  "</head>\r\n",
  "<body>\r\n",
  "<h1>Greenhouse</h1>\r\n",
  "</body>\r\n",
  "</html>\r\n\r\n",
};

//functions

String GetResponse(String AT_Command, int wait, char *waitFor=NULL) {
  String tmpData="";
  String dbgData="";
  byte cntr=0;
resend:
  espSerial.println(AT_Command);
  delay(10);
  while (espSerial.available() > 0 )  {
    char c = espSerial.read();
    tmpData += c;

    if ( tmpData.indexOf(AT_Command) > -1 )
      tmpData = "";
    else
      tmpData.trim();
  }
  dbgData=tmpData;
  if (tmpData.indexOf("busy") > -1 ){
      delay(100);
      dbgData+="...busy...\n";
      tmpData="";
      if (++cntr < 10) goto resend;
 }
 delay(wait);
 return dbgData;
}

void sendCommand ( char * cmd,  uint16_t wait, bool cipsend=false ) {
  Serial.println(cmd);
  Serial.println(GetResponse(cmd, wait));
}

void setupWifi()
{
  //connect wifi
  Serial.println("Restarting ESP");
  digitalWrite (wifiResetPin, LOW);
  delay(10000);
  digitalWrite (wifiResetPin, HIGH);
  delay(10000);
  clearSerialBuffer();
  sendCommand ("AT+CIOBAUD=9600", 1000);
  sendCommand ("AT+RST", 8000);
  delay(10000);
  sendCommand ( "AT+CWMODE=1", 2000 );
  sendCommand ( "AT+CWJAP=\"SSID\",\"passwd\"", 10000);
  sendCommand ( "AT+CIPMUX=1", 2000 );
  sendCommand ( "AT+CIPSERVER=1,8080\r", 8000 );
  sendCommand ( "AT+CIPSTO=120\r", 2000 );
  sendCommand ( "AT+CIPSTO?\r", 2000 );
  sendCommand ( "AT+CIFSR\r", 3000);
  sendCommand ( "AT+GMR\r", 3000);
}

void setup() {
  uint8_t cntr;
  for (cntr=3;cntr < HOME_PAGE_STATIC_ARR_SZ;cntr++){
    staticContLen+=homePageArray[cntr].length();
  }

  Serial.begin(SERIAL_BAUD);

  servo1.attach(servo1Pin); // Attaches the servo on pin 14 to the servo1 object
  servo1.write(180);  // Put servo1 at home position
  delay(2000);
  servo1.detach();

  // Open serial communications and wait for port to open:
  espSerial.begin(9600);
  espSerial.setTimeout(15000);
  delay(3000);
  Serial.println("\nStarting up...");
  Serial.print("Dht11 Lib version ");
  Serial.println(Dht11::VERSION);
  analogReference(INTERNAL);
  pinMode(buttonPin, INPUT);
  pinMode(wifiResetPin, OUTPUT);
  digitalWrite (wifiResetPin, HIGH);

  lcd.begin(16, 2); // Set the display to 16 columns and 2 rows
  lcd.clear();
  lcd.setCursor(0,0);
  
  lcd.print("Setting up Wifi.");
  setupWifi();
  
  lcd.setCursor(0,0);
  lcd.print("Wifi is set up.");
  delay(5000);
  lcd.clear();
  
  state = STATE_INITIAL;
}

void loop() 
{
  uint8_t ch_id=0;
  uint16_t packet_len;
  char *pb;

  lcd.setCursor(0,0); // Set cursor to home position
  readValues();
  handleState();
  if(espSerial.available()) // check if the esp is sending a message 
  {
    espSerial.readBytesUntil('\n', buffer, BUFFER_SIZE);
    if (strncmp(buffer, "+IPD,", 5) == 0) {
      // request: +IPD,ch,len:data
      sscanf(buffer + 5, "%d,%d", &ch_id, &packet_len);
      if (packet_len > 0) {
        // read serial until packet_len character received
        // start from :
        pb = buffer + 5;
        while (*pb != ':') pb++;
        pb++;
        if (strncmp(pb, "GET / ", 6) == 0) {
          //Serial.println(buffer);
          //Serial.print( "get Status from ch:" );
          //Serial.println(ch_id);
          delay(100);
          clearSerialBuffer();
          homepage(ch_id);
        }
      }
    }
  }
  clearBuffer();
  if (!(++wifiCheckCntr)){
    espSerial.println("AT+GMR\r");
    if(espSerial.find("OK")){
      Serial.println("Wifi OK");
    }else{
      Serial.println("Wifi NOK, restart WIfi");
      setupWifi();
    }
  }
  delay(500); 
}

void sendToClient(String *data, uint8_t ch_id)
{
  char command[20] = {0};
  snprintf(command,19,"AT+CIPSEND=%d,%d", ch_id, data->length());
  espSerial.println(command);
  if(espSerial.find(">")){
    espSerial.print(*data);
    delay(200);
    if (espSerial.find("OK")){
        while(espSerial.available()) // check if the esp is sending a message 
        {
          char temp=espSerial.read();
          Serial.print(temp);
        }
    }
    else{
      Serial.println("send data failed!");
    }
  }else{
    Serial.println("D'OH! Conn. fail!");
    setupWifi();
  }
  //esp seems wery slow at times, so just in case...
  delay(500);
}

char *get_temp_warn()
{
  if (tempSensorValue <=5) return "WARN!";
  if (tempSensorValue < 15 && tempSensorValue >5) return "cold";
  if (tempSensorValue < 20 && tempSensorValue >14) return "chilly";
  if (tempSensorValue < 31 && tempSensorValue >19) return "OK";
  if (tempSensorValue > 30) return "HOT!";
  return "";
}

void homepage(uint8_t ch_id) {
  //create dynamic content first, so that we get the length.
  String tempstr = "<p>Temperature:";
  tempstr += tempSensorValue;
  tempstr += "&deg;C ";
  tempstr += get_temp_warn();
  tempstr += "</br>min:";
  tempstr += minC;
  tempstr += " max:";
  tempstr += maxC;
  tempstr += " ";
  tempstr += "</p>\r\n";
  String humstr = "<p>Hum:";
  humstr += humSensorValue;
  humstr += "&#37;</p>\r\n";
  String windowstr = "<p><b>Window is ";
  windowstr += ((hatchOpen)?"Open</b></p>\r\n":"Closed</b></p>\r\n");
  //count dynamic lengts together and Set Content Len in header
  String hdrContLen = "Content-Length: ";
  hdrContLen += String((tempstr.length() +
                        humstr.length() +
                        windowstr.length()+
                        staticContLen), DEC);
  hdrContLen += "\r\n";
  
  Serial.println("Show page");
  Serial.println(hdrContLen);
  sendToClient(&homePageArray[0], ch_id);
  sendToClient(&homePageArray[1], ch_id);
  sendToClient(&hdrContLen, ch_id);
  sendToClient(&homePageArray[2], ch_id);
  sendToClient(&homePageArray[3], ch_id);
  sendToClient(&homePageArray[4], ch_id);
  sendToClient(&homePageArray[5], ch_id);
  sendToClient(&homePageArray[6], ch_id);
  //dynamic part
  sendToClient(&tempstr, ch_id);
  sendToClient(&humstr, ch_id);
  sendToClient(&windowstr, ch_id);
  //end of html page
  sendToClient(&homePageArray[7], ch_id);
  sendToClient(&homePageArray[8], ch_id);  
  Serial.println("Show done");
}

void clearSerialBuffer(void) {
  while ( espSerial.available() > 0 ) {
     espSerial.read();
  }
}


      humLevel = humLimitPotValue;
    }
  }

  if (state == STATE_SETUP) {
    stateSetup();
  }
  if (state == STATE_WORKING || state == STATE_INITIAL) {
    stateWorking();
  }
}

bool read_from_am2320() {
  switch (th.Read()) {
    case 2:
      LOG_DEBUGLN("CRC failed");
      return false;
      break;
    case 1:
      LOG_DEBUGLN("Sensor offline");
      return false;
      break;
    default:

      break;
  }
  return true;

}

void readSoilMoisture() {
  uint16_t tempAnalogValue = 0;
  tempAnalogValue = analogRead(MOIST_SENS1_PIN);
  delay(10);
  tempAnalogValue = analogRead(MOIST_SENS1_PIN);
  LOG_DEBUGLN(tempAnalogValue);
  moisture = uint8_t(100 * (1 - ((double)(tempAnalogValue) / 1023)));
}

void readTemperatureLimit() {
  uint16_t tempAnalogReadVal = 0;
  //read twice because of reading issues (sometimes)
  tempAnalogReadVal = analogRead(TEMP_POT_PIN); // Read the pot value
  delay(10);
  tempAnalogReadVal = analogRead(TEMP_POT_PIN); // Read the pot value
  tempLimitPotValue = map(tempAnalogReadVal, 0, 1023, 10, 100); // Map the values from 10 to 100 degrees
}

void readHumidityLimit() {
  uint16_t tempAnalogReadVal = 0;
  //read twice because of reading issues (sometimes)
  tempAnalogReadVal = analogRead(HUM_POT_PIN); // Read the pot value
  delay(10);
  tempAnalogReadVal = analogRead(HUM_POT_PIN); // Read the pot value
  humLimitPotValue = map(tempAnalogReadVal, 0, 1023, 20, 100); // Map the values from 20 to 100%
}

void readValues()
{
  if (true == read_from_am2320()) {
    if (isnan(th.h) || isnan(th.t)) {
      LOG_DEBUGLN("Invalid values from AM2320");
    } else {
      humSensorValue = th.h;
      tempSensorValue = th.t;
    }
  } else {
    LOG_DEBUGLN("Failed to get values from AM2320");
  }

  LOG_DEBUG("HUM: ");
  LOG_DEBUGLN(humSensorValue);
  LOG_DEBUG("TEMP: ");
  LOG_DEBUGLN(tempSensorValue);

  readSoilMoisture();
  LOG_DEBUG("SOIL: ");
  LOG_DEBUGLN(moisture);
  delay(10);
  readTemperatureLimit();
  LOG_DEBUG2("tp:");
  LOG_DEBUGLN2(tempLimitPotValue);
  delay(10);
  readHumidityLimit();
  LOG_DEBUG2("hp:");
  LOG_DEBUGLN2(humLimitPotValue);

  buttonState = digitalRead(BTNPIN); // Check for button press
}

void turnServo(uint16_t degree)
{
  servo1.attach(SERVOPIN); // Attaches the servo on pin 14 to the servo1 object
  servo1.write(degree);  // Put servo1 at home position
  delay(3000);
  servo1.detach();  //detach not to disturb SoftwareSerial
