serial_instruments

mail@pastecode.io avatar
unknown
python
6 months ago
12 kB
2
Indexable
Never
import serial
from time import sleep, time
import serial.tools.list_ports


def printCOMports(showIDN=True):
    ports = serial.tools.list_ports.comports()
    for port, desc, hwid in sorted(ports):
        print("{}: {} [{}]".format(port, desc, hwid))
        if showIDN:
            try:
                inst = SerialInstrument(port)
                IDN = inst.get_idn()
                print('\t' + IDN)
            except:
                pass

# Set instrument to remote mode to be able to control it
class SerialInstrument:
    def __init__(self, COMport, timeout=2, baudrate=9600, EOL='\r\n', removeEOL=True, tsleep=0):
        self.serial = serial.Serial()
        self.serial.port = COMport
        self.serial.timeout = timeout
        self.serial.baudrate = baudrate
        self.EOL = EOL
        self.removeEOL = removeEOL
        self.tsleep = tsleep
        if not self.serial.is_open:
            self.serial.open()

    def write(self, command):
        self.serial.write((command + self.EOL).encode())
        sleep(self.tsleep)

    def read(self):
        answer = self.serial.readline().decode()
        if self.removeEOL:
            answer = answer.rstrip(self.EOL)
        return answer

    def query(self, command):
        self.write(command)
        return self.read()

    def remote_mode(self):
        self.write('SYST:REM')

    def local_mode(self):
        self.write('SYST:LOC')

    def get_idn(self):
        return self.query('*IDN?')

    def get_error(self):
        return self.query('SYST:ERR?')

    def get_version(self):
        return self.query('SYST:VERS?')

    def beep(self):
        self.write('SYST:BEEP')

    def reset(self):
        self.write('*RST')

    def cls(self):  # Clear status (clear errors)
        self.write('*CLS')

    def display_on(self):
        self.write('DISP ON')

    def display_off(self):
        self.write('DISP OFF')

# Multimeter Hewlett Packard 34401A
class HP34401A(SerialInstrument):
    def __init__(self, COMport, timeout=2, baudrate=9600, EOL='\r\n', removeEOL=True, tsleep=0.15):
        super().__init__(COMport, timeout, baudrate, EOL, removeEOL, tsleep)
        self.__modes = {
            'VDC': ['CONF:VOLT:DC DEF, DEF', 'DC Voltage measurement'],
            'VAC': ['CONF:VOLT:AC DEF, DEF', 'AC Voltage measurement'],
            'IDC': ['CONF:CURR:DC DEF, DEF', 'DC Current measurement'],
            'IAC': ['CONF:CURR:AC DEF, DEF', 'AC Current measurement'],
            'R': ['CONF:RES DEF, DEF', '2-wire resistance measurement'],
            'R4W': ['CONF:FRES DEF, DEF', '4-wire resistance measurement'],
            'F': ['CONF:FREQ DEF, DEF', 'Frequency measurement'],
            'T': ['CONF:PER DEF, DEF', 'Period measurement']
        }

    def help_modes(self):
        for mode in self.__modes:
            comment = self.__modes[mode][1]
            print(mode + ': ' + comment)

    def meas(self, mode='VDC', num_of_samples=1):
        cmd_mode = self.__modes[mode][0]
        self.write(cmd_mode)
        cmd = 'SAMP:COUN ' + str(num_of_samples)
        self.write(cmd)
        self.write('READ?')
        sleep(0.4*num_of_samples)
        data_str = self.read()
        data = []
        for str_value in data_str.split(','):
            if str_value:
                data.append(float(str_value))
        if len(data) == 1:
            return data[0]
        else:
            return data

    def display_text(self, text):
        # Display has 12 characters
        # It is recommended to use uppercase
        self.write('DISP:TEXT "' + text[:12] + '"')

    def running_text(self, text):
        # Set self.tsleep time to change speed of running text
        # Recomended = 0.15
        text = ' '*12 + text
        tstart = time()
        i = 0
        text_len = len(text)
        while i <= text_len:
            self.display_text(text[i:])
            i += 1

    def display_clear(self):
        self.write('DISP:TEXT:CLE')


# Power Supply BK Precision 9129B
class BK9129B(SerialInstrument):
    def __init__(self, COMport, timeout=2, baudrate=9600, EOL='\r\n', removeEOL=True, tsleep=0):
        super().__init__(COMport, timeout, baudrate, EOL, removeEOL, tsleep)
        self.__voltages = [0, 0, 0]
        self.__currents = [0, 0, 0]
        self.__outputs = [0, 0, 0]
        self.__CH_selected = 0

    def reset(self):
        self.write('*RST')
        self.__voltages = [0, 0, 0]
        self.__currents = [3.1, 3.1, 3.1]
        self.__outputs = [0, 0, 0]
        self.__CH_selected = 0

    def __select_channel(self, CH_num):
        if self.__CH_selected != CH_num:
            self.write('INST:NSEL ' + str(CH_num))
            self.__CH_selected = CH_num

    def __send_channel_command(self, channel, command, value):
        self.__select_channel(channel)
        self.write(command + ' ' + str(value))
        if command == 'VOLT':
            self.__voltages[channel - 1] = value
        elif command == 'CURR':
            self.__currents[channel - 1] = value
        elif command == 'CHAN:OUTP':
            self.__outputs[channel - 1] = value

    def __meas_value(self, channel, value_str):
        measured = None
        data = ''
        if isinstance(channel, int):
            ch = 'CH' + str(channel)
            data = self.query('MEAS:' + value_str + '? ' + ch)
        if data:
            measured = float(data)
        return measured

    def meas_current(self, channel):
        return self.__meas_value(channel, 'CURR')

    def meas_voltage(self, channel):
        return self.__meas_value(channel, 'VOLT')

    def meas_power(self, channel):
        return self.__meas_value(channel, 'POW')

    def __meas_values(self, value_str):
        measured = []
        data = self.query('MEAS:' + value_str + '? ALL')
        if data:
            measured = [float(i) for i in data.split(', ')]
        return measured

    def meas_currents(self):
        return self.__meas_values('CURR')

    def meas_voltages(self):
        return self.__meas_values('VOLT')

    def meas_powers(self):
        return self.__meas_values('POW')

    @property
    def voltage1(self):
        return self.__voltages[0]

    @voltage1.setter
    def voltage1(self, value):
        self.__send_channel_command(1, 'VOLT', value)

    @property
    def voltage2(self):
        return self.__voltages[1]

    @voltage2.setter
    def voltage2(self, value):
        self.__send_channel_command(2, 'VOLT', value)

    @property
    def voltage3(self):
        return self.__voltages[2]

    @voltage3.setter
    def voltage3(self, value):
        self.__send_channel_command(3, 'VOLT', value)

    @property
    def current1(self):
        return self.__currents[0]

    @current1.setter
    def current1(self, value):
        self.__send_channel_command(1, 'CURR', value)

    @property
    def current2(self):
        return self.__currents[1]

    @current2.setter
    def current2(self, value):
        self.__send_channel_command(2, 'CURR', value)

    @property
    def current3(self):
        return self.__currents[2]

    @current3.setter
    def current3(self, value):
        self.__send_channel_command(3, 'CURR', value)

    @property
    def output1(self):
        return self.__outputs[0]

    @output1.setter
    def output1(self, value):
        self.__send_channel_command(1, 'CHAN:OUTP', value)

    @property
    def output2(self):
        return self.__outputs[1]

    @output2.setter
    def output2(self, value):
        self.__send_channel_command(2, 'CHAN:OUTP', value)

    @property
    def output3(self):
        return self.__outputs[2]

    @output3.setter
    def output3(self, value):
        self.__send_channel_command(3, 'CHAN:OUTP', value)

    @property
    def voltages(self):
        return self.__voltages

    @voltages.setter
    def voltages(self, values):
        self.__voltages = values
        self.write('APP:VOLT ' + ','.join(str(value) for value in values))

    @property
    def currents(self):
        return self.__currents

    @currents.setter
    def currents(self, values):
        self.__currents = values
        self.write('APP:CURR ' + ','.join(str(value) for value in values))

    @property
    def outputs(self):
        return self.__outputs

    @outputs.setter
    def outputs(self, values):
        self.__outputs = values
        self.write('APP:OUT ' + ','.join(str(value) for value in values))


# Power Supply BK Precision 9201B
class BK9201B(SerialInstrument):
    def __init__(self, COMport, timeout=2, baudrate=9600, EOL='\r\n', removeEOL=True, tsleep=0):
        super().__init__(COMport, timeout, baudrate, EOL, removeEOL, tsleep)
        self.__voltage = 0
        self.__current = 0
        self.__output = 0

    def reset(self):
        self.write('*RST')
        self.__voltage = 0
        self.__current = 10.1
        self.__output = 0

    def __meas_value(self, value_str):
        measured = None
        data = self.query('MEAS:' + value_str + '? ')
        if data:
            measured = float(data)
        return measured

    def meas_current(self):
        return self.__meas_value('CURR')

    def meas_voltage(self):
        return self.__meas_value('VOLT')

    def meas_power(self):
        return self.__meas_value('POW')

    @property
    def voltage(self):
        return self.__voltage

    @voltage.setter
    def voltage(self, value):
        self.write('VOLT ' + str(value))
        self.__voltage = value

    @property
    def current(self):
        return self.__current

    @current.setter
    def current(self, value):
        self.write('CURR ' + str(value))
        self.__current = value

    @property
    def output(self):
        return self.__output

    @output.setter
    def output(self, value):
        self.write('OUTP ' + str(value))
        self.__output = value


# Universal calibrator Digistant 4420-V001
class DIGISTANT4420(SerialInstrument):
    def __init__(self, COMport, timeout=2, baudrate=9600, EOL='\r\n', removeEOL=True, tsleep=0):
        super().__init__(COMport, timeout, baudrate, EOL, removeEOL, tsleep)
        self._stx = chr(2)  # '\x02' - start of text
        self._eot = chr(4)  # '\x04' - end of transmition
        self._etx = chr(3)  # '\x03' - end of text
        self._enq = chr(5)  # '\x05' - enquiry
        self._ack = chr(6)  # '\x06' - acknowledge
        self._nak = chr(21)  # '\x15' - negative acknowledge
        self._cr = chr(13)  # '\x0d' = '\r' - carriage return
        self._lf = chr(10)  # '\x0a' = '\n' - new line / line feed
        self._esc = chr(27)  # '\x1b' - escape
        self._select = self._eot + '0000SR' + self._enq
        self._polling = self._eot + '0000PO' + self._enq

    def write(self, command):
        self.serial.write(self._select.encode())
        sleep(self.tsleep)
        self.serial.write((self._stx + command + self.EOL + self._etx).encode())
        sleep(self.tsleep)

    def read(self):
        self.serial.reset_input_buffer()
        self.serial.write(self._polling.encode())
        sleep(self.tsleep)
        answer = self.serial.readline().decode()
        while len(answer) > 1 and not answer[0].isalnum():
            answer = answer[1:]
        if not answer[0].isalnum():
            answer = self.serial.readline().decode()
            while len(answer) > 1 and not answer[0].isalnum():
                answer = answer[1:]
        if self.removeEOL:
            answer = answer.rstrip(self.EOL)
        return answer

    def query(self, command):
        self.write(command)
        return self.read()

"""
# Example code to test class:
bk = BK9129B('COM5')
bk.remote_mode()
bk.voltages = [10, 5, 3]
bk.currents = [2,1,0.2]
bk.outputs = [1,1,0]
bk.voltage1 = 12
print(bk.meas_voltage(1))
print(bk.meas_voltages())
"""
Leave a Comment