-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathArduinoThermalFanControl.ino
More file actions
225 lines (207 loc) · 7.47 KB
/
Copy pathArduinoThermalFanControl.ino
File metadata and controls
225 lines (207 loc) · 7.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
* This code controls a fan to cool and a heater to heat in order to maintain thermal control of the cryoscope cryocooler's reject surface.
* The central computer handles PID and sends commands over Serial to determine the fan percentage (from 0-100) and the heater status (ON or OFF).
* The Arduino implements a 5 minute watchdog timer to ensure that if communication is lost, the Arduino will soft-reboot and set the fan and heater to safe values (fan off, heater off) until communication is restored.
*/
#include <pwm.h>
#define FAN1_PIN 10
#define FAN2_PIN 11
#define RELAY_HEATER_MOTOR_PIN 6
#define RELAY_HEATER_REJECT_PIN 7
#define RELAY_FAN1_PIN 4
#define RELAY_FAN2_PIN 5
#define RELAY_STARTUP_COMPUTER 3
// 15 minutes timeout
#define WATCHDOG_TIMEOUT 900000UL
// Startup temperature threshold for the computer relay
#define STARTUP_TEMP (273 - 40)
PwmOut fan1Pwm(FAN1_PIN);
PwmOut fan2Pwm(FAN2_PIN);
unsigned long lastCommandTime = 0;
unsigned long lastStartupCheckTime = 0;
// 5 second interval between automatic startup-relay temperature checks
#define STARTUP_CHECK_INTERVAL 5000UL
// Tracked relay/output states, since the PWM and relay drivers can't be read back
int fan1Speed = 0;
int fan2Speed = 0;
bool motorHeaterOn = false;
bool rejectHeaterOn = false;
bool startupRelayOn = false;
// Set by SHUTDOWN; blocks the relay until temp drops below then rises above STARTUP_TEMP again
bool startupShutdownLatched = false;
bool startupHasCooled = false;
float resistance = 0;
#define PT1000_PIN A4
#define PT1000_RREF 1330.0 // Reference resistor value for PT1000 voltage divider with the PT1000 forming the bottom part of the divider
#define PT1000_R0 1000.0
#define PT1000_BETA 03.85 // change in resistance per degree Kelvin
#define PT1000_T0 273.15 // Reference temperature for the PT1000 thermistor (25°C in Kelvin)
#define PT1000_REFVOLTAGE 4.5 // Reference voltage for the PT1000 voltage divider
#define ADC_AVG_NUM 1000
float getPT1000Temperature() {
//average ADC_AVG_NUM readings to reduce noise
int rawValue = 0;
for (int i = 0; i < ADC_AVG_NUM; i++) {
rawValue += analogRead(PT1000_PIN);
}
float voltage = (((double)rawValue / (double)ADC_AVG_NUM) / 1023.0) * PT1000_REFVOLTAGE;
resistance = (PT1000_RREF * voltage) / (PT1000_REFVOLTAGE - voltage);
// float temperature = 1.0 / (1.0 / PT1000_T0 + (1.0 / PT1000_BETA) * log(resistance / PT1000_RREF)); //in Kelvin
float temperature = (resistance - PT1000_R0) / PT1000_BETA + PT1000_T0; //in Kelvin
//TODO WIP
return temperature;
}
void readPT1000() {
Serial.print(getPT1000Temperature());
}
// Turns on the computer startup relay once the reject surface is warm enough
void updateStartupRelay() {
float temperature = getPT1000Temperature();
if (startupShutdownLatched) {
if (temperature <= STARTUP_TEMP) {
startupHasCooled = true;
}
if (startupHasCooled && temperature > STARTUP_TEMP) {
startupShutdownLatched = false;
startupHasCooled = false;
} else {
startupRelayOn = false;
digitalWrite(RELAY_STARTUP_COMPUTER, LOW);
return;
}
}
// only ever turn the startup relay on, let the computer issue a shutdown command
startupRelayOn = temperature > STARTUP_TEMP;
if (startupRelayOn) {
digitalWrite(RELAY_STARTUP_COMPUTER, HIGH);
}
}
void printStatus() {
Serial.print("STATUS:TEMP=");
Serial.print(getPT1000Temperature());
Serial.print(",Resistance=");
Serial.print(resistance);
Serial.print(",MOTORHEATER=");
Serial.print(motorHeaterOn ? "ON" : "OFF");
Serial.print(",REJECTHEATER=");
Serial.print(rejectHeaterOn ? "ON" : "OFF");
Serial.print(",FAN1_PWM=");
Serial.print(fan1Speed);
Serial.print(",FAN2_PWM=");
Serial.print(fan2Speed);
Serial.print(",STARTUP_RELAY=");
Serial.print(startupRelayOn ? "ON" : "OFF");
Serial.print(",SHUTDOWN_LATCHED=");
Serial.println(startupShutdownLatched ? "ON" : "OFF");
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
fan1Pwm.begin(25000.0f, 0.0f);
fan2Pwm.begin(25000.0f, 0.0f);
pinMode(RELAY_HEATER_MOTOR_PIN, OUTPUT);
pinMode(RELAY_HEATER_REJECT_PIN, OUTPUT);
pinMode(RELAY_FAN1_PIN, OUTPUT);
pinMode(RELAY_FAN2_PIN, OUTPUT);
pinMode(RELAY_STARTUP_COMPUTER, OUTPUT);
digitalWrite(RELAY_HEATER_MOTOR_PIN, LOW);
digitalWrite(RELAY_HEATER_REJECT_PIN, LOW);
digitalWrite(RELAY_FAN1_PIN, LOW);
digitalWrite(RELAY_FAN2_PIN, LOW);
digitalWrite(RELAY_STARTUP_COMPUTER, LOW);
lastCommandTime = millis();
lastStartupCheckTime = millis();
}
void loop() {
// Check watchdog timer
if (millis() - lastCommandTime > WATCHDOG_TIMEOUT) {
// Watchdog timeout - set safe state and soft reboot
if (!startupShutdownLatched){
fan1Pwm.pulse_perc(0);
fan2Pwm.pulse_perc(0);
digitalWrite(RELAY_HEATER_MOTOR_PIN, LOW);
digitalWrite(RELAY_HEATER_REJECT_PIN, LOW);
digitalWrite(RELAY_FAN1_PIN, LOW);
digitalWrite(RELAY_FAN2_PIN, LOW);
Serial.println("WATCHDOG TIMEOUT - Soft reboot");
delay(100);
NVIC_SystemReset();
}
}
if (millis() - lastStartupCheckTime > STARTUP_CHECK_INTERVAL) {
updateStartupRelay();
lastStartupCheckTime = millis();
}
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
command.trim();
lastCommandTime = millis(); // Kick the watchdog
// Parse command format: "FAN:value" or "MOTORHEATER:state"/"REJECTHEATER:state"
// Example: "FAN:75" sets fan to 75%, "MOTORHEATER:ON" or "REJECTHEATER:OFF"
if (command.startsWith("FAN1:")) {
int fanSpeed = command.substring(5).toInt();
fanSpeed = constrain(fanSpeed, 0, 100);
fan1Speed = fanSpeed;
fan1Pwm.pulse_perc(fanSpeed);
if (fanSpeed == 0) {
digitalWrite(RELAY_FAN1_PIN, HIGH);
} else {
digitalWrite(RELAY_FAN1_PIN, LOW);
}
Serial.print("FAN1 set to ");
Serial.print(fanSpeed);
Serial.println("%");
}
else if (command.startsWith("FAN2:")) {
int fanSpeed = command.substring(5).toInt();
fanSpeed = constrain(fanSpeed, 0, 100);
fan2Speed = fanSpeed;
fan2Pwm.pulse_perc(fanSpeed);
if (fanSpeed == 0) {
digitalWrite(RELAY_FAN2_PIN, HIGH);
} else {
digitalWrite(RELAY_FAN2_PIN, LOW);
}
Serial.print("FAN2 set to ");
Serial.print(fanSpeed);
Serial.println("%");
}
else if (command.startsWith("MOTORHEATER:")) {
String heaterState = command.substring(12);
if (heaterState == "ON") {
motorHeaterOn = true;
digitalWrite(RELAY_HEATER_MOTOR_PIN, HIGH);
Serial.println("MOTORHEATER ON");
}
else if (heaterState == "OFF") {
motorHeaterOn = false;
digitalWrite(RELAY_HEATER_MOTOR_PIN, LOW);
Serial.println("MOTORHEATER OFF");
}
}
else if (command.startsWith("REJECTHEATER:")) {
String heaterState = command.substring(13);
if (heaterState == "ON") {
rejectHeaterOn = true;
digitalWrite(RELAY_HEATER_REJECT_PIN, HIGH);
Serial.println("REJECTHEATER ON");
}
else if (heaterState == "OFF") {
rejectHeaterOn = false;
digitalWrite(RELAY_HEATER_REJECT_PIN, LOW);
Serial.println("REJECTHEATER OFF");
}
}
else if (command == "SHUTDOWN") {
startupShutdownLatched = true;
startupHasCooled = false;
startupRelayOn = false;
digitalWrite(RELAY_STARTUP_COMPUTER, LOW);
Serial.println("SHUTDOWN");
}
else if (command == "STATUS") {
updateStartupRelay();
printStatus();
}
}
}