Here's the code, I'm not sure if it's junk but it seems to work.
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2 // DS18B20 Data
#define FAN_PWM_PIN 9 // MOSFET PWM
#define AC_PIN 4 // A/C override (active-low)
#define MANUAL_PIN 5 // Manual override (active-low)
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// PID variables - adjustable via serial
float Kp = 5.0, Ki = 0.2, Kd = 0.2; // Milder PID
float targetTemp = 185.0; // Target 185°F
float integral = 0, lastError = 0;
int acDuty = 35; // A/C min duty 35%
const int MIN_DUTY = 15;
const int MAX_DUTY = 95;
int pwmValue = 0;
bool enabled = true;
void setup() {
pinMode(FAN_PWM_PIN, OUTPUT);
pinMode(AC_PIN, INPUT_PULLUP);
pinMode(MANUAL_PIN, INPUT_PULLUP);
analogWrite(FAN_PWM_PIN, 0);
sensors.begin();
Serial.begin(9600);
Serial.println("Ready - Commands: T### (temp), P#.# (Kp), I#.# (Ki), D#.# (Kd), A## (AC %), E (enable), D (disable)");
}
void loop() {
// Handle serial commands
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command.startsWith("T")) {
float newTemp = command.substring(1).toFloat();
if (newTemp >= 150.0 && newTemp <= 200.0) {
targetTemp = newTemp;
integral = 0;
Serial.print("Target Temp set to "); Serial.println(targetTemp);
} else {
Serial.println("Error: Temp 150-200°F");
}
} else if (command.startsWith("P")) {
float newKp = command.substring(1).toFloat();
if (newKp >= 0.1 && newKp <= 20.0) {
Kp = newKp;
Serial.print("Kp set to "); Serial.println(Kp);
} else {
Serial.println("Error: Kp 0.1-20.0");
}
} else if (command.startsWith("I")) {
float newKi = command.substring(1).toFloat();
Ki = newKi;
Serial.print("Ki set to "); Serial.println(Ki);
} else if (command.startsWith("D") && command.length() > 1) { // Kd command
float newKd = command.substring(1).toFloat();
Kd = newKd;
Serial.print("Kd set to "); Serial.println(Kd);
} else if (command == "D") { // Disable command
enabled = false;
pwmValue = 0;
analogWrite(FAN_PWM_PIN, pwmValue);
Serial.println("Fan Disabled");
} else if (command == "E") { // Enable command
enabled = true;
Serial.println("Fan Enabled");
} else if (command.startsWith("A")) {
int newAcDuty = command.substring(1).toInt();
if (newAcDuty >= MIN_DUTY && newAcDuty <= MAX_DUTY) {
acDuty = newAcDuty;
Serial.print("AC Duty set to "); Serial.print(acDuty); Serial.println("%");
} else {
Serial.println("Error: AC Duty 15-95%");
}
} else {
Serial.println("Unknown command - Use: T###, P#.#, I#.#, D#.#, A##, E, D");
}
}
// Fan control
sensors.requestTemperatures();
float temp = sensors.getTempFByIndex(0);
int acState = digitalRead(AC_PIN);
int manualState = digitalRead(MANUAL_PIN);
if (!enabled) {
pwmValue = 0;
analogWrite(FAN_PWM_PIN, pwmValue);
} else if (manualState == LOW) { // Manual - max speed (95%)
pwmValue = map(MAX_DUTY, 0, 100, 0, 255);
analogWrite(FAN_PWM_PIN, pwmValue);
} else { // PID with A/C min duty
float error = temp - targetTemp; // Positive = too hot, negative = too cold
integral += error * 0.1; // Always update integral
float derivative = (error - lastError) / 0.1;
float output = Kp * error + Ki * integral + Kd * derivative;
// Scale duty: output drives PWM, A/C sets minimum
float duty;
if (output >= 0) { // Above or at target - fan speeds up
duty = MIN_DUTY + output; // Output adds to min duty
} else { // Below target - fan slows down
duty = MIN_DUTY; // Stay at min duty when too cold
}
// Apply A/C minimum if active
if (acState == LOW) {
duty = max(duty, acDuty); // A/C forces at least 35%
}
duty = constrain(duty, MIN_DUTY, MAX_DUTY);
pwmValue = map(duty, 0, 100, 0, 255);
analogWrite(FAN_PWM_PIN, pwmValue);
lastError = error;
}
// Debug output
Serial.print("Temp: "); Serial.print(temp);
Serial.print(" Error: "); Serial.print(temp - targetTemp);
Serial.print(" Duty: "); Serial.print(map(pwmValue, 0, 255, 0, 100));
Serial.print(" A/C: "); Serial.print(acState);
Serial.print(" Manual: "); Serial.print(manualState);
Serial.print(" Enabled: "); Serial.println(enabled);
delay(100);
}