#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// #include <Adafruit_SH110X.h>
#include <Keypad.h>
#include <PID_v1.h>


// intervals------------------------------------------------------
unsigned long previousMillis_for_updating_display = 0;
const long interval_for_updating_display = 5000; // interval for updating display
unsigned long previousMillis_for_reading_sensors = 0;
const long interval_for_reading_sensors = 2000;  // interval for reading sensors


// Temperature Sensor (DS18B20) -----------------------------------
// Define the number of sensors
const int NUM_DS18B20_SENSORS = 4;
// Define an array to hold the addresses of all sensors
// { 0x28, 0x8C, 0xBC, 0x80, 0xE3, 0xE1, 0x3C, 0xB1 },
// { 0x28, 0x48, 0x9B, 0x80, 0xE3, 0xE1, 0x3C, 0x5B },
// { 0x28, 0xEF, 0x2A, 0x58, 0xD4, 0xE1, 0x3C, 0xB9 },
// { 0x28,  0x8, 0x5E, 0x80, 0xE3, 0xE1, 0x3C, 0xDE }
DeviceAddress sensorAddresses[NUM_DS18B20_SENSORS] = {
  { 0x10, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52 },
  { 0x10, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x23 },
  { 0x10, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x97 },
  { 0x10, 0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4F }
};
// Define a structure to hold temperature data for one sensor
struct Ds18b20_SensorData {
  float tempC;
  float tempF;
};
// Create an array of SensorData structures to hold data for all sensors
Ds18b20_SensorData ds18b20_SensorDataArray[NUM_DS18B20_SENSORS];
float average_temperature = 0.0;
// const byte ONE_WIRE_BUS = 0;
#define ONE_WIRE_BUS 4
// Setup a oneWire instance to communicate with a OneWire device
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);


void reading_ds18b20_sensors() {
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");

  for (int i = 0; i < NUM_DS18B20_SENSORS; i++) {
    // Get temperature data for each sensor
    ds18b20_SensorDataArray[i].tempC = sensors.getTempC(sensorAddresses[i]);
    ds18b20_SensorDataArray[i].tempF = sensors.getTempF(sensorAddresses[i]);

    // Print temperature data for each sensor
    Serial.print("Sensor ");
    Serial.print(i + 1);
    Serial.print(" (*C): ");
    Serial.print(ds18b20_SensorDataArray[i].tempC);
    Serial.print(" Sensor ");
    Serial.print(i + 1);
    Serial.print(" (*F): ");
    Serial.println(ds18b20_SensorDataArray[i].tempF);
  }
}


// Humidity & Temperature Sensor (DHT11) ------------------------
// for simulator-------------------------------------------------
#define DHT_PIN1 1   // Digital pin connected to the DHT sensor
#define DHT_PIN2 2
#define DHT_PIN3 3
#define DHT_PIN4 4

// #define DHT_PIN1 0   // Digital pin connected to the DHT sensor
// #define DHT_PIN2 1
// #define DHT_PIN3 15
// #define DHT_PIN4 2

#define NUM_DHT11_SENSORS 4

#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

struct DHT_SensorData {
  float humidity;
  float temperatureC;
  float temperatureF;
};

DHT dht[NUM_DHT11_SENSORS] = {
  DHT(DHT_PIN1, DHTTYPE), // Initialize DHT sensor 1
  DHT(DHT_PIN2, DHTTYPE), // Initialize DHT sensor 2
  DHT(DHT_PIN3, DHTTYPE), // Initialize DHT sensor 3
  DHT(DHT_PIN4, DHTTYPE)  // Initialize DHT sensor 4
};

DHT_SensorData dht11_SensorDataArray[NUM_DHT11_SENSORS];
float average_humidity = 0.0;

void reading_dht11_sensors() {
  for (int i = 0; i < NUM_DHT11_SENSORS; i++) {
    dht11_SensorDataArray[i].humidity = dht[i].readHumidity();
    dht11_SensorDataArray[i].temperatureC = dht[i].readTemperature();
    dht11_SensorDataArray[i].temperatureF = dht[i].readTemperature(true);

    if (isnan(dht11_SensorDataArray[i].humidity) || isnan(dht11_SensorDataArray[i].temperatureC) || isnan(dht11_SensorDataArray[i].temperatureF)) {
      Serial.print("Failed to read from DHT sensor ");
      Serial.println(i + 1);
      continue;
    }

    Serial.print("Sensor ");
    Serial.print(i + 1);
    Serial.print(": Humidity: ");
    Serial.print(dht11_SensorDataArray[i].humidity);
    Serial.print("%  Temperature: ");
    Serial.print(dht11_SensorDataArray[i].temperatureC);
    Serial.print("°C ");
    Serial.print(dht11_SensorDataArray[i].temperatureF);
    Serial.println("°F");
  }
}




// PI Controller--------------------------------------------------
//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
double Kp = 2, Ki = 2, Kd = 0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, REVERSE);



// state------------------------------------------------
// input_setpoint / output / input
String state = "output";


// Setpoint---------------------------------------------
float new_setpoint = 0;
float temperature_setpoint = 32.0;
float humidity_setpoint = 30.0;

int z2 = 0;
char attempt_setpoint[4] = {'_', '_', '_', '_'}; // create an array for the four attempts


// PINS ------------------------------------------------
// #define KEYPAD_PIN1 13
// #define KEYPAD_PIN2 12
// #define KEYPAD_PIN3 14
// #define KEYPAD_PIN4 27
// #define KEYPAD_PIN5 26
// #define KEYPAD_PIN6 25
// #define KEYPAD_PIN7 33
// #define KEYPAD_PIN8 32

// for simulator-------------------------------------------------
// byte rowPins[ROWS] = {17, 16, 15, 14}; //connect to the row pinouts of the keypad
// byte colPins[COLS] = {13, 12, 11, 10}; //connect to the column pinouts of the keypad
#define KEYPAD_PIN1 10
#define KEYPAD_PIN2 11
#define KEYPAD_PIN3 12
#define KEYPAD_PIN4 13
#define KEYPAD_PIN5 14
#define KEYPAD_PIN6 15
#define KEYPAD_PIN7 16
#define KEYPAD_PIN8 17

// #define SSR_PIN1 16
// #define SSR_PIN2 17
// #define SSR_PIN3 5
// #define SSR_PIN4 18
// for simulator-------------------------------------------------
#define SSR_PIN1 18
#define SSR_PIN2 19
#define SSR_PIN3 20
#define SSR_PIN4 21


// OLED SH------------------------------------------------
// /* Uncomment the initialize the I2C address , uncomment only one, If you get a totally blank screen try the other*/
// #define i2c_Address 0x3c //initialize with the I2C addr 0x3C Typically eBay OLED's
// //#define i2c_Address 0x3d //initialize with the I2C addr 0x3D Typically Adafruit OLED's
// #define SCREEN_WIDTH 128 // OLED display width, in pixels
// #define SCREEN_HEIGHT 64 // OLED display height, in pixels
// #define OLED_RESET -1   //   QT-PY / XIAO/media/numan/New Volume/0 Backup 0/3 Laptop (dell latitude e7440)/backup 1 (6 Oct 2023)/snap/arduino/85/Arduino/libraries/PID
// Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// #define OLED_WHITE_COLOR SH110X_WHITE
// #define OLED_BLACK_COLOR SH110X_BLACK 

// OLED SDD------------------------------------------------
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define OLED_WHITE_COLOR WHITE
#define OLED_BLACK_COLOR BLACK 


// Keypad -------------------------------------------------
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {KEYPAD_PIN8, KEYPAD_PIN7, KEYPAD_PIN6, KEYPAD_PIN5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {KEYPAD_PIN4, KEYPAD_PIN3, KEYPAD_PIN2, KEYPAD_PIN1}; //connect to the column pinouts of the keypad

//Create an object of keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
#define PASSWORD_LENGTH 4
char PASSWORD[PASSWORD_LENGTH] = {'1', '2', '3', '4'}; // Put the wanted password keys here
char attempt[PASSWORD_LENGTH] = {0, 0, 0, 0}; // create an array for the four attempts
int z = 0;



// 'locks', 24x24px
const unsigned char lock_bitmap_24px [] PROGMEM = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x00, 0x7f, 0xfc,
  0x3c, 0x3f, 0xfc, 0x7e, 0x3f, 0xfc, 0x7e, 0x3f, 0xfc, 0x7e, 0x3f, 0xfc, 0x7e, 0x3f, 0xfc, 0x7e,
  0x3f, 0xf0, 0x00, 0x0f, 0xe0, 0x00, 0x07, 0xe0, 0x00, 0x07, 0xe0, 0x00, 0x07, 0xe0, 0x00, 0x07,
  0xe0, 0x00, 0x07, 0xe0, 0x00, 0x07, 0xe0, 0x00, 0x07, 0xe0, 0x00, 0x07, 0xf0, 0x00, 0x0f, 0xf8,
  0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

// 'lock (1)', 24x24px
const unsigned char unlock_bitmap_24px [] PROGMEM = {
  0xff, 0xfe, 0x07, 0xff, 0xfc, 0x03, 0xff, 0xf8, 0x01, 0xff, 0xf0, 0x60, 0xff, 0xf0, 0xf0, 0xff,
  0xf1, 0xf8, 0xff, 0xf1, 0xf8, 0xff, 0xf1, 0xf8, 0xff, 0xf1, 0xf8, 0xff, 0xf1, 0xf8, 0xc0, 0x00,
  0xf8, 0x80, 0x00, 0x7f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f,
  0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00,
  0x00, 0x3f, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x7f
};

// 'hen', 64x64px
const unsigned char hen_bitmap_64px [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x3e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x63, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x36, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x1c, 0xe1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x66, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x7e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x00, 0x38, 0x00,
  0x00, 0x13, 0x00, 0x0c, 0x00, 0x00, 0x78, 0x00, 0x00, 0x13, 0x00, 0x06, 0x00, 0x00, 0xc8, 0x00,
  0x00, 0x1f, 0x00, 0x03, 0x00, 0x03, 0x98, 0x00, 0x00, 0x0e, 0x00, 0x01, 0xe0, 0x0f, 0x1c, 0x00,
  0x00, 0x06, 0x00, 0x60, 0xff, 0xfe, 0x06, 0x00, 0x00, 0x0c, 0x01, 0xe0, 0x0f, 0xf0, 0x04, 0x00,
  0x00, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00,
  0x00, 0x08, 0x0c, 0x00, 0x03, 0x00, 0x1e, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x03, 0xf0, 0x06, 0x00,
  0x00, 0x08, 0x08, 0x00, 0x00, 0xfe, 0x06, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x06, 0x0c, 0x00,
  0x00, 0x0c, 0x0c, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x06, 0x18, 0x00,
  0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x06, 0x06, 0x00, 0x08, 0x38, 0x30, 0x00,
  0x00, 0x06, 0x07, 0x00, 0x0f, 0xf0, 0x60, 0x00, 0x00, 0x03, 0x03, 0x80, 0x07, 0x80, 0x60, 0x00,
  0x00, 0x03, 0x80, 0xe0, 0x01, 0x80, 0xc0, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x03, 0x00, 0xc0, 0x00,
  0x00, 0x00, 0xe0, 0x1f, 0x0e, 0x01, 0x80, 0x00, 0x00, 0x00, 0x70, 0x07, 0xfc, 0x03, 0x00, 0x00,
  0x00, 0x00, 0x1c, 0x00, 0x70, 0x06, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0c, 0x00, 0x00,
  0x00, 0x00, 0x03, 0x80, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x30, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x7e, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x80, 0xc0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x61, 0xc1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xf3, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x1f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xf6, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xf0, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'smart', 64x64px
const unsigned char smart_poultry_farm_bitmap_64px [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x07, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x03, 0x87, 0xe1, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0xf8, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x0e, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x3f, 0x80, 0x00, 0x00,
  0x00, 0x00, 0x0f, 0xe0, 0x07, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0xfe, 0x00, 0x00,
  0x00, 0x01, 0xf8, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x03, 0xe0, 0x00,
  0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00,
  0x00, 0x18, 0x0f, 0xff, 0xff, 0xf0, 0x18, 0x00, 0x00, 0x38, 0x0f, 0xff, 0xff, 0xf0, 0x1c, 0x00,
  0x00, 0x70, 0x0c, 0x01, 0x80, 0x30, 0x0e, 0x00, 0x00, 0xe0, 0x0c, 0x01, 0x80, 0x30, 0x07, 0x00,
  0x01, 0xc0, 0x0c, 0x01, 0x80, 0x30, 0x03, 0x80, 0x03, 0x80, 0x0c, 0x01, 0x80, 0x30, 0x01, 0xc0,
  0x07, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00, 0xe0, 0x0f, 0x00, 0x0f, 0xff, 0xff, 0xf0, 0x00, 0xf0,
  0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0,
  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
  0x03, 0x0f, 0x8f, 0xff, 0xff, 0xff, 0xf0, 0xc0, 0x03, 0x0f, 0x8f, 0xff, 0xff, 0xff, 0xf0, 0xc0,
  0x03, 0x1f, 0x80, 0x07, 0xe0, 0x00, 0x30, 0xc0, 0x03, 0x3c, 0xc0, 0x0f, 0xf0, 0x00, 0x30, 0xc0,
  0x03, 0x3c, 0xc0, 0x1d, 0xb8, 0x00, 0x30, 0xc0, 0x03, 0x0e, 0xc0, 0x01, 0x9c, 0x00, 0x30, 0xc0,
  0x03, 0x06, 0x60, 0x01, 0x8e, 0x00, 0x30, 0xc0, 0x03, 0x06, 0x63, 0xc1, 0x87, 0x00, 0x30, 0xc0,
  0x03, 0x0e, 0x7f, 0xc1, 0x83, 0x80, 0x30, 0xc0, 0x03, 0x1c, 0x7e, 0xe1, 0x81, 0xc0, 0x30, 0xc0,
  0x03, 0x18, 0x18, 0x61, 0x80, 0xe0, 0x30, 0xc0, 0x03, 0x18, 0x00, 0x61, 0x80, 0x70, 0x30, 0xc0,
  0x03, 0x19, 0xc0, 0x61, 0x80, 0x38, 0x30, 0xc0, 0x03, 0x18, 0xfc, 0xe1, 0x80, 0x1c, 0x30, 0xc0,
  0x03, 0x1c, 0x79, 0xc1, 0x80, 0x0e, 0x30, 0xc0, 0x03, 0x0e, 0x01, 0x81, 0x80, 0x07, 0x30, 0xc0,
  0x03, 0x07, 0x03, 0x81, 0x80, 0x03, 0xb0, 0xc0, 0x03, 0x03, 0x87, 0x01, 0x80, 0x01, 0xf0, 0xc0,
  0x03, 0x01, 0xfe, 0x01, 0x80, 0x00, 0xf0, 0xc0, 0x03, 0x00, 0xfc, 0x01, 0x80, 0x00, 0x70, 0xc0,
  0x03, 0xf8, 0x30, 0x7f, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xf8, 0x30, 0x7f, 0xff, 0xff, 0xff, 0xc0,
  0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// 'fan_bitmap', 16x16px
const unsigned char fan_bitmap_16px [] PROGMEM = {
  0x00, 0x00, 0x06, 0x18, 0x05, 0x00, 0x04, 0x80, 0x04, 0x9f, 0x07, 0x80, 0x3e, 0xfc, 0x4c, 0x44,
  0x8c, 0x48, 0xfe, 0xf0, 0x07, 0x80, 0x04, 0x9f, 0x04, 0x80, 0x02, 0x80, 0x01, 0x98, 0x00, 0x00
};

// 'thermometer_bitmap', 16x16px
const unsigned char thermometer_bitmap_16px [] PROGMEM = {
  0x01, 0x80, 0x02, 0x40, 0x02, 0x5c, 0x02, 0x40, 0x02, 0x48, 0x02, 0x40, 0x02, 0x40, 0x02, 0x5c,
  0x06, 0x60, 0x0d, 0xb0, 0x0b, 0xd0, 0x0b, 0xd0, 0x0b, 0xd0, 0x09, 0x90, 0x06, 0x60, 0x03, 0xc0
};

// 'humidity_bitmap', 16x16px
const unsigned char humidity_bitmap_16px [] PROGMEM = {
  0x02, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x09, 0x80, 0x10, 0x80, 0x30, 0x00, 0x60, 0x00, 0x40, 0xee,
  0xc1, 0xbb, 0x80, 0x00, 0x8e, 0xee, 0xdb, 0xbb, 0x40, 0x00, 0x60, 0x40, 0x39, 0xc0, 0x0f, 0x80
};


// 'Disconnect', 16x16px
const unsigned char disconnect_bitmap_16px [] PROGMEM = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0x03, 0xfb, 0x33, 0xf1, 0xf3, 0xf8, 0xe3, 0xfc, 0x67,
  0xe6, 0x3f, 0xc7, 0x1f, 0xcf, 0x8f, 0xcc, 0xdf, 0xc0, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff
};

// OLED Display Functions ------------------------------------------------
void display_pass() {
  display.setTextSize(2);
  display.setCursor(z * 15, 25);
  display.println("*");
  display.display();
}

void display_border() {
  display.drawRect(0, 0, 128, 64, OLED_WHITE_COLOR);
  //  display.drawRoundRect(0, 0, 128, 64, 4, OLED_WHITE_COLOR);
}

void display_setpoint() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(20, 10);
  display.println("SP temp");
  display.setCursor(10, 40);
  display.print("[ ");
  for (int i = 0; i < 4; i++) {
    display.print(attempt_setpoint[i]);
  }
  display.println(" ]");
  display.display();
}

void display_key_msg(String msg) {
  display.setTextSize(1);
  display.setCursor(15, 45);
  display.println(msg);
  display.display();
  delay(2000);
}


void display_fan(int x, int y, int fan_number, int fan_state) {
  display.setTextColor(OLED_WHITE_COLOR);    // set text color
  display.setTextSize(1);

  display.drawBitmap(x, y, fan_bitmap_16px, 16, 16, 1);

  display.setCursor(x, y + 20);   // set position to display (x,y)
  display.print("Fan");
  display.println(fan_number);

  display.fillRoundRect(x + 28, y + 8, 22, 10, 2, OLED_WHITE_COLOR);

  display.setTextColor(OLED_BLACK_COLOR, OLED_WHITE_COLOR);  // 'inverted' text
  display.setCursor(x + 30, y + 10);   // set position to display (x,y)
  if (fan_state == 0) {
    display.println("OFF");          // set text
  } else {
    display.println("ON");          // set text
  }

}


void display_temperature_sensor(int x, int y, int sensor_number, float temperature) {
  display.setTextColor(OLED_WHITE_COLOR);    // set text color
  display.setTextSize(1);

  display.drawBitmap(x, y, thermometer_bitmap_16px, 16, 16, 1);

  display.setCursor(x, y + 20);    // set position to display (x,y)
  display.print("Temp");
  display.println(sensor_number);  // set text


  //  disconnect_bitmap_16px
  if (temperature == -127.0) {
    display.drawBitmap(x + 35, y + 5, disconnect_bitmap_16px, 16, 16, 1);
  } else {
    display.fillRoundRect(x + 28, y + 8, 27, 10, 2, OLED_WHITE_COLOR);
    display.setTextColor(OLED_BLACK_COLOR, OLED_WHITE_COLOR);  // 'inverted' text
    display.setCursor(x + 30, y + 10);   // set position to display (x,y)
    display.println(temperature, 1);     // set text
  }

}


void display_humidity_sensor(int x, int y, int sensor_number, float humidity) {
  display.setTextColor(OLED_WHITE_COLOR);    // set text color
  display.setTextSize(1);

  display.drawBitmap(x, y, humidity_bitmap_16px, 16, 16, 1); //good1

  display.setCursor(x, y + 20);    // set position to display (x,y)
  display.print("Hum");
  display.println(sensor_number);  // set text

  if (humidity >= 0 && humidity <= 100) {
    display.fillRoundRect(x + 28, y + 8, 22, 10, 2, OLED_WHITE_COLOR);
    display.setTextColor(OLED_BLACK_COLOR, OLED_WHITE_COLOR); // 'inverted' text
    display.setCursor(x + 30, y + 10); // set position to display (x,y)
    display.print(humidity, 1);         // set text
    display.println("%");            // set text
  } else {
    display.drawBitmap(x + 35, y + 5, disconnect_bitmap_16px, 16, 16, 1);
  }

}

void display_sensor_setpoint(String sensor, float sensor_value, float sensor_setpoint, char unit) {
  display.setTextColor(OLED_WHITE_COLOR);    // set text color
  display.setTextSize(2);

  //sp and pv
  display.setCursor(40, 10);
  display.println(sensor);
  display.setTextSize(1);
  display.setCursor(35, 35);
  display.print("PV : ");
  display.print(sensor_value, 1);
  display.println(unit);
  display.setCursor(35, 45);
  display.print("SP : ");
  display.print(sensor_setpoint, 1);
  display.println(unit);
}


void updateDisplay() {
  static int state = 0; // Keeps track of which display state we are in
  display.clearDisplay(); // clear display
  display_border();


  switch (state) {
    case 0:
      display.drawLine(0, 32, display.width() - 1, 32, OLED_WHITE_COLOR);
      display.drawLine(64, 0, 64, display.height() - 1, OLED_WHITE_COLOR);
      display_fan(5, 2, 1, digitalRead(SSR_PIN1));
      display_fan(5, 35, 2, digitalRead(SSR_PIN2));
      display_fan(70, 2, 3, digitalRead(SSR_PIN3));
      display_fan(70, 35, 4, digitalRead(SSR_PIN4));
      break;
    case 1:
      display.drawLine(0, 32, display.width() - 1, 32, OLED_WHITE_COLOR);
      display.drawLine(64, 0, 64, display.height() - 1, OLED_WHITE_COLOR);
      display_temperature_sensor(5, 2, 1, ds18b20_SensorDataArray[0].tempC);
      display_temperature_sensor(5, 35, 2, ds18b20_SensorDataArray[1].tempC);
      display_temperature_sensor(70, 2, 3, ds18b20_SensorDataArray[2].tempC);
      display_temperature_sensor(70, 35, 4, ds18b20_SensorDataArray[3].tempC);
      break;
    case 2:
      display.drawLine(0, 32, display.width() - 1, 32, OLED_WHITE_COLOR);
      display.drawLine(64, 0, 64, display.height() - 1, OLED_WHITE_COLOR);
      display_humidity_sensor(5, 2, 1, dht11_SensorDataArray[0].humidity);
      display_humidity_sensor(5, 35, 2, dht11_SensorDataArray[1].humidity);
      display_humidity_sensor(70, 2, 3, dht11_SensorDataArray[2].humidity);
      display_humidity_sensor(70, 35, 4, dht11_SensorDataArray[3].humidity);
      break;
    case 3:
      display_sensor_setpoint("Temp", average_temperature, temperature_setpoint, 'C');
      break;
    case 4:
      display_sensor_setpoint("Hum", average_humidity, humidity_setpoint, '%');
      break;
  }

  display.display();

  // Increment state and loop back to 0 if greater than 4
  state = (state + 1) % 5;
}


// Keypad Functions ------------------------------------------------
void correctKEY() { // do this if the correct KEY is entered
  display.drawBitmap(55, 10, lock_bitmap_24px, 24, 24, 1);
  //  display_key_msg("KEY ACCEPTED");
  display_key_msg("Correct Password");
  state = "input_setpoint";
}

void incorrectKEY() { // do this if an incorrect KEY is entered
  display.drawBitmap(55, 10, unlock_bitmap_24px, 24, 24, 2);
  //  display_key_msg("KEY REJECTED!");
  display_key_msg("Incorrect Password");
  state = "output";
}

void checkKEY() {
  int correct = 0;
  //loop to let users enter four keys
  for (int i = 0; i < PASSWORD_LENGTH ; i++ ) {
    //compare the entered key with the password
    if (attempt[i] == PASSWORD[i]) {
      correct++; // incease it, whenever there is a correct key
    }
  }

  // if the correct keys become four keys do ...
  if (correct == PASSWORD_LENGTH) {
    correctKEY();
  } else {
    incorrectKEY();
  }

  // clear previous key input
  for (int zz = 0; zz < PASSWORD_LENGTH; zz++) {
    attempt[zz] = 0;
  }
}

void readKeypad(String for_why) {
  if (for_why == "for_password")
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)
      {
        case '*':       // the password should be entered after this symbol
          display.setTextSize(1);         // set text size
          display.setTextColor(OLED_WHITE_COLOR);    // set text color
          z = 0;
          state = "input";
          break;
        case '#':      // the password should be finished by this symbol
          if (state == "input")
          {
            // delay(100); // added debounce
            display.clearDisplay();
            // display.display();
            if (z == PASSWORD_LENGTH)
            {
              checkKEY();  // call this function
            }
            else
            {
              incorrectKEY();
            }
            z = 0;
            break;
          }
        default:
          if (state == "input")
          {
            attempt[z] = key;
            z++;
            if (z == 1)
            {
              display.clearDisplay();
            }
            display_pass();
            // Serial.println("*");
          }
      }
    }
  }
  else if (for_why == "for_control")
  {
    char key = keypad.getKey();
    if (key != NO_KEY)
    {
      switch (key)
      {
        case '#'://-----------------------------------
          display.clearDisplay();
          state = "output";
          delay(100); // added debounce
          for (int i = 0; i < 4; i++)
          {
            attempt_setpoint[i] = '_';
          }
          new_setpoint = 0;
          break;

        default://-----------------------------------
          if (z2 == 2)
          {
            attempt_setpoint[z2] = '.';
            attempt_setpoint[z2 + 1] = key;

            String neww = "0.#";
            neww[2] = key;
            float temp = neww.toFloat();
            new_setpoint += temp;
            temperature_setpoint = new_setpoint;
            Setpoint = temperature_setpoint;

            for (int i = 0; i < 4; i++)
            {
              attempt_setpoint[i] = '_';
            }
            z2 = 0;
            new_setpoint = 0;/////////
            state = "output";
            break;
          }
          else
          {
            attempt_setpoint[z2] = key;
            String temp_str = "0";
            temp_str[0] = key;
            int newint = temp_str.toInt();
            if (new_setpoint == 0)
            {
              new_setpoint += newint;
            }
            else
            {
              new_setpoint *= 10;
              new_setpoint += newint;
            }
            z2++;
          }
          display.clearDisplay();
          display_setpoint();
          // Serial.println("*");
      }
    }
    display_setpoint();
  }
}


// ========================= Void Setup ============================
void setup() {
  Serial.begin(115200);
  //  Relays Pin Configuration-----------------------
  pinMode(SSR_PIN1, OUTPUT);
  pinMode(SSR_PIN2, OUTPUT);
  pinMode(SSR_PIN3, OUTPUT);
  pinMode(SSR_PIN4, OUTPUT);

  digitalWrite(SSR_PIN1, LOW);
  digitalWrite(SSR_PIN2, LOW);
  digitalWrite(SSR_PIN3, LOW);
  digitalWrite(SSR_PIN4, LOW);

  //  PI Controller Configuration--------------------
  Setpoint = temperature_setpoint;
  //turn the PID on
  myPID.SetMode(AUTOMATIC);

  //  OLED Display Configuration---------------------
  // display.begin(i2c_Address, true); // Address 0x3C default

  //  // initialize OLED display with I2C address 0x3C
  //  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  //    Serial.println(F("failed to start SSD1306 OLED"));
  //    while (1);
  //  }


  //  OLED SDD1306--------------------------------------------------
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
   //  Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();
  display.fillRoundRect(10, 15, 50, 40, 4, OLED_WHITE_COLOR);
  display.setTextSize(1);         // set text size
  display.setTextColor(OLED_BLACK_COLOR);    // set text color
  display.setCursor(20, 25);
  display.println("SMART");
  display.setCursor(15, 35);
  display.println("POULTRY");
  
  //  display.drawBitmap(35, 2, hen_bitmap_64px, 64, 64, 1);
  display.drawBitmap(64, 2, smart_poultry_farm_bitmap_64px, 64, 64, 1);
  display.setTextColor(OLED_WHITE_COLOR);
  display.display();




  //  DS18B20 Configuration--------------------------
  sensors.begin();

  //  DHT11 Configuration----------------------------
  for (int i = 0; i < NUM_DHT11_SENSORS; i++) {
    dht[i].begin();
  }

}


// ========================= Void Loop ============================
void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis_for_reading_sensors >= interval_for_reading_sensors) {
    previousMillis_for_reading_sensors = currentMillis;
    reading_ds18b20_sensors();
    reading_dht11_sensors();

    float sum = 0.0;
    for (int i = 0; i < NUM_DS18B20_SENSORS; i++) {
      sum += ds18b20_SensorDataArray[i].tempC;
    }
    average_temperature = sum / NUM_DS18B20_SENSORS;

    sum = 0.0;
    for (int i = 0; i < NUM_DHT11_SENSORS; i++) {
      sum += dht11_SensorDataArray[i].humidity;
    }
    average_humidity = sum / NUM_DHT11_SENSORS;

    Input = average_temperature;
    myPID.Compute();
    //  Relay Control with Pi controller---------------
    if (Output > 0) {
      if (Output > 30) {
        digitalWrite(SSR_PIN4, HIGH);
      } else if (Output > 20) {
        digitalWrite(SSR_PIN3, HIGH);
        digitalWrite(SSR_PIN4, LOW);
      } else if (Output > 10) {
        digitalWrite(SSR_PIN2, HIGH);
        digitalWrite(SSR_PIN3, LOW);
        digitalWrite(SSR_PIN4, LOW);
      } else {
        digitalWrite(SSR_PIN1, HIGH);
        digitalWrite(SSR_PIN2, LOW);
        digitalWrite(SSR_PIN3, LOW);
        digitalWrite(SSR_PIN4, LOW);
      }
    } else {
      digitalWrite(SSR_PIN1, LOW);
      digitalWrite(SSR_PIN2, LOW);
      digitalWrite(SSR_PIN3, LOW);
      digitalWrite(SSR_PIN4, LOW);
    }
  }



  //  State Management-------------------------------
  if (state == "input_setpoint")
  {
    readKeypad("for_control");
  }
  else {
    readKeypad("for_password");
    if (state == "output") {
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis_for_updating_display >= interval_for_updating_display) {
        previousMillis_for_updating_display = currentMillis;
        updateDisplay();
      }
    }
  }
}
esp:0
esp:1
esp:2
esp:3
esp:4
esp:5
esp:6
esp:7
esp:8
esp:9
esp:10
esp:11
esp:12
esp:13
esp:14
esp:15
esp:16
esp:17
esp:18
esp:19
esp:20
esp:21
esp:26
esp:33
esp:34
esp:35
esp:36
esp:37
esp:38
esp:39
esp:40
esp:41
esp:42
esp:45
esp:46
esp:3V3
esp:5V
esp:GND.1
esp:TX
esp:RX
esp:RST
esp:GND.2
Loading
ds18b20
Loading
ds18b20
Loading
ds18b20
Loading
ds18b20
r1:1
r1:2
dht1:VCC
dht1:SDA
dht1:NC
dht1:GND
dht2:VCC
dht2:SDA
dht2:NC
dht2:GND
dht3:VCC
dht3:SDA
dht3:NC
dht3:GND
dht4:VCC
dht4:SDA
dht4:NC
dht4:GND
r2:1
r2:2
r3:1
r3:2
r4:1
r4:2
r5:1
r5:2
Loading
ssd1306
keypad1:R1
keypad1:R2
keypad1:R3
keypad1:R4
keypad1:C1
keypad1:C2
keypad1:C3
keypad1:C4
NOCOMNCVCCGNDINLED1PWRRelay Module
relay3:VCC
relay3:GND
relay3:IN
relay3:NC
relay3:COM
relay3:NO
NOCOMNCVCCGNDINLED1PWRRelay Module
relay4:VCC
relay4:GND
relay4:IN
relay4:NC
relay4:COM
relay4:NO
NOCOMNCVCCGNDINLED1PWRRelay Module
relay5:VCC
relay5:GND
relay5:IN
relay5:NC
relay5:COM
relay5:NO
NOCOMNCVCCGNDINLED1PWRRelay Module
relay6:VCC
relay6:GND
relay6:IN
relay6:NC
relay6:COM
relay6:NO