[Git][NTPsec/ntpsec][master] ntpheatusb: Heat up an NTP server with a USB relay.

Gary E. Miller gitlab at mg.gitlab.com
Mon Mar 20 23:13:22 UTC 2017


Gary E. Miller pushed to branch master at NTPsec / ntpsec


Commits:
cfc6c377 by Gary E. Miller at 2017-03-20T16:12:07-07:00
ntpheatusb: Heat up an NTP server with a USB relay.

- - - - -


2 changed files:

- contrib/README
- + contrib/ntpheatusb


Changes:

=====================================
contrib/README
=====================================
--- a/contrib/README
+++ b/contrib/README
@@ -2,9 +2,17 @@ The following tools are not production-ready. They are included only as
 conveniences, examples or rudimentary starting points for other development
 efforts.
 
-cpu-temp-log, smartctl-temp-log, temper-temp-log and zone-temp-log
-have been replaced by ntplogtemp.  Those programs will stay in contrib for
-a while to give users a chance to migrate.
+ntpheat is a program to exercise a CPU until the CPU reachs a certain
+temperature.  The idea if to get the temperature around the system
+oscillator to be near the zero TC point.  Tested on RasPi wrapped
+in bubble wrap.  Formerly known as makeheat.
+
+ntpheatusb is a program to control an external USB relayto maintain
+a stable temperature.  See the blog post "More Heat" for details.
+
+The next 4 tools cpu-temp-log, smartctl-temp-log, temper-temp-log and
+zone-temp-log have been replaced by ntplogtemp.  Those programs will stay
+ in contrib for a while to give users a chance to migrate.
 
 cpu-temp-log is a tool to use the output of 'sensors -u' and write the
 motherboard temperatures to stdout.  Useful to create a log that can be used
@@ -24,8 +32,3 @@ zone-temp-log reads /sys/class/thermal/thermal_zone*/temp to find the CPU
 temperature.  Writes all temperatures found to stdout on one line, preceded by
 the Unix UTC time in seconds.  This is useful on any Linux system that
 supports the /sys/class/thermal/thermal_zone*/temp interface.
-
-ntpheat is a program to exercise a CPU until the CPU reachs a certain
-temperature.  The idea if to get the temperature around the system
-oscillator to be near the zero TC point.  Tested on RasPi wrapped
-in bubble wrap.  Formerly known as makeheat.


=====================================
contrib/ntpheatusb
=====================================
--- /dev/null
+++ b/contrib/ntpheatusb
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+#
+# generate some heat!
+#
+# Wrap your RasPi in a closed box.  Get a usbrelay1 to control
+# an incadescent light bulb in the box.  Heat to 45C.  profit.
+#
+# This code depends on the program 'usbrelay' to manage your usbrelay
+# conencted device. Get it here:  git at github.com:darrylb123/usbrelay.git
+#
+# makeheatusb will use a lot less CPU than ntpheat, and more directly
+# heats the XTAL rather than the CPU.
+#
+# Avoid the desire to increase the wait time.  The realy clocks twice
+# per cycle, and those cycles add up.  Minimize wear on your relay.
+#
+# Try the simple P controller (the defaul) before tyring the PID controller.
+# the PID controller may take some fiddling with the constants to get
+# it working better than the simple P controller.
+
+import argparse
+import subprocess
+import sys
+import time
+
+try:
+    import ntp.util
+except ImportError as e:
+    sys.stderr.write("makeheatusb: can't find Python NTP modules "
+                     "-- check PYTHONPATH.\n%s\n" % e)
+    sys.exit(1)
+
+
+def run_binary(cmd):
+    """\
+Run a binary
+Return its output if good, None if bad
+"""
+
+    try:
+        # sadly subprocess.check_output() is not in Python 2.6
+        # so use Popen()
+        # this throws an exception if not found
+        proc = subprocess.Popen(cmd,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.STDOUT,
+                                universal_newlines=True)
+        output = proc.communicate()[0].split("\n")
+
+        if proc.returncode:
+            # non-zero return code, fail
+            print("Return %s" % proc.returncode)
+            return None
+
+    except ImportError as e:
+        sys.stderr.write("Unable to run %s binary\n" % cmd[0])
+        print(cmd)
+        sys.stderr.write("%s\n" % e)
+        return None
+
+    return output
+
+
+class PID:
+    """\
+Discrete PID control
+"""
+
+    def __init__(self, setpoint=0.0,
+                 P=2.0, I=0.0, D=1.0,
+                 Derivator=0, Integrator=0,
+                 Integrator_max=100, Integrator_min=-100):
+
+        self.Kp = P
+        self.Ki = I
+        self.Kd = D
+        self.Derivator = Derivator
+        self.Integrator = Integrator
+        self.Integrator_max = Integrator_max
+        self.Integrator_min = Integrator_min
+
+        self.set_point = setpoint
+        self.error = 0.0
+
+    def __repr__(self):
+        return ("D_value=%s, I_value=%s" % (self.D_value, self.I_value))
+
+    def update(self, current_value):
+        """
+        Calculate PID output value for given reference input and feedback
+        """
+
+        self.error = self.set_point - current_value
+
+        self.P_value = self.Kp * self.error
+        self.D_value = self.Kd * (self.error - self.Derivator)
+        self.Derivator = self.error
+
+        self.Integrator = self.Integrator + self.error
+
+        if self.Integrator > self.Integrator_max:
+                self.Integrator = self.Integrator_max
+        elif self.Integrator < self.Integrator_min:
+                self.Integrator = self.Integrator_min
+
+        self.I_value = self.Integrator * self.Ki
+
+        PID = self.P_value + self.I_value + self.D_value
+
+        return PID
+
+# Work with argvars
+parser = argparse.ArgumentParser(description="make heat with USB relay")
+parser.add_argument('-p', '--pid',
+                    action="store_true",
+                    dest='pid',
+                    help="Use PID controller instead of simple P controller.")
+parser.add_argument('-t', '--temp',
+                    default=[45.0],
+                    dest='target_temp',
+                    help="Temperature to hold in C.  Default is 45.0C",
+                    nargs=1,
+                    type=float)
+parser.add_argument('-w', '--wait',
+                    default=[60],
+                    dest='wait',
+                    help="Set delay time in seconds, default is 60",
+                    nargs=1,
+                    type=float)
+parser.add_argument('-v', '--verbose',
+                    action="store_true",
+                    dest='verbose',
+                    help="be verbose")
+parser.add_argument('-V', '--version',
+                    action="version",
+                    version="makeheatusb %s" % ntp.util.stdversion())
+args = parser.parse_args()
+
+zone0 = '/sys/class/thermal/thermal_zone0/temp'
+cnt = 0
+
+temp_gate = args.target_temp[0]
+period = float(args.wait[0])
+
+# you will need to personalize these to your relay ID:
+usbrelay_on = ['usbrelay', '959BI_1=1']
+usbrelay_off = ['usbrelay', '959BI_1=0']
+
+# to adjsut the PID variables
+# set I and D to zero
+#
+# increase P until you get a small overshoot, and mostly damped response,
+# to a large temp change
+#
+# then increase I until the persistent error goes away.
+#
+# if the temp oscillates then increase D
+#
+pid = PID(setpoint=temp_gate, P=35.0, I=10.0, D=5.0)
+
+try:
+    while True:
+        # grab the needed output
+        output = run_binary(["temper-poll", "-c"])
+        try:
+            # make sure it is a temperature
+            temp = float(output[0])
+        except:
+            # bad data, exit
+            # FIXME, should try to recover?
+            print("temper read failed: %s" % output)
+            sys.exit(1)
+
+        # the +2- is to create and 80/20 band around the setpoint
+        p_val = pid.update(temp) + 20
+        p_val1 = p_val
+        if p_val > 100:
+            p_val1 = 100
+        elif p_val < 0:
+            p_val1 = 0
+
+        if temp > temp_gate:
+            perc_t = 0
+        elif temp < (temp_gate - 3):
+            perc_t = 100
+        else:
+            perc_t = ((temp_gate - temp) / 3) * 100
+
+        if args.pid:
+            # use PID controller
+            perc = p_val1
+        else:
+            # use P controller
+            perc = perc_t
+
+        if perc > 0:
+            output = run_binary(usbrelay_on)
+            time_on = period * (perc / 100)
+            time.sleep(time_on)
+        else:
+            time_on = 0
+
+        time_off = period - time_on
+        output = run_binary(usbrelay_off)
+
+        if args.verbose:
+            print("Temp %s, perc %.2f, p_val %s/%s"
+                  % (temp, perc_t, p_val, p_val1))
+            print("on %s, off %s" % (time_on, time_off))
+            print(pid)
+
+        if 0 < time_off:
+            time.sleep(time_off)
+
+except KeyboardInterrupt:
+    print("\nCaught ^C")
+    run_binary(usbrelay_off)
+    sys.exit(1)
+    # can't fall through ???
+
+except IOError:
+    # exception catcher
+    # turn off the heat!
+    run_binary(usbrelay_off)
+    print("\nCaught exception, exiting\n")
+    sys.exit(1)



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/commit/cfc6c377ba09484387e15405e0212b9c5c61fc80
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20170320/8b2b2e28/attachment.html>


More information about the vc mailing list