I'm evolving a hot tub controller project, which consists of:
- An 80s-era Cal Spas hot tub (pretty easy to understand and work on, all mechanical switches, etc.)
- Raspberry Pi 2
- WiFi dongle to attach to my home WiFi network
- DS18b20 waterproof thermometer (up over the edge of the hot tub, hanging in the water)
- An 8-channel 5V relay module, spliced in parallel (considering switching to series) into the "top side" control dials (Jets, Heater, Lights, Cold air blower, Hot air blower)
- A couple of ThingSpeak data logging channels - one for (upward) data logging, one for (downward) commands
Algorithm-wise: I read the temperature every 60-ish seconds, make sure the temp hasn't exceeded my safety threshold (104 degrees), log the temperature to ThingSpeak, then make a decision based on (a) whether the hot tub is heating or cooling, and (b) the rate of heating or cooling.
If the hot tub is cooling (heater not on), I look at the rate of cooling:
- If cooling > 6 degrees per hour, alert and go into "safe mode" (no longer attempt to automatically regulate temperature) -- see heating < 1 degree per hour (below) for explanation of why
- If cooling 2-6 degrees per hour, alert that the cover is likely open (cooling quicker than normal)
- If cooling between 0 and 2 degrees per hour, this is normal
- If actually warming, throw an alert and go into "safe mode" (because I don't know what's going on) <-- overly-safe? considering backing away from this
- If heating > 9 degrees per hour, alert and go into "safe mode" (because I don't know what's going on; my hot tub doesn't heat this fast under normal conditions)
- f heating 5-8 degrees per hour, this is normal (6.4 is average)
- If heating 1-5 degrees per hour, alert that the cover is likely open (heating slower than normal)
- If heating < 1 degree per hour, alert and go into "safe mode" -- Two misuse cases:
- There's a mechanical over-temp switch that pops and causes circulation pump to run, but shuts off the heater element; this over-temp switch is erratic and I don't want to run the circulation pump forever if I'm not heating.
- If the DS18b20 is pulled out of the water, we have no idea whether we're properly heating. The apparent temperature drops rapidly in this case. My wife may or may not have accidentally done exactly this the first day I started heating under RPi automation.
Code: Select all
if currentTemp < targetTemp - tempSlop: # 102.0 - 0.5 = 101.5 heaterOn() elif currentTemp > targetTemp + tempSlop: # 102.0 + 0.5 = 102.5 heaterOff()
This is all happy goodness. I *love* this project, and it tickles me whenever I hear it kick on and off.
Once in a while it'll kick into "safe mode" when nothing's really gone wrong. This is due to the sensitivity of the readings coming out of the DS18b20, and my ability to "smooth" them. Its apparent resolution is something like 0.11 degrees (Fahrenheit). It always jumps in quanta of that value. Also, while the temperature is gradually declining during the cooling cycle, it will sometimes toggle between two adjacent values before settling on the lower one, e.g.:
Code: Select all
(Cooling cycle) 100.5116 100.5116 100.5116 100.5116 100.4 100.5116 100.5116 100.5116 100.5116 100.4 100.4 100.5116 100.4 100.4 100.4 100.4 100.4 [...]
I also have aberrations during the cooling cycle. I was in the hot tub once when it heated to 102.5, turned off, and (due to us moving around in the hot tub?) appeared to "heat" very briefly while in cooling mode, and my program popped into safe mode. We started getting cold when we figured it out.
Lastly: When the tub goes into cooling mode, one of two (or both) things happen:
- The temperature in the water stratifies, so warmer water is at the top and cooler water is at the bottom.
- The water in the plumbing (out to the circulation pump, through the heater), which is outside the tub itself, cools more rapidly than the water inside the tub.
Code: Select all
101.5 <-- pump kicks on 101.2 101.0 <-- hits bottom, now on the upswing 101.3 101.5 <-- now recovered 101.6 ...
Because of this, I don't make any of the above temperature-based decisions within 5 minutes of the heater kicking on. I wait 5 readings, purge the temperature history, and start from scratch at minute 6.
So, all the background covered, to my solicitation for advice:
What is the best way to "smooth" the stream of outputs from the DS18b20 (which are, again, roughly 1 minute apart) and sensibly figure out what the actual heating/cooling slope looks like?
I've tried some naive approaches like comparing the first and last readings in a 5-reading window (sometimes shows the opposite of what I want), to average the first couple and the last couple, etc. These all have their disadvantages, are are really susceptible to the toggling back and forth of super-sensitive readings.
This strikes me as more of a statistics question, and less of a DS18b20 question. I'm hoping one or two of you are wizzes at this kind of thing.
Thanks in advance!