python - pyvisa - Trying to read variable length data from device that has no read _termination

884 views Asked by At

I am able to read and write from/to a signal generator using miniterm.py. There is no read_termination character in the stream, (per the manual from the device manufacturer). I have attached debug output from miniterm to show a few transactions. Is there something I am missing to get pyvisa to read an arbitrary length return string without termination?

------------------------miniterm output --------------------------

Settings: /dev/ttyUSB0 9600,8,N,1 RTS: active DTR: active BREAK: inactive CTS: inactive DSR: inactive RI: inactive CD: inactive software flow control: inactive hardware flow control: inactive serial input encoding: UTF-8 serial output encoding: UTF-8 EOL: LF filters: debug default direct

[TX:'R'] [TX:'M'] [TX:'W'] [TX:'\n'] [RX:'3'] 3 [RX:'6'] 6 [TX:'R'] [TX:'F'] [TX:'W'] [TX:'\n'] [RX:'0'] 0 [TX:'R'] [TX:'M'] [TX:'A'] [TX:'\n'] [RX:'2'] 2 [RX:'0'] 0 [RX:'0'] 0 [TX:'R'] [TX:'F'] [TX:'A'] [TX:'\n'] [RX:'1'] 1 [RX:'0'] 0 [RX:'0'] 0 [RX:'0'] 0


Any pointers in the right direction would be helpful. This is a really strange type of device. Most everything I have tried ends in either a 'device in use' or 'timeout' error.

Thank you in advance.

Robin

2

There are 2 answers

0
Robin Handelman On

The read_data() function below solved the problem to some extent. Now to figure out why back to back writes fail unless time.sleep(x) of about .2 seconds is not used between writes.

def read_data(cmd):
    data=""
    byte=""
    looper = True
    dds.write(cmd)
    while(looper==True):
       try:
          byte = dds.read_bytes(1)
          data += byte.decode()
       except:
          looper = False
    return(data)
0
MitchiLaser On

I had the same issue with a measurement instrument and It took a while to find a solution for this:

device.bytes_in_buffer gives you the amount of bytes inside the buffer and

answer = device.read_bytes( device.bytes_in_buffer ) returns you the bytes inside the bufffer.

After that you can call answer.decode() to get the ASCII string the device returned you. Here is a longer example of a Python class I wrote to make communication with the device easier:

import pyvisa as visa
import logging as log
import time
from typing import Final, Union

class interface:  # provide write(), read(), read_raw(), ask() and ask_raw() function

    _device = None

    def __init__(self, device) -> None:
        self._device = device

    def write(self, command) -> None:
        self._device.write("%s" % ( command))

    def read(self) -> str:
        return self.read_raw().decode()

    def read_raw(self) -> str:
        return self._device.read_bytes( self._device.bytes_in_buffer )

    def ask(self, command) -> str:
        return self.ask_raw(command).decode()

    # flush buffer, this is necessary to prevent from errors because the last SCPI command took to long to be received
    def flush_buffer(self) -> None:
        self._device.flush(visa.constants.VI_READ_BUF)

    def ask_raw(self, command) -> str:
        self.flush_buffer()
        self.write(command)
        time.sleep(0.2) # this is needed for the device to process the SCPI command
        return self.read_raw()

    def boolean_property(
        self,
        scpi_cmd: str,                  # command name of scpi option
        err_description: str,           # generate usefull error messages
        value: Union[bool, None] = None # True or False -> set scpi option to value, None -> get current value of scpi option
    ) -> Union[bool, None]:

        # if parameter value is None -> get current state from scpi option
        if value == None:
            return {
                "ON": True,
                "1": True,
                "OFF": False,
                "0": False
            }[str(
                self.ask("%s?" % (scpi_cmd.upper()))
            ).upper()]   # uppercase conversion is used to prevent issues with unexpected lowercase letters

        # check wether the parameter is a boolean
        if not type(value) == bool:
            return log.error("%s: Cannot assign \"%s\" for %s, must be bool(True) or bool(False)" % (self._device, value, err_description))

        # write the scpi command
        self.write("%s %s" % (
            scpi_cmd,
            {
                True: "ON",
                False: "OFF"
            }[value]
        ))