/*
//uma função que altera os valores de PID automaticamente
//deverá iniciar com uma configuração "A" apenas ao aquecer a resistencia
//deverá alterar para uma configuração "B" após chegar na temperatura desejada
//deverá retornar para a configuração "A" caso seja necessário aquecer ainda mais a resistencia
//desligar a resistencia
outros defeitos
erro ao alterar o setpoint após um tempo da temperatura estabilizar
Erro ao usar a função esfriar
led do ventilador piscando usando a potencia máxima
Venilador não desliga ao abrir a porta da estufa
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] = {
{10.0, 1.5, 80.0}, // Variavel
{255.0, 255.0, 255.0}, // Limite máximo
{0.0, 0.0, 0.0} // Limite minimo
};
int FanAjust[3][5] = {
{1, 1, 0, 0, 70}, // Variavel
{1, 1, 1, 1, 100}, // Limite máximo
{0, 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, 30, 60}, // 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, 4, 3, 2}; //define os limites de posiiconamento do cursor em cada menu
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
bool estadoA = 0; // usado para controle de potencia do cooler
bool estadoB = 0; // usado para controle de potencia da resistencia
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 OneF = 0; // usado para atualizar o Setpoint
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 = 10.0; // para aquecer
double Ki = 1.5;
double Kd = 80.0;
//double Kp = 30.0; // para manter a temperatura á 75% potencia
//double Ki = 10.3;
//double Kd = 16.6;
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
bool Power_Fan = 0; //liga ouy desliga a função de controle do cooler
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
int TempoA = 500; //tempo de controle do cooler
int TempoB = 1000; //tempo de controle da resistencia
int TempoC = 20000; //tempo de controle de segurança do termistor
int potenciaA = 50; //define a potencia energetica para controle do cooler
int potenciaB = 0; //define a potencia energetica para controle da resistencia
double porcentagem = 0; //usado para calcular a porcentagem de tempo em que ficará ligado a resistencia e o cooler
double TempoOn = 0; //usado para definir o tempo em que ficará ligada a resistencia e o cooler
double TempoOff = 0; //usado para definir o tempo em que ficará desligado a resistencia e o cooler
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
unsigned long Tp5 = 0; //delay de verificação de segurança do termistor
unsigned long Tp6 = 0; //delay de controle de potencia do cooler
unsigned long Tp7 = 0; //delay de controle da resistencia
// ==== 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;
}
}
bool DelayP(unsigned long &TempoAnterior3, unsigned long intervalo3) {
if (millis() - TempoAnterior3 >= intervalo3) {
TempoAnterior3 = millis();
return true;
} else {
return false;
}
}
void loop() {
Sistema();
//seguranca();
Ventilacao();
Display();
tempo();
}
void Display() {
// String AutoTune[] = {"Voltar", "Iniciar", "PWM: ", "Controle: ", "Ruido: ", "Tempo:"};
if (menu == 0) { //tela principal
if (Delay(Tp1, 100)) {
lcd.clear();
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 (OneF == 1) { //função que atua uma vez a cada nova definição de setpoint
DefineTemp = setpoint; //guarda o valor do setpoint atual
OneF = 0; //desativa a função OneF para o histórico "DefineTemp"
}
if (Delay(Tp2, 500)) { //delay que se repete a cada meio segundo
Icon = !Icon; //inverte o estado logico de "Icon" a cada meio segundo
}
if (Icon == 1) { //desenha 3 patinhas no Display a cada vez que Icon é equivalente a 1
lcd.setCursor(13, 1); //define a posição do cursor no display
lcd.write(byte(0)); //desenha a patinha 1
lcd.setCursor(12, 1); //define a posição do cursor no display
lcd.write(byte(0)); //desenha a patinha 2
lcd.setCursor(11, 1); //define a posição do cursor no display
lcd.write(byte(0)); //desenha a patinha 3
}
lcd.setCursor(0, 0); // imprime a temperatura escolhida pelo usuário antes de ser definida no setpoint
lcd.print("Sv:"); //
lcd.setCursor(3, 0); //
lcd.print(DefineTemp); //
} else {
if (OneF == 0) { //função que atua uma vez a cada nova definição de setpoint
setpoint = DefineTemp;
OneF = 1; //dfaz reset da função OneF
}
if (Delay(Tp2, 500)) { //delay que se repete a cada meio segundo
Icon++; //aumenta o valor de "Icon" a cada meio segundo até que chegue em 3
if (Icon == 3) { //verifica se "Icon" alcançou seu limite
Icon = 0; //reseta "Icon" após chegar no limite
}
}
lcd.setCursor(11 + Icon, 1); //define o cursor em 1 posição diferente a cada mudança de "Icon"
lcd.write(byte(0)); //desenha uma patinha em cada posição definida no cursor até que a proxima seja desenhada
lcd.setCursor(0, 0); // imprime o valor real do output
lcd.print("Sv:"); //
lcd.setCursor(3, 0); //
lcd.print(setpoint); //
}
}
} 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: ", "Pw: "};
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 if (Position < 4) {
lcd.setCursor(11, 0);
lcd.print(config[Position - 1]);
lcd.setCursor(11, 1);
lcd.print(config[Position]);
} else {
lcd.setCursor(11, 0);
lcd.print(config[Position - 1]);
lcd.setCursor(11, 1);
lcd.print(FanAjust[0][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 if (Position + C_Position <= 4) {
FanAjust[0][Position + C_Position - 1] = !FanAjust[0][Position + C_Position - 1];
} else {
FanAjust[0][4] = FanAjust[0][4] + 5; //aumenta o valor de potencia de 5%
if (FanAjust[0][4] >= 105) {
FanAjust[0][4] = 0; //zera o valor da potencia
}
potenciaA = FanAjust[0][4]; //define o valor da potencia do cooler
}
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
TempoC = valor; //define o tempo de leitura da temperatura em Delay8
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;
}
}
}
///---------------------------------------------------BOTÃO SELECT---------------------------------------------------------///
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) {
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;
}
}
///---------------------------------------------------BOTÃO + ---------------------------------------------------------------///
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;
}
}
///---------------------------------------------------BOTÃO - ---------------------------------------------------------------///
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.0 - 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) {
Power_Fan = 1; //Liga o ventilador
} else {
Power_Fan = 0; //Desliga o ventilador
}
} else {
if (Saida == HIGH) {
Power_Fan = 0; //Desliga o ventilador
} else {
Power_Fan = 1; //Liga o ventilador
}
}
} else {
if (setpoint_Anterior + 3 >= setpoint && setpoint_Anterior - 3 <= setpoint && setpoint > tempAmbiente) { //inicia a ventilação apenas ao chegar próximo do setpoint
Power_Fan = 1; //Liga o ventilador
} else {
Power_Fan = 0; //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) {
Power_Fan = 1; //Liga o ventilador
} else {
Power_Fan = 0; //Desliga o ventilador
}
} else {
if (Saida == HIGH) {
Power_Fan = 0; //Desliga o ventilador
} else {
Power_Fan = 1; //Liga o ventilador
}
}
} else {
if (setpoint_Anterior + 3 >= setpoint && setpoint_Anterior - 3 <= setpoint && setpoint > tempAmbiente) { //inicia a ventilação apenas ao chegar próximo do setpoint
Power_Fan = 1; //Liga o ventilador
} else {
Power_Fan = 0; //desliga o ventilador
}
}
} else {
Power_Fan = 0; //desliga o ventilador
}
if (Power_Fan == 1) {
if (DelayP(Tp6, TempoA)) { //faz controle de velocidade do fan
porcentagem = (double)potenciaA / 100; //divide o valor da potencia(0 a 100) para obter uma porcentagem
TempoOn = 500 * porcentagem; //multiplica o valor da porcentagem para obter uma porcentagem de 1 segundo
TempoOff = 500 - TempoOn; //obtem a porcentagem restante de milisegundos que sobrarem para usar como tempo desligado
if (estadoA == 1 && potenciaA >= 1) {
digitalWrite(Rele_Power_Fan, HIGH);
estadoA = 0;
TempoA = (int)TempoOn; //define o tempo em que a resistencia ficará desligada
} else {
digitalWrite(Rele_Power_Fan, LOW);
estadoA = 1;
TempoA = (int)TempoOff; //Define o tempo em que a resistencia ficará ligada;
}
}
} else {
digitalWrite(Rele_Power_Fan, LOW);
}
}
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 Sistema() {
int Map = (int)output;
potenciaB = map(Map, 0, 255, 0, 100);
if (DefineTemp <= 0) { //força o desligamento da resistencia
digitalWrite(Portas[2], LOW);
} else {
digitalWrite(Portas[2], HIGH);
}
if (DefineTemp > 0) {
if (DelayP(Tp7, TempoB)) { //faz controle de potencia da resistencia
porcentagem = (double)potenciaB / 100; //divide o valor da potencia(0 a 100) para obter uma porcentagem
TempoOn = 1000 * porcentagem; //multiplica o valor da porcentagem para obter uma porcentagem de 1 segundo
TempoOff = 1000 - TempoOn; //obtem a porcentagem restante de milisegundos que sobrarem para usar como tempo desligado
if (estadoB == 1 && potenciaB >= 1) {
digitalWrite(Power_Resistance, LOW); //liga a resistencia
estadoB = 0;
TempoB = (int)TempoOn; //define o tempo em que a resistencia ficará desligada
} else {
digitalWrite(Power_Resistance, HIGH); //desliga a resistencia
estadoB = 1;
TempoB = (int)TempoOff; //Define o tempo em que a resistencia ficará ligada;
}
}
} else {
digitalWrite(Power_Resistance, HIGH); //mantem desligado a resistencia
}
double leitura = lerTemperaturaNTC();
// Tempo entre leituras (para sistemas térmicos, 1 segundo é bom)
if (Delay(Tp0, 500)) {
// Lê a temperatura atual (simulação)
input = filtrarTemperatura(leitura);
meuPID.Compute(); //faz o controle do PID
if (input >= setpoint - tolerance && input <= setpoint + tolerance) { //verifica se a leitura alcançou o setpoint
if (OneE == 0 && TempoAjust[0][0] > 0) { //verificação do timmer
OneE = 2; //indica ao sistema que a temperatura alcançou o setpoint
sequencia0 = 1; //Faz a inicialização do timer caso haja algum
}
if (OneG == 1) { //função de verificação que se repete apenas uma vez
OneG = 0; //desativa a função de verificação
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); //imprime o histórico "setpoint_Anterior"
}
} else {
OneG = 1; //reseta a verificação "Setpoint_Anterior"
}
}
}
// =============================
// Supervisão 2: Faixa de tolerância
// =============================
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á 3 Wachdog's de temperatura onde um verifica se a temperatura desce outro se a temperatura sobe
inicialmente deverá atuar de acordo com a posição dos setpoint "A" e "B" sendo o "A" setpoint principal e "B" = setpoint_Anterior
o terceiro Wachdog de atuará apenas quando ambos os setpoint's estiverem com os mesmo valores ou mais proximos, guardados para sí EX:
{Setpoint = 10 | ou | Setpoint = 11 }
{Setpoint_Anterior = 10 | -- | Setpoint_Anterior = 9 }
Isso indica que a temperatura chegou no Setpoint definido e não há necessidade de verificar se a temperatura está apenas subindo ou descendo
o terceiro Wachdog deverá atuar fazendo uma contagem de tempo Ex: 10s, até que a temperatura volte para o setpoint
Caso isso não ocorra até o final dos 10s, deverá iniciar o procesimento de segurança, forçando o usuário reiniciar o sistema
LOGICA DE FUNCIONAMENTO: teórico
1). Se o setpoint = Setpoint_anterior (ambos igual a 0 ou menor que 30), verificar qual o valor atual da leitura (Entrada/input)
A) se o valor da entrada for maior que 30C (até o momento considerado como temperatura ambiente), Verificar se a temperatura está descendo
B) se o valor da entrada for igual ou menor que 30C, não fazer nada.
2) Se o setpoint = Setpoint_anterior (Ambos acima de 30 graus) Verificar o valor da entrada
A) se o valort é igual (não fazer nada)
B) se o valor atá muito acima ou muito abaixo (Iniciar contador de tempo)
3) Se o setpoint for menor que o setpoint_anterior (Verificar se a temperatura está descendo)
4) se o setpoint for maior que o setpoint_anterior (Verificar se a temperatura está subindo)
*/
///-------------------------------------------------- PROGRAMA novo ---------------------------------------------------///
//variaveis da função
int setpoint_A = (int)setpoint; //define os valores convertidos afim de evitar possíveis bugs
int setpoint_B = (int)setpoint_Anterior; //
if (setpoint_B == setpoint_A && setpoint_A <= tempAmbiente && input > tempAmbiente) { // Lógica 1) - A
} else if (setpoint_B == setpoint_A && setpoint_A > tempAmbiente && (input > setpoint_A + tolerance * 2 || input < setpoint_A - tolerance * 2)) { //lógica 2) - B
} else if (setpoint_B < setpoint_A) { // Lógica 3)
} else if (setpoint_B > setpoint_A) { // Lógica 4)
}
///------------------------------------------------- PROGRAMA ANTIGO ---------------------------------------------------///
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)) {
if (input > setpoint + 4 * tolerance) { //verifica se a leitura de entrada é maior que a permitida pelo setpoint
security &= tempAcima(input, setpoint); //verifica se a temperatura está descendo
} else if (input < setpoint - 4 * tolerance) { //verifica se a leitura de entrada é menor que a permitida pelo setpoint
security &= tempAbaixo(input, setpoint); //verifica se a temperatura está subindo
}
} else { //se nenhuma das condições acima estiver de acordo, todo o programa deverá parar
Tp4 = millis(); //reseta o contador de tempo
}
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 (DelayS(Tp5, TempoC)) {
if (input < UltimaLeitura + T_Objetivo) {
Serial.println("ERRO: Temperatura não está subindo!");
return false; // Falha → desliga tudo
}
// Atualiza referência
UltimaLeitura = input;
}
} else {
Tp5 = millis(); //reseta o contador de tempo
}
return true;
}
bool tempAcima(double input, double setpoint) {
// Só verifica se a temperatura está diminuindo
if (input > setpoint + tolerance) {
if (DelayS(Tp4, TempoC)) {
if (input > UltimaLeitura - T_Objetivo) {
Serial.println("ERRO: Temperatura não está Descendo!");
return false; // Falha → desliga tudo
}
// Atualiza referência
UltimaLeitura = input;
}
}// else {
Tp4 = millis(); //reseta o contador de tempo
return true; // OK
// }
}