Created
November 24, 2017 15:30
-
-
Save Rawze/e1a7e378289bf0a1c48d9ece4d703e63 to your computer and use it in GitHub Desktop.
Prostar A/C code for Arduino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Prostar Air Conditioner Arduino Sketch | |
* 08-09-2017 by Rawze. | |
* Use at own risk. | |
*/ | |
#include "Arduino.h" | |
//########################################################## | |
//hardware connections ... | |
#define LED_PIN 6 | |
#define COMPRESSOR_PIN 8 | |
#define FAN_PIN 9 | |
#define PRESS_SENSOR_PIN (unsigned char)A0 | |
#define EVAP_TEMP_PIN (unsigned char)A1 | |
/* Define the hardware pin logic condition for relays. Some relay boards use reversed logic to | |
* turn on their relays. | |
*/ | |
#define RELAY_ON false | |
#define RELAY_OFF true | |
//########################################################## | |
/* | |
* Some Hex Value feedback on input pin 'PRESS_SENSOR_PIN' that were measured while using an | |
* A/C pressure gauge test set. This list is used for converting/interpolating raw values to | |
* actual pressures. | |
*/ | |
#define TABLE_MAX_ENTRIES 20 | |
const int press_table[TABLE_MAX_ENTRIES][2] { | |
//table structure: {raw value, psi}. | |
{0x001C,0}, //full vacuum. | |
{0x0033,14}, //1 atmosphere (14 psi) empty system. | |
{0x0086,43}, //about 43psi | |
{0x00BC,70}, //about 70psi | |
{0x00E6,100}, //about 100psi | |
{0x0183,175}, //about 175psi | |
{0x019C,200}, //about 200psi | |
{0x01AC,215}, //about 200psi | |
{0x01BC,225}, //about 225psi | |
{0x0215,250}, //about 250psi 533 91, 75, | |
{0x0270,325}, //about 325psi 624 | |
{0x02A5,350}, //about 350psi 677 | |
{0x02B5,360}, //about 350psi 693 | |
{0x7FFF,0x7FFF}, //end of table marker. | |
}; | |
//########################################################## | |
/* | |
* Some Hex Value feedback on input pin 'EVAP_TEMP_PIN' that were measured while using a | |
* temp gun. This list is used for converting/interpolating raw values to | |
* actual temperature in F. | |
*/ | |
const int temp_table[TABLE_MAX_ENTRIES][2] { | |
//table structure: {raw value, degrees F}. | |
{0x0000,0}, //??. | |
{0x0072,36}, //about 36-F. | |
{0x00AD,83}, //about 83-F. | |
{0x00BC,98}, //about 98-F. | |
{0x00BF,103}, //about 103-F. | |
{0x00D3,113}, //about 113-F. | |
{0x7FFF,0x7FFF}, //end of table. | |
}; | |
//########################################################## | |
/* System Settings... | |
* | |
* MIN_SYSTEM_PRESSURE: - Below this pressure, the system is assumed to be out of refrigerant. | |
* The compressor should not run when the system is empty to prevent damage. | |
* | |
* MAX_SYSTEM_PRESSURE: - Above this pressure, the system is assumed to be overheated or in | |
* an over-pressure state. The compressor should not run to prevent damage to the system | |
* or the compressor. | |
* | |
* FAN_ON_PRESSURE: - Above this pressure, the condenser is assumed to be reaching excess | |
* temperatures and the engine fan should be engaged to lower it. | |
* | |
* SAFE_PRESSURE: - At or below this pressure, the system is considered in its "safe zone | |
* pressure", indicating engine fan assistance is no longer required. | |
* | |
* COMPRESSOR_RESTART_DELAY(Milliseconds): - This delay is used to prevent sudden re-start | |
* of the compressor when an over-pressure condition occurs. | |
* | |
* COMPRESSOR_DEBOUNCE_DELAY(Milliseconds): - Prevents the compressor from turning on/off | |
* too quickly and burning out the clutch. | |
* | |
* FAN_OFF_DELAY(Milliseconds): - This delay is used as an additional on-time for the engine fan | |
* once the system pressure has dropped below the "safe zone pressure". It is used as a de-bounce | |
* for to prevent excess or frequent on/off cycles. | |
* | |
* DE_ICE_DURATION(Milliseconds): - Span of time to prevent re-start of the compressor once the | |
* evaporater core is below a min temp. NOTE: Compressor debounce delay will be added to this time | |
* during a re-start. | |
* | |
* EVAP_MIN_TEMP(F): - Min temp the core is allowed to get before compressor is shut down. | |
* | |
* EVAP_ALLOWED_TEMP(F): - Temp that must be reached before the compressor is allowed to re-start | |
* after it has been turned off. | |
* | |
*/ | |
#define MIN_SYSTEM_PRESSURE 45 | |
#define MAX_SYSTEM_PRESSURE 360 | |
#define FAN_ON_PRESSURE 320 | |
#define SAFE_PRESSURE 215 | |
#define COMPRESSOR_RESTART_DELAY 20000 | |
#define COMPRESSOR_DEBOUNCE_DELAY 5000 | |
#define FAN_OFF_DELAY 25000 | |
#define DE_ICE_DURATION 17000 // 17000 = 22 sec. (de-ice duration(17 seconds) + compressor de-bounce delay(5 more seconds)). | |
#define EVAP_MIN_TEMP 31 | |
#define EVAP_ALLOWED_TEMP 43 | |
/* | |
* Enable/Disable the serial port feedback of the compressor hex values. This is only used | |
* for testing/debugging purposes. It can be Commented out when not needed. | |
*/ | |
#define TERMINAL_AVAIL | |
#define STATUS_MSG_INTERVAL 600 | |
//########################################################## | |
class Switch_T { //Standard Switch Template class by Rawze (08-09-2017). | |
public: | |
void animate(uint32_t _now) { | |
if( m_duration && ((_now - m_start_time) > m_duration) ) { | |
m_duration = 0; | |
m_state = !m_state; | |
} | |
} | |
void DelayedOn(uint32_t _now, uint32_t duration){ m_start_time = _now; m_duration = duration; m_state = false; } | |
void DelayedOff(uint32_t _now, uint32_t duration){ m_start_time = _now; m_duration = duration; m_state = true; } | |
bool GetState(){ return m_state; } | |
bool IsBusy(){ return (m_duration > 0); } | |
bool IsInNonBusyState(bool state){ return ( (m_state == state) && (m_duration == 0) ); } | |
void Reset(bool state = false) {m_duration = 0; m_start_time = 0; m_state = state;} | |
void SetState(bool state){m_duration = 0; m_state = state; } | |
void ToggleState(){ m_state = !m_state; } | |
private: | |
bool m_state = false; | |
uint32_t m_duration = 0; | |
uint32_t m_start_time = 0; | |
}; | |
//########################################################## | |
/*Quick Interpolate a value from range A into range B. by Rawze 05-12-2016. | |
* Great for conversion of values by way of a lookup table or for scaling | |
* values up/down, or simply into a different range. | |
*/ | |
template <typename _T> _T interpolate(_T val, _T i_min, _T i_max, _T o_min, _T o_max ){ | |
float _v=(float)val, _il=(float)i_min, _ih=(float)i_max, _ol=(float)o_min, _oh=(float)o_max; | |
float i_span = _ih - _il; if(!i_span) return 0; //prevent div by 0. | |
float o_span = _oh - _ol; if(!o_span) return 0; //prevent div from 0. | |
i_span = (o_span / i_span); | |
i_span = _ol + ((_v - _il) * i_span); | |
return (_T)(i_span); | |
} | |
//########################################################## | |
/* Use a lookup table to find an analog value. | |
* CAUTION: The routine assumes the table is sort ordered min to max from 0 to | |
* highest entry down the left hand column. | |
* Assumed table structure: {raw value, result}. | |
*/ | |
int GetAnalogVal(const int lookup_value, const int table[][2]){ | |
//create a reference to the table structure. | |
enum {table_col_left = 0,table_col_right = 1}; | |
//start at the bottom/lowest entry. | |
int _ptr = 0; | |
int _upper_left = table[_ptr][table_col_left]; | |
int _upper_right = table[_ptr][table_col_right]; | |
if(lookup_value <= _upper_left) { return _upper_right; } //at or below min range?. | |
//find the entry that is slightly too high. Skip the lowest, we already checked it. | |
for (_ptr = 1; _ptr < TABLE_MAX_ENTRIES; ++_ptr ){ | |
//grab the next pair. | |
_upper_left = table[_ptr][table_col_left]; | |
_upper_right = table[_ptr][table_col_right]; | |
if(_upper_left > lookup_value){ break; } //slightly higher? | |
if(_upper_left == 0x7FFF || _upper_left == lookup_value ){ | |
return _upper_right; //out of range, or a quick reply in case we got an exact hit. | |
} | |
} | |
//Out of range check just in case we hit the end of the table. | |
if( _ptr == TABLE_MAX_ENTRIES ){ return 0x7FFF; } | |
--_ptr; //back up one so we can obtain the lower bounds to interpolate from. | |
int _lower_left = table[_ptr][table_col_left]; | |
int _lower_right = table[_ptr][table_col_right]; | |
int _entry_min = table[_ptr][1]; | |
int _psi_min = table[_ptr][0]; | |
return interpolate<int>( | |
lookup_value, //lookup value to scale/convert. | |
_lower_left, //input range min. one row back from our upper. | |
_upper_left, //input range max. | |
_lower_right, // opt range min. one row back from our last result. | |
_upper_right //opt range max. | |
); | |
} | |
//globals... | |
//########################################################## | |
Switch_T CompressorSwitch; //On.Off switch for the A/C compressor clutch. logic true = run the compressor | |
Switch_T EngineFanSwitch; //On.Off switch for the engine fan. logic true = run the fan. | |
Switch_T EvaporatorTempSwitch; //On.Off safety switch for the evaporater core to prevent freezing. logic true = in range. | |
#ifdef TERMINAL_AVAIL | |
Switch_T SerialTimer; //Trigger for displaying data to the serial terminal. | |
#endif //~TERMINAL_AVAIL | |
//########################################################## | |
void setup() | |
{ | |
//Setup IO pins... | |
pinMode(PRESS_SENSOR_PIN, INPUT); | |
pinMode(EVAP_TEMP_PIN, INPUT); | |
pinMode(LED_PIN, OUTPUT); | |
pinMode(COMPRESSOR_PIN, OUTPUT); | |
pinMode(FAN_PIN, OUTPUT); | |
digitalWrite(COMPRESSOR_PIN, RELAY_OFF); //apply compressor state. | |
digitalWrite(FAN_PIN, RELAY_OFF); //apply engine fan state. | |
//set initial state. | |
CompressorSwitch.SetState(false); | |
EngineFanSwitch.SetState(false); | |
EvaporatorTempSwitch.SetState(false); | |
uint32_t _now = millis(); | |
//Terminal setup ... | |
delay(300); | |
#ifdef TERMINAL_AVAIL | |
Serial.begin(19200); | |
Serial.setTimeout(500); | |
delay(1000); | |
Serial.print("\n\nReboot!!\n\n"); | |
SerialTimer.DelayedOn(_now,STATUS_MSG_INTERVAL); | |
#endif //~TERMINAL_AVAIL | |
} | |
//########################################################## | |
//Main program. | |
void loop(){ | |
uint32_t _now = millis(); | |
delay(300); | |
//get system pressure. | |
int raw_sig = analogRead(PRESS_SENSOR_PIN); | |
int SystemPressure = GetAnalogVal(raw_sig, press_table); | |
//get core temp. | |
raw_sig = analogRead(EVAP_TEMP_PIN); | |
int EvapTemp = GetAnalogVal(raw_sig, temp_table); | |
// Evaporater Switch logic ... | |
if( | |
(EvaporatorTempSwitch.IsInNonBusyState(false)) || //Simply not on yet? | |
(EvapTemp < EVAP_MIN_TEMP) || //Temp too low? | |
(EvapTemp < EVAP_ALLOWED_TEMP && EvaporatorTempSwitch.IsBusy()) //Still in recovery? | |
){ | |
EvaporatorTempSwitch.DelayedOn(_now,DE_ICE_DURATION); //off now, then auto-on after a delay. | |
} | |
// ~ Evaporater Switch logic ... | |
// A/C compressor clutch logic ... | |
if(SystemPressure > MAX_SYSTEM_PRESSURE){ | |
CompressorSwitch.DelayedOn(_now,COMPRESSOR_RESTART_DELAY); //off now, then auto-on after a delay. | |
} | |
else if(SystemPressure < MIN_SYSTEM_PRESSURE){ | |
CompressorSwitch.SetState(false); //off now and set not busy. | |
} | |
else if( CompressorSwitch.IsInNonBusyState(false) ){ //no errors or conditions, but still not on?... | |
CompressorSwitch.DelayedOn(_now,COMPRESSOR_DEBOUNCE_DELAY); | |
} | |
// ~ A/C compressor clutch logic | |
// Engine fan clutch logic ... | |
if(SystemPressure > FAN_ON_PRESSURE){ | |
EngineFanSwitch.DelayedOff(_now,FAN_OFF_DELAY); //turn on now and set a delay before it can be off again. | |
} | |
// ~ Engine fan clutch logic | |
//Update hardware logic states ... | |
bool CompressorState = (CompressorSwitch.GetState() && EvaporatorTempSwitch.GetState()) ? RELAY_ON : RELAY_OFF; | |
bool FanState = (EngineFanSwitch.GetState()) ? RELAY_ON : RELAY_OFF; | |
// ~ Update hardware pin states | |
//report pressure to terminal. | |
#ifdef TERMINAL_AVAIL | |
if( SerialTimer.GetState() ){ | |
SerialTimer.DelayedOn(_now,STATUS_MSG_INTERVAL); | |
Serial.print("\n System Pressure: " );Serial.print(SystemPressure); | |
Serial.print(" Evap Temp(f): " );Serial.print(EvapTemp); | |
Serial.print(" Cmprsr sw: " );Serial.print( (CompressorState == RELAY_ON) , HEX ); | |
Serial.print(" Fan sw: " );Serial.print( (FanState == RELAY_ON), HEX ); | |
Serial.print(" Evap temp sw: " );Serial.print( EvaporatorTempSwitch.GetState(), HEX ); | |
} | |
#endif //~TERMINAL_AVAIL | |
//Send hardware states to pins ... | |
digitalWrite(COMPRESSOR_PIN, CompressorState); //apply compressor state. | |
digitalWrite(FAN_PIN, FanState); //apply engine fan state. | |
// ~ Update hardware pin states | |
// Object animation routines. | |
/* Animation routines allow objects to operate separate of main program | |
* code space, simulating a multi-threading environment. | |
*/ | |
CompressorSwitch.animate(_now); | |
EngineFanSwitch.animate(_now); | |
EvaporatorTempSwitch.animate(_now); | |
SerialTimer.animate(_now); | |
// ~ Object animation routines | |
} | |
//########################################################## |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment