#include <DHTesp.h>         // DHT for ESP32 library
#include <WiFi.h>           // WiFi control for ESP32
#include "Thingsboard.h"
#include <ESP32Servo.h>
#include <RTClib.h>

#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

#define WIFI_AP_NAME              "Wokwi-GUEST"
#define WIFI_PASSWORD             ""
#define THINGSBOARD_SERVER        "thingsboardcloud.adamrabbani.com"
#define THINGSBOARD_ACCESSTOKEN   "WsussnQmR9CeapYT2Xjh"

WiFiClient espClient;
ThingsBoard tb(espClient);
int status = WL_IDLE_STATUS;

uint8_t pin_control[] = { 2, 4, 5 };

DHTesp dht;
#define DHT_PIN 15

// send message delay
int quant = 10;
int send_delay = 1000;
int send_time_passed = 0;

bool subscribed = false;

float temperature = 0;
bool is_heater_on = false;

//============================================================//
Servo myservo;
#define Servo_PIN 26

// servo delay
const long servo_delay = 3000; 
int servo_time_passed = 0;
int recent_servo_open = -1;

bool cmd_servo_open = false;
bool control_auto = false;
bool servo_lamp = 0;


#define trigPin 13
#define echoPin 12
int foodTankPercentage = 0;

#define phPIN 34
int phValue = 0;

RTC_DS1307 rtc;


RPC_Response processSetSwitchAuto(const RPC_Data &data)
{
  Serial.println("processSetSwitchAuto");

  control_auto = data;

  Serial.println(control_auto);

  return String(control_auto);
}

RPC_Response processGetSwitchAuto(const RPC_Data &data)
{
  Serial.println("processGetSwitchAuto");
  return String(control_auto);
}

RPC_Response processSwitchManualFeeder(const RPC_Data &data)
{
  Serial.println("processSwitchManualFeeder");

  cmd_servo_open = true;

  Serial.println(cmd_servo_open);

  return String(cmd_servo_open);
}

// RPC callback handlers
RPC_Callback callbacks[] = {
  { "setSwitchAuto",       processSetSwitchAuto },
  { "getSwitchAuto",       processGetSwitchAuto },  
  { "setSwitchManualFeeder",       processSwitchManualFeeder },
};

int measureFoodTank(){
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  float distance = duration * 0.034 / 2;
  int value = map(distance, 0, 400, 0, 100);
  return value;
}

int measurePH(){
  int ph = analogRead(phPIN);
  int value = map(ph, 0, 4095, 0, 14);
  return value;
}

void checkConditionTemp(){
  if(temperature < 20 ){
    is_heater_on = true;
  }else{
     is_heater_on = false;
  }
}

void setup() {
  Serial.begin(115200);

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  
  myservo.attach(Servo_PIN);
  myservo.write(0);

  int i = 0;
  for (size_t i = 0; i < COUNT_OF(pin_control); ++i) {
    pinMode(pin_control[i], OUTPUT);
  }

  dht.setup(DHT_PIN, DHTesp::DHT22);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  connectWifi();

}

void loop() 
{
  delay(quant);

  send_time_passed += quant;

  foodTankPercentage = measureFoodTank();
  phValue = measurePH();

  checkConditionTemp();

  // Reconnect to WiFi, if needed
  if (WiFi.status() != WL_CONNECTED) {
    connectWifi();
    return;
  }

  if (!tb.connected()) {
    subscribed = false;

    Serial.print("Koneksi ke: ");
    Serial.print(THINGSBOARD_SERVER);
    Serial.print(" dengan token ");
    Serial.println(THINGSBOARD_ACCESSTOKEN);

    if (!tb.connect(THINGSBOARD_SERVER, THINGSBOARD_ACCESSTOKEN)) {
      Serial.println("gagal terhubung");
      return;
    }
  }

  // Subscribe for RPC, if needed
  if (!subscribed) {
    Serial.println("Subscribing for RPC... ");
    
    if (!tb.RPC_Subscribe(callbacks, COUNT_OF(callbacks))) {
      Serial.println("Failed to subscribe for RPC");
      return;
    }

    Serial.println("Subscribe done");
    subscribed = true;
  } 

  digitalWrite(pin_control[0], control_auto);
  digitalWrite(pin_control[1], servo_lamp);
  digitalWrite(pin_control[2], is_heater_on);

  if (send_time_passed > send_delay) {
    Serial.println();
    Serial.print("Sending data... ");
    TempAndHumidity dHT22Values = dht.getTempAndHumidity();    

    if (isnan(dHT22Values.humidity) || isnan(dHT22Values.temperature)) {
      Serial.println("Gagal membaca sensor!");
    } else {

      temperature = dHT22Values.temperature;

      tb.sendTelemetryFloat("suhu", temperature);

      Serial.print("Suhu: ");
      Serial.print(temperature);

    }

    tb.sendAttributeBool("servoIndicator", servo_lamp);
    tb.sendTelemetryInt("foodTankValue", foodTankPercentage);
    tb.sendTelemetryInt("phValue", phValue);
    tb.sendAttributeBool("heaterIndicator", is_heater_on);

    Serial.print("foodTankPercentage: ");
    Serial.println(foodTankPercentage);
    Serial.print("phValue: ");
    Serial.println(phValue);

    send_time_passed = 0;
  }
    
    if(cmd_servo_open && !control_auto){
      openServoFeeder(-1);
    }else if(control_auto){
       feederScheduler();
    }else{
      myservo.write(0);
      cmd_servo_open = false;
    }

  tb.loop();
}

void connectWifi()
{
  Serial.println("Koneksi ke AP ...");

  WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Terkoneksi ke AP");
}

void openServoFeeder(int open_time){
  servo_time_passed += quant;

      myservo.write(90);
      servo_lamp = true;

      if (servo_time_passed > servo_delay) {
        myservo.write(0);
        servo_lamp = false;
        cmd_servo_open = servo_lamp;
        
        recent_servo_open = open_time;
        servo_time_passed = 0;
      }
}

void feederScheduler(){
  DateTime now = rtc.now();
    Serial.print(now.hour());
    Serial.print(':');
    Serial.print(now.minute());
    Serial.print(':');
    Serial.print(now.second());
    Serial.println();

    if (now.hour() == 8 && recent_servo_open != 8) {
      openServoFeeder(8);

    }else if (now.hour() == 12 && recent_servo_open != 12) {
      openServoFeeder(12);

    }else if (now.hour() == 16 && recent_servo_open != 16){

      openServoFeeder(16);

    }
}
esp:VIN
esp:GND.2
esp:D13
esp:D12
esp:D14
esp:D27
esp:D26
esp:D25
esp:D33
esp:D32
esp:D35
esp:D34
esp:VN
esp:VP
esp:EN
esp:3V3
esp:GND.1
esp:D15
esp:D2
esp:D4
esp:RX2
esp:TX2
esp:D5
esp:D18
esp:D19
esp:D21
esp:RX0
esp:TX0
esp:D22
esp:D23
dht1:VCC
dht1:SDA
dht1:NC
dht1:GND
led1:A
led1:C
led4:A
led4:C
ultrasonic1:VCC
ultrasonic1:TRIG
ultrasonic1:ECHO
ultrasonic1:GND
servo1:GND
servo1:V+
servo1:PWM
pot1:VCC
pot1:SIG
pot1:GND
GND5VSDASCLSQWRTCDS1307+
rtc1:GND
rtc1:5V
rtc1:SDA
rtc1:SCL
rtc1:SQW
led2:A
led2:C