#include <ESP32Servo.h>          // Biblioteca para controlar servos motores
#include <Wire.h>                // Biblioteca para comunicação I2C
#include <LiquidCrystal_I2C.h>   // Biblioteca para o display LCD via I2C
#include <DHT.h>                 // Biblioteca para o sensor DHT22
#include <WiFi.h>                // Biblioteca para comunicação Wi-Fi
#include <PubSubClient.h>        // Biblioteca para cliente MQTT
#include <ArduinoJson.h>         // Biblioteca para manipulação de JSON

// Definição de pinos para os componentes
#define TRIG_PIN         33      // Pino para o trigger do sensor ultrassônico
#define ECHO_PIN         32      // Pino para o echo do sensor ultrassônico
#define SERVO_PIN_1      5       // Pino para o servo motor 1
#define SERVO_PIN_2      17      // Pino para o servo motor 2
#define BUZZER_PIN       14      // Pino para o buzzer
#define DIST_THRESHOLD   10      // Limiar de distância em centímetros

#define LCD_ADDR         0x27    // Endereço I2C do display LCD
#define LCD_COLS         16      // Número de colunas no LCD
#define LCD_ROWS         2       // Número de linhas no LCD

#define WIFI_SSID        "Wokwi-GUEST" // SSID da rede Wi-Fi
#define WIFI_PASSWORD    ""            // Senha da rede Wi-Fi
#define MQTT_SERVER      "mqtt-dashboard.com" // Servidor MQTT
#define MQTT_PORT        1883          // Porta do servidor MQTT
#define TEMP_TOPIC       "esp32/" // Tópico MQTT para temperatura
#define DIST_TOPIC       "esp32/"    // Tópico MQTT para distância
#define SERVO_CTRL_TOPIC "esp32/servoControl" // Novo tópico MQTT para controle de servos

Servo servo1;                         // Objeto do servo motor 1
Servo servo2;                         // Objeto do servo motor 2
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS); // Objeto do display LCD
DHT dht(25, DHT22);                   // Objeto do sensor DHT22

WiFiClient espClient;                 // Objeto do cliente Wi-Fi
PubSubClient client(espClient);       // Objeto do cliente MQTT

void setup() {
  Serial.begin(115200);               // Inicialização da comunicação serial
  Wire.begin(23, 22);                 // Inicialização do barramento I2C
  lcd.init();                         // Inicialização do display LCD
  lcd.backlight();                    // Liga a luz de fundo do LCD
  lcd.setCursor(0, 0);                // Define a posição do cursor no LCD
  lcd.print("Distance:");             // Imprime o texto no LCD
  lcd.setCursor(0, 1);                // Define a posição do cursor no LCD
  lcd.print("Temp:");                 // Imprime o texto no LCD

  // Configuração dos pinos
  pinMode(TRIG_PIN, OUTPUT);          // Define o pino TRIG_PIN como saída
  pinMode(ECHO_PIN, INPUT);           // Define o pino ECHO_PIN como entrada
  pinMode(BUZZER_PIN, OUTPUT);        // Define o pino BUZZER_PIN como saída

  servo1.attach(SERVO_PIN_1);         // Anexa o servo motor 1 ao pino correspondente
  servo2.attach(SERVO_PIN_2);         // Anexa o servo motor 2 ao pino correspondente
  servo1.write(0);                    // Define a posição inicial do servo motor 1
  servo2.write(0);                    // Define a posição inicial do servo motor 2

  setup_wifi();                       // Configura a conexão Wi-Fi
  client.setServer(MQTT_SERVER, MQTT_PORT); // Configura o servidor MQTT
  client.setCallback(callback);       // Define a função de callback para mensagens MQTT
}

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Conectando-se a ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Conecta-se à rede Wi-Fi

  while (WiFi.status() != WL_CONNECTED) { // Aguarda até a conexão ser estabelecida
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi conectado");
  Serial.println("Endereço IP: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Tentando se reconectar ao MQTT Broker...");
    if (client.connect("949494949449494949494HHHHH")) { // Tenta conectar-se ao servidor MQTT
      Serial.println("Conectado ao MQTT Broker");
      client.subscribe(SERVO_CTRL_TOPIC); // Inscreve-se no tópico de controle de servos
      client.subscribe(DIST_TOPIC);       // Inscreve-se no tópico de distância
    } else {
      Serial.print("Falha na conexão, rc=");
      Serial.print(client.state());
      Serial.println(" Tentando novamente em 5 segundos");
      delay(5000);
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  String message;
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.print("Mensagem recebida [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(message);

  if (String(topic) == SERVO_CTRL_TOPIC) {
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, message);
    float distance_cm = doc["distance"];
    if (distance_cm < DIST_THRESHOLD) {
      servo1.write(180); // Move o servo 1 para 180 graus
      servo2.write(180); // Move o servo 2 para 180 graus
      delay(10000); // Aguarda 10 segundos
      servo1.write(0); // Retorna o servo 1 para 0 graus
      servo2.write(0); // Retorna o servo 2 para 0 graus
    }
  }

  if (String(topic) == DIST_TOPIC) {
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, message);
    float distance_cm = doc["distance"];
    if (distance_cm < DIST_THRESHOLD) {
      servo1.write(180); // Move o servo 1 para 180 graus
      servo2.write(180); // Move o servo 2 para 180 graus
    } else {
      servo1.write(0); // Retorna o servo 1 para 0 graus
      servo2.write(0); // Retorna o servo 2 para 0 graus
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Leitura da distância usando o sensor ultrassônico
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  long duration_us = pulseIn(ECHO_PIN, HIGH);
  float distance_cm = (duration_us * 0.0343) / 2;

  // Leitura da temperatura usando o sensor DHT22
  float temperature = dht.readTemperature();

  // Exibição da distância e temperatura no LCD
  lcd.setCursor(10, 0);
  lcd.print("     "); // Limpa leitura anterior
  lcd.setCursor(10, 0);
  lcd.print(distance_cm); // Exibe distância no LCD

  lcd.setCursor(13, 1);
  lcd.print("       "); // Limpa leitura anterior
  lcd.setCursor(10, 1);
  lcd.print(temperature, 1); // Exibe temperatura no LCD com uma casa decimal

  // Publica os valores de distância e temperatura nos tópicos MQTT
  if (client.connected()) {
    // Cria o documento JSON para a distância
    DynamicJsonDocument distDoc(1024);
    distDoc["sensor"] = "esp32/distance";
    distDoc["valor"] = distance_cm;
    String distMessage;
    serializeJson(distDoc, distMessage);
    client.publish(DIST_TOPIC, distMessage.c_str());

    // Cria o documento JSON para a temperatura
    DynamicJsonDocument tempDoc(1024);
    tempDoc["sensor"] = "esp32/temperature";
    tempDoc["valor"] = temperature;
    String tempMessage;
    serializeJson(tempDoc, tempMessage);
    client.publish(TEMP_TOPIC, tempMessage.c_str());

    Serial.println("Dados publicados nos tópicos MQTT");
  }

  // Controle do buzzer baseado na distância
  if (distance_cm < DIST_THRESHOLD) {
    digitalWrite(BUZZER_PIN, HIGH); // Liga o buzzer quando a distância for menor que o limite
  } else {
    digitalWrite(BUZZER_PIN, LOW); // Desliga o buzzer
  }

  delay(2000); // Aguarda um intervalo antes da próxima leitura
}