#!/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.