196 lines
4.9 KiB
Python
196 lines
4.9 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
# pot_cap.py
|
||
|
# 2016-09-26
|
||
|
# Public Domain
|
||
|
|
||
|
import time
|
||
|
import pigpio
|
||
|
|
||
|
class reader:
|
||
|
"""
|
||
|
A class to measure the time taken to charge a capacitor
|
||
|
through a resistance. The time taken will be propotional
|
||
|
to the voltage, resistance, and capacitance. If two values
|
||
|
are fixed the third can be estimated.
|
||
|
|
||
|
The following circuit should be used.
|
||
|
|
||
|
3V3 ----- Resistor --+-- Capacitor ----- Ground
|
||
|
|
|
||
|
+-- GPIO
|
||
|
|
||
|
"""
|
||
|
def __init__(self, pi, gpio, drain_ms=1.0, timeout_s=1.0):
|
||
|
"""
|
||
|
Instantiate with the Pi and GPIO of the resistor/capacitor
|
||
|
system to monitor.
|
||
|
|
||
|
Optionally the time taken to fully drain the capacitor
|
||
|
may be give as drain_ms. The value defaults to
|
||
|
1 millisecond.
|
||
|
|
||
|
Optionally a timeout may be specified as timeout_s. The
|
||
|
value defaults to 1.0 seconds.
|
||
|
|
||
|
If the readings appear to vary too much in static
|
||
|
conditions or there are many False results with massive
|
||
|
readings perhaps the capacitor isn't fully discharging.
|
||
|
Try increasing draim_ms.
|
||
|
|
||
|
If False is always returned perhaps the capacitor needs
|
||
|
more time to charge. Try increasing timeout_s.
|
||
|
"""
|
||
|
|
||
|
self.pi = pi
|
||
|
self.gpio = gpio
|
||
|
self.drain_ms = drain_ms
|
||
|
self.timeout_s = timeout_s
|
||
|
self._timeout_us = timeout_s * 1000000.0
|
||
|
|
||
|
"""
|
||
|
Use a script on the daemon to do the time critical bit.
|
||
|
|
||
|
It saves the tick in v0, changes the mode of p0 to an input,
|
||
|
gets the tick and subtracts the first tick, divides by 2 to
|
||
|
get the range, adds the range to the first tick to get the
|
||
|
estimated start tick. Returns the estimated start tick in p2
|
||
|
and range in p3. Finally p1 is incremented to indicate the
|
||
|
script has completed.
|
||
|
"""
|
||
|
|
||
|
self._sid = pi.store_script(
|
||
|
b't sta v0 m p0 r t sub v0 div 2 sta p3 add v0 sta p2 inr p1 ')
|
||
|
|
||
|
s = pigpio.PI_SCRIPT_INITING
|
||
|
|
||
|
while s == pigpio.PI_SCRIPT_INITING:
|
||
|
s, p = self.pi.script_status(self._sid)
|
||
|
time.sleep(0.001)
|
||
|
|
||
|
self._cb = pi.callback(gpio, pigpio.EITHER_EDGE, self._cbf)
|
||
|
|
||
|
def _cbf(self, g, l, t):
|
||
|
"""
|
||
|
Record the tick when the GPIO becomes high.
|
||
|
"""
|
||
|
if l == 1:
|
||
|
self._end = t
|
||
|
|
||
|
def read(self):
|
||
|
"""
|
||
|
Triggers and returns a reading.
|
||
|
|
||
|
A tuple of the reading status (True for a good reading,
|
||
|
False for a timeout or outlier), the reading, and the
|
||
|
range are returned.
|
||
|
|
||
|
The reading is the number of microseconds taken for the
|
||
|
capacitor to charge. The range is a measure of how
|
||
|
accurately the start of recharge was measured as +/-
|
||
|
microseconds.
|
||
|
"""
|
||
|
|
||
|
timeout = time.time() + self.timeout_s
|
||
|
|
||
|
self.pi.write(self.gpio, 0)
|
||
|
|
||
|
time.sleep(self.drain_ms/1000.0)
|
||
|
|
||
|
while (self.pi.read(self.gpio) != 0) and (time.time() < timeout):
|
||
|
time.sleep(0.001)
|
||
|
|
||
|
self._end = None
|
||
|
|
||
|
self.pi.run_script(self._sid, [self.gpio, 0])
|
||
|
|
||
|
while time.time() < timeout:
|
||
|
s, p = self.pi.script_status(self._sid)
|
||
|
if p[1]:
|
||
|
break
|
||
|
time.sleep(0.001)
|
||
|
|
||
|
# p[2] is start charge tick from script
|
||
|
# p[3] is +/- range from script
|
||
|
|
||
|
if time.time() < timeout:
|
||
|
|
||
|
_start = p[2]
|
||
|
if _start < 0:
|
||
|
_start += (1<<32)
|
||
|
|
||
|
while self._end is None and time.time() < timeout:
|
||
|
time.sleep(0.001)
|
||
|
|
||
|
if self._end is not None:
|
||
|
diff = pigpio.tickDiff(_start, self._end)
|
||
|
# Discard obvious outliers
|
||
|
if (diff < self._timeout_us) and (p[3] < 6):
|
||
|
return True, diff, p[3]
|
||
|
else:
|
||
|
return False, diff, p[3]
|
||
|
|
||
|
return False, 0, 0
|
||
|
|
||
|
def cancel(self):
|
||
|
"""
|
||
|
Cancels the reader and releases resources.
|
||
|
"""
|
||
|
self.pi.delete_script(self._sid)
|
||
|
self._cb.cancel()
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
|
||
|
import sys
|
||
|
import time
|
||
|
import pigpio
|
||
|
import pot_cap
|
||
|
|
||
|
RUN_TIME = 30
|
||
|
POT_CAP_GPIO = 23
|
||
|
DRAIN_MS = 1.0
|
||
|
TIMEOUT_S = 1.0
|
||
|
|
||
|
# ./pot_cap.py [run time [gpio [drain ms [timeout s]]]]
|
||
|
|
||
|
if len(sys.argv) > 1:
|
||
|
run_time = float(sys.argv[1])
|
||
|
else:
|
||
|
run_time = RUN_TIME
|
||
|
|
||
|
if len(sys.argv) > 2:
|
||
|
pot_cap_gpio = int(sys.argv[2])
|
||
|
else:
|
||
|
pot_cap_gpio = POT_CAP_GPIO
|
||
|
|
||
|
if len(sys.argv) > 3:
|
||
|
drain_ms = float(sys.argv[3])
|
||
|
else:
|
||
|
drain_ms = DRAIN_MS
|
||
|
|
||
|
if len(sys.argv) > 4:
|
||
|
timeout_s = float(sys.argv[4])
|
||
|
else:
|
||
|
timeout_s = TIMEOUT_S
|
||
|
|
||
|
pi = pigpio.pi() # Connect to Pi.
|
||
|
|
||
|
print("# rt={:.1f} g={} drain={:.1f} timeout={:.1f}".format(
|
||
|
run_time, pot_cap_gpio, drain_ms, timeout_s))
|
||
|
|
||
|
# Instantiate Pot/Cap reader.
|
||
|
pc = pot_cap.reader(pi, pot_cap_gpio, drain_ms, timeout_s)
|
||
|
|
||
|
start = time.time()
|
||
|
|
||
|
while (time.time()-start) < run_time:
|
||
|
|
||
|
s, v, r = pc.read()
|
||
|
if s and r < 4:
|
||
|
print("{} {} {}".format(s, v, r))
|
||
|
time.sleep(0.01)
|
||
|
|
||
|
pc.cancel() # Cancel the reader.
|
||
|
pi.stop() # Disconnect from Pi.
|
||
|
|