Skip to content

Instantly share code, notes, and snippets.

@greasemonkey-btbm
Last active January 30, 2022 20:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greasemonkey-btbm/75df0855565f91c9f119aadf29df0c78 to your computer and use it in GitHub Desktop.
Save greasemonkey-btbm/75df0855565f91c9f119aadf29df0c78 to your computer and use it in GitHub Desktop.
# ******************************************************************************
# 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