TylerH
Posts: 2
Joined: Thu Jun 25, 2020 8:41 pm

Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 1:28 am

Hi,

For part of a project I am working on, I would like to measure the time it takes for a capacitor to discharge to some arbitrary voltage (effectively simulate analog using digital pins). To do this, I made a simple RC circuit shown below and set the GPIO pins to one of two states:

State 1 - Pin 8 charges the capacitor, pin 6 is the ground
State 2 - Pin 8 changes to an input, pin 6 is the ground

Once the circuit is in state 2, the value of pin 8 is checked at a specified sampling interval until the value returned is a 0 instead of a 1. I naively assumed that the time until the pin went from a value of 1 to 0 should be relatively consistent since it should be tied to the time constant of the circuit. In actuality, this value varies wildly (39[ms] lowest and 90[ms] highest recorded). Sometimes the pin value will even go from 1 to 0 nearly immediately then switch back to 1 for more than 10 milliseconds before going back to 0, etc. As a result, my code compares the running average of the last 5 values to a threshold in order to help reduce the impact of the low input "outliers". If anyone has any ideas as to why this simple proof-of-concept isn't returning consistent values, I would greatly appreciate your help. Thanks! References below:

Image

Here is the code:

Code: Select all

import numpy as np
import RPi.GPIO as GPIO
import time

# Initial Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)

# Settings
pin_A = 8
charge_time = 2
sample_interval = 0.001 # Time interval between samples

# Main Loop
# A High - Charging
GPIO.setup(pin_A, GPIO.OUT, initial=GPIO.HIGH)
print('Charging')
time.sleep(charge_time)

# A Low - Discharging
GPIO.setup(pin_A, GPIO.IN)

# Measure time until Pin A is low
pin_A_high = True
time_pin_A_high = 0
last_pin_values = np.array([]) # Used to find running average of last 5 values of pin A
while pin_A_high:
	pin_value = GPIO.input(pin_A)
	print(pin_value)
	
	if last_pin_values.size == 5: # Removes first element in vector containing last 5 pin A values
		last_pin_values = np.delete(last_pin_values, 0)
	
	if pin_value == 1:
		last_pin_values = np.append(last_pin_values, 1)
		time_pin_A_high += sample_interval
		time.sleep(sample_interval)
	elif pin_value == 0:
		last_pin_values = np.append(last_pin_values, 0)
		pin_value_sum = sum(last_pin_values)
		if (pin_value_sum < 3) & (last_pin_values.size == 5): # If 2 of last 5 values of pin A are LOW and at least 5 samples taken
			pin_A_high = False
		else:
			time_pin_A_high += sample_interval
			time.sleep(sample_interval)
			
print('High to Low Time: %f' % time_pin_A_high)

plugwash
Forum Moderator
Forum Moderator
Posts: 3602
Joined: Wed Dec 28, 2011 11:45 pm

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 1:45 am

What value is the capacitor? (I can't make out whether it's a n or a micro sign in your handwriting)

cleverca22
Posts: 580
Joined: Sat Aug 18, 2012 2:33 pm

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 2:13 am

there is also the gpio drive strength, and potential jitter in the timing to deal with
it may help if you set a 2nd gpio pin high, the instant you detect the cap going low, and then compare everything on a scope and see if things make sense

mwrich4
Posts: 99
Joined: Wed Jul 25, 2012 1:24 pm
Location: Stuart, Florida

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 5:33 am

doing this seems straightforward, but gpio will not wait until 0 volts to show off or 3.3(5.0) to show high. They are digital inputs, plus depending on your port configuration, the Input pin could charge your cap if you had it configured for pull-up for example.

Use an oscilloscope to monitor what is happening.

If you are polling, other events in the OS can delay your observations. This concept would be better proven on a small kit board with no OS and no other background operations.

User avatar
joan
Posts: 14887
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 5:55 am

This code will give accurate results once you have sorted out reasonable component values.

http://abyz.me.uk/rpi/pigpio/examples.h ... pot_cap_py

User avatar
Milliways
Posts: 534
Joined: Fri Apr 25, 2014 12:18 am
Location: Sydney, Australia

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 7:31 am

Similar circuits are used to measure capacitance (with relatively low precision), but you have deliberately nobbled yours by using 2 resistors.
I can't imagine why you included the resistor in parallel.

It will also take an age to discharge!

Effectively you are charging from 1.65V via 500kΩ.
The GPIO detects state change at ~1.3V (depends on Pi) - although the specs normally specify 1.8V as minimum guaranteed HIGH and 0.8V as maximum guaranteed LOW.

Splash out and use 2 pins!

TylerH
Posts: 2
Joined: Thu Jun 25, 2020 8:41 pm

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 5:46 pm

Thanks everyone for the input and Joan for the helpful example. Unfortunately I am not experienced enough to understand much of the code in the example (yet :D ); however, the circuit schematic helped me understand how to better arrange my components.

I have gotten the circuit to work now, albeit with some precision lacking as mentioned by an above poster. I intend to use pigpio to hopefully improve this, but I need to learn the pigpio library first so this may take a while. I will provide my schematic and code here in case it helps others in the future (or in case someone notices something potentially wrong with it :lol: )

Thanks again for the help everyone!

Schematic
Image
Code:

Code: Select all

import numpy as np
import RPi.GPIO as GPIO
import time

# Initial Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)

# Settings
pin_A = 10 # Measuring Pin
pin_B = 8 # High/Low Swapping Pin
charge_time = 3
sample_interval = 0.001 # Time interval between samples
GPIO.setup(pin_A, GPIO.IN)

discharge_times = np.array([])
for i in range(10):
	# Main Loop
	# A High - Charging
	GPIO.setup(pin_B, GPIO.OUT, initial=GPIO.HIGH)
	print('Charging')
	time.sleep(charge_time)

	# A Low - Discharging
	GPIO.output(pin_B, GPIO.LOW)

	# Measure time until Pin A is low
	pin_A_high = True
	time_pin_A_high = 0
	last_pin_values = np.array([]) # Used to find running average of last 5 values of pin A
	while pin_A_high:
		pin_value = GPIO.input(pin_A)
		
		if last_pin_values.size == 5: # Removes first element in vector containing last 5 pin A values
			last_pin_values = np.delete(last_pin_values, 0)
		
		if pin_value == 1:
			last_pin_values = np.append(last_pin_values, 1)
			time_pin_A_high += sample_interval
			time.sleep(sample_interval)
		elif pin_value == 0:
			last_pin_values = np.append(last_pin_values, 0)
			pin_value_sum = sum(last_pin_values)
			if (pin_value_sum < 3) & (last_pin_values.size == 5): # If 2 of last 5 values of pin A are LOW and at least 5 samples taken
				pin_A_high = False
			else:
				time_pin_A_high += sample_interval
				time.sleep(sample_interval)
				
	print('High to Low Time: %f' % time_pin_A_high)
	discharge_times = np.append(discharge_times, time_pin_A_high)
	
valid_samples = np.delete(discharge_times, [0,1]) # Disregard first two samples
average_discharge_time = np.average(valid_samples)
print("Average discharge time: %f" % average_discharge_time)
Return:
Image

cleverca22
Posts: 580
Joined: Sat Aug 18, 2012 2:33 pm

Re: Problem measuring capacitor discharge time using GPIO pins

Wed Jul 01, 2020 10:34 pm

i see part of the problem, the `time.sleep(sample_interval)` in a loop

that will delay for AT LEAST 1ms, but easily more if the system is busy, you must record the current time when you start, and the timestamp before&after the sleep where you want to stop measuring
then compute both before-start and after-start, and it changed between those 2 numbers

its best done with a monotonic timer, rather then date/time, so ntp wont affect it either

Return to “Beginners”