Last active
January 30, 2022 20:08
-
-
Save greasemonkey-btbm/75df0855565f91c9f119aadf29df0c78 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ****************************************************************************** | |
# Measurement script for 24V digital input current profiles | |
# Rigol DP832 & DMM7510 & Keithley 196 | |
# https://btbm.ch/24vdc-iec-61131-2-compliant-digital-inputs-with-jelly-bean-parts/ | |
# Copyleft 2022 Grease monkey - No rights reserved | |
# !!! Don't laugh at the code. I change engine oils for a living !!! | |
# ****************************************************************************** | |
import vxi11 | |
import pyvisa | |
import time | |
import math | |
import matplotlib.pyplot as plt | |
from matplotlib.patches import Polygon | |
from matplotlib.collections import PatchCollection | |
import numpy as np | |
rm = pyvisa.ResourceManager() | |
# Rigol DP832 @ 192.168.1.108 | |
psu = vxi11.Instrument("192.168.1.108") | |
# Keithley DMM7510 @ 192.168.1.50 | |
dmm7510 = vxi11.Instrument("192.168.1.50") | |
# Keithley 196 @ GPIB 7 | |
dmm196 = rm.open_resource('GPIB0::7::INSTR') | |
def psu_set_voltage(channel, voltage): | |
if channel == 1 or channel == 2 or channel == 3: | |
if 0 <= voltage <= 32: | |
psu.write(":SOUR" + str(channel) + ":VOLT " + str(voltage)) | |
else: | |
print("Invalid voltage") | |
exit() | |
else: | |
print("Invalid channel name") | |
exit() | |
def psu_on(channel): | |
if channel == "CH1" or channel == "CH2" or channel == "CH3" or channel == "ALL": | |
psu.write(":OUTP " + channel + ",ON") | |
else: | |
print("Invalid channel name") | |
exit() | |
def psu_off(channel): | |
if channel == "CH1" or channel == "CH2" or channel == "CH3" or channel == "ALL": | |
psu.write(":OUTP " + channel + ",OFF") | |
else: | |
print("Invalid channel name") | |
exit() | |
# This function reads two consecutive values from the 196 | |
# and compares them to see if the readings have settled | |
# it also retries to read values if no reading was received | |
def dmm196_read_value(settling_time=0.0, settling_difference=0.0001): | |
measurements = [1, 2] | |
n = 0 | |
while abs(measurements[0] - measurements[1]) > settling_difference: | |
measurement = float('nan') | |
while math.isnan(measurement): | |
try: | |
time.sleep(0.01) | |
measurement = float(dmm196.read()) | |
measurements[n] = measurement | |
if n < 1: | |
n += 1 | |
else: | |
n = 0 | |
except: | |
pass | |
time.sleep(settling_time) | |
return measurements[0] | |
# Number of measurements | |
no_of_measurements = 300 | |
# Maximum test voltage | |
max_volts = 30 | |
psu_set_voltage(2, 0) | |
psu_set_voltage(3, 5) | |
psu_on("CH2") | |
psu_on("CH3") | |
# Wait a bit till the circuit settles | |
time.sleep(1) | |
# Setup DMM7510 to measure Volts DC | |
dmm7510.write("FUNC \"CURR\"") | |
dmm7510.write(":SENS:CURR:NPLCycles MAX") | |
# Setup DMM196 to measure DC Voltage | |
# Function = F0 (Volts) | |
# Range = R4 (300V) | |
# Filter = P0 (Disabled) | |
# Rate = S3 (6 1/2 digits) | |
# Trigger mode = T0 (Continuous on Talk) | |
# Data format = G1 (without prefixes) | |
dmm196.write('F0R4P0S3T0G1X') | |
voltage_array = [] | |
current_array = [] | |
for i in range(0, no_of_measurements + 1): | |
voltage_setpoint = max_volts / no_of_measurements * i | |
if voltage_setpoint < 3 or i % 20 == 0: | |
psu_set_voltage(2, voltage_setpoint) | |
# Wait a bit till the circuit settles | |
time.sleep(0.3) | |
# Measure voltage | |
voltage = float(dmm196_read_value(0.2, 0.01)) | |
print(str(voltage) + " V ", end='') | |
# Measure current | |
dmm7510.write("FUNC \"CURR\"") | |
dmm7510.write(":SENS:CURR:NPLCycles MAX") | |
# Convert to mA | |
current = float(dmm7510.ask("MEAS?")) * 1000 | |
print(str(current) + " mA ", end='') | |
print(str(current * voltage) + " mW") | |
if voltage_setpoint == 24: | |
power_at_24VDC = voltage * current / 1000 | |
# Store the measurements | |
voltage_array.append(voltage) | |
current_array.append(current) | |
# Close GPIB connection | |
rm.close() | |
psu_off("ALL") | |
plt.rcParams['figure.dpi'] = 150 | |
fig, ax = plt.subplots() | |
"$Y_{axis}$" | |
ax.set(xlabel="$I_{IN}$ (mA)", ylabel="$V_{IN}$ (V)", | |
title='Digital input IEC61131-2 Type 1') | |
y = np.array([[2, 15], [15, 15], [15, 30], [2, 30]]) | |
p = Polygon(y, facecolor='gray', alpha=0.5, edgecolor='black', linewidth=1) | |
ax.add_patch(p) | |
y = np.array([[0, 0], [15, 0], [15, 5], [0.5, 5], [0.5, 15], [0, 15]]) | |
p = Polygon(y, facecolor='gray', alpha=0.5, edgecolor='black', linewidth=1) | |
ax.add_patch(p) | |
ax.text(8, 22, 'ON', fontsize=15) | |
ax.text(7, 1.5, 'OFF', fontsize=15) | |
ax.text(10, 28, 'Power Dissipation', fontsize=12) | |
ax.text(10, 26, '@$V_{IN}$ = 24VDC $\\rightarrow$ ' + str(round(power_at_24VDC, 2)) + "W", fontsize=10) | |
ax.text(10, 24, 'Maximum $\\rightarrow$ ' + str(round(voltage_array[-1] * current_array[-1] / 1000, 2)) + "W", | |
fontsize=10) | |
ax.text(10, 22, '$I_{IN (MAX)}$ $\\rightarrow$ ' + str(round(max(current_array), 2)) + "mA", fontsize=10) | |
ax.set_xlim([-0.1, 15.5]) | |
ax.set_ylim([0, 32]) | |
ax.annotate("", xy=(0, 32), xytext=(0, 0), arrowprops=dict(arrowstyle="->"), alpha=0.6) | |
ax.annotate("", xy=(15.5, 0), xytext=(0, 0), arrowprops=dict(arrowstyle="->"), alpha=0.6) | |
plt.xticks(np.arange(0, 15.5, 1)) | |
plt.yticks(np.arange(0, 33, 5)) | |
plt.box(on=None) | |
ax.plot(current_array, voltage_array, color='r') | |
plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment