#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Adafruit_NeoPixel.h>
#include "DFRobot_MultiGasSensor.h" // Sensor de O2 SEN0465

/*:::::::::::::: Defines ::::::::::::::*/
/* Define do CO2 */
#define MG_PIN                 (32)    
#define BOOL_PIN               (2)
#define DC_GAIN                (8.5)  
#define READ_SAMPLE_INTERVAL   (50)    
#define READ_SAMPLE_TIMES      (5)    
#define ZERO_POINT_VOLTAGE     (0.220)
#define REACTION_VOLTAGE       (0.030)

/* Define do O2 */
#define I2C_ADDRESS    0x74
DFRobot_GAS_I2C gas(&Wire, I2C_ADDRESS);

/* Credenciais Wi-Fi */
const char* ssid = "maxxxxxa"; 
const char* password = "zoxxxxxxxx1";

/* URL do Google Forms */
String url = "https://docs.google.com/forms/d/xxxxxxxxxxxDgtic/formResponse";

// Endereço do LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
int lcdState = 0; // estado do LCD
/*:::::::::::::: Variáveis Globais ::::::::::::::*/
float CO2Curve[3] = {2.602, ZERO_POINT_VOLTAGE, (REACTION_VOLTAGE / (2.602 - 3))};
int percentage;
float volts;
unsigned long lastSendTime = 0; // Variável para controlar o envio dos dados
const unsigned long intervalo = 5000; //  segundos
// Definições do NeoPixel
#define LED_PIN 18
#define NUM_LEDS 3
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);



void setup() {
    Serial.begin(9600);
    // Inicializa o LCD
    lcd.init();
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("Iniciando...");
    ligarWiFi();
    inicializarSensores();
    // Inicializa NeoPixel
    strip.begin();
    strip.setBrightness(40); // 30% de brilho
    strip.show();
}

void loop() {
    unsigned long currentMillis = millis();
    if (currentMillis - lastSendTime >= intervalo) {
        lerSensores();
        enviarDados();
        atualizarLCD();
        lastSendTime = currentMillis;
    }
}
void setLEDColor(uint8_t r, uint8_t g, uint8_t b) {
    for (int i = 0; i < NUM_LEDS; i++) {
        strip.setPixelColor(i, strip.Color(r, g, b));
    }
    strip.show();
}
String classificarCO2(int co2) {
  if (co2 <= 1000) {
        
        return "Normal";
    } else if (co2 <= 2000) {
        
        return "Moderado";
    } else if (co2 <= 5000) {
         
        return "Alto";
    } else {
       
        return "Perigoso";
    }


}

String classificarO2(float o2) {
  if (o2 >= 19.5) {
      
        return "Normal";
        
    } else if (o2 >= 16) {
        return "Baixo";
       
    } else if (o2 >= 10) {
       
        return "Muito Baixo";
  
    } else {
     
        return "Perigoso";
        
    }
}

void atualizarLCD() {
    lcd.clear();
    float temperatura = gas.readTempC();
    float o2 = gas.readGasConcentrationPPM();
    String co2Class = classificarCO2(percentage);
    String o2Class = classificarO2(o2);
    
    if (lcdState == 0) {
        // Exibir CO2 na linha 1 e classificação na linha 2
        lcd.setCursor(0, 0);
        lcd.print("CO2: " + String(percentage) + " ppm");
        lcd.setCursor(0, 1);
        lcd.print(co2Class);
        atualizarNeopixelCO2(percentage);
    } else if (lcdState == 1) {
        // Exibir O2 na linha 1 e classificação na linha 2
        lcd.setCursor(0, 0);
        lcd.print("O2: " + String(o2) + " %vol");
        lcd.setCursor(0, 1);
        lcd.print(o2Class);
        atualizarNeopixelO2(o2);
    } else {
        // Exibir Temperatura centralizada
        lcd.setCursor(2, 0);
        lcd.print("Temperatura");
        lcd.setCursor(5, 1);
        lcd.print(String(temperatura) + "C");
        setLEDColor(0, 0, 0); // Apagar LEDs enquanto mostra temperatura

    }
    
    lcdState = (lcdState + 1) % 3; // Alternar entre os três estados
}
// Função para atualizar LEDs baseados em CO2
void atualizarNeopixelCO2(int co2) {
    if (co2 <= 1000) {
        setLEDColor(0, 255, 0); // Verde (Normal)
    } else if (co2 <= 2000) {
        setLEDColor(255, 255, 0); // Amarelo (Moderado)
    } else if (co2 <= 5000) {
        setLEDColor(255, 165, 0); // Laranja (Alto)
    } else {
        setLEDColor(255, 0, 0); // Vermelho (Perigoso)
    }
}

// Função para atualizar LEDs baseados em O2
void atualizarNeopixelO2(float o2) {
    if (o2 >= 19.5) {
        setLEDColor(0, 255, 0); // Verde (Normal)
    } else if (o2 >= 16) {
        setLEDColor(255, 255, 0); // Amarelo (Baixo)
    } else if (o2 >= 10) {
        setLEDColor(255, 165, 0); // Laranja (Muito Baixo)
    } else {
        setLEDColor(255, 0, 0); // Vermelho (Perigoso)
    }
}

void ligarWiFi() {
    Serial.println("A ligar à rede Wi-Fi...");
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("\nLigado à rede Wi-Fi com sucesso!");
}

void inicializarSensores() {
    pinMode(BOOL_PIN, INPUT);
    digitalWrite(BOOL_PIN, HIGH);  
    Serial.println("Sensor CO2 inicializado.");
    
    while (!gas.begin()) {
        Serial.println("Não há dispositivos O2 disponíveis!");
        delay(1000);
    }
    Serial.println("Sensor O2 inicializado!");
    gas.changeAcquireMode(gas.PASSIVITY);
    delay(1000);
    gas.setTempCompensation(gas.OFF);  
}

void lerSensores() {
    volts = MGRead(MG_PIN);
    percentage = MGGetPercentage(volts, CO2Curve);
    if (percentage == -1) {
        percentage = 400;
    }
    
    Serial.print("CO2: "); Serial.print(percentage); Serial.println(" ppm");
    Serial.print("O2: "); Serial.print(gas.readGasConcentrationPPM()); Serial.println(" %vol");
    Serial.print("Ambient ");
    Serial.println(gas.queryGasType());
    Serial.print("Temperatura: "); Serial.print(gas.readTempC()); Serial.println(" °C");
}

void enviarDados() {
    HTTPClient http;
    String dados = "entry.140413188=" + String(gas.readGasConcentrationPPM()) +
                   "&entry.1499636342=" + String(percentage) +
                   "&entry.1665406568=" + String(gas.readTempC());

    http.begin(url);
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");
    int codigo_resposta = http.POST(dados);
    http.end();
    
    Serial.print("Código de resposta: ");
    Serial.println(codigo_resposta);
}

float MGRead(int mg_pin) {
    float v = 0;
    for (int i = 0; i < READ_SAMPLE_TIMES; i++) {
        v += analogRead(mg_pin);
        delay(READ_SAMPLE_INTERVAL);
    }
    return (v / READ_SAMPLE_TIMES) * 3.3 / 4096;
}

int MGGetPercentage(float volts, float *pcurve) {
    if ((volts / DC_GAIN) >= ZERO_POINT_VOLTAGE) {
        return -1;
    } else {
        return pow(10, ((volts / DC_GAIN) - pcurve[1]) / pcurve[2] + pcurve[0]);
    }
}

