First and foremost, I've been reading a bunch about Arduino for the past few months. Delved into PID control for a fan on an amp rack in my truck. Mostly straightforward but for the life of me cannot get the PWM for the fan to work.
Components:
Arduino UNO
DHT22 temp sensor
BLEXAR app
HM10 BT transmitter/receiver
Noctura 4 wire PWM fan (NF-A8)
2N3904 BJTs to drive fan relay and PWM to fan
IN4001 diode around the relay coil
Status LED (flashes every 5 seconds)
Libraries:
PID from Dave
Software Serial
DHT
I would like to have the relay open when the duty cycle is below Noctura's min duty cycle (approx 20%) and closed when above 20%. As written, nothing happens. If I force the RELAYPIN 8 to HIGH the relay closes, fan starts but still no change in speed based on temp.
Any help would be appreciated.
Mike
// Sketch uses DHT22 temp sensor with BlEXAR BT to display temperature. Also uses PID to control Noctura 4 wire fan.
#include <DHT.h>
#include <SoftwareSerial.h>
#include <PID_v1.h>
// -----PINS **cannot use PIN 3**
//PIN 0 - RX
//PIN 1 - TX
const int RELAYPIN = 8; // pin number for fan control relay
const int LEDPIN = 9; // pin number for the running LED (on side of the amp rack) CHANGE PIN NUMBER
const int DHTPIN = 2; // pin number for temp sensor
#define PWM11 OCR2B // pin number for 25kHz PWM to fan on Timer 2 (pins 3 and ll only)
// -----CONSTANTS
const int BLINKDURATION = 500; // number of millisecs the running LED is on
const int LEDINTERVAL = 5000; // number of millisecs between blinks
const int TEMPINTERVAL = 2000; // number of millisecs between temp data from DHT22
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
const int KP = 0.4; //PID constants - evaluate adding aggressive numbers per example
const int KI = 0.4;
const int KD = 0.05;
const byte TIMER2_CONSTANT = 79; // set Timer2 to 25kHz output
const int MIN_DUTY_CYCLE = 15; // minimum duty cycle for fan (is about 20% or 450 rpm) 15/79 for OCR2B @ 25kHz
//----- VARIABLES
byte LED_STATE = LOW; // record whether the LED is on or off
double T = 0; // current temp from DH22
double TEMP_SETPOINT = 88; // temperature setpoint 88F
double DUTY_CYCLE = 0; // PID output to control duty cycle of fan; set to zero at setup
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousLedMillis = 0; // will store last time the LED was updated
unsigned long previousTempMillis = 0; // will store last time temp data sent to Arduino
//Initialize libraries
DHT dht(DHTPIN, DHTTYPE);
SoftwareSerial ble_device(0,1); // HM-10 TX/RX pins
PID FAN_PID(&T, &DUTY_CYCLE, &TEMP_SETPOINT, KP, KI, KD, REVERSE); //INPUT, OUTPUT, TARGET TEMP, CONS1, CONS2, CONS3, MODE
//========
void setup() {
pinMode(PWM11, OUTPUT); //set PWMPIN as output
pwm25kHzBegin(); //Set timer2 for 25kHz PWM signal with 0% duty cycle on startup
pinMode(LEDPIN, OUTPUT); // set the Led pin as output:
pinMode(RELAYPIN, OUTPUT); //set the fan relay pin as output
pinMode(DHTPIN, INPUT); //set the DHT pin as input
dht.begin(); //start DHT sensor
ble_device.begin(9600); //prepare BLE module
FAN_PID.SetMode(AUTOMATIC); //set PID calc to automatic
//fanPID.SetSampleTime(); // IS THIS NEEDED?
FAN_PID.SetOutputLimits(0,255); //min and max duty output
}
//=======
void pwm25kHzBegin() {
TCCR2A = 0; // TC2 Control Register A
TCCR2B = 0; // TC2 Control Register B
TIMSK2 = 0; // TC2 Interrupt Mask Register
TIFR2 = 0; // TC2 Interrupt Flag Register
TCCR2A |= (1 << COM2B1) | (1 << WGM21) | (1 << WGM20); // OC2B cleared/set on match when
// up/down counting, fast PWM
TCCR2B |= (1 << WGM22) | (1 << CS21); // prescaler 8
OCR2A = 79; // TOP overflow value (Hz)
OCR2B = 0;
}
void loop() {
currentMillis = millis(); // latest value of millis() for all calculations
// update LED_STATE: determine if need to turn LED on or off
if (LED_STATE == LOW) { // if the Led is off, must wait for LEDINTERVAL to
// expire to turn on LED
if (currentMillis - previousLedMillis >= LEDINTERVAL) { // LEDINTERVAL is up, so change to HIGH
LED_STATE = HIGH; // change LED_STATE to HIGH
previousLedMillis += LEDINTERVAL; // save the time when we made the change
}
}
else { // if LED is on, turn off if BLINKDURATION expired
if (currentMillis - previousLedMillis >= BLINKDURATION) { // BLINKDURATION expired, turn LED off
LED_STATE = LOW; // change LED_STATE to LOW
previousLedMillis += BLINKDURATION; // save the time when we made the change
}
}
digitalWrite(LEDPIN, LED_STATE); // switch LED on or off
// update temperaure from DHT22, convert and send to BLE device
if (currentMillis - previousTempMillis >= TEMPINTERVAL) { // TEMPINTERVAL exceeded; get temp
// data, convert and send to BLE device
T = dht.readTemperature(true); // get temperature data from DHT in degree F
char T_str[6]; // prepare character array to send
dtostrf(T,2,1,T_str); // format data into char array
ble_device.write(T_str); // send to BLExAR
previousTempMillis += TEMPINTERVAL; // save the time temp data sent to BT device to allow for delay
} // otherwise do nothing
FAN_PID.Compute(); // determine duty cycle
// Turn the fans ON/OFF if duty cycle too low ~ 20% per Noctura
// Arduino puts out small duty cycle even at zero
if (round(DUTY_CYCLE) < MIN_DUTY_CYCLE) { // calculated duty cycle < min duty cycle
PWM11 = 0; // set DUTY_CYCLE to 0
digitalWrite(RELAYPIN, LOW); // turn off power to BJT to fan relay
}
else {
PWM11 = (DUTY_CYCLE / 255) * 79; // output of PID 0- 255; Timer2 is 0 - 79
digitalWrite(RELAYPIN, HIGH); // turn on power to BJT on fan relay
}
}
