Search code examples
pythonvisual-studio-codepyvisa

pyvisa script only works when stepping through code


I have written a small python script to enable the output of a HAMEG HMG4040 triple power-supply. I am using pyvisa 1.14.1 along with NI-VISA 20.0

import pyvisa
import time

# Initialize the PyVISA resource manager
pyvisa.log_to_screen()
rm = pyvisa.ResourceManager()

# List all connected VISA devices
def list_devices():
    devices = rm.list_resources()
    if devices:
        print("Available VISA devices:")
        for idx, device in enumerate(devices):
            print(f"{idx + 1}: {device}")
    else:
        print("No VISA devices found.")
    return devices

# Function to query the device
def query_device(resource_name, command):
    try:
        # Open the device resource
        with rm.open_resource(resource_name) as instrument:
            instrument.timeout = 5000  # Set timeout to 1000 ms (1 seconds)
            # Write the query command to the device
            instrument.write(command)
            # Read the response from the device
            response = instrument.read()
            return response
    except Exception as e:
        print(f"Error querying device: {e}")
        return None

# Function to query the device
def send_cmd_device(resource_name, command):
    try:
        # Open the device resource
        with rm.open_resource(resource_name) as instrument:
            instrument.timeout = 5000  # Set timeout to 1000 ms (1 seconds)
            # Write the query command to the device
            instrument.write(command)
            return 1
    except Exception as e:
        print(f"Error querying device: {e}")
        return None

# Main function
if __name__ == "__main__":
    # List available devices
    devices = list_devices()
    
    if devices:
        # Choose the device you want to query
        resource_name = devices[0]  # Use the first detected device for this example
        
        # Command to send to the device (replace with actual command)
        command = "*IDN?"
        
        # Query the device and get the response
        response = query_device(resource_name, command)
        
        if response:
            print(f"Response from HMG4040: {response}")
        else:
            print("No response from the device.")

        state = input("specify PSUP state: [ON/OFF]\n")

        #select and activate output 1,2 and 4

        with rm.open_resource(resource_name) as instrument:
            instrument.timeout = 1000  # Set timeout to 1000 ms (1 seconds)
            instrument.write("INST OUT1")
            instrument.write("OUTP:SEL " + state)
            instrument.write("INST OUT3")
            instrument.write("OUTP:SEL " + state)
            instrument.write("INST OUT4")
            instrument.write("OUTP:SEL " + state)
            instrument.write("OUTP:GEN " + state)
        
        send_cmd_device(resource_name, "INST OUT1")
        send_cmd_device(resource_name, "OUTP:SEL " + state)
        send_cmd_device(resource_name, "INST OUT3")
        send_cmd_device(resource_name, "OUTP:SEL " + state)
        send_cmd_device(resource_name, "INST OUT4")
        send_cmd_device(resource_name, "OUTP:SEL " + state)
        send_cmd_device(resource_name, "OUTP:GEN " + state)

    else:
        print("No devices to query.")

If I comment out the send_cmd_device() lines the script is working fine either in debug mode or normal python execution.

However, if I comment out this part:

        with rm.open_resource(resource_name) as instrument:
            instrument.timeout = 1000  # Set timeout to 1000 ms (1 seconds)
            instrument.write("INST OUT1")
            instrument.write("OUTP:SEL " + state)
            instrument.write("INST OUT3")
            instrument.write("OUTP:SEL " + state)
            instrument.write("INST OUT4")
            instrument.write("OUTP:SEL " + state)
            instrument.write("OUTP:GEN " + state)

The script only works if I place a breakpoint and step, in debug mode, through each line including the send_cmd_device() lines.

Could someone explain why the script doesn't work in normal full speed exection when using the method call? I have tried adding delays everywhere but that didn't help.


Solution

  • I've never worked with this instrument in particular, but I've seen similar issues in other instruments where the commands are either sent too quickly in succession or one of the commands is badly formatted. Based on the instrument documentation it supports the common *OPC command for "Operation Complete." As a debugging step, I would suggest to query *OPC after every command. Right now, your code is stepping through as fast as python and the GPIB interface on your controller can go, with no regard for the internal state of the machine. By querying for status, you can be sure that each step was completed correctly before starting the next step. Try modifying the problem section as follows:

    with rm.open_resource(resource_name) as instrument:
        instrument.timeout = 1000  # Set timeout to 1000 ms (1 seconds)
        instrument.write("INST OUT1")
        print(instrument.query("*OPC?"))
        instrument.write("OUTP:SEL " + state)
        print(instrument.query("*OPC?"))
        instrument.write("INST OUT3")
        print(instrument.query("*OPC?"))
        instrument.write("OUTP:SEL " + state)
        print(instrument.query("*OPC?"))
        instrument.write("INST OUT4")
        print(instrument.query("*OPC?"))
        instrument.write("OUTP:SEL " + state)
        print(instrument.query("*OPC?"))
        instrument.write("OUTP:GEN " + state)
        print(instrument.query("*OPC?"))
    

    If this works, remove the queries one-by-one to find the point where the instrument fails again. Then you know the step that needs a longer delay. For this reason, I often end up writing GPIB drivers that use a combination of .write() followed by .query("*OPC?") for particular items that take extra time.

    Update

    Based on the comments, I'm starting to think this is not an issue of the commands executing too quickly, but rather because the GPIB resource is opened many times. It's better to open the resource once and keep the connection rather than opening many times. In theory, opening many times should also work, just more slowly. In practice, there may be some conflict/overload in the GPIB controller. Here's a refactored version that passes the instrument resource to query_device and send_cmd_device. In addition, it uses instrument.query in send_cmd_device to check that each command completed correctly.

    import pyvisa
    
    # Initialize the PyVISA resource manager
    pyvisa.log_to_screen()
    rm = pyvisa.ResourceManager()
    
    
    # List all connected VISA devices
    def list_devices():
        devices = rm.list_resources()
        if devices:
            print("Available VISA devices:")
            for idx, device in enumerate(devices):
                print(f"{idx + 1}: {device}")
        else:
            print("No VISA devices found.")
        return devices
    
    
    # Function to query the device
    def query_device(instrument, command):
        try:
            # Write the query command to the device
            instrument.write(command)
            # Read the response from the device
            response = instrument.read()
            return response
        except Exception as e:
            print(f"Error querying device: {e}")
            return None
    
    
    # Function to query the device
    def send_cmd_device(instrument, command):
        try:
            instrument.timeout = 5000  # Set timeout to 1000 ms (1 seconds)
            # Write the query command to the device
            instrument.write(command)
            print(instrument.query("*OPC?"))
            return 1
        except Exception as e:
            print(f"Error querying device: {e}")
            return None
    
    
    # Main function
    if __name__ == "__main__":
        # List available devices
        devices = list_devices()
    
        if devices:
            # Choose the device you want to query
            resource = rm.open_resource(devices[0])  # Use the first detected device for this example
    
            # Command to send to the device (replace with actual command)
            command = "*IDN?"
    
            # Query the device and get the response
            response = query_device(resource, command)
    
            if response:
                print(f"Response from HMG4040: {response}")
            else:
                print("No response from the device.")
    
            state = input("specify PSUP state: [ON/OFF]\n")
    
            # select and activate output 1,2 and 4
    
            resource.timeout = 1000  # Set timeout to 1000 ms (1 seconds)
            resource.write("INST OUT1")
            resource.write("OUTP:SEL " + state)
            resource.write("INST OUT3")
            resource.write("OUTP:SEL " + state)
            resource.write("INST OUT4")
            resource.write("OUTP:SEL " + state)
            resource.write("OUTP:GEN " + state)
    
            send_cmd_device(resource, "INST OUT1")
            send_cmd_device(resource, "OUTP:SEL " + state)
            send_cmd_device(resource, "INST OUT3")
            send_cmd_device(resource, "OUTP:SEL " + state)
            send_cmd_device(resource, "INST OUT4")
            send_cmd_device(resource, "OUTP:SEL " + state)
            send_cmd_device(resource, "OUTP:GEN " + state)
    
        else:
            print("No devices to query.")