Hi all, I am working on a PID controlled humidity system. The aim is to take the humidity input from a DHT11 sensor and reduce the humidity using a PWM enabled Fan.
I would like the system to be controlled by a PID controller to ensure fast response when high humidity is detected.
The board I'm using is a mega 2560. The fan is a 12V fan that will connected to a battery with the PWM input coming from pin 5. ( I believe the entire battery and 2560 need to share a ground.)
My code is below, but I can't seem to get it to work correctly. The fan will spin at maximum speed when the PWM wire is 0V.
I am still learning so much of the code is cobbled together from other projects I have read through.
#include <PID_v1.h>
#include <dht.h>
#include <LiquidCrystal.h>
#define DHTPIN 4 // Pin connected to DHT sensor
#define DHTTYPE DHT11 // Defines DHT type so board can read output
#define FAN_PIN 5 // Pin connected to PWM pin on the fan
#define PIN_SENSE 2 // Pin where we connected the fan sense pin. Must be an interrupt capable pin (2 or 3 on Arduino Uno)
#define DEBOUNCE 0 // 0 is fine for most fans, some fans may require 10 or 20 to filter out noise
#define FANSTUCK_THRESHOLD 500 // If no interrupts were received for 500ms, consider the fan as stopped and report 0 RPM
dht DHT;
const int RS = 7, EN = 8, D4 = 9, D5 = 10, D6 = 11, D7 = 12; // Assign pins to LCD pads
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7); // Set pins that are connected to LCD, 4-bit mode
// PID Constants
double Kp = 1.0; // Proportional constant
double Ki = 0.5; // Integral constant
double Kd = 0.2; // Derivative constant
// Setpoint (desired humidity)
double setpoint = 65; // Target humidity
// Variables for PID control
double input, output;
double humidity, lastHumidity;
double dt = 1.2; // Time interval for PID calculation
// PWM Duty Cycle Settings
const int MIN_DUTY_CYCLE = 51; // Minimum duty cycle (20% of 255)
const int MAX_DUTY_CYCLE = 255; // Maximum duty cycle (100% of 255)
// Create PID controller object
PID pid(&humidity, &output, &setpoint, Kp, Ki, Kd, DIRECT);
// Interrupt handler. Stores the timestamps of the last 2 interrupts and handles debouncing
volatile unsigned long ts1 = 0, ts2 = 0;
// Calculates the RPM based on the timestamps of the last 2 interrupts. Can be called at any time.
unsigned long calcRPM() {
if (millis() - ts2 < FANSTUCK_THRESHOLD && ts2 != 0) {
return (60000 / (ts2 - ts1)) / 2;
}
else {
return 0;
}
}
void tachISR() {
unsigned long m = millis();
if ((m - ts2) > DEBOUNCE) {
ts1 = ts2;
ts2 = m;
}
}
void setup() {
Serial.begin(9600);
pinMode(FAN_PIN, OUTPUT);
delay(1000);
lcd.begin(16, 2); // Set 16 columns and 2 rows of 16x2 LCD display
// Initialize humidity and PID variables
input = DHT.humidity; // Initial humidity reading
lastHumidity = input;
humidity = input;
// Set PID parameters
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(dt * 1000); // Convert dt to milliseconds for PID sample time
pinMode(PIN_SENSE, INPUT_PULLUP); // Set the sense pin as input with pullup resistor
attachInterrupt(digitalPinToInterrupt(PIN_SENSE), tachISR, FALLING); // Set tachISR to be triggered when the signal on the sense pin goes low
setupTimer1(); // Initialize Timer 1 for PWM
}
void loop() {
delay(100);
Serial.print("RPM: ");
Serial.println(calcRPM());
// Read humidity from DHT11 sensor
DHT.read11(DHTPIN);
input = DHT.humidity;
// Calculate PID output
pid.Compute();
// Map the PID output to the duty cycle range
int pwmDutyCycle = map(output, -100, 100, MIN_DUTY_CYCLE, MAX_DUTY_CYCLE);
// Apply PWM to the output pin
analogWrite(FAN_PIN, pwmDutyCycle);
// Print humidity and output values
Serial.print("Humidity: ");
Serial.print(input);
Serial.print("%, Output PWM: ");
Serial.print(pwmDutyCycle * 100 / MAX_DUTY_CYCLE);
Serial.println("%");
// Display temperature, humidity, RPM, and PWM on LCD display
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(DHT.temperature);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("Humidity: ");
lcd.print(DHT.humidity);
lcd.print("%");
delay(2000); // Wait for 2 seconds before displaying RPM and PWM
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RPM: ");
lcd.print(calcRPM());
lcd.setCursor(0, 1);
lcd.print("PWM: ");
lcd.print(pwmDutyCycle * 100 / MAX_DUTY_CYCLE);
lcd.print("%");
delay(1000); // Wait for 1 second before switching back to temperature and humidity
}
void setupTimer1() {
// Set PWM frequency to about 25kHz on pin 5 (Timer 1, phase correct PWM, prescale 1)
TCCR1A = 0;
TCCR1B = 0;
TCCR1A = (1 << WGM11);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
ICR1 = 400;
}
