#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DHT.h"
// --- MAPEAMENTO DE PINOS (GPIO DO RASPBERRY PI PICO) ---
#define DHTPIN 2 // GPIO 2 (Pino físico 4)
#define DHTPIN2 3 // Alterado para GPIO 3 (Pino físico 5) para liberar GPIO 6/7
#define DHTTYPE DHT22
// Pinos I2C para o LCD (Barramento I2C0 padrão)
const int PINO_SDA = 4; // GPIO 4 (Pino físico 6)
const int PINO_SCL = 5; // GPIO 5 (Pino físico 7)
// Atuadores e Botões
const int PINO_PWM_VENT = 6; // GPIO 6 (Pino físico 9) - PWM do Ventilador
const int PINO_PWM_RELE = 7; // GPIO 7 (Pino físico 10) - PWM/Controle de Umidade
const int PINO_BOTATO_INC = 8; // GPIO 8 (Pino físico 11) - Botão Aumentar Setpoint
const int PINO_BOTATO_DEC = 9; // GPIO 9 (Pino físico 12) - Botão Diminuir Setpoint
DHT dht(DHTPIN, DHTTYPE);
DHT dht2(DHTPIN2, DHTTYPE);
// --- CONFIGURAÇÃO DO LCD I2C ---
LiquidCrystal_I2C lcd(0x27, 16, 2);
// --- PARÂMETROS DO PID DE TEMPERATURA ---
float Kp_T = 25.0;
float Ki_T = 0.1;
float Kd_T = 1.0;
// --- PARÂMETROS DO PID DE UMIDADE ---
float Kp_H = 22.0;
float Ki_H = 0.05;
float Kd_H = 0.5;
// --- VARIÁVEIS DE CONTROLE ---
float tempDesejada = 35.0; // Setpoint Temperatura (°C)
float umiDesejada = 55.0; // Setpoint Umidade (%)
float erroAnterior_T = 0.0;
float integral_T = 0.0;
float erroAnterior_H = 0.0;
float integral_H = 0.0;
unsigned long tempoAnterior = 0;
// --- VARIÁVEIS DOS BOTÕES (DEBOUNCE) ---
unsigned long ultimoTempoBotao = 0;
const unsigned long delayDebounce = 200;
// Protótipo da função (Boa prática para o compilador do Pico)
void atualizarDisplaySetpoint();
void setup() {
Serial.begin(115200); // 115200 é o padrão recomendado para o Pi Pico
// Inicializa os pinos dos botões com resistor de Pull-up interno
pinMode(PINO_BOTATO_INC, INPUT_PULLUP);
pinMode(PINO_BOTATO_DEC, INPUT_PULLUP);
// Define os pinos I2C corretos antes de iniciar o Wire
Wire.setSDA(PINO_SDA);
Wire.setSCL(PINO_SCL);
Wire.begin();
// Inicializa o LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Sumry 10.2kW PID");
lcd.setCursor(0, 1);
lcd.print("Iniciando...");
delay(2000);
lcd.clear();
// Inicializa saídas
pinMode(PINO_PWM_VENT, OUTPUT);
pinMode(PINO_PWM_RELE, OUTPUT);
tempoAnterior = millis();
dht.begin();
dht2.begin();
}
void loop() {
unsigned long tempoAtual = millis();
// --- LEITURA DOS BOTÕES DE AJUSTE DO SETPOINT ---
if (tempoAtual - ultimoTempoBotao > delayDebounce) {
if (digitalRead(PINO_BOTATO_INC) == LOW) {
tempDesejada += 0.5;
ultimoTempoBotao = tempoAtual;
atualizarDisplaySetpoint();
} else if (digitalRead(PINO_BOTATO_DEC) == LOW) {
tempDesejada -= 0.5;
ultimoTempoBotao = tempoAtual;
atualizarDisplaySetpoint();
}
}
double dt = (double)(tempoAtual - tempoAnterior) / 1000.0;
// Executa o cálculo a cada 1 segundo (1000ms)
if (dt >= 1.0) {
float h1 = dht.readHumidity();
float t1 = dht.readTemperature();
float h2 = dht2.readHumidity();
float t2 = dht2.readTemperature();
// Validação da leitura do sensor 1
if (isnan(h1) || isnan(t1)) {
Serial.println("Falha no sensor DHT 1!");
lcd.setCursor(0, 0);
lcd.print("Erro Sensor 1 ");
tempoAnterior = tempoAtual; // Evita travamento do contador de tempo
return;
}
// Validação da leitura do sensor 2
if (isnan(h2) || isnan(t2)) {
Serial.println("Falha no sensor DHT 2!");
lcd.setCursor(0, 1);
lcd.print("Erro Sensor 2 ");
tempoAnterior = tempoAtual;
return;
}
// --- CÁLCULO DAS MÉDIAS ---
float tempMedia = (t1 + t2) / 2.0;
float umiMedia = (h1 + h2) / 2.0;
// ==========================================
// 1. PID DE TEMPERATURA (Controle Exaustor)
// ==========================================
float erro_T = tempMedia - tempDesejada;
float P_T = Kp_T * erro_T;
integral_T += erro_T * dt;
if (integral_T > 100.0) integral_T = 100.0;
if (integral_T < -100.0) integral_T = -100.0;
float I_T = Ki_T * integral_T;
float derivativa_T = (erro_T - erroAnterior_T) / dt;
float D_T = Kd_T * derivativa_T;
float saidaPID_T = P_T + I_T + D_T;
if (saidaPID_T > 255) saidaPID_T = 255;
if (saidaPID_T < 0) saidaPID_T = 0;
analogWrite(PINO_PWM_VENT, (int)saidaPID_T);
erroAnterior_T = erro_T;
// ==========================================
// 2. PID DE UMIDADE (Controle Desumidificador)
// ==========================================
float erro_H = umiMedia - umiDesejada;
float P_H = Kp_H * erro_H;
integral_H += erro_H * dt;
if (integral_H > 100.0) integral_H = 100.0;
if (integral_H < -100.0) integral_H = -100.0;
float I_H = Ki_H * integral_H;
float derivativa_H = (erro_H - erroAnterior_H) / dt;
float D_H = Kd_H * derivativa_H;
float saidaPID_H = P_H + I_H + D_H;
if (saidaPID_H > 255) saidaPID_H = 255;
if (saidaPID_H < 0) saidaPID_H = 0;
analogWrite(PINO_PWM_RELE, (int)saidaPID_H);
erroAnterior_H = erro_H;
// Finaliza o ciclo de tempo
tempoAnterior = tempoAtual;
// ==========================================
// 3. EXIBIÇÃO NO DISPLAY LCD I2C
// ==========================================
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(tempMedia, 1);
lcd.print("C");
lcd.print((char)223);
lcd.print("SP:");
lcd.print(tempDesejada, 1);
lcd.print((char)223);
int pctPWM_H = map((int)saidaPID_H, 0, 255, 0, 100);
lcd.setCursor(0, 1);
lcd.print("U:");
lcd.print((int)umiMedia);
lcd.print("%D:");
lcd.print(pctPWM_H);
lcd.print("%C:");
lcd.print((int)saidaPID_T);
lcd.print(" "); // Espaços extras para limpar fantasmas visuais
// ==========================================
// 4. MONITOR SERIAL
// ==========================================
Serial.print("T1: "); Serial.print(t1, 1);
Serial.print("°C | T2: "); Serial.print(t2, 1);
Serial.print("°C | T_Média: "); Serial.print(tempMedia, 1);
Serial.print("°C | PWM_Vent: "); Serial.print((int)saidaPID_T);
Serial.print(" || H1: "); Serial.print(h1, 0);
Serial.print("% | H2: "); Serial.print(h2, 0);
Serial.print("% | H_Média: "); Serial.print(umiMedia, 0);
Serial.print("% | PWM_Umi: "); Serial.println((int)saidaPID_H);
}
}
void atualizarDisplaySetpoint() {
lcd.setCursor(9, 0); // Ajustado levemente a posição para cobrir o espaço correto
lcd.print("SP:");
lcd.print(tempDesejada, 1);
lcd.print((char)223);
lcd.print(" ");
}