#include <TimerOne.h>
#include <ACI_10K_an.h> // Para conversão precisa do termistor NTC 10K
#include <LiquidCrystal.h>
const int termQuentePin = A0; // Pino do Termistor lado Quente
const int termFrioPin = A1; // Pino do Termistor lado Frio
const int peltierPin = 10; // Pino PWM para controle da Pastilha Peltier (Verificar se é 10 ou 9)
const int potPin = A4; // Pino do Potenciômetro para ajuste da temperatura alvo
LiquidCrystal lcd(12, 11, 9, 8, 7, 6);
// Instâncias para os termistores
Aci_10K termistorQuente;
Aci_10K termistorFrio;
double tempQuente; // Temperatura lado Quente
double tempFrio; // Temperatura lado Frio
double tempAlvo; // Temperatura alvo para o lado Frio
// Variáveis PID
double P = 0.0, I = 0.0, D = 0.0, PID_output = 0;
double Kp = 10.0, Ki = 0.1, Kd = 0.5; // Coeficientes PID (ajuste conforme necessário)
double lastTemperature = 0; // Última temperatura lida para o cálculo de D
double error = 0; // Erro do PID
unsigned long lastProcessTime = 0; // Para cálculo do delta t do PID
const unsigned long PID_INTERVAL = 1000; // Intervalo de 1 segundo para o ciclo PID
// Variáveis para o controle da temperatura do lado quente
unsigned long lastHotSideCheckTime = 0;
const unsigned long HOT_SIDE_CHECK_INTERVAL = 2000; // Verifica o lado quente a cada 2 segundos
const double MAX_HOT_SIDE_TEMP = 50.0; // Temperatura máxima permitida para o lado quente
void setup() {
Serial.begin(9600); // Inicia a comunicação serial
Serial.println("Sistema de Controle Peltier Iniciado!");
lcd.begin(16, 2);
// Agora você pode interagir com o LCD, por exemplo:
lcd.print("Ola Mundo!");
// Seta o modo dos pinos utilizados (entrada/saida)
pinMode(termQuentePin, INPUT);
pinMode(termFrioPin, INPUT);
pinMode(potPin, INPUT);
pinMode(peltierPin, OUTPUT);
// Lê as temperaturas iniciais e define lastTemperature para um primeiro cálculo de D válido
readAll();
lastTemperature = tempFrio; // Importante para o termo D
// Inicializa o Timer1 para um PWM inicial (pode ser 0) e interrupção para o PID
// Não usaremos Timer1.initialize() para a interrupção de 1ms, mas sim millis() no loop
// O PWM será atualizado no loop principal
Timer1.initialize(1000); // Frequência do PWM (1000us = 1kHz)
Timer1.pwm(peltierPin, 0); // Começa com Peltier desligada
lastProcessTime = millis(); // Inicializa o tempo para o primeiro ciclo PID
}
void loop() {
unsigned long currentTime = millis();
// 1. Leitura de Sensores e Potenciômetro
// É bom ler sempre para ter os dados mais atualizados, mesmo que o PID não rode a cada ciclo
readAll();
// 2. Cálculo do PID (Executado a cada PID_INTERVAL)
if (currentTime - lastProcessTime >= PID_INTERVAL) {
unsigned long deltaTime = currentTime - lastProcessTime;
lastProcessTime = currentTime; // Atualiza o tempo para a próxima iteração
// Erro: Agora considera o sinal (+ ou -)
error = tempAlvo - tempFrio;
if (error == 0) { // Se o erro for zero, a saída PID pode ser mantida ou zerada se não houver "manutenção"
PID_output = 0.0; // Zera a saída se o erro for zero para parar o resfriamento
I = 0.0; // Reseta o termo integral para evitar "wind-up" quando o erro é zero
} else {
P = error * Kp;
I = I + error * Ki; // Acumula o erro no termo integral (sem delta time, pois a frequência é fixa)
D = ((tempFrio - lastTemperature) * Kd); // Termo derivativo (mudança da temp / tempo)
// Limita o termo integral para evitar "wind-up" (ajuste conforme a necessidade)
if (I > 500) I = 500;
if (I < -500) I = -500; // Se o PID pode ser bidirecional, senão só um lado.
PID_output = P + I + D;
}
lastTemperature = tempFrio; // Atualiza a última temperatura para o próximo cálculo de D
// 3. Limitação da Saída do PID para a Faixa do PWM (0-1023)
if (PID_output < 0) {
Serial.print("PID ajustado de ");
Serial.print(PID_output);
Serial.println(" para 0.0 (fora da faixa de resfriamento)");
PID_output = 0.0;
} else if (PID_output > 1023) {
Serial.print("PID ajustado de ");
Serial.print(PID_output);
Serial.println(" para 1023.0 (limite maximo do PWM)");
PID_output = 1023.0;
}
// 4. Mudar o pulso do PWM da Peltier
Timer1.pwm(peltierPin, int(PID_output));
// 5. Imprime os valores para depuração
printTemp();
}
// 6. Verificação de Segurança da Temperatura do Lado Quente (não-bloqueante)
if (currentTime - lastHotSideCheckTime >= HOT_SIDE_CHECK_INTERVAL) {
lastHotSideCheckTime = currentTime;
if (tempQuente >= MAX_HOT_SIDE_TEMP) {
Serial.println("ATENCAO: Temperatura do lado quente EXCEDIDA! Peltier DESLIGADO.");
Timer1.pwm(peltierPin, 0); // Desliga a Peltier
// Opcional: Adicionar lógica para ligar um ventilador no dissipador do lado quente
}
}
}
/**
* @brief Calcula a temperatura em Celsius a partir da leitura analógica do termistor.
* @param pin O pino analógico onde o termistor está conectado.
* @return A temperatura em graus Celsius.
*/
float calcTemp(int pin) {
// Usando a biblioteca ACI_10K_an para termistores NTC 10K
// Certifique-se de que a resistência de pull-up para o termistor está correta (10k Ohms)
// Se você não tem essa biblioteca ou um NTC 10K, esta linha precisa ser ajustada.
return termistorFrio.getTemp(analogRead(pin)); // Assumindo o mesmo tipo de termistor para ambos
}
/**
* @brief Imprime os valores de temperatura e PID no Serial Monitor.
*/
void printTemp() {
Serial.println("#########################################");
Serial.print("Temperatura do lado quente: ");
Serial.print(tempQuente);
Serial.println(" C");
Serial.print("Temperatura do lado frio: ");
Serial.print(tempFrio);
Serial.println(" C");
Serial.print("Temperatura alvo: ");
Serial.print(tempAlvo);
Serial.println(" C");
Serial.print("Erro: ");
Serial.println(error);
Serial.print("PID Output (PWM): ");
Serial.println(int(PID_output));
}
/**
* @brief Lê todos os sensores (temperaturas e potenciômetro).
*/
void readAll() {
tempQuente = calcTemp(termQuentePin);
tempFrio = calcTemp(termFrioPin);
// Mapeia a leitura do potenciômetro (0-1023) para uma faixa de temperatura alvo (-10 a 30 Celsius)
tempAlvo = map(analogRead(potPin), 0, 1023, -10, 30);
}