Problem:
Although I’ve accomplished the ability to run long DLTS, IVT, and CVT measurements autonomously, I was worried about running the compressor when I wasn’t there. The compressor is water cooled and if I were to forget to turn the supply on or if the building services were to turn it off without me knowing then it would thermal cycle over and over. This means the He compressor would heat up to its trip temperature, cool down, and then do it again until I was able to cut power.
Solution:
This solution was actually pretty simple but turned out there were some weird hiccups along the way. The hardware solution should look very familiar since I used a lot of the same hardware and software as the Ethernet Controlled ND Filter including the ENC28J60. I chose ethernet because I have a distrust of wireless connections and serial connections along and the fact that ethernet has an almost bullet-proof physical connection. I decided that simply having a dumb switch wouldn’t work so I added a microphone used as feedback to check whether or not the compressor was actually on. The hardware schematic is shown below.
To ensure that the relay could handle the voltage that the physical switch on the compressor could, I traced the wires and found that the 250VAC input voltage was transformed down to ~20VAC so that it could power the compressor contactor and the timing circuit used to turn the compressor back on during a thermal fault. This was perfect since I originally thought about trying to switch the 250VAC at 10A which would require a beefier relay and additionally circuitry to controll it.
Once I hooked it up, it worked like a charm and I only needed to write some Arduino code and LabVIEW code to complete the work. The Arduino runs a TCP server on it where it receives plain text strings that tell it to do something at which point it complies and then responds with some data, like the current volume or the compressor relay state. The LabVIEW program simply runs a TCP client and sends and receives these text strings which is updated on the front panel shown below.
I decided, as a fail-safe, to make sure that if the Arduino ever lost power, it would go back to the same state as it was in prior to turning it off thus the need for EEPROM. Every time the relay changed its state I could simply save the new state into a reserved spot in the non-volatile memory of the Arduino so that when powered on it would look at the same spot and restore its state. Next, knowing that electronics can behave very strangely in noisy environments, I needed to make sure it didn’t hang. A watchdog time would be perfect for this and since I programmed the current state into the flash memory, restarting the Arduino programmatically wouldn’t be a problem. HOWEVER, as it turns out, my cheapness bit me and WDT doesn’t work on these eBay Arduino Nano’s due to an error in the bootloader. Whenever the WDT would fire it would put the Arduino into a boot-loop and it would never come back.
Luckily, this program is well documented and I simply just needed to flash the Nano with a new bootloader following the directions here. After frying a few other Arduinos trying to get it to work (hooked up ground and 5V backward), it flashed and my work was complete. I could now successfully remote desktop into the computer running my LabVIEW program and turn the compressor on and off from the confort of my own bed. And better yet, I programmed the functionality into the DLTS, IVT, and CVT programs so it could be turned off automatically. Check out the Arduino code below.
Arduino Code
Again, the code is long but pretty simple. I use the UIPEthernet library as my ENC28J60 controller and then the built-in libraries for EEPROM, and WDT. The microphone is connected to A7 and the relay is connected to D4 while the EEPROM locations for the On and Off voltage thresholds and the relay position are at 0x00, 0x02, and 0x04 respectively. Once the ethernet TCP server is established, the WDT is set, and the relay position is restored it just waits. Whenever it receives one of the two character ‘codes’, it will execute a set of functions. For example if the TCP server receives the “R0” command it will set the digital pin High (relay off), set the RelayPosition variable to 0, reset the thermal trip variable, save the current state of the relay position, check the volume, and then respond to the TCP client with a string of data. Check out the code for other commands and send me an email if you have questions.
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include <UIPEthernet.h>
#include <EEPROM.h>
#include <MemoryFree.h>
#include <avr/wdt.h>
EthernetServer server = EthernetServer(1010);
const int RELAY = 4;
const int MicroPhone = A7;
float Volume = 0;
unsigned long Timer = 0;
unsigned long Timer2 = 0;
int RelayPosition = 0;
int onThreshold = 20;
int offThreshold = 25;
unsigned long TimeLimit = 10000;
int thermalTrip = 0;
uint8_t mac[6] = {0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0x06};
IPAddress myIP(169, 254, 195, 139);
size_t size;
EthernetClient client;
String MessageStr = "";
void setup()
{
wdt_disable();
wdt_enable(WDTO_4S);
pinMode(7,OUTPUT);
digitalWrite(7,LOW);
delay(10);
pinMode(7, OUTPUT);
delay(10);
digitalWrite(7,HIGH);
wdt_reset();
Ethernet.begin(mac, myIP);
server.begin();
wdt_reset();
onThreshold = EEPROM.read(0);
offThreshold = EEPROM.read(2);
RelayPosition = EEPROM.read(4);
if(RelayPosition){
digitalWrite(RELAY,LOW);
pinMode(RELAY, OUTPUT);
digitalWrite(RELAY,LOW);
thermalTrip = 0;
RelayPosition = 1;
EEPROM.update(4,(int)RelayPosition);
}else{
digitalWrite(RELAY,HIGH);
pinMode(RELAY, OUTPUT);
digitalWrite(RELAY,HIGH);
thermalTrip = 0;
RelayPosition = 0;
EEPROM.update(4,(int)RelayPosition);
}
Timer2 = millis();
}
void loop()
{
wdt_reset();
Ethernet.maintain();
if (client = server.available())
{
while ((size = client.available()) > 0)
{
char* msg = (char*)malloc(size);
size = client.read(msg, size);
for(int i=0;i<size;i++){MessageStr = String(MessageStr + String(*(msg+i)));}
free(msg);
}
Ethernet.maintain();
ParseString();
client.println(MessageStr);
client.stop();
MessageStr = "";
}
Ethernet.maintain();
CheckThermalFault();
if(abs(millis()-Timer2)>15000){
digitalWrite(7,LOW);
delay(10);
digitalWrite(7,HIGH);
delay(10);
Ethernet.begin(mac, myIP);
server.begin();
Timer2 = millis();
}
}
void ParseString(void){
String Message = MessageStr;
int StrLen = Message.length();
String strType = Message.substring(0, 2);
String strInfo;
if(StrLen>3){strInfo=Message.substring(2, StrLen - 1);}else{strInfo=String("Null");}
if (strType == "R0") {
//R0 Stands for turning the relay off
SetRelay(0);
Volume = CheckVolume(250);
MessageStr = String("R0" + String(Volume));
return;
} else if (strType == "R1") {
//R1 stands for turning the relay on
SetRelay(1);
Volume = CheckVolume(250);
MessageStr = String("R1" + String(Volume));
return;
} else if (strType == "VO") {
//VO stands for the current volume
Volume = CheckVolume(250);
MessageStr = String("VO" + String(Volume));
return;
} else if (strType == "R?") {
//R? stands for checking the relay
if(RelayPosition){
MessageStr = String("R1");
}else{
MessageStr = String("R0");
}
return;
}else if (strType == "CT") {
//CT stands for calibrating the Thresholds
CalibrateThresholds();
MessageStr = String("CT"+String(onThreshold)+"-"+String(offThreshold));
return;
}else if (strType == "TT") {
//TT Thermal Trip checking
MessageStr = String("TT"+String(thermalTrip));
return;
}else if (strType == "ST") {
//TT Thermal Trip checking
Volume = CheckVolume(100);
MessageStr = String("R"+String(RelayPosition)+"\nVO"+String(Volume)+"\nTT"+String(thermalTrip)+"\nCT"+String(onThreshold)+"-"+String(offThreshold));
return;
}
}
void SetRelay(int MODE) {
if(MODE){
digitalWrite(RELAY,LOW);
thermalTrip = 0;
RelayPosition = 1;
EEPROM.update(4,(int)RelayPosition);
}else{
digitalWrite(RELAY,HIGH);
RelayPosition = 0;
EEPROM.update(4,(int)RelayPosition);
}
delay(250);
return;
}
int CheckVolume(float HowLong){
float Value = 0;
for(int i = 0;i<HowLong;i++){
Value = Value + abs(analogRead(MicroPhone)/1024.0);
delay(1);
}
Value = Value/HowLong*100.0;
return (int) Value;
}
int CheckThermalFault(void){
Volume = CheckVolume(100);
Ethernet.maintain();
if((RelayPosition)&&(Volume < onThreshold)&&(Timer == 0)){
thermalTrip = 1;
Timer = millis();
} else if((RelayPosition)&&(Volume < onThreshold)){
if(abs(millis()-Timer)>TimeLimit){
SetRelay(0);
thermalTrip = 2;
return 1;
}
}else{
Timer = 0;
}
return 0;
}
void CalibrateThresholds(void){
float Helper=0;
float Helper2=0;
if(RelayPosition){
SetRelay(0);
Helper = CheckVolume(1000);
SetRelay(1);
Helper2 = CheckVolume(1000);
Helper = abs((Helper2+Helper)/2);
EEPROM.update(0,(int)Helper);
EEPROM.update(2,(int)Helper);
}else{
SetRelay(1);
Helper = CheckVolume(1000);
SetRelay(0);
Helper2 = CheckVolume(1000);
Helper = abs((Helper2+Helper)/2);
EEPROM.update(0,(int)Helper);
EEPROM.update(2,(int)Helper);
}
onThreshold = EEPROM.read(0);
offThreshold = EEPROM.read(2);
return;
}