// *********** USER INPUTS **********
#define FAN1SETPOINT 195 // fan 1 trigger setpoint to turn on fan
#define FAN2SETPOINT 200 // fan 2 trigger setpoint to turn on fan
#define MAX_OFF_TEMP 205 // max temp before fans release on WOT or voltage or sufficient speed
#define HYSTERESIS 3 // number of degrees F below setpoint to turn fan back off after triggered
#define THROTTLE_LIMIT 80 // throttle threshold to determine if WOT mode - turn off fans during WOT
#define RPM_LIMIT 500 // RPM threshold to determine if in start mode - turn off fans during startup or engine off
#define AC_SPEED 20 // MPH threshold to turn on fan 1 if not moving
#define AC_DIFFERENTIAL 15 // difference in F between condenser and ambient to trigger AC
#define AC_WARNING 40 // difference in F between condenser and ambient to double fans and flash warning
#define NO_FAN_SPEED 50 // MPH threshold for fans not necessary
#define REQ_VOLTAGE 12.1 // minimum voltage threshold to turn on fans
char array[] = "Welcome Aboard!! "; //the string to place on first line of the LCD during setup
int arraylength = 32; // number of characters to scroll
int character_time = 250; //the value of delay time in mS between scroll characters
// THERMISTOR1 INPUTS - AMBIENT TEMPS
#define THERMISTORNOMINAL1 10000 // resistance at 25 degrees C
#define SERIESRESISTOR1 10000 // the value of the 'other' resistor
#define TEMPERATURENOMINAL1 25 // temp. for nominal resistance
#define BCOEFFICIENT1 3950 // The beta coefficient of the thermistor
// THERMISTOR2 INPUTS - CONDENSER TEMPS
#define THERMISTORNOMINAL2 10000 // resistance at 25 degrees C
#define SERIESRESISTOR2 10000 // the value of the 'other' resistor
#define TEMPERATURENOMINAL2 25 // temp. for nominal resistance
#define BCOEFFICIENT2 3950 // The beta coefficient of the thermistor
// BOTH THERMISTORS SAMPLES LOOP
#define NUMSAMPLES 10 // how many samples to take and average, more takes longer but is more 'smooth'
// **** PINS USED ****
int RelayDrive1 = 2; // pin 2 digital is for output relay1 drive connected to base of TIP122 through 1k resistor (emitter to board ground, collector to pin 86 on relay, 1N4002 diode from 86 to 85)
int RelayDrive2 = 3; // pin 3 digital is for output relay2 drive connected to base of TIP122 through 1k resistor (emitter to board ground, collector to pin 86 on relay, 1N4002 diode from 86 to 85)
int potPin = 3;
#define THERMISTOR1PIN A1 // pin A1 analog is for 10k thermistor temperature sensor input (10k resistor from A1 to vcc)
#define THERMISTOR2PIN A2 // pin A2 analog is for 10k thermistor temperature sensor input (10k resistor from A1 to vcc)
// input Tx Rx from OBD CAN to arduino
// pins A4 & A5 used for LCD I2C 16x2 outputs
#include <OBD2UART.h> // library for calls from OBD2
#include <Wire.h> // library for calls from OBD2
#include <LiquidCrystal_I2C.h> // library for LCD
// Set the LCD address to for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x3F, 16, 2); // try 0x27 if does not work
COBD obd;
// Establish loop variables
int fan1state = 0;
int fan2state = 0;
int threshold1 = FAN1SETPOINT;
int threshold2 = FAN2SETPOINT;
// CREATE DELTA CHARACTER FOR AC DISPLAY
byte delta[8] =
{
B00000,
B00000,
B00100,
B01010,
B11111,
B00000,
B00000,
B00000
};
// ***************** SETUP ****************
void setup() {
Serial.begin(9600);
pinMode(RelayDrive1, OUTPUT); // declare relaydrive1 as an output
digitalWrite(RelayDrive1, LOW); //Turn the Relay1 Off
pinMode(RelayDrive2, OUTPUT); // declare relaydrive2 as an output
digitalWrite(RelayDrive2, LOW); //Turn the Relay2 Off
// initialize the LCD
lcd.init();
// Turn on the backlight and print a message.
lcd.backlight();
// **** delay(2000);
lcd.setCursor(15, 0); // set the cursor to column 15, line 0
for (int positionCounter = 0; positionCounter < arraylength; positionCounter++)
{
lcd.scrollDisplayLeft(); //Scrolls the contents of the display one space to the left.
lcd.print(array[positionCounter]); // Print a message to the LCD.
delay(character_time); //wait for set microseconds
}
lcd.clear(); //Clears the LCD screen and positions the cursor in the upper-left corner.
lcd.setCursor(0, 0);
lcd.print("OBD");
lcd.setCursor(0, 1);
lcd.print("initiating...");
// start serial communication
// ****** obd.begin();
// initiate OBD-II connection until success
// ***** while (!obd.init());
lcd.createChar(0, delta);
lcd.clear();
}
// ****************** LOOP *******************
void loop() {
// ** COLLECT LOOP DATA **
// OBD variables
int throttle;
// save throttle position in variable 'throttle', return true on success
if (obd.readPID(PID_THROTTLE, throttle)) {
// do nothing
}
else {
throttle = 0; // if throttle call is false, set throttle to zero
}
int rpm;
// save RPM in variable 'rpm', return true on success
if (obd.readPID(PID_RPM, rpm)) {
// do nothing
}
else {
rpm = 1500; // if rpm call is false, set RPM to 1500 (as if idle)
}
int VEHvoltage;
// save vehicle voltage in variable 'VEHvoltage', return true on success
if (obd.readPID(PID_CONTROL_MODULE_VOLTAGE * 10, VEHvoltage)) {
// do nothing
}
else {
VEHvoltage = 135;
}
int coolantC;
// save coolant temperature in variable 'coolantC', return true on success
if (obd.readPID(PID_COOLANT_TEMP, coolantC)) {
// do nothing
}
else {
coolantC = (max(max(FAN2SETPOINT, FAN2SETPOINT), MAX_OFF_TEMP)) * 9 / 5 + 32;
}
int speedK;
// save vehicle speed in variable 'speedK', return true on success
if (obd.readPID(PID_SPEED, speedK)) {
// do nothing
}
else {
speedK = (AC_SPEED /.621 - 2);
}
int throttle = 50; // *** DELETE HARD CODE
int rpm = 200; // *** DELETE HARD CODE
int VEHvoltage = 142; // *** DELETE HARD CODE
int speedK = 10; // *** DELETE HARD CODE
float pot;
pot = analogRead(potPin); // *** DELETE POT TESTING
pot = pot / 1000; // *** DELETE POT TESTING
float coolantC = 100 - pot * 25; // *** DELETE POT TESTING
// COLLECT THERMISTOR TEMPERATURES
int i;
float average;
float average2;
// take N samples in a row, with a slight delay
int samples[NUMSAMPLES];
int samples2[NUMSAMPLES];
for (i = 0; i < NUMSAMPLES; i++) {
samples[i] = analogRead(THERMISTOR1PIN);
samples2[i] = analogRead(THERMISTOR2PIN);
delay(10);
}
// average all the samples out
average = 0;
average2 = 0;
for (i = 0; i < NUMSAMPLES; i++) {
average += samples[i];
average2 += samples2[i];
}
average /= NUMSAMPLES;
average2 /= NUMSAMPLES;
// convert the value to resistance
average = 1023 / average - 1;
average2 = 1023 / average2 - 1;
average = SERIESRESISTOR1 / average;
average2 = SERIESRESISTOR2 / average2;
float steinhart;
steinhart = average / THERMISTORNOMINAL1; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT1; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL1 + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert to C
float ambientC = steinhart;
steinhart = average2 / THERMISTORNOMINAL2; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT2; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL2 + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert to C
float condenserC = steinhart;
// Conversions
float ambientF = ambientC * 9 / 5 + 32;
float coolantF = coolantC * 9 / 5 + 32;
float condenserF = condenserC * 9 / 5 + 32;
float speedM = speedK * 0.621;
int differential = condenserF - ambientF;
differential = 5; // **** REMOVE HARDCODE
// AC status
int ACtrigger;
Serial.print("*** differential: ");
Serial.print(differential);
Serial.print(" AC");
if ((differential) < AC_DIFFERENTIAL) {
ACtrigger = 0;
Serial.println(" off");
}
else {
ACtrigger = 1;
Serial.println(" on");
}
// ACtrigger = 1; // **** REMOVE HARDCODE
// Etablish thresholds based on current state
if ((fan1state == 0) || (coolantF < (FAN1SETPOINT - HYSTERESIS))) {
threshold1 = FAN1SETPOINT;
}
else {
threshold1 = FAN1SETPOINT - HYSTERESIS;
}
if ((fan2state == 0) || (coolantF < (FAN2SETPOINT - HYSTERESIS))) {
threshold2 = FAN2SETPOINT;
}
else {
threshold2 = FAN2SETPOINT - HYSTERESIS;
}
// ** SET FANS BASED ON CONDITIONS **
// TURN OFF FANS IF SUFFICIENT SPEED OR WOT OR INSUFFICIENT VOLTAGE OR INSUFFICIENT RPM AND TEMP BELOW RELEASE THRESHOLD AND AC DIFF BELOW MAX
if ( ( (throttle >= THROTTLE_LIMIT)
|| (VEHvoltage < REQ_VOLTAGE * 10)
|| ((rpm < RPM_LIMIT) && (rpm > 0))
|| (speedM >= NO_FAN_SPEED)
) && (coolantF < MAX_OFF_TEMP) && (differential < AC_WARNING)) {
digitalWrite(RelayDrive1, LOW); //Turn the Relay1 Off
digitalWrite(RelayDrive2, LOW); //Turn the Relay2 Off
fan1state = 0;
fan2state = 0;
}
else {
// CHECK FAN 1 FOR THRESHOLD (override if AC triggered on)
if ((ACtrigger == 1) && (speedM < AC_SPEED) && (differential < AC_WARNING) && (rpm >= RPM_LIMIT)) {
digitalWrite(RelayDrive1, HIGH); //Turn the Relay1 On
fan1state = 1;
}
else {
if ((coolantF >= threshold1) || (differential >= AC_WARNING)) {
digitalWrite(RelayDrive1, HIGH); //Turn the Relay1 On
fan1state = 1;
}
else {
digitalWrite(RelayDrive1, LOW); //Turn the Relay1 Off
fan1state = 0;
}
}
// CHECK FAN 2 FOR THRESHOLD (or if AC > max threshold)
if ((coolantF >= threshold2) || (differential >= AC_WARNING)) {
digitalWrite(RelayDrive2, HIGH); //Turn the Relay2 On
fan2state = 1;
}
else {
digitalWrite(RelayDrive2, LOW); //Turn the Relay2 Off
fan2state = 0;
}
} // end else condition if not WOT
// LCD READOUTS
int ambient = ambientF;
int coolant = coolantF;
lcd.setCursor(0, 0); // set the cursor to column 0, line 0
lcd.print("CLT");
lcd.setCursor(4, 0);
lcd.print(coolant);
lcd.setCursor(0, 1); // set the cursor to column 0, line 1
lcd.print("AMB");
lcd.setCursor(4, 1);
lcd.print(ambient);
lcd.setCursor(10, 1);
lcd.print(VEHvoltage / 10.0, 1);
lcd.setCursor(14, 1);
lcd.print("v ");
// coolant
if (coolant < -9) {
lcd.setCursor(7,0);
lcd.print((char)223);
}
else if (coolant <0) {
lcd.setCursor(6,0);
lcd.print((char)223);
lcd.setCursor(7,0);
lcd.write(254);
}
else if (coolant < 10) {
lcd.setCursor(5,0);
lcd.print((char)223);
lcd.setCursor(6,0);
lcd.write(254);
lcd.setCursor(7,0);
lcd.write(254);
}
else if (coolant < 100) {
lcd.setCursor(6,0);
lcd.print((char)223);
lcd.setCursor(7,0);
lcd.write(254);
}
else {
lcd.setCursor(7,0);
lcd.print((char)223);
}
// ambient
if (ambient < -9) {
lcd.setCursor(7,1);
lcd.print((char)223);
}
else if (ambient <0) {
lcd.setCursor(6,1);
lcd.print((char)223);
lcd.setCursor(7,1);
lcd.write(254);
}
else if (ambient < 10) {
lcd.setCursor(5,1);
lcd.print((char)223);
lcd.setCursor(6,1);
lcd.write(254);
lcd.setCursor(7,1);
lcd.write(254);
}
else if (ambient < 100) {
lcd.setCursor(6,1);
lcd.print((char)223);
lcd.setCursor(7,1);
lcd.write(254);
}
else {
lcd.setCursor(7,1);
lcd.print((char)223);
}
// AC WARNING FLASH
if (differential >= AC_WARNING) {
lcd.setCursor(9,0);
lcd.print(" AC");
lcd.write(254);
lcd.write(0);
lcd.print(differential);
if (differential < 10) {
lcd.setCursor(15,0);
lcd.write(254);
}
delay(1000);
lcd.setCursor(9,0);
lcd.print(" ******");
}
else {
// if WOT or HWY or RPM or VLT
if ((rpm < RPM_LIMIT) && (rpm > 0) && (fan1state == 0)) {
lcd.setCursor(9,0);
lcd.print("RPM< --");
}
else if ((rpm < RPM_LIMIT) && (rpm > 0) && (fan2state == 0)) {
lcd.setCursor(9,0);
lcd.print("RPM< *-");
}
else if ((rpm < RPM_LIMIT) && (rpm > 0)) {
lcd.setCursor(9,0);
lcd.print("RPM< **");
}
else if ((VEHvoltage / 10.0 < REQ_VOLTAGE) && (fan1state == 0)) {
lcd.setCursor(9,0);
lcd.print("VLT< --");
}
else if ((VEHvoltage / 10.0 < REQ_VOLTAGE) && (fan2state == 0)) {
lcd.setCursor(9,0);
lcd.print("VLT< *-");
}
else if (VEHvoltage / 10.0 < REQ_VOLTAGE) {
lcd.setCursor(9,0);
lcd.print("VLT< **");
}
else if ((throttle >= THROTTLE_LIMIT) && (fan1state == 0)) {
lcd.setCursor(9,0);
lcd.print("WOT --");
}
else if ((throttle >= THROTTLE_LIMIT) && (fan2state == 0)) {
lcd.setCursor(9,0);
lcd.print("WOT *-");
}
else if (throttle >= THROTTLE_LIMIT) {
lcd.setCursor(9,0);
lcd.print("WOT **");
}
else if ((speedM >= NO_FAN_SPEED) && (fan1state == 0)) {
lcd.setCursor(9,0);
lcd.print("HWY --");
}
else if ((speedM >= NO_FAN_SPEED) && (fan2state == 0)) {
lcd.setCursor(9,0);
lcd.print("HWY *-");
}
else if (speedM >= NO_FAN_SPEED) {
lcd.setCursor(9,0);
lcd.print("HWY **");
}
// else if AC
else if ((ACtrigger == 1) && (fan1state == 1) && (coolantF < min(threshold1, threshold2))) {
lcd.setCursor(9,0);
lcd.print(" AC");
lcd.write(254);
lcd.write(0);
lcd.print(differential);
if (differential < 10) {
lcd.setCursor(15,0);
lcd.write(254);
}
}
else if ((fan1state == 1) && (fan2state == 0)) {
lcd.setCursor(9,0);
lcd.print("FAN *-");
}
else if ((fan1state == 1) && (fan2state == 1)) {
lcd.setCursor(9,0);
lcd.print("FANS **");
}
else if ((fan1state == 0) && (fan2state == 0)) {
lcd.setCursor(9,0);
lcd.print("fan off");
}
else {
lcd.setCursor(9,0);
lcd.print("missed?");
}
// FLASH IF HOT
if (coolantF >= MAX_OFF_TEMP) {
delay(500);
lcd.setCursor(14,0);
lcd.print("--");
delay(200);
}
}
}