/*
   Display 16x2
   [][][][x][][][][][][][][][x][][][]
   [][][][][][][][][][][][][][][][]
   Filament dryer
  Conexões do display e botões (Vista de cima)
  [A][B][C][D][E][F][G][H][I]  [J][K][L][M][N][O][P][Q]
  A = 5v
  B = D7
  C = D6
  D = GND
  E = WLED
  F = D5
  G = D4
  H = GND
  I = BLED
  J = EN
  K = GLED
  L = YLED
  M = RS
  N = RLED
  O = BT+
  P = BTM
  Q = BT-
  Placa do microcntrolador (Vista dos pinos)
  [A][B][C][D][E][F][G][H][I][J][K]  [L][M][N][O][P][Q]
   PORTA     OBJETIVO
  A = D5  => D4 display
  B = D4  => D5 display
  C = D3  => D6 Display
  D = D2  => D7 Display
  E = D6  => EN Display
  F = D7  => RS Display
  G = D8  => YLED
  H = D9  => WLED
  I = D10 => GLED
  J = D11 => RLED
  K = D12 => BLED
  L = 5V  => 5V
  M = A0  => Termistor
  N = GND => GND
  O = A1  => BTM
  P = A2  => BT+
  Q = A3  => BT-
*/
// ==== Inclusão de bibliotecas: ====
#include <LiquidCrystal.h>
#include "neotimer.h"
#include <PID_v1_bc.h>
// ==== Definição de portas: ====
const int rs = 7, en = 6, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
const int Power_Resistance = 9; //Envia sinais de controle da resistencia para aquecer a camara
#define Buzzer 8                //Ativa o buzzer em casos de emergencia 
#define Rele_Power_Circuit 10   //Liga/Desliga o circuito de controle da resistencia/buzzer/Fan
#define Security_Rele 11  //Desliga apenas a Resistencia em casos de emergência
#define Rele_Power_Fan 12       //Desliga apenas o Fan caso seja necessário
#define Termistor A0            //Faz a leitura de temperatura 
#define Door 13                 //usado para um sensor na porta
int Portas[] = {Buzzer, Rele_Power_Circuit, Security_Rele, Rele_Power_Fan, Termistor};
int Bot[] = {A1, A2, A3}; //define botões Menu / + / -
int AntBot[] = {0, LOW, LOW};
// ==== Variaveis: ====
byte Paw[] = {
  B00000,
  B00100,
  B10101,
  B10001,
  B01110,
  B11111,
  B11011,
  B00000
};
float AutoAjust[3][4] = { //configurações
  {127.0, 1.0, 5.0, 30.0},
  {255.0, 1.0, 50.0, 60.0},
  {0.0, 0.0, 0.0, 0.0}
};
double pidAjust[3][3] = {
  {30.0, 1.5, 10.0},       // Variavel
  {255.0, 255.0, 255.0}, // Limite máximo
  {0.0, 0.0, 0.0}        // Limite minimo
};
bool FanAjust[3][4] = {
  {1, 1, 0, 0}, // Variavel
  {1, 1, 1, 1}, // Limite máximo
  {0, 0, 0, 0}  // Limite minimo
};
int TempoAjust[3][4] = {
  {0, 0, 0, 0},      // Variavel
  {360, 360, 2, 30}, // Limite máximo minutos
  {0, 0, 0, 0}       // Limite minimo
};
int alarmAjust[3][3] = {
  {1, 20, 40},  // Variavel
  {1, 120, 120}, // Limite máximo
  {0, 0,  0}    // Limite minimo
};
int Position = 0;    // define a posição dos itens no menu
int C_Position = 0;    // define a posição do cursor no menu
byte menu = 0;        // define a configuração escolhida no menu
const int MaxMenuLimits[] = {110, 5, 5, 2, 3, 3, 2};
const int MinMenuLimits[] = {0, 0, 0, 0, 0, 0, 0};
int AltSetpoint = 0;  // define se está em modo de setpoint
bool Saida = LOW;     // define a saida da resistencia
bool buzzer = LOW;    // usado para inverter o estado logico da porta do buzzer
byte Icon = 0;        // faz piscar um icOneA indicando que está no modo de setpoint
byte OneA = 0;        // variavel que limita a quantidade de vezes em que um comando será exewcutado
byte OneB = 0;        // usado para resetar o contador de tempo de um botão
byte OneC = 0;        // usado para resetar o contador de tempo de um botão
byte OneD = 0;        // usado para resetar o contador de tempo de um botão
byte OneE = 0;        // usado para ativar o timer da estufa
byte OneF = 0;        // usado na segurança de leitura da estufa
byte OneG = 0;        // usado para atualizar o setpoint_Anterior
byte count0 = 0;      // contador usado no toque do alarme
byte count1 = 0;      // contador usado no botão ok
byte count2 = 0;      // contador usado no botão +
byte count3 = 0;      // contador usado no botão -
byte sequencia0 = 0;  // contador da sequencia do timer de tempo da estufa
byte Edit = 0;        // permite ou não alterar o valor de alguma configuração
float Val = 0;          // usado para alterar os valores das Configurações
// ==== Variáveis do PID ====
double setpoint;    // Temperatura desejada (por exemplo, 50 ºC)
double input;       // Temperatura atual lida do sensor
double output;      // Saída do PID (usada para controle do aquecedor)
// ==== variáveis de segurança ====
double UltimaLeitura = 0.0; //guarda a ultima leitura feita até que chegue no setpoint
const double T_Objetivo = 1.0; //define a quantidade minima que a temperatura deverá subir ou descer por um determinado periodo
const double tolerance = 5.0;  //tolerancia minima e máxima de oscilação aceitavel do termistor
const int tempAmbiente = 30;  //define a temperatura ambiente do termistor na função de segurança
double setpoint_Anterior = 0.0; //guarda o ultimo setpoint enviado para o controlador até que a temperatura chegue no novo setpoint
// ==== Parâmetros PID (ajuste necessário conforme o sistema real) ====
double Kp = 30.0;
double Ki = 1.5;
double Kd = 10.0;
const float R_SERIE = 100000.0; // Resistor de 10k em série com o NTC
const float BETA = 3950.0;     // Constante beta do NTC
const float T0 = 298.15;       // Temperatura nominal em Kelvin (25°C)
const float R0 = 100000.0;      // Resistência do NTC a 25°C (10kΩ)
const float VCC = 5.0;         // Tensão de alimentação do divisor
int RunnoutTemp = 20;  //define um valor maximo de ultrapassagem do setpoint
int TimerTemp = 30000; //define o tempo maximo de espera até que a temperatura altere
int MaxTemp = 110;     //define o valor máximo de temperatura
int MinTemp = 0;       //define o valor minimo de temperatura
int DefineTemp = 00.0;  //obtem um valor definido pelo usuário e guarda em setpoint
unsigned long Tp0 = 0; //delay de leitura e monitoramento do PID
unsigned long Tp1 = 0; //delay de atualização do display
unsigned long Tp2 = 0; //delay de animação dos icones
unsigned long Tp3 = 0; //delay de contagem dos botões
unsigned long Tp4 = 0; //delay de verificação de segurança do termistor 
// ==== Definição de bibliotecas: ====
PID meuPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
Neotimer Delay0; //delay do timer da estufa
Neotimer Delay8 = Neotimer(20000); //delay de segurança da estufa leitura temperatura
Neotimer Delay9 = Neotimer(50000); //delay de segurança da estufa sobre-temperatura
void setup() {
  delay(100);
  lcd.begin(16, 2);
  Serial.begin(9600);
  lcd.createChar(0, Paw);
  pinMode(Power_Resistance, OUTPUT);
  pinMode(Termistor, INPUT);
  pinMode(Door, INPUT);
  for (int Np = 0; Np <= 3; Np++) {
    pinMode(Portas[Np], OUTPUT);
  }
  //resistencia 5v para manter desligado
  //buzzer 0v para manter desligado
  //rele fan 5v para manter desligado
  //rele circuito 5v para manter desligado
  //rele redsistencia 5v para manter desligado
  setpoint = 00.0;                    // Define o setpoint (exemplo: 50 graus Celsius)
  meuPID.SetOutputLimits(0, 255);     // PWM 8 bits - Configura a saída máxima do PWM
  meuPID.SetMode(AUTOMATIC);          // Inicializa o PID em modo automático
  analogWrite(Power_Resistance, 0);   // Inicializa a saída do aquecedor desligada
  lcd.clear(); //limpa o display
  for (int Np = 1; Np <= 3; Np++) {
    digitalWrite(Portas[Np], HIGH);
  }
  digitalWrite(Power_Resistance, HIGH);
  lcd.setCursor(5, 0);
  lcd.print("Coffee");           // Escreve um Nome Logo
  digitalWrite(Portas[1], LOW);  // Liga o circuito de aquecimento
  digitalWrite(Portas[2], LOW);
  delay(1000);
  digitalWrite(Portas[0], HIGH); // liga o buzzer
  lcd.setCursor(3, 0);           // Move o cursor no display
  lcd.write(byte(0));            // desenha icOneAs ao lado do nome
  lcd.setCursor(12, 0);          // Move o cursor no displayc
  lcd.write(byte(0));            // desenha icOneAs ao lado do nome
  delay(500);                    // tempo em de espera com o buzzer ativo
  digitalWrite(Portas[0], LOW);  // desliga o buzzer
  delay(1500);
  digitalWrite(Portas[0], HIGH); // liga o buzzer
  delay(100);
  digitalWrite(Portas[0], LOW);  // desliga o buzzer
  delay(100);
  digitalWrite(Portas[0], HIGH); // liga o buzzer
  lcd.setCursor(0, 1);
  lcd.print(" Filament Dryer");
  delay(100);
  digitalWrite(Portas[0], LOW);  // desliga o buzzer
  delay(100);
  delay(1600);
  lcd.clear(); //limpa o display
   for (int nl = 0; nl <= 50; nl++){
    double leitura = lerTemperaturaNTC();
    input = filtrarTemperatura(leitura);
  }
    UltimaLeitura = input;
    Serial.print("primeira leitura guardada: "); Serial.println(UltimaLeitura);
}
bool Delay(unsigned long &TempoAnterior, unsigned long intervalo) { //função de delay 
 if(millis() - TempoAnterior >= intervalo) { //verifica se o tempo atual(millis()) subtraido pelo tempoAnterior é maior ou igual ao tempode intervalo  
  TempoAnterior = millis(); //guarda o tempo atual do millis() para a proxima interrupção
  return true;
 } else { return false; }
}
bool DelayS(unsigned long &TempoAnterior2, unsigned long intervalo2){
  if (millis() - TempoAnterior2 >= intervalo2){
    TempoAnterior2 = millis();
    return true;
  } else { return false; }
}
void loop() {
//  seguranca();
  Ventilacao();
  Display();
  tempo();
  //int Map = map((int)output, 0, 225, 255, 0);
  int Map = output;
  //analogWrite(Power_Resistance, Map);
  if (Map >= 0 && Map <= 127) {
    Saida = HIGH;
  } else if (Map >= 128 && Map <= 256) {
    Saida = LOW;
  }
  digitalWrite(Power_Resistance, Saida);
  if (DefineTemp <= 0) { //força o desligamento da resistencia
    digitalWrite(Portas[2], LOW);
  } else {
    digitalWrite(Portas[2], HIGH);
  }
  double leitura = lerTemperaturaNTC();
  // Tempo entre leituras (para sistemas térmicos, 1 segundo é bom)
  if (Delay(Tp0, 100)) {
    // Lê a temperatura atual (simulação)
    input = filtrarTemperatura(leitura);
    /*
      if (autoTuning) {
        if (tuner.Runtime()) {
          // AutoTune completo
          Kp = tuner.GetKp();
          Ki = tuner.GetKi();
          Kd = tuner.GetKd();
          pid.SetTunings(Kp, Ki, Kd);
          autoTuning = false;
          //Serial.println("AutoTune finalizado!");
          //Serial.print("Kp: "); Serial.println(Kp);
          //Serial.print("Ki: "); Serial.println(Ki);
          //Serial.print("Kd: "); Serial.println(Kd);
        }
      } else { */
    // PID normal
    meuPID.Compute();
    //}
    // Aplica a saída no aquecedor
    /* Exibe no monitor serial
      Serial.print("Temperatura: ");
      Serial.print(input);
      Serial.print(" °C | Setpoint: ");
      Serial.print(setpoint);
      Serial.print(" °C | Saída PWM: ");
      Serial.println(Map);*/
    if (input + tolerance >= setpoint && input - tolerance <= setpoint) {
      if (OneE == 0 && TempoAjust[0][0] > 0) {
        OneE = 2;
        sequencia0 = 1;
      }
      if (OneG == 1) {
        OneG = 0;
        setpoint_Anterior = setpoint; //define o mesmo valor do setpoint no anterior quando a leitura entra na faixa de temperatura do setpoint
        Serial.print("setpoint_anterior atualizado: "); Serial.println(setpoint_Anterior);
      }
    } else {
      OneG = 1;
    }
  }
}
void Display() {
// String AutoTune[] = {"Voltar", "Iniciar", "PWM: ", "Controle: ", "Ruido: ", "Tempo:"}; 
  if (menu == 0) { //tela principal
    if (Delay(Tp1, 100)) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Sv:");
      lcd.setCursor(3, 0);
      lcd.print(setpoint);
      lcd.setCursor(0, 1);
      lcd.print("Pv:");
      lcd.setCursor(3, 1);
      lcd.print(input);
      lcd.setCursor(9, 0);
      lcd.print("Pw:");
      lcd.setCursor(12, 0);
      lcd.print(output);
      if (AltSetpoint == 1) { //definir um novo setpoint
        if (Delay(Tp2, 500)) {
          Icon = !Icon;
        }
        if (Icon == 1) {
          lcd.setCursor(13, 1);
          lcd.write(byte(0));
          lcd.setCursor(12, 1);
          lcd.write(byte(0));
          lcd.setCursor(11, 1);
          lcd.write(byte(0));
        }
      } else {
        if (Delay(Tp2, 500)) {
          Icon++;
          if (Icon == 3) {
            Icon = 0;
          }
        }
        lcd.setCursor(11 + Icon, 1);
        lcd.write(byte(0));
      }
    }
  } else if (menu == 1) { // menu de seleção
    String MenuI[] = {"Voltar", "Resfriar", "AutoTune", "PID", "Cooler", "Tempo", "Seguranca"};
    lcd.setCursor(2, 0); //define a primeira linha
    lcd.print(MenuI[Position]);  //imprime uma configuração na primeira linha
    lcd.setCursor(2, 1); //define a segunda linha
    lcd.print(MenuI[Position + 1]);  //imprime uma configuração na segunda linha
    lcd.setCursor(0, C_Position);
    lcd.write(byte(0));              //imprime a patinha do cursor
  } else if (menu == 2) { //iniciar AutoTune
    menu = 1;
  } else if (menu == 3) { // configuração de PID
   String PID_I[] = {"Voltar", "Kp: ", "Ki: ", "Kd: "};
    lcd.setCursor(2, 0);             //define a primeira linha
    lcd.print(PID_I[Position]);      //imprime uma configuração na primeira linha
    lcd.setCursor(2, 1);             //define a segunda linha
    lcd.print(PID_I[Position + 1]);  //imprime uma configuração na segunda linha
    if (Position == 0) {
      lcd.setCursor(5, 1);
      lcd.print(pidAjust[0][Position]);
    } else {
      lcd.setCursor(5, 0);
      lcd.print(pidAjust[0][Position - 1]);
      lcd.setCursor(5, 1);
      lcd.print(pidAjust[0][Position]);
    }
    lcd.setCursor(0, C_Position);    //move o cursor
    lcd.write(byte(0));
    if (Edit == 1) {
      if (OneA == 1) {
        Serial.println("Modo de edição ativo");
        int Np = Position + C_Position; //guarda a posição do cursor para obter um valor do PID
        pidAjust[0][0] = meuPID.GetKp();
        pidAjust[0][1] = meuPID.GetKi();
        pidAjust[0][2] = meuPID.GetKd();
        if (Position == 0 && C_Position == 1) {
          Val = pidAjust[0][Position];        //Obtém um valor de configuração do PID
        } else {
          Val = pidAjust[0][Position - 1 + C_Position];
        }
        // Val = pidAjust[0][Np];             //Obtém um valor de configuração do PID
        OneA = 0;                        //evita com que o valor seja resetado
      }
      if (Position == 0 && C_Position == 0) { //volta para o menu de configgurações
        Edit = 0;       //desativa o modo de edição
        Position = 0;   //reseta a posição dos icOneAs
        C_Position = 0; //reseta a posição do cursor
        menu = 1;       //retorna para o menu principal
        lcd.clear();    //apaga os icOneAs da configuração atual
      } else if (Position == 0 && C_Position == 1) {
        if (Val > pidAjust[1][Position]) { //verifica se é maior que o limite máximo
          Val = pidAjust[1][Position];
        } else if (Val < pidAjust[2][Position]) { //verifica se é menor que o limite minimo
          Val = pidAjust[2][Position];
        }
        pidAjust[0][Position] = Val;
      } else {
        if (Val > pidAjust[1][Position - 1 + C_Position]) { //verifica se é maior que o limite máximo
          Val = pidAjust[1][Position - 1 + C_Position];
        } else if (Val < pidAjust[2][Position - 1 + C_Position]) { //verifica se é menor que o limite minimo
          Val = pidAjust[2][Position - 1 + C_Position];
        }
        pidAjust[0][Position - 1 + C_Position] = Val;
      }
      meuPID.SetTunings(pidAjust[0][0], pidAjust[0][1], pidAjust[0][2]);
    }
  } else if (menu == 4) { // configuração de refrigeração
     String Refrigeration[] = {"Voltar", "Fan: ", "Porta: ", "SincR: ", "!SincR: "};
    String config[] = {"", "", "", ""}; //Guarda a configuração escrita
    for (int Nc = 0; Nc <= 3; Nc++) {
      if (FanAjust[0][Nc] == 1) {
        config[Nc] = "True";
      } else {
        config[Nc] = "False";
      }
    }
    lcd.setCursor(2, 0); //define a primeira linha
    lcd.print(Refrigeration[Position]);  //imprime uma configuração na primeira linha
    lcd.setCursor(2, 1); //define a segunda linha
    lcd.print(Refrigeration[Position + 1]);  //imprime uma configuração na segunda linha
    if (Position == 0) {
      lcd.setCursor(11, 1);
      lcd.print(config[Position]);
    } else {
      lcd.setCursor(11, 0);
      lcd.print(config[Position - 1]);
      lcd.setCursor(11, 1);
      lcd.print(config[Position]);
    }
    lcd.setCursor(0, C_Position);
    lcd.write(byte(0));
    if (Edit == 1) {
      if (OneA == 1) {
        Serial.println("Modo de edição ativo");
        OneA = 0;        //evita com que o valor seja resetado
      }
      if (Position == 0 && C_Position == 0) { //retorno ao menun principal
        Edit = 0;       //desativa o modo de edição
        Position = 0;   //reseta a posição dos icOneAs
        C_Position = 0; //reseta a posição do cursor
        menu = 1;       //retorna para o menu principal
        lcd.clear();    //apaga os icOneAs da configuração atual
      } else if (Position == 0 && C_Position == 1) {
        FanAjust[0][Position] = !FanAjust[0][Position];
      } else {
        FanAjust[0][Position + C_Position - 1] = !FanAjust[0][Position + C_Position - 1];
      }
      lcd.clear(); //limpa o display
      Edit = 0;   //desativa o modo de edição
    }
  } else if (menu == 5) { // configuração de tempo
     String Tempo[] = {"Voltar", "TR-Ativo: ", "TF-Ativo: ", "U.P.Ativo: ", "FinalTempo: "};
    lcd.setCursor(2, 0); //define a primeira linha
    lcd.print(Tempo[Position]);  //imprime uma configuração na primeira linha
    lcd.setCursor(2, 1); //define a segunda linha
    lcd.print(Tempo[Position + 1]);  //imprime uma configuração na segunda linha
    lcd.setCursor(0, C_Position);
    lcd.write(byte(0));
    String perifericos[] = {"NDA", "RST", "FAN"};
    if (Position == 0) {
      lcd.setCursor(13, 1);
      lcd.print(TempoAjust[0][Position]);
    } else if (Position == 2) {
      lcd.setCursor(13, 0);
      lcd.print(TempoAjust[0][Position - 1]);
      lcd.setCursor(13, 1);
      lcd.print(perifericos[TempoAjust[0][2]]);
    } else if (Position == 3) {
      lcd.setCursor(13, 0);
      lcd.print(perifericos[TempoAjust[0][2]]);
      lcd.setCursor(13, 1);
      lcd.print(TempoAjust[0][Position]);
    } else {
      lcd.setCursor(13, 0);
      lcd.print(TempoAjust[0][Position - 1]);
      lcd.setCursor(13, 1);
      lcd.print(TempoAjust[0][Position]);
    }
    if (Edit == 1) {
      if (OneA == 1) {
        Serial.println("Modo de edição ativo");
        if (Position == 0 && C_Position == 1) {
          Val = TempoAjust[0][Position];        //Obtém um valor de configuração do PID
        } else {
          Val = TempoAjust[0][Position - 1 + C_Position];
        }
        lcd.clear();    //apaga os icones da configuração atual
        OneA = 0;       //evita com que o valor seja resetado
      }
      if (Position == 0 && C_Position == 0) { //retorno ao menun principal
        Edit = 0;       //desativa o modo de edição
        Position = 0;   //reseta a posição dos icones
        C_Position = 0; //reseta a posição do cursor
        menu = 1;       //retorna para o menu principal
        lcd.clear();    //apaga os icones da configuração atual
      }  else if (Position == 0 && C_Position == 1) {
        if (Val > TempoAjust[1][Position]) { //verifica se é maior que o limite máximo
          Val = TempoAjust[1][Position];
        } else if (Val < TempoAjust[2][Position]) { //verifica se é menor que o limite minimo
          Val = TempoAjust[2][Position];
        }
        TempoAjust[0][Position] = Val;
      } else {
        if (Val > TempoAjust[1][Position - 1 + C_Position]) { //verifica se é maior que o limite máximo
          Val = TempoAjust[1][Position - 1 + C_Position];
        } else if (Val < TempoAjust[2][Position - 1 + C_Position]) { //verifica se é menor que o limite minimo
          Val = TempoAjust[2][Position - 1 + C_Position];
        }
        TempoAjust[0][Position - 1 + C_Position] = Val;
      }
      TempoAjust[0][1] = TempoAjust[0][0];
      TempoAjust[0][0] = TempoAjust[0][1];
    }
  } else if (menu == 6) { // configuração de alarme
    String config[] = {"False", "True"}; //definição se o alarme está ativo ou não (não desativa a segurança do rojeto)
     String Seguranca[] = {"Voltar", "Alarme: ", "T.SobTemp: ", "T.LeiTemp: "};
    lcd.setCursor(2, 0); //define a primeira linha
    lcd.print(Seguranca[Position]);  //imprime uma configuração na primeira linha
    lcd.setCursor(2, 1); //define a segunda linha
    lcd.print(Seguranca[Position + 1]);  //imprime uma configuração na segunda linha
    if (Position == 0) {
      lcd.setCursor(10, 1);
      lcd.print(config[alarmAjust[0][0]]);
    } else if (Position == 1) {
      lcd.setCursor(10, 0);
      lcd.print(config[alarmAjust[0][0]]);
      lcd.setCursor(13, 1);
      lcd.print(alarmAjust[0][Position]);
    } else {
      lcd.setCursor(13, 0);
      lcd.print(alarmAjust[0][Position - 1]);
      lcd.setCursor(13, 1);
      lcd.print(alarmAjust[0][Position]);
    }
    lcd.setCursor(0, C_Position);
    lcd.write(byte(0));
    if (Edit == 1) {
      if (OneA == 2) {
        Serial.println("Modo de edição ativo");
        if (Position == 0 && C_Position == 1) {
          Val = alarmAjust[0][Position];        //Obtém um valor de configuração do alarme
        } else {
          Val = alarmAjust[0][Position - 1 + C_Position];
        }
        lcd.clear();    //apaga os icones da configuração atual
        OneA = 1;       //evita com que o valor seja resetado
      }
      if (Position == 0 && C_Position == 0) { //retorno ao menun principal
        Edit = 0;       //desativa o modo de edição
        Position = 0;   //reseta a posição dos icones
        C_Position = 0; //reseta a posição do cursor
        menu = 1;       //retorna para o menu principal
        lcd.clear();    //apaga os icones da configuração atual
      }  else if (Position == 0 && C_Position == 1) {
        if (Val > alarmAjust[1][Position]) { //verifica se é maior que o limite máximo
          Val = alarmAjust[1][Position];
        } else if (Val < alarmAjust[2][Position]) { //verifica se é menor que o limite minimo
          Val = alarmAjust[2][Position];
        }
        alarmAjust[0][Position] = Val;
      } else {
        if (Val > alarmAjust[1][Position - 1 + C_Position]) { //verifica se é maior que o limite máximo
          Val = alarmAjust[1][Position - 1 + C_Position];
        } else if (Val < alarmAjust[2][Position - 1 + C_Position]) { //verifica se é menor que o limite minimo
          Val = alarmAjust[2][Position - 1 + C_Position];
        }
        alarmAjust[0][Position - 1 + C_Position] = Val;
      }
    } else {
      if (OneA == 1) {
        unsigned long valor = alarmAjust[0][2] * 1000; //multiplica 1 segundo pelo valor escolhido em T.LeiTemp
        Delay8.set(valor); //define o tempo de leitura da temperatura em Delay8
        Delay8.repeatReset();
        Serial.print("tempo leitura atualizado: ");
        Serial.println(valor);
        valor = alarmAjust[0][1] * 1000; //multiplica 1 segundo pelo valor escolhido em T.SobTemp
        Delay9.set(valor); //define o tempo da sobre-temperatura em Delay9
        Delay9.repeatReset();
        Serial.print("tempo sobre-temperatirura atualizado: ");
        Serial.println(valor);
        OneA = 0;
      }
    }
  }
  if (digitalRead(Bot[0]) == HIGH && AntBot[0] <= 1000) {     //ajustar/confirmar/menu
    if (Delay(Tp3, 100) && count1 < 22) {    //repetição de 1 milisegundo para contagem do botão
      count1++;
      AntBot[0] = AntBot[0] + 50;
      // Serial.print("AumentoA: ");
      // Serial.println(AntBot[0]);
    }
  } else if (AntBot[0] > 50 && AntBot[0] > 1000) { //opção caso o botão seja pressionado por mais de 1 segundo
    if (menu == 0) {
      AntBot[0] = 0; //reseta a contagem de tempo do botão pressionado
      lcd.clear();   //limpa o display para exibir uma nova tela
      menu = 1;      //seleciona o menu de configurações
    } else {
      AntBot[0] = 0; //reseta a contagem de tempo do botão pressionado
      lcd.clear();
    }
    Serial.println("OK Menu");
    OneB = 1;
  } else if (AntBot[0] > 50 && AntBot[0] <= 1000) { //opção caso o botão seja apertado por menos de 1 segundo
    if (menu == 0) {
      if (AltSetpoint == 1) { //verifica o estado de AltSetpoint para garantir a escolha do setpoint correto
        setpoint = DefineTemp;
      }
      AltSetpoint = !AltSetpoint; //altera o estado de Altsetpoint para poder alterar o valor de setpoint principal
    } else if (menu == 1) { //menu de configurações
      if (Position + C_Position == 1) { //Função resfriar
        DefineTemp = 0;
        setpoint = DefineTemp;
        lcd.clear();
        menu = 0;
      } else { //seleção de configuração
        menu = Position + C_Position;
        Position = 0;
        C_Position = 0;
        lcd.clear();
      }
    } else if (menu == 3) { //menu ajuste PID
      Edit = !Edit;
      OneA = 1;
    } else if (menu == 4) { //menu ventilação
      Edit = 1;
    } else if (menu == 5) { //menu de Tempo
      Edit = !Edit;
      OneA = 1;
    } else if (menu == 6) { //menu alarmes
      Edit = !Edit;
      OneA = 2;
    }
    AntBot[0] = 0; //reseta a contagem de tempo do botão pressionado
    OneB = 1;
  } else {
    if (OneB == 1) {
      count1 = 0; // reseta a contagem do botão 0 - 22
      AntBot[0] = 0; //reseta a contagem de tempo do botão pressionado
      OneB = 0;
    }
  }
  if (digitalRead(Bot[1]) == HIGH && AntBot[1] <= 1000) {     //botão +
    if (Delay(Tp3, 100) && count2 < 22) {    //repetição de 1 milisegundo para contagem do botão
      count2++;
      AntBot[1] = AntBot[1] + 50;
      // Serial.print("AumentoB: ");
      // Serial.println(AntBot[1]);
    }
  } else if (AntBot[1] > 50 && AntBot[1] > 1000 && digitalRead(Bot[1]) == HIGH) { //opção caso o botão seja pressionado por mais de 1 segundo
    if (menu == 3) {
      Val = Val + 0.10;
    } else {
      Val = Val + 1;
    }
    OneC = 1; //usado para resetar AntBot[1] e o Delay4
    Serial.println("OK avanço");
  }  else if (AntBot[1] > 50 && AntBot[1] <= 1000) { //opção caso o botão seja apertado por menos de 1 segundo
    if (menu == 0 && AltSetpoint == 1) {              //verifica se está na tela inicial
      DefineTemp = DefineTemp + 5; //Aumenta o Valor de setpoint
      if (DefineTemp > MaxTemp) {  //limita o aumento de setpoint maximo
        DefineTemp = MaxTemp;
      }
      setpoint = DefineTemp;
    } else if (menu == 1) { // configurações do menu
      if (Edit == 1) {
        Val = Val + 0.10;
        Serial.print("Valor aumentado: "); Serial.println(Val);
      } else {
        C_Position--;        //diminui a posição do cursor até o limite do display
        if (C_Position < 0) { //verifica se o cursor ultrapassou o limite
          C_Position = 0;    //retorna o cursor para o seu limite minimo
          Position--;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position <= MinMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é menor que o limite
          Position = MinMenuLimits[menu];     //define o valor minimo aceitavel pelo limite de icOneAs do menu
        }
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
        //lcd.clear();
      }
    } else if (menu == 3) { //configuração
      if (Edit == 1) {
        Val = Val + 0.10;
        Serial.print("Valor aumentado: "); Serial.println(Val);
      } else {
        C_Position--;        //diminui a posição do cursor até o limite do display
        if (C_Position < 0) { //verifica se o cursor ultrapassou o limite
          C_Position = 0;    //retorna o cursor para o seu limite minimo
          Position--;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position <= MinMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é menor que o limite
          Position = MinMenuLimits[menu];     //define o valor minimo aceitavel pelo limite de icOneAs do menu
        }
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
        //lcd.clear();
      }
    } else if (menu == 4) { //configurações do menu
      C_Position--;        //diminui a posição do cursor até o limite do display
      if (C_Position < 0) { //verifica se o cursor ultrapassou o limite
        C_Position = 0;    //retorna o cursor para o seu limite minimo
        Position--;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
      }
      if (Position <= MinMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é menor que o limite
        Position = MinMenuLimits[menu];     //define o valor minimo aceitavel pelo limite de icOneAs do menu
      }
      Serial.print("posição: ");
      Serial.println(Position);
      Serial.print("Cursor: ");
      Serial.println(C_Position);
      //lcd.clear();
    } else if (menu == 5) {
      if (Edit == 1) {
        Val = Val + 1;
        Serial.print("Valor aumentado: "); Serial.println(Val);
      } else {
        C_Position--;        //diminui a posição do cursor até o limite do display
        if (C_Position < 0) { //verifica se o cursor ultrapassou o limite
          C_Position = 0;    //retorna o cursor para o seu limite minimo
          Position--;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position <= MinMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é menor que o limite
          Position = MinMenuLimits[menu];     //define o valor minimo aceitavel pelo limite de icOneAs do menu
        }
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
        //lcd.clear();
      }
    } else if (menu == 6) {
      if (Edit == 1) {
        Val = Val + 1;
        Serial.print("Valor aumentado: "); Serial.println(Val);
      } else {
        C_Position--;        //diminui a posição do cursor até o limite do display
        if (C_Position < 0) { //verifica se o cursor ultrapassou o limite
          C_Position = 0;    //retorna o cursor para o seu limite minimo
          Position--;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position <= MinMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é menor que o limite
          Position = MinMenuLimits[menu];     //define o valor minimo aceitavel pelo limite de icOneAs do menu
        }
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
        //lcd.clear();
      }
    }
    lcd.clear();
    OneC = 1; 
    AntBot[1] = 0; //reseta a contagem de tempo do botão pressionado
  } else {
    if (OneC == 1) {
      count2 = 0; // reseta a contagem do botão 0 - 22
      AntBot[1] = 0; //reseta a contagem de tempo do botão pressionado
      OneC = 0;
    }
  }
  if (digitalRead(Bot[2]) == HIGH && AntBot[2] <= 1000) {     //botão -
    if (Delay(Tp3, 100) && count3 < 22) {    //repetição de 1 milisegundo para contagem do botão
      count3++;
      AntBot[2] = AntBot[2] + 50;
      // Serial.print("AumentoC: ");
      // Serial.println(AntBot[2]);
    }
  } else if (AntBot[2] > 50 && AntBot[2] > 1000 && digitalRead(Bot[2]) == HIGH) { //opção caso o botão seja pressionado por mais de 1 segundo
    if (menu == 3) {
      Val = Val - 0.10;
    } else {
      Val = Val - 1;
    }
    OneD = 1;
    Serial.println("OK Retorno");
  } else if (AntBot[2] > 50 && AntBot[2] <= 1000) { //opção caso o botão seja apertado por menos de 1 segundo
    if (menu == 0 && AltSetpoint == 1) {              //verifica se está na tela inicial
      DefineTemp = DefineTemp - 5; //Diminui o valor de setpoint
      if (DefineTemp < MinTemp) {  //limita a diminuição do setpoint Minimo
        DefineTemp = MinTemp;
      }
      setpoint = DefineTemp;
    } else if (menu == 1) {
      if (Edit == 1) {
        Val = Val - 0.10;
        Serial.print("Valor diminuido: "); Serial.println(Val);
      } else {
        C_Position++;
        if (C_Position > 1) { //verifica se o cursor ultrapassou o limite
          C_Position = 1;    //retorna o cursor para o seu limite minimo
          Position++;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position > MaxMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é maior que o limite aceitavel
          Position = MaxMenuLimits[menu];     //define o valor maximo aceitavel pelo limite de icOneAs do menu
        }
        //lcd.clear();
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
      }
    } else if (menu == 3) {
      if (Edit == 1) {
        Val = Val - 0.10;
        Serial.print("Valor diminuido: "); Serial.println(Val);
      } else {
        C_Position++;
        if (C_Position > 1) { //verifica se o cursor ultrapassou o limite
          C_Position = 1;    //retorna o cursor para o seu limite minimo
          Position++;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position > MaxMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é maior que o limite aceitavel
          Position = MaxMenuLimits[menu];     //define o valor maximo aceitavel pelo limite de icOneAs do menu
        }
        //lcd.clear();
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
      }
    } else if (menu == 4) {
      C_Position++;
      if (C_Position > 1) { //verifica se o cursor ultrapassou o limite
        C_Position = 1;    //retorna o cursor para o seu limite minimo
        Position++;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
      }
      if (Position > MaxMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é maior que o limite aceitavel
        Position = MaxMenuLimits[menu];     //define o valor maximo aceitavel pelo limite de icOneAs do menu
      }
      //lcd.clear();
      Serial.print("posição: ");
      Serial.println(Position);
      Serial.print("Cursor: ");
      Serial.println(C_Position);
    } else if (menu == 5) {
      if (Edit == 1) {
        Val = Val - 1;
        Serial.print("Valor diminuido: "); Serial.println(Val);
      } else {
        C_Position++;
        if (C_Position > 1) { //verifica se o cursor ultrapassou o limite
          C_Position = 1;    //retorna o cursor para o seu limite minimo
          Position++;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position > MaxMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é maior que o limite aceitavel
          Position = MaxMenuLimits[menu];     //define o valor maximo aceitavel pelo limite de icOneAs do menu
        }
        //lcd.clear();
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
      }
    } else if (menu == 6) {
      if (Edit == 1) {
        Val = Val - 1;
        Serial.print("Valor diminuido: "); Serial.println(Val);
      } else {
        C_Position++;
        if (C_Position > 1) { //verifica se o cursor ultrapassou o limite
          C_Position = 1;    //retorna o cursor para o seu limite minimo
          Position++;        //diminui valor de position para alterar a posição dos icOneAs (Sobe eles no display)
        }
        if (Position > MaxMenuLimits[menu]) { //verifica se o valor de posição dos itens de menu é maior que o limite aceitavel
          Position = MaxMenuLimits[menu];     //define o valor maximo aceitavel pelo limite de icOneAs do menu
        }
        //lcd.clear();
        Serial.print("posição: ");
        Serial.println(Position);
        Serial.print("Cursor: ");
        Serial.println(C_Position);
      }
    }
    lcd.clear();
    OneD = 1;
    AntBot[2] = 0; //reseta a contagem de tempo do botão pressionado
  } else {
    if (OneD == 1) {
      count3 = 0; // reseta a contagem do botão 0 - 22
      AntBot[2] = 0; //reseta a contagem de tempo do botão pressionado
      OneD = 0;
    }
  }
}
double lerTemperaturaNTC() {
  int leitura = analogRead(Termistor);
  // Converte leitura (0–1023) para tensão
  float Vout = leitura * VCC / 1023.0;
  // Calcula resistência do termistor (NTC)
  float R_NTC = (VCC * R_SERIE / Vout) - R_SERIE;
  // Aplica a equação simplificada de Steinhart-Hart
  float temperaturaK = 1.0 / (1.0 / T0 + (1.0 / BETA) * log(R_NTC / R0));
  float temperaturaC = temperaturaK - 273.15;
  return temperaturaC;
}
double filtrarTemperatura(double leitura) {
  static double tempFiltrada = 0;  
  const double alpha = 0.1; // fator de suavização (0.0 a 1.0)
  
  tempFiltrada = alpha * leitura + (1 - alpha) * tempFiltrada;
  return tempFiltrada;
}
void Autotune() {
}
void Ventilacao() {
  /* modo 1 inicia habilita o ventilador
     modo 2 define se o ventilador será desligado ou não quando a porta é aberta
     modo 3 define se o ventilador ficará sincronizado com a resistencia (liga quando a resistencia tbm liga)
     modo 4 define se a sincronização será invertida (modo 3 precisa está habilitado para funcionar)
  */
  if (FanAjust[0][0] == 1 && FanAjust[0][1] == 1) { //verifica se a ventilação está habilitada em conjunto com a porta
    if (digitalRead(Door) == HIGH) {//verifica se a porta está aberta
      digitalWrite(Rele_Power_Fan, HIGH); //força o desligamento do ventilador
    } else {
      //executar comandos padrões
      if (FanAjust[0][2] == 1) { //verifica se a sincronização com a resistencia está ativa
        if (FanAjust[0][3] == 1) {//verifica se a sincronização invertida está habilitada
          if (Saida == HIGH) {
            digitalWrite(Rele_Power_Fan, LOW); //Liga o ventilador
          } else {
            digitalWrite(Rele_Power_Fan, HIGH); //Desliga o ventilador
          }
        } else {
          if (Saida == HIGH) {
            digitalWrite(Rele_Power_Fan, HIGH); //Desliga o ventilador
          } else {
            digitalWrite(Rele_Power_Fan, LOW); //Liga o ventilador
          }
        }
      } else {
        if (input + 3 >= setpoint && input - 3 <= setpoint) { //inicia a ventilação apenas ao chegar próximo do setpoint
          digitalWrite(Rele_Power_Fan, LOW); //Liga o ventilador
        } else {
          digitalWrite(Rele_Power_Fan, HIGH); //desliga o ventilador
        }
      }
    }
  } else if (FanAjust[0][0] == 1) { //verifica se apenas o ventilador está ativo
    //executar comandos padrões
    if (FanAjust[0][2] == 1) { //verifica se a sincronização com a resistencia está ativa
      if (FanAjust[0][3] == 1) {//verifica se a sincronização invertida está habilitada
        if (Saida == HIGH) {
          digitalWrite(Rele_Power_Fan, LOW); //Liga o ventilador
        } else {
          digitalWrite(Rele_Power_Fan, HIGH); //Desliga o ventilador
        }
      } else {
        if (Saida == HIGH) {
          digitalWrite(Rele_Power_Fan, HIGH); //Desliga o ventilador
        } else {
          digitalWrite(Rele_Power_Fan, LOW); //Liga o ventilador
        }
      }
    } else {
      if (input + 3 >= setpoint && input - 3 <= setpoint) { //inicia a ventilação apenas ao chegar próximo do setpoint
        digitalWrite(Rele_Power_Fan, LOW); //Liga o ventilador
      } else {
        digitalWrite(Rele_Power_Fan, HIGH); //desliga o ventilador
      }
    }
  } else {
    digitalWrite(Rele_Power_Fan, HIGH); //desliga o ventilador
  }
}
void tempo() {
  /* modo 1 define quanto tempo ficará ligado a resistencia
     modo 2 define quanto tempo ficará ligado o ventilador
     modo 3 define qual periferico (ventilador ou resistencia) ficará ligado por ultimo
     modo 4 define o tempo em que o ultimo periférico ficará ligado
  */
  if (sequencia0 == 1) {
    if (OneE == 2) { //verifica se a temperatura chegou no ponto determinado
      if (TempoAjust[0][0] > 0) { //verifica se um novo timer foi definido
        unsigned long valor = TempoAjust[0][0] * 60000; //multiplica 1 minuto pelo valor de TempoAjust[][] e guarda em "valor"
        Delay0.set(valor); //define o tempo
        OneE--; //diminui o valor de one para evitar repetição de comandos
        Serial.println("sequencia de tempo iniciada");
      }
    }
    if (OneE == 1) {
      if (Delay0.repeat()) { //finaliza a contagem de tempo da estufa e mantêm ligado um ultimo periferico caso habilitado
        if (TempoAjust[0][2] == 1) { //verifica se a resistencia ficará ativa por ultimo
          FanAjust[0][0] = 0; //desliga o ventilador para que a resistencia continue funcionando
          sequencia0 = 3;
          Serial.print("resistencia selecionado");
        } else if (TempoAjust[0][2] == 2) { //verifica se o ventilador ficará ativo por ultimo
          sequencia0 = 3;
          FanAjust[0][0] = 1; //habilita o ventilador
          FanAjust[0][2] = 1; //ativa a sincronização com a resistencia
          FanAjust[0][3] = 1; //inverte a sincronização com a resistencia para que o ventilador fique ligado]
          DefineTemp = 0; //reseta a variavel controlada pelo usuário
          setpoint = 0; //define 0 para desligar o aquecimento da resistencia
          Serial.print("ventilador selecionado");
        } else {
          Serial.print("nenhum periférico selecionado");
          Delay0.repeatReset(); //reseta o contador de tempo
          Delay0.set(0); //reseta o tempo de contagem para 0
          sequencia0 = 2;
        }
        if (TempoAjust[0][2] > 0 && TempoAjust[0][3] == 0) {
          TempoAjust[0][3] = 1; //define 1 mminuto caso usuário tenha esquecido de definir um tempo
        }
        Serial.println(" tempo finalizado!");
        OneE = 3;
      }
    }
  } else if (sequencia0 == 2) {// verifica se o tempo de funcionamento da estufa esgotou
    if (Delay0.repeat(8) && alarmAjust[0][0] == 1) { //toca o alarme 4 vezes caso o alarme esteja habilitado
      count0++; //aumenta a contagem para finalizar a sequencia do timer
      Delay0.set(500);  //define o tempo do delay para 500 milisegundos
      buzzer = !buzzer; //altera o estado lógico  da porta do buzzer
      digitalWrite(Buzzer, buzzer); //inverte o estado da porta do buzzer 8 vezes
    } else if (alarmAjust[0][0] == 0) { //caso o alarme esteja dsabilitado
      count0 = 0; //reseta a contagem da animação do buzzer
      Delay0.repeatReset(); //reseta o delay7
      Delay0.set(0); //define
      sequencia0 = 0;
      OneE = 0;
      TempoAjust[0][0] = 0; //reseta o multiplicador de minutos
      DefineTemp = 0; //reseta a variavel controlada pelo usuário
      setpoint = 0; //define 0 para desligar o aquecimento da resistencia
      FanAjust[0][0] = 1; //mantém o ventilador habilitado
      FanAjust[0][2] = 0; //desliga a sincronização com a resistencia (caso esteja ativa)
      FanAjust[0][3] = 0; //desliga a inversão da sincronização com a resistencia (caso esteja ativa)
    }
    if (count0 >= 8) {
      digitalWrite(Buzzer, LOW); //desliga o buzzer
      count0 = 0; //reseta a contagem da animação do buzzer
      Delay0.repeatReset(); //reseta o delay7
      Delay0.set(0); //define
      sequencia0 = 0;
      OneE = 0;
      TempoAjust[0][0] = 0; //reseta o multiplicador de minutos
      DefineTemp = 0; //reseta a variavel controlada pelo usuário
      setpoint = 0; //define 0 para desligar o aquecimento da resistencia
      FanAjust[0][0] = 1; //mantém o ventilador habilitado
      FanAjust[0][2] = 0; //desliga a sincronização com a resistencia (caso esteja ativa)
      FanAjust[0][3] = 0; //desliga a inversão da sincronização com a resistencia (caso esteja ativa)
      Serial.print("Fim do timner");
    }
  } else if (sequencia0 == 3) { //caso algum outro periferico continue ativo no fim do tempo
    if (OneE == 3) {
      unsigned long int valor = TempoAjust[0][3] * 60000; //multiplica o tempo final em minutos pelo valor nescolhido ekjm TempoAjust
      Delay0.set(valor); //define esse valor no contador de tempo final da estufa
      Delay0.repeatReset(); //reseta o contador para executar a nova contagem
      OneE--; //reseta OneE para evitar repetição de comandos
    }
    if (Delay0.repeat()) {
      count0 = 0;           //define 0 em count0 para iniciar o larme de aviso (se habilitado)
      Delay0.set(0);        //zera o tempo do contador
      Delay0.repeatReset(); //reseta o contador
      sequencia0 = 2;       //define 2 em sequencia0 para tocar o alarme de aviso do timer
    }
  }
}
void seguranca() {
  /* modo 1 habilita ou não o som de alarme para quaisquer função
     modo 2 define o tempo de leitura caso a resistencia ultrapasse a temperatura definida pelo usuário
     modo 3 define o tempo de leitura padrão caso a temperatura não aumente
  */
  bool security = true;
  /* haverá 2 setpoint's onde há o principal e o ultimo setpoint definido
     se a temperatura está abaixo de 30 e se o setpoint é igual a zero (nada deverá acontecer)
     se a temperatura está acima dos 30 se o setpoint é igual a zero (verificar se a temperatura está descendo)
     se a temperatura está abaixo dos 30 e o setpoint é maior que 30 (verificar se a temperatura sobe)
     se a temperatura está muito acima do setpoint (verificar se é menor ou igua ao setpoint_anterior) {
      verificar se a temperatura está descendo
     }
     se o setpoint_Atual for maior que setpoint_Anterior onde ambos são maiores que 30 (verificar se a temperatura sobe, quando a temperatura chegar no ponto o setpoint anterior será igual ao atual)
     se o setpoint_Atual for menor que o setpoint_Anterior ondem ambos ainda são maores que 30 (verificar se a temperatura está descendo)
     se o setpoint_Atual for igual ao setpoint_Anterior onde ambos são maior que 30 (verificar se a temperatura não está fora da tolerancia permitida);
  */
  if (setpoint < tempAmbiente && setpoint_Anterior < tempAmbiente && input > tempAmbiente) { //verificação de inicialização de programa
    security &= tempAcima(input, setpoint); //verifica se a temperatura está descendo
  } else if (setpoint > tempAmbiente && setpoint_Anterior < tempAmbiente && input > setpoint) { //verifica o primeiro setpoint em caso de reset de programa
    security &= tempAcima(input, setpoint); //verifica se a temperatura está descendo
  } else if (setpoint > tempAmbiente && setpoint_Anterior < tempAmbiente && input < setpoint) { //verifica o primeiro setpoint em condição inicial
    security &= tempAbaixo(input, setpoint); //verifica se a temperatura está subindo
  } else if (setpoint > tempAmbiente && setpoint_Anterior < setpoint && (input >= setpoint_Anterior + tolerance || input >= setpoint_Anterior - 2 * tolerance) ) {
    //verifica se o setpoint foi aumentado para uma temperatura maior que a anterior
    security &= tempAbaixo(input, setpoint); //verifica se a temperatura está subindo
  } else if (setpoint > tempAmbiente && setpoint_Anterior > setpoint && (input <= setpoint_Anterior + 2 * tolerance || input >= setpoint_Anterior - tolerance || input < setpoint_Anterior - tolerance)) {
    //verifica se o setpoint foi diminuido para um temperatura menor que a anterior
    security &= tempAcima(input, setpoint); //verifica se a temperatura está descendo
  } else if (setpoint < tempAmbiente && setpoint_Anterior > tempAmbiente && input > tempAmbiente && (input <= setpoint_Anterior + 2 * tolerance || input >= setpoint_Anterior - tolerance || input < setpoint_Anterior - tolerance)) {
    //verifica se o setpoint foi desligado
    security &= tempAcima(input, setpoint); //verifica se a temperatura está descendo
  } else if (setpoint > tempAmbiente && setpoint_Anterior > tempAmbiente && (setpoint_Anterior <= setpoint + tolerance && setpoint_Anterior >= setpoint - tolerance)) {
    security &= tempSetpoint(input, setpoint); //verifica se a temperatura está dentre os limites
  } else { //se nenhuma das condições acima estiver de acordo, todo o programa deverá parar
    
  }
  if (!security) { //verifica se security não foi afetado (false)
    digitalWrite(Buzzer, HIGH); //ativa o alarme
    DefineTemp = 0; //reseta a variavel controlada pelo usuário
    setpoint = 0; //define 0 para desligar o aquecimento da resistencia
    digitalWrite(Power_Resistance, HIGH); //desliga a resistencia digitalmente
    digitalWrite(Security_Rele, LOW); //Liga o relé que desliga a resistencia
    FanAjust[0][0] = 1; //habilita o ventilador
    FanAjust[0][2] = 1; //ativa a sincronização com a resistencia
    FanAjust[0][3] = 1; //inverte a sincronização com a resistencia para que o ventilador fique ligado
    while (1) {} //trava o programa
  }
}
bool tempAbaixo(double input, double setpoint) {
  // Só verifica se a temperatura está subindo
  if (input < setpoint - tolerance ) {
    if (Delay8.repeat()) {
      if (input < UltimaLeitura + T_Objetivo) {
        Serial.println("ERRO: Temperatura não está subindo!");
        return false; // Falha → desliga tudo
      }
      // Atualiza referência
      UltimaLeitura = input;
    }
  } else {return true;} //ok
}
bool tempAcima(double input, double setpoint) {
  // Só verifica se a temperatura está diminuindo
  if (input > setpoint + tolerance) {
    if (Delay) {
      if (input > UltimaLeitura - T_Objetivo) {
        Serial.println("ERRO: Temperatura não está Descendo!");
        return false; // Falha → desliga tudo
      }
      // Atualiza referência
      UltimaLeitura = input;
    }
  } else { return true; } //ok
}
// =============================
// Supervisão 2: Faixa de tolerância
// =============================
bool tempSetpoint(double input, double setpoint) {
  unsigned long int valor;
  if (input > setpoint + tolerance + 1 || input < setpoint - tolerance - 1) {
  if (input > setpoint + tolerance + 1) {
    digitalWrite(Rele_Power_Fan, LOW);
  } 
    if (OneF == 1) {
      OneF = 0;
      valor = alarmAjust[0][1] * 1000;
      Tp4 = millis(); //define o valor de Tp4 para resetar o contador
    } else {
      if (DelayS(Tp4, valor)) {
        Serial.println("ERRO: Temperatura fora da faixa permitida!");
        return false; // Falha → desliga tudo
      }
    }
  } else {
    // Voltou para a faixa → reseta
    if(OneF == 0){
      OneF = 1;
      Tp4 = millis(); //define o valor de Tp4 para resetar o contador
    }
  }
  return true; // OK
}