Tuesday, March 7, 2017

Logging the water height in a well cheaply and non-intrusively

I made a little gadget to observe the water height in my water well over a period of days or weeks. I'd previously found the height by tapping on the top of the well and recording the echoes with a phone, but I wanted to see how the water height varies over the course of a day, week, or month. Unfortunately, I have to eat, sleep, go to work, clean the house, socialize, and do a variety of other tasks that get in the way of tapping on the top of my well every 30 seconds or so around the clock and analyzing the recorded sound file to determine the water height. Luckily, microcontrollers have none of those obligations and are perfectly happy to tap, listen, and analyze all day and all night, so I whipped up a water well watching robot to record the data to an SD card, where I can download it at my leisure.

Broadly, the device consists of a solenoid (the tapper), a microphone with amplifier (the listener), and a Adafruit M0 Adalogger microcontroller (the analyzer).


The microcontroller activates the solenoid, which taps the top of the well. The sound wave travels down the well, reflects off of the water surface, and travels back up the well, where the microphone detects the sound. The controller records the sounds coming from the microphone, listening for both the initial tap and the reflected echo. I chose the M0 because of its 32 kB of RAM. To get the finest precision possible, you need to record the microphone data at high frequency. With the ADC set to record the microphone reading every 40ish microseconds, I can record up to roughly seven thousand data points, or about 0.3 seconds of data. A plain-jane Arduino with an ATmega328 has only 2 kB of RAM, and would only be able to record about 0.02 seconds of data - not enough to catch the echo.

After recording, the microcontroller stores the data to the attached SD card.

The raw recorded data looks like this:


Just by inspection, it's clear that the echos occur about every 60,000 microseconds. We could easily write some code to teach the microcontroller to estimate the time between the start of the first two pulses, convert that to a distance, record it, and we'd be ready for the robots to take over!

Except it wasn't always so reliable. I found that occasionally, there would be noise that confused the logic, causing it to mistakenly think a random noise was the echo, and incorrectly estimate the depth to the water. Or the solenoid would come un-moored from the brick I placed on it to keep it pinging in the same place, and would dance over to a spot on the well that made a much quieter tap on the well. Or both:


What's going on in this case? I'm pretty sure the water depth didn't change by 25% in the 30 seconds between readings, so I bet the peak near 70,000 microseconds is probably the true echo and the large peak at 50,000 microseconds is likely noise.

One side note on this - the microphone amplifier I'm using is an adjustable-gain amplifier. I set the gain to the least sensitive setting (40 dB) for the first 30 milliseconds so the initial tap doesn't saturate the detector, then ramp it up to the second most sensitive setting (50 dB). That's why the initial tap and the echo sometimes appear to have similar amplitudes - in reality, the echo is at least 10-15 decibels or so quieter than the initial tap.

Aside aside, I noticed that the true echoes all had a peak-to-trough time (inverse frequency) of roughly 1200 microseconds, or peak-to-peak time of roughly 2400 microseconds, or roughly 415 Hz frequency. 



The noise, however, was typically of a different frequency, often much higher. Note the same x-axis span below as above.


If we can create a bandpass filter to only pass signals that approximately match this 415 Hz frequency, then we can eliminate the noise from contention. As a simple bandpass filter, I settled on the absolute value of difference between two moving averages, one 1200 microseconds behind the other. For true signals at the proper frequency, when the moving average reaches the peak, it subtracts the negative part of the peak from the positive part, giving a double amplitude response. For high frequency noise, each 1200-microsecond average will smooth out the high frequency peaks and troughs. For low frequency noise, the signal will not have moved much in the 1200 microsecond time between the two averages, so the difference will be close to zero. Here it is applied to the above noise (and the following true echo signal).


Boom! The high frequency noise disappears, while the true echo is amplified.

I don't actually know the fundamental reason that the frequency of the echo is 415 Hz. I suspect it has to do with the well's casing diameter (4" Schedule 40 PVC), but I frankly don't know. Sound travels roughly 16 inches in 1200 microseconds, which is roughly 4 x the casing inner diameter? Someone who knows more about acoustics could probably educate me on this subject, or I should bang on some wells of different diameters and see what frequency response they have.

Now time to see how the water level changes over time! I ran it overnight and got the following results. I've omitted a few outliers.



The data suggests that the water level varies by a few tenths of a foot over a day period with no draw. It's hard to say this definitively, though, as there are several potential sources of error that could be masking any level change. For instance, the speed of sound varies with temperature. A one-degree Fahrenheit increase in the average temperature of the air in the well would cause a perceived depth increase of 0.04 feet, even if the actual level did not change. The well should be fairly well-insulated (ha ha), but over the course of a season, the ground temperature could change appreciably, so I wouldn't trust the measurements to an accuracy of more than about 3-5% over long periods of time on this factor alone.

I changed the ping interval to four seconds and watched the level after drawing water from the well. The pump kicks on and off with a pressure switch in a pressure bladder at the surface. You can see several draw down and build up cycles in the first 150 seconds, at which point the flow of water was stopped and the pump remained off. The water level was initially drawn down to about 36 feet from its initial 33.4 feet, taking about five minutes to recover 95% of the level, and about 15 minutes to fully recover to the initial level. I'll have to run the pump for a much longer period and see what the drawdown is for higher usage rates to estimate a maximum well deliverability. I still have no idea at what level the pump in my well is set or what the total depth of the well is.




Here's the code if you're curious.


Saturday, January 7, 2017

How to find the water depth in a well ... with a cell phone!

Have a water well? Want to figure out how deep the water level is? So did I, so I made an overly complicated contraption to do so. But before I get into that, there's a much, much easier way to figure it out. You simply need to time the echo of a loud noise traveling down the well and back, and use the speed of sound in air to estimate the length the sound traveled. To accomplish that, the hardware you need is a cell phone... and that's it.

Begin recording a video (or just sound) on the phone. Place the cell phone on top of the well, then knock the top of the well with your knuckles or with an object. You should be able to hear the echo of the knock reverberating in the well. Stop recording the video, then load it to a computer. Convert the video or sound recording file to a .WAV sound file (there's several places online that will do this, I used this one). Open the resulting .WAV file with a program that allows you to visualize the sound waveform. I used Audacity (here).

I made a short video showing how to use the Audacity program here:



Zoom in on the knock sound, and if it all worked out, you should be able to see high amplitude echoes well above the background noise level. Use the software to measure the time between the leading edge of the initial sound and the leading edge of any echoes you observe.




I like the Audacity program because if you choose the Selection tool from the top toolbar and click and drag within the waveform area, the Selection start and end time is given in the box at the bottom, with a precision of one thousandth of a second.


In my case, the echoes were about 0.060 seconds apart. Next, we need to translate that to a distance. The gas in the well above the water should have a composition close to air, so we can assume the speed of sound is that of air. The speed of sound varies with temperature. The temperature of the air in the well is probably closer to the ground temperature than the ambient temperature, so assuming 72 F (yep, it's hot here), that gives me a speed of sound of about 1130 ft/s or 344 m/s from the table here.

Then, it's a simple matter of multiplying the echo interval by the speed of sound to find the total distance that the sound traveled each echo.

Sound travel distance = speed x time
Sound travel distance = 1130 feet/second x 0.060 seconds
Sound travel distance = 68 feet

Don't forget that the sound has to travel down the well, reflect off of the water, and travel back up the well before you hear the echo. The total travel distance for the sound is therefore twice the water depth. Divide the total travel distance by two and that will give you the water depth!

Depth = Total travel distance / 2
Depth = 68 feet / 2 = 34 feet


One little caveat here - we're assuming that the echo we're hearing is from the surface of the water, and not the sound bouncing off of a change in the diameter of the well or some trash stuck higher up in the well. To confirm that the echo response is indeed the water, try running the pump for a few minutes and measuring the water depth again. The water level will fall while the pump is running in most wells, so you should see a slight increase in the water depth in the second measurement. If you don't see a decrease, either the water level is not drawn down appreciably while the pump is running, or there is an obstruction higher up in the well that the sound is echoing off of, masking the water level.

And there you have it! I bet the salesman didn't tout the high precision water well depth estimating capabilities of your phone when you bought yours! The next time you upgrade, be sure to insist on this feature, along with the ability to make phone calls!

For my next trick, I want to be able to log the well depth over a period of time, and watch as it rises and falls as the pump is turned on and off. Since I don't want to sit there whacking the well all day, I'm putting together a microcontroller datalogger to do the whacking, listening, and data interpretation. More on that next time.

Saturday, November 5, 2016

Arduino 4-20 mA input

This is a quick discussion of how to feed a 4-20 mA signal into an Arduino. Looking for 4-20 mA output from an Arduino? See here.

I bought a pressure transmitter off of ebay that outputs a 4-20 mA current signal, because I like to live it up. The 4-20 mA current is very common in industrial control systems, so it's relatively easy to find various transmitters with this output. However, an Arduino can't natively read a current signal with its analog input pins - they measure voltage. We need to convert this current signal to a voltage. Fortunately, this is a piece of cake to do with a simple circuit.


Precious peppy pressure


The transmitter I bought is a Setra 209 model (datasheet here). It takes a 9-30V input, and acts as a current controller to put out a 4 to 20 milliamp signal proportional to the pressure it senses, with a span of zero to 100 psig. Plotting it out:



We can use a simple resistor circuit to generate a voltage readable to the Arduino that is proportional to the current. The Arduino AnalogRead function gives a 0-1023 value output proportional to the measured voltage on the analog input pin, with 0 corresponding to 0V or Ground (duh) and 1023 corresponding to the reference voltage (5V for the plain jane Arduino if nothing's connected to the Aref pin).


That's really it. I don't think electronics gets any easier than this.

The standard Arduino's analog input pins can read zero to 5 volts. If 5 volts is our maximum signal at 20 milliamps, that requires a resistance of 250 ohms:


R = V/I

R = 5 V/ 0.02 A = 250 ohms

If this were instead a 3.3V microcontroller, we'd require a 165 ohm resistor to read the full range output by the same math. Whichever reference voltage, you can use a lower resistor, but it cuts down the readable span.

We can now convert the measured voltage back to whatever units our signal is in. To do this, we just need a little algebra! We can plot an x-y chart, where the x-axis is the measured current/voltage/AnalogRead output and the y-axis is the process variable (pressure in this case). 

I live my life by the principles of slope intercept form

Usually, a precision resistor is used for the reference resistor, with a low temperature coefficient (change in resistance with temperature), but for my purposes, an ordinary resistor suffices. I had a nominally 220 ohm resistor I measured as 217 ohms actual resistance, and a 33 ohm resistor that measured true, which combined in series gave 250 ohms. We don't need exactly the correct resistance, though - we can compensate in code. Here's everything breadboarded up - not much to it:


The transmitter worked like a champ, with the measured signal tracking the manual gauge I had connected to it as I varied the pressure.


One other nice feature of the 4-20 mA signal scheme is that the low end of the signal output is 4 milliamps, not zero. This makes it easy to detect errors, as happened on the above data logging run when I bumped the wire around 10,000 milliseconds in. The raw reading went to zero, when it should have bottomed out at about 200 if it were a real zero signal. This tells me that something is up with the wiring at this point, and the data can't be trusted. If I wrote a program to use this transmitter reading as an input, I would know to handle any zero signal readings as an error, not as a true data point.


Here's the code: 
float PressureReading;     
float ResistorValue = 250;  //reference resistor resistance
float Pmax = 100;           //Max of process variable range
float Pmin = 0;             //Min of process variable range
float Vref = 5;             //Reference voltage for AnalogRead


void setup() {
    Serial.begin(9600);
    Serial.println("Time, Raw reading, Corrected reading");
}

void loop() {
  Serial.print(millis());
  Serial.print(",");

  //Read the voltage across the reference resistor
  PressureReading = analogRead(A3);
  
  Serial.print(PressureReading);
  Serial.print(",");

  //Convert the voltage signal to a pressure reading
  PressureReading =(Pmax-Pmin)*Vref/(1023*ResistorValue*(0.02-0.004))*(PressureReading-1023*0.004*ResistorValue/Vref);

  Serial.println(PressureReading);
  
  delay(500);
  
}

Sunday, October 30, 2016

DIY Signal to Pressure Transducer

I was bored, so I made a home-built signal to pressure transducer. You can use this sort of thing to control the position of a pneumatically actuated valve, among other sundry uses. It works by balancing the flow of air in to a chamber through a needle valve with flow of air out of the chamber through a solenoid valve. The solenoid valve can be quickly opened and closed. By adjusting the relative amount of time the solenoid valve spends open and closed, the pressure in the chamber can be precisely controlled.




The maximum rate at which air can leave the chamber must be balanced with the maximum rate that air can enter the chamber so the output can be varied over a wide range. The maximum exit rate is dictated by the size of the hole the air can flow through when the solenoid valve is wide open. I used a Clippard EV-3-12 solenoid valve I had sitting around, which from the given air consumption rate (0.6 scfm at 100 psi supply pressure), has a roughly 0.025" flow path. I wanted a slightly lower air consumption rate, so I made my own 1/64" (0.016") orifice fitting by filling a 10-32 fitting with epoxy and drilling a 1/64" hole in the set epoxy.

Nothing to sneeze at - you'll lose the drill bit!
1/64" hole? How in the world do you do that? With a 1/64" drill bit of course! I got a couple of these TINY bits for just this sort of occasion. A handy dandy orifice flow calculation shows that this should have a 0.25 scfm air consumption when choked from 100 psig,

The air inlet area must be even smaller than this if it will allow pressures to be controlled over a wide range. Luckily a needle valve I have can be slightly cracked open to just allow enough flow that allows a good range output with decent (5-10 second) re-pressurization times.

The solenoid valve takes 12 volts to open. An IRF510 transistor connected to an Arduino's digital output pin allows the amount of time the solenoid valve spends open and closed to be adjusted to change the equilibrium pressure in the chamber. You can't just use the Arduino PWM output to throttle the valve, though - the on/off frequency used by the PWM pins is way, way faster than the amount of time the valve takes to open and close (which is 5-10 milliseconds from the valve specs). When the output signal to the valve is greater than 50%, I programmed the valve to spend 20 milliseconds off, followed by a variable amount of time on proportional to the output signal. Vice versa for when the output is less than 50%.

I then connected the pressure chamber to the pneumatic actuator of a ball valve I had sitting around.  Although flow control with a ball valve is usually a pretty dicey affair (not that I haven't seen it done), this ball valve should now be able to somewhat precisely control flow! I see another day of boredom avoided with that!

Here's the whole shebang in action:




Sunday, August 21, 2016

Cheap pressure transmitter hack attempt...and failure

Looking for a low cost pressure transducer, I came across these mini digital tire pressure gauges. I thought there might be some signal on its circuit board I could extract to read the pressure from another device. Let's crack it open and see!

The gauge is a Slime brand, model 20268

First things, the device comes with a Schrader valve type fitting to connect to a tire stem to read the pressure. I wanted to connect it to an NPT fitting, so I filled the Schraeder valve connection  point with epoxy. Once it set, I drilled and tapped a 10-32 hole, for which I had adapters to NPT fittings.




On the inside, the device has a pressure sensor chip directly on its main circuit board, which is exposed to the pressure tap. An o-ring seals the circuit board to the pressure tap in a half-torso'd attempt to keep leaks to a minimum. Interesting.

Under the cover


Other side of the circuit board. Note the pressure sensor chip with the O-ring around it. Not the tightest seal, but I guess it really doesn't need to be for measuring a tire's pressure.

Back side of the plastic case - the o-ring fits in the circular groove at the top.
Putting the gauge back together and applying air pressure, it read right in line with my air compressor's gauge. It does appear to latch the highest reading it observed, keeping that reading even after the pressure was removed. This makes sense for a tire pressure sensor - you may not be in a position to easily read the gauge when pressing it against a tire valve stem, but it will retain the reading after you remove it from pressure.

See the force over area!
 
The foolproof way to read a pressure off of the board would be to read the LCD signal points. I really didn't want to eat up 12ish digital inputs and then code an interpreter that converted the LCD signal to a number, so I continued trying to find some sort of output signal on the board.

There are several test points on the circuit board, but I couldn't discern any useful signal from any of them that seemed to vary with the applied pressure. Striking out on them, I attempted to go straight for the sensor chip. I soldered wires directly to the "S+" and "S-" test points, which I took to be the sensor output. Hooking these up to an Arduino's analog input points, I couldn't see any discernible signal when I varied the pressure - they gave a ~1V output for 200 milliseconds, followed by 1 second at 0V, repeated, regardless of the applied pressure. 

Putting the gauge back together, the gauge no longer works - only reads zero now, regardless of applied pressure. I suppose I probably fried something when soldering on to the test points, or inadvertently applied a voltage somewhere that fried the sensor, so this one is a strikeout. There are several other cheap tire pressure gauge models out there, so I may try again with another.

Hey, $7 for a weekend of tinkering ain't a bad deal.


Saturday, July 23, 2016

Arduino-Cryocooler Handshake!

Just look at this!

The curved traces are "artistic" or something
That's my first attempt at having a printed circuit board manufactured, and it turned out mostly okay! I had breadboarded-up an Arduino-cryocooler interface circuit, and was dreading wiring it up using perfboard. I'd heard of the numerous small-quantity PCB manufacturers around these days and decided to try one out, Seeed Studio. I drafted up the circuit in Fritzing, a free PCB CAD program, uploaded the Gerber file output to the Seeed Studio website, and ordered 5 (the minimum quantity) boards for $19. Three to six weeks later, the boards showed up, apparently after a wild weekend in Singapore, looking just like I'd drafted! I soldered one up, powered it up...and realized I'd omitted one trace wire and one resistor.
Whoopsie! Good thing I'm a very forgiving customer to myself.
A couple Whoopsie Wires later and it was transmitting an external thermocouple reading to the cryocooler! It does this by simulating a resistance between two pins on the cryocooler's main control board. These pins are normally connected to the thermistor in the Superlink's Dewar. This thermistor's resistance varies between roughly 5 to 12 kiloohms in the normal working temperature range of the cryocooler. I made a circuit consisting of a 12K resistor in parallel with two optoisolators, one in series with a 12K resistor and one in series with an 800K resistor. Both optoisolators are controlled by an Arduino PWM pins, and open/close the path to give an effective resistance that corresponds to the proper temperature. A capacitor is installed across the positive and negative leads to smooth out the PWM pulses.


Two optoisolators are used because when I tried with just one, the Arduino's 8-bit PWM output wasn't fine enough to adequately control the output when the temperature was close to the lower end of the range. The second optoisolator is in series with a much higher value resistor, and thereby contributes a much smaller signal to the output. Playing around with the values, 800K was approximately the correct value to make the fine output roughly linear between the coarse pulses, giving it roughly 12 bits or so of usable PWM output range, which is fine enough for this purpose. I used optoisolators rather than transistors because I don't know what's going on in the thermistor measuring circuit - I didn't want to take any chances with frying anything on either side with weird voltages.

The Arduino (actually an Ardweeny arduino clone) also connects to the cryocooler's serial interface to read the temperature it just attempted to transmit to the cryocooler, correcting it if it's off. It also reads the power output to the cryocooler. I added a nice little OLED display to show the measured temperature, temperature reported by the cryocooler, and the cryocooler power, so it doesn't need to be hooked up to the computer to read these values. I also added an on/off switch to allow me to soft shut down the cryocooler through its software before turning off its power. This should help avoid damage from hard stops.

The OLED in Alluring Aqua
I now have a self-contained, mobile liquid air generating machine! First order of business is to (of course...) make liquid nitrogen ice cream! I can't wait to see how the most expensive bowl of ice cream I've ever had tastes! After that, it's back to the drawing board to work up a way to liquefy gases other than air.



Update: Aw hell ya

I've loaded the board design (with corrections to my original errors) and the Arduino code here.

The other parts of the quixotic Cryocooler quest are here - Part 1 and Part 2 and Part 3 and Part 4

Saturday, March 19, 2016

Simplified Superlink Interface!

Here is part 4 of the Cryocooler Saga! You can see Part 1 and Part 2 and Part 3 also.

tl;dr - Here's a program for interfacing with a Superconductor Technologies Superlink

There's a lot of data pumped out by the cryocooler while it's operating, including various temperatures, voltages, power consumption, et cetera. Unfortunately, I could only access that data through the manufacturer's software, which won't run on anything beyond Windows XP. I have a 10 year old laptop that will run it, but I mean c'mon, it's a ten year old computer. I'm not sure how much longer ol' Speed Turtle here is going to keep on staying critical-failure-free.

High Technology
Besides, I'd ultimately like a way to replace the cold-side temperature measurement the cryocooler uses with a different device, which would be greatly simplified if the device measuring the temperature can see what it's transmitting to the Superlink.

The System Status Portal software turns out to be a pretty face for the serial back-and-forth between the computer and the Superlink. The link runs at 19200 baud, 8 data bits, zero parity, no flow control. I ran a serial monitor program while the computer had the software open and talking to the Superlink, and it showed that the back and forth looked something like this:

Request: 3/13/2016 9:59:32 PM.57264 (+0.2567 seconds)

 3C 53 59 20 4F 50 3D 22 4F 4B 22 2F 3E 0D 0A      <SY OP="OK"/>.. 

Answer: 3/13/2016 9:59:32 PM.57264 (+0.0000 seconds)

 3C 53 59 20 4F 50 3D 22 4F 4B 22 2F 3E 0A         <SY OP="OK"/>.  

Request: 3/13/2016 9:59:32 PM.60364 (+0.0000 seconds)

 3C 54 4D 20 4F 50 3D 22 47 54 22 20 4C 43 3D 22   <TM OP="GT" LC="
 43 52 22 2F 3E 0D 0A                              CR"/>..         

Answer: 3/13/2016 9:59:32 PM.60364 (+0.0000 seconds)

 0D 3C 54 4D 20 4F 50 3D 22 47 54 22 20 4C 43 3D   .<TM OP="GT" LC=
 22 43 52 22 3E 30 20 30 20 31 38 20 33 35 3C 2F   "CR">0 0 18 35</
 54 4D 3E 0A                                       TM>.            

Request: 3/13/2016 9:59:32 PM.63364 (+0.0000 seconds)

 3C 54 4D 20 4F 50 3D 22 47 54 22 20 4C 43 3D 22   <TM OP="GT" LC="
 41 43 22 2F 3E 0D 0A                              AC"/>..         

Answer: 3/13/2016 9:59:32 PM.63364 (+0.0000 seconds)

 0D 3C 54 4D 20 4F 50 3D 22 47 54 22 20 4C 43 3D   .<TM OP="GT" LC=
 22 41 43 22 3E 41 43 42 20 33 20 41 43 39 20 31   "AC">ACB 3 AC9 1
 30 3C 2F 54 4D 3E 0A 0D                           0</TM>..    

The interaction consists of query strings sent by the computer that are returned with the data requested. Pouring through the decompiled code for the System Status Portal (what can I say, I live a wild life), I managed to figure out which query strings returned the data I was interested in. Turns out, if you want to find the cold side temperature and cooler rejection temperature, you ask:

<TP OP="GT" LC="MS"/>

which is responded to with something like:

<TP OP="GT" LC="MS">F 6D80 3BE8 4D60 3C78</TP>

Each of those five hex numbers in the middle there correspond to five different measurements it's returning. Turns out the cold side wide range temperature is the second one (6D80) and the cooler rejection temperature is the third one (3BE8). That hex number is pretty meaningless, though. Pouring through more of the System Status Portal code, I found the formulas it used to convert those numbers to actual temperatures. The formula is:

Temp = (5 * (hex string converted to decimal)/32768 - Offset)/Gain

where for the cold wide range temperature, the offset is 5.814 and the gain is -0.01559. There's a different offset and gain for the rejection temperature and the other temperatures returned. Convert the two-letter parts of the query string ("TP", "OP", "LC", "MS") to their ASCII equivalent hex codes (TP=5450) and then convert that to a decimal number (hex 5450 = 21584), and search the System Status Portal code for that decimal number to work through the logic to find which data points are revealed by which query strings!

Similarly, the cooler power draw can be elucidated with the proper query string and formula, as can every other measurement that shows up in the System Status Portal. In addition to reading measurement values, the System Status Portal can also change the operating state of the cooler, switching it between manual mode where the cooler can be shut off and automatic mode, where the cooler will automatically ramp up power as the cold side temperature declines, keeping the cooler within its safe operating limits. This is accomplished with another query string,

<TP OP="ST" LC="SM">1 4</TP>

will set the cooler to manual shutdown mode, while

<TP OP="ST" LC="SM">0 4</TP>

will turn it back to automatic mode.

I needed an excuse to brush off my infantile Python skills, so I wrote a user interface that will allow you to see the cold side wide range temperature, cooler rejection temperature, cooler power, and status. You can also toggle between Manual Shutdown mode and Automatic mode, so you can shut down the cryocooler safely without the System Status Portal program. I've loaded the Python code to github - you'll need Python installed (I used version 3.5), along with the Tkinter and pySerial libraries to run it. I've only tested it on my Windows 10 computer, but it appears to work pretty well. Here it is on Github. There's a lot of room for improvement and expansion, so if you want to add functionality, go for it!




I next plan to replace the cold side temperature measurement with an external thermocouple, using an Arduino to simulate a resistance to send the temperature measurement to the Superlink. I'll then use the serial handshake to allow the Arduino to talk to the Superlink to read the temperature it's trying to simulate, allowing it to precisely control its output to make sure the Superlink is seeing the same temperature it is.