I work at a microwave technology company and part of my job is developing software to interface with the devices we use using Python, specifically PyVISA. I'm currently trying to write a program that interfaces with several different types of devices that have different command structures, so before executing the code, I want to be able to check which type of device is connected (all use GPIB addresses). How can I go about doing this? Right now I've been trying to send identity commands using nested try/except blocks as shown below because the different devices have different identify commands:
import pyvisa as visa
address = "GPIB0::6::INSTR"
rm = visa.ResourceManager()
device = rm.open_resource(address)
try:
device.write("*IDN?")
identity = device.read()
except visa.errors.VisaIOError:
try:
device.write("I")
device.write("STB?")
identity = device.read()
except visa.errors.VisaIOError:
try:
device.write("ID?")
identity = device.read()
except visa.errors.VisaIOError:
identity = "Unknown"
print(identity)
The device I'm testing the code with requires the device.write("ID?") version of this command, however instead of returning the identity, it's returning the numerical error code. Is there a better way to implement this?
I would remove the try/except block and see exactly where the device is failing. Since you are getting a numerical error code, I'm betting the error flow is as follows:
The script sends *IDN?
The device doesn't recognize this command and goes into an error state.
The script sends I
The device is already in an error state and doesn't respond.
The script queries STB?
The device reports the error it logged earlier.
The script interprets this response as the device name, even though it's the answer to the Status Byte query.
I find it's often better to write the identity function specifically for each device. For example: def identity_dev1() for a device that needs *IDN? and def identity_dev2() for a device that needs I and so on.
If you want a monolithic function that can do all of the above, you'll have to change the way you handle the status byte. Here's an example:
import pyvisa as visa
address = "GPIB0::6::INSTR"
rm = visa.ResourceManager()
device = rm.open_resource(address)
identity = None
try:
device.write("*IDN?")
identity = device.read()
except visa.errors.VisaIOError:
device.write("*RST") # get rid of the error state and try something else
if not identity:
try:
device.write("ID?")
identity = device.read()
except visa.errors.VisaIOError:
device.write("*RST") # get rid of error state
if not identity:
identity = "Unknown"
In general it's better to not do extra try/except behavior over GPIB communication. Trying the wrong command takes extra time and can put the instrument in an error state. In the code above, I use the *RST command to leave the error state, but this isn't guaranteed to work on all tools.
print(identity)
Related
I am currently working on a simple project in micropython, using a BMP388 barometric pressure sensor (datasheet here: https://www.mouser.com/pdfdocs/BST-BMP388-DS001-01.pdf). I am having trouble with Micropython's writeto_mem() and writeto() functions (documentation: https://docs.micropython.org/en/latest/library/machine.I2C.html). I do not understand why these functions appear unable to write to my registers, while the readfrom_mem() function works just fine.
Here are code snippets I've tried:
from machine import I2C
from time import sleep
_address = 0x76 #or 0x77 if I disconnect a certain pin, but this is not an issue.
i2c = I2C(scl=Pin(22), sda=Pin(21))
def set_sampling_v1():
i2c.writeto(_address, bytes(0x1c), False)
i2c.writeto(_address, bytes(0x0d), True)
#I also tried i2c.writeto(_address, bytes(0x1c0d), True)
print(i2c.readfrom_mem(_address, 0x1c, 1)) #get what I just wrote
print(i2c.readfrom_mem(_address, 0x00, 1)) #get device ID, confirm we're able to read
def set_sampling_v2():
i2c.writeto_mem(_address, 0x1C, bytes(0x0D))
print(i2c.readfrom_mem(_address, 0x1c, 1)) #get what I just wrote
print(i2c.readfrom_mem(_address, 0x00, 1)) #get device ID, confirm we're able to read
set_sampling_v1()
#Or try:
#set_sampling_v2()
In both set_sampling functions, the readfrom_mem() functions return:
x00
b'P' #which is just x50, and is the correct value.
In the case of v1, I am unsure how exactly micropython handles writeto(), but the I2C protocol takes the device address, a "RW" bit, and then a pair of bytes, before a stop condition, with the RW bit set to 0 for "write". So I tried both attempts at writing which made sense to try. In v2, since it is using the "convenience" function, I assume writeto_mem() replicates what I tried to do in the first version. I also assume micropython is able to supply the correct RW bit value when supplied with an address. Presumably it's doing something like (_address | 0x01) in the underlying function.
To summarize i2c operation, the BMP388 takes the device's address and a RW bit set to tell it whether to read or write. Then it takes a pair of bytes, one specifying the register and one specifying the data to be written. A stop condition follows at the end, to tell it that communication is complete. This is also why I've tried writing 0x1c0d as well as trying two separate write commands, since I was concerned the writeto() function would send the address register once per function call instead of just once. The writeto_mem() function supposedly just needs the device address, target register, and byte to be written, and so I didn't both testing alternative approaches. It's also worth noting, in version 1 of the sample function, the line
i2c.writeto(_address, 0x1c0d, True)
has the same x00 and b'p' result when I call the readfrom_mem() function.
Since the read functions return the correct device ID, I trust that x00 is what's actually in the the 0x1c register (which is where you set the sensor's oversampling rate for pressure and temperature). I've also successfully used the readfrom_mem() function on other registers to read and confirm their default values. Taken together, this convinces me that the function is reading the value in 0x1c and returning x00 because the writeto functions are failing to write.
Any insight into this would be welcome. I am about to review the datasheet to ensure there isn't something that must be set in a different register before I'm able to alter the contents of read/write registers, but I don't recall seeing such a thing before, and I'm wondering if anyone has insight into this problem.
If you need additional information I haven't provided, please let me know, and I will attempt to update this post as promptly as I can.
Hi guys I'm working a on script that will get data from a host using the Data Communications Standard (Developed by: Data Communication Standard Committee Lens Processing Division of The Vision Council), by serial port and pass the data into ModBus Protocol for the device to perform it's operations.
Since I don't fiscally have access to the host machine I'm trying to develop a secondary script to emulate the host. I am currently on the stage where I need to read a lot of information from the serial port and I get only part of the data. I was hoping to get the whole string sent on the send_job() function on my host emulator script.
Guys also can any of you tell me if this would be a good approach? the only thing the machine is supposed to do is grab 2 values from the host response and assign them to two modbus holding registers.
NOTE: the initialization function is hard coded because it will always be the same and the actual response data will not matter except for status. Also the job request is hard coded i only pass the job # that i get from a modbus holding register, the exact logic on how the host resolved this should not matter i only need to send the job number scanned from the device in this format.
main script:
def request_job_modbus(job):
data = F'[06][1c]req=33[0d][0a]job={job}[0d][0a][1e][1d]'.encode('ascii')
writer(data)
def get_job_from_serial():
response = serial_client.read_all()
resp = response.decode()
return resp
# TODO : SEND INIT SEQUENCE ONCE AND VERIFY IF REQUEST status=0
initiation_request()
init_response_status = get_init_status()
print('init method being active')
print(get_init_status())
while True:
# TODO: get job request data
job_serial = get_job_from_serial()
print(job_serial)
host emulation script:
def send_job():
job_response = '''[06][1c]ans=33[0d]job=30925[0d]status=0;"ok"[0d]do=l[0d]add=;2.50[0d]ar=1[0d]
bcerin=;3.93[0d]bcerup=;-2.97[0d]crib=;64.00[0d]do=l[0d]ellh=;64.00[0d]engmask=;613l[0d]
erdrin=;0.00[0d]erdrup=;10.00[0d]ernrin=;2.00[0d]ernrup=;-8.00[0d]ersgin=;0.00[0d]
ersgup=;4.00[0d]gax=;0.00[0d]gbasex=;-5.30[0d]gcrosx=;-7.96[0d]kprva=;275[0d]kprvm=;0.55[0d]
ldpath=\\uscqx-tcpmain-at\lds\iot\do\800468.sdf[0d]lmatid=;151[0d]lmatname=;f50[0d]
lnam=;vsp_basic_fh15[0d]sgerin=;0.00[0d]sgerup=;0.00[0d]sval=;5.18[0d]text_11=;[0d]
text_12=;[0d]tind=;1.53[0d][1e][1d]'''.encode('ascii')
writer(job_response)
def get_init_request():
req = p.readline()
print(req)
request = req.decode()[4:11]
# print(request)
if request == 'req=ini':
print('request == req=ini??? <<<<<<< cumple condicion y enviala respuesta')
send_init_response()
send_job()
while True:
# print(get_init_request())
get_init_request()
what I get in screen: main script
init method being active
bce
erd
condition was met init status=0
outside loop
ers
condition was met init status=0
inside while loop
trigger reset <<<--------------------
5782
`:lmatid=;151[0d]lmatname=;f50[0d]
lnam=;vsp_basic_fh15[0d]sgerin=;0.00[0d]sgerup=;0.00[0d]sval=;5.18[0d]text_11=;[0d]
text_12=;[0d]tind=;1.53[0d][1e][1d]
outside loop
condition was met init status=0
outside loop
what I get in screen: host emulation script
b'[1c]req=ini[0d][0a][1e][1d]'
request == req=ini??? <<<<<<< cumple condicion y enviala respuesta
b''
b'[06][1c]req=33[0d][0a]job=5782[0d][0a][1e][1d]'
b''
b''
b''
b''
b''
b''
I'm suspect you're trying to write too much at once to a hardware buffer that is fairly small. Especially when dealing with low power hardware, assuming you can stuff an entire message into a buffer is not often correct. Even full modern PC's sometimes have very small buffers for legacy hardware like serial ports. You may find when you switch from development to actual hardware, that the RTS and DTR lines need to be used to determine when to send or receive data. This will be up to whoever designed the hardware unfortunately, as they are often also ignored.
I would try chunking your data transfer into smaller bits as a test to see if the whole message gets through. This is a quick and dirty first attempt that may have bugs, but it should get you down the right path:
def get_job_from_serial():
response = b'' #buffer for response
while True:
try:
response += serial_client.read() #read any available data or wait for timeout
#this technically could only be reading 1 char at a time, but any
#remotely modern pc should easily keep up with 9600 baud
except serial.SerialTimeoutException: #timeout probably means end of data
#you could also presumably check the length of the buffer if it's always
#a fixed length to determine if the entire message has been sent yet.
break
return response
def writer(command):
written = 0 #how many bytes have we actually written
chunksize = 128 #the smaller you go, the less likely to overflow
# a buffer, but the slower you go.
while written < len(command):
#you presumably might have to wait for p.dtr() == True or similar
#though it's just as likely to not have been implemented.
written += p.write(command[written:written+chunksize])
p.flush() #probably don't actually need this
P.S. I had to go to the source code for p.read_all (for some reason I couldn't find it online), and it does not do what I think you expect it does. The exact code for it is:
def read_all(self):
"""\
Read all bytes currently available in the buffer of the OS.
"""
return self.read(self.in_waiting)
There is no concept of waiting for a complete message, it just a shorthand for grab everything currently available.
I'm using RS-232 port to communicate with KeithleyInstruments(SCPI Protocol) and have a problem.I can send the write command but when I send a query command it*s show the error below.
import visa
rm = visa.ResourceManager()
inst = rm.list_resources()
print inst
# print inst --> (u'USB0::0x05E6::0x2200::9060025::INSTR', u'ASRL1::INSTR', u'ASRL6::INSTR', u'ASRL7::INSTR', u'ASRL10::INSTR', u'GPIB0::16::INSTR')
keithleyInst= rm.open_resource('ASRL7::INSTR')
print keithleyInst.write("*rst")
print keithleyInst.write(":meas:temp?")
print keithleyInst.query(":meas:temp?")
Error:
pyvisa.errors.VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.
A query is a write and a read combined, so you only need the query, not the write.
If it still times out after removing the extra write, try setting a really long timeout like:
keithleyInst.timeout = 5000
To give it 5 seconds to respond. You can always shorten this once you've got it working.
If it still doesn't respond, perhaps the instrument is not sending the termination character that VISA expects.
Try communicating with the instrument with a terminal program or National Instruments' Measurement & Automation program to find out for sure what termination character it is sending (if it is sending anything).
You can change the termination character VISA expects by
keithleyInst.read_termination = '\r'
or something similar.
As Jeanne Pindar answered, this can be due to a delay in answering or a bad read termination. It can also be linked to the baud rate of RS232 devices. You can set it with :
inst.baud_rate = 11520
Look at your constructor datasheet to specify the correct baudrate.
The issue
I am trying to play with a Bluefruit LE Friend dongle (Adafruit).
They provide a python library to communicate with it.
Unfortunately, the example they provide doesn't work:
user#server# python ../../low_level.py
Using adapter: user-VirtualBox
Disconnecting any connected UART devices...
Searching for UART device...
Connecting to device...
Discovering services...
Traceback (most recent call last):
File "../../low_level.py", line 106, in <module>
ble.run_mainloop_with(main)
File "build/bdist.linux-x86_64/egg/Adafruit_BluefruitLE/bluez_dbus/provider.py", line 118, in _user_thread_main
File "../../low_level.py", line 70, in main
device.discover([UART_SERVICE_UUID], [TX_CHAR_UUID, RX_CHAR_UUID])
File "build/bdist.linux-x86_64/egg/Adafruit_BluefruitLE/bluez_dbus/device.py", line 106, in discover
File "build/bdist.linux-x86_64/egg/Adafruit_BluefruitLE/bluez_dbus/device.py", line 137, in advertised
File "/usr/lib/python2.7/uuid.py", line 134, in __init__
raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string
What I tried
I added a line in /usr/lib/python2.7/uuid.py to print the uuid value when the exception is raised; I get the value 1800. I can't find where does this value comes from.
I can't find the file build/bdist.linux-x86_64/egg/Adafruit_BluefruitLE/bluez_dbus/device.py to debug with that one…
Here's the code, from their GitHub repo:
# Example of low level interaction with a BLE UART device that has an RX and TX
# characteristic for receiving and sending data. This doesn't use any service
# implementation and instead just manipulates the services and characteristics
# on a device. See the uart_service.py example for a simpler UART service
# example that uses a high level service implementation.
# Author: Tony DiCola
import logging
import time
import uuid
import Adafruit_BluefruitLE
# Enable debug output.
#logging.basicConfig(level=logging.DEBUG)
# Define service and characteristic UUIDs used by the UART service.
UART_SERVICE_UUID = uuid.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
TX_CHAR_UUID = uuid.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E')
RX_CHAR_UUID = uuid.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E')
# Get the BLE provider for the current platform.
ble = Adafruit_BluefruitLE.get_provider()
# Main function implements the program logic so it can run in a background
# thread. Most platforms require the main thread to handle GUI events and other
# asyncronous events like BLE actions. All of the threading logic is taken care
# of automatically though and you just need to provide a main function that uses
# the BLE provider.
def main():
# Clear any cached data because both bluez and CoreBluetooth have issues with
# caching data and it going stale.
ble.clear_cached_data()
# Get the first available BLE network adapter and make sure it's powered on.
adapter = ble.get_default_adapter()
adapter.power_on()
print('Using adapter: {0}'.format(adapter.name))
# Disconnect any currently connected UART devices. Good for cleaning up and
# starting from a fresh state.
print('Disconnecting any connected UART devices...')
ble.disconnect_devices([UART_SERVICE_UUID])
# Scan for UART devices.
print('Searching for UART device...')
try:
adapter.start_scan()
# Search for the first UART device found (will time out after 60 seconds
# but you can specify an optional timeout_sec parameter to change it).
device = ble.find_device(service_uuids=[UART_SERVICE_UUID])
if device is None:
raise RuntimeError('Failed to find UART device!')
finally:
# Make sure scanning is stopped before exiting.
adapter.stop_scan()
print('Connecting to device...')
device.connect() # Will time out after 60 seconds, specify timeout_sec parameter
# to change the timeout.
# Once connected do everything else in a try/finally to make sure the device
# is disconnected when done.
try:
# Wait for service discovery to complete for at least the specified
# service and characteristic UUID lists. Will time out after 60 seconds
# (specify timeout_sec parameter to override).
print('Discovering services...')
device.discover([UART_SERVICE_UUID], [TX_CHAR_UUID, RX_CHAR_UUID])
# Find the UART service and its characteristics.
uart = device.find_service(UART_SERVICE_UUID)
rx = uart.find_characteristic(RX_CHAR_UUID)
tx = uart.find_characteristic(TX_CHAR_UUID)
# Write a string to the TX characteristic.
print('Sending message to device...')
tx.write_value('Hello world!\r\n')
# Function to receive RX characteristic changes. Note that this will
# be called on a different thread so be careful to make sure state that
# the function changes is thread safe. Use Queue or other thread-safe
# primitives to send data to other threads.
def received(data):
print('Received: {0}'.format(data))
# Turn on notification of RX characteristics using the callback above.
print('Subscribing to RX characteristic changes...')
rx.start_notify(received)
# Now just wait for 30 seconds to receive data.
print('Waiting 60 seconds to receive data from the device...')
time.sleep(60)
finally:
# Make sure device is disconnected on exit.
device.disconnect()
# Initialize the BLE system. MUST be called before other BLE calls!
ble.initialize()
# Start the mainloop to process BLE events, and run the provided function in
# a background thread. When the provided main function stops running, returns
# an integer status code, or throws an error the program will exit.
ble.run_mainloop_with(main)
This code depends on D-Bus, specifically the 'properties' offered by it. I would to say that the problem is here:
uuids = self._props.Get(_INTERFACE, 'UUIDs')
https://github.com/adafruit/Adafruit_Python_BluefruitLE/blob/master/Adafruit_BluefruitLE/bluez_dbus/device.py#L131
Probably you either didn't properly install D-Bus, or there's been another issue (linking to it, configuration, version issue, etc). When you try and get the 'UUIDs' property, you get invalid strings, which then get sense down to uuid.UUID() here:
return [uuid.UUID(str(x)) for x in uuids]
and you get the error you've seen. I would print out the value of the uuids array for debugging purposes, see what the value of the elements in it are, and proceed accordingly.
Good luck!
Well u have two ways to fix it.
Right way read more about this and configure your adafruit bluetooth.
Or bad way u can just comment this if statement check
if len(hex) != 32:
raise ValueError('badly formed hexadecimal UUID string')
in /usr/lib64/python2.7/uuid.py problem is comming from the len of "hex" parameter is need to be configured like thist 128 bit 6e400001b5a3f393e0a9e50e24dcca9e and not 16 bit0x1800.
To see which parameters of GATT u need to configure just put a
print "this is value of hex", hex
in /usr/lib64/python2.7/uuid.py before the previosly commented if statement.
I'm having trouble getting my Windows 7 laptop to talk to a Newport CONEX-PP motion controller. I've tried python (Spyder/Anaconda) and a serial port streaming program called Termite and in either case the results are the same: no response from the device. The end goal is to communicate with the controller using python.
The controller connects to my computer via a USB cable they sold me that is explicitly for use with this device. The connector has a pair of lights that blink when the device receives data (red) or sends data (green). There is also a packaged GUI program that comes with the device that seems to work fine. I haven't tried every button, the ones I have tried have the expected result.
The documentation for accessing this device is next to non-existant. The CD in the box has one way to connect to it and the webpage linked above has a different way. The first way (CD from the box) creates a hierarchy of modules that ends in a module it does not recognize (this is a code snippet provided by Newport):
import sys
sys.path.append(r'C:\Newport\MotionControl\CONEX-PP\Bin')
import clr
clr.AddReference("Newport.CONEXPP.CommandInterface")
from CommandInterfaceConexPP import *
import System
instrument="COM5"
print 'Instrument Key=>', instrument
myPP = ConexPP()
ret = myPP.OpenInstrument(instrument)
print 'OpenInstrument => ', ret
result, response, errString = myPP.SR_Get(1)
That last line returns:
Traceback (most recent call last):
File "< ipython-input-2-5d824f156d8f >", line 2, in
result, response, errString = myPP.SR_Get(1)
TypeError: No method matches given arguments
I'm guessing this is because the various module references are screwy in some way. But I don't know, I'm relatively new to python and the only time I have used it for serial communication the example files provided by the vendor simply worked.
The second way to communicate with the controller is via the visa module (the CONEX_SMC_common module imports the visa module):
import sys
sys.path.append(r'C:\Newport\NewportPython')
class CONEX(CONEXSMC): def __init__(self):
super(CONEX,self).__init__() device_key = 'com5'
self.connect=self.rm.open_resource(device_key, baud_rate=57600, timeout=2000, data_bits=8, write_termination='\r\n',read_termination='\r\n')
mine.connect.read()
That last mine.connect.read() command returns:
VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.
If, instead, I write to the port mine.connect.write('VE') the light on the connector flashes red as if it received some data and returns:
(4L, < StatusCode.success: 0 >)
If I ask for the dictionary of the "mine" object mine.__dict__, I get:
{'connect': <'SerialInstrument'(u'ASRL5::INSTR')>,
'device_key': u'ASRL5::INSTR',
'list_of_devices': (u'ASRL5::INSTR',),
'rm': )>}
The ASRL5::INSTR resource for VISA is at least related to the controller, because when I unplug the device from the laptop it disappears and the GUI program will stop working.
Maybe there is something simple I'm missing here. I have NI VISA installed and I'm not just running with the DLL that comes from the website. Oh, I found a Github question / answer with this exact problem but the end result makes no sense, the thread is closed after hgrecco tells him to use "open_resource" which is precisely what I am using.
Results with Termite are the same, I can apparently connect to the controller and get the light to flash red, but it never responds, either through Termite or by performing the requested action.
I've tried pySerial too:
import serial
ser = serial.Serial('com5')
ser.write('VE\r\n')
ser.read()
Python just waits there forever, I assume because I haven't set a timeout limit.
So, if anyone has any experience with this particular motion controller, Newport devices or with serial port communication in general and can shed some light on this problem I'd much appreciate it. After about 7 hours on this I'm out of ideas.
After coming back at this with fresh eyes and finding this GitHub discussion I decided to give pySerial another shot because neither of the other methods in my question are yet working. The following code works:
import serial
ser = serial.Serial('com5',baudrate=115200,timeout=1.0,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)
ser.write('1TS?\r\n')
ser.read(10)
and returns
'1TS000033\r'
The string is 9 characters long, so my arbitrarily chosen 10 character read ended up picking up one of the termination characters.
The problem is that python files that come with the device, or available on the website are at best incomplete and shouldn't be trusted for anything. The GUI manual has the baud rate required. I used Termite to figure out the stop bit settings - or at least one that works.
3.5 years later...
Here is a gist with a class that supports Conex-CC
It took me hours to solve this!
My device is Conex-CC, not PP, but it's seem to be the same idea.
For me, the serial solution didn't work because there was absolutely no response from the serial port, either through the code nor by direct TeraTerm access.
So I was trying to adapt your code to my device (because for Conex-CC, even the code you were trying was not given!).
It is important to say that import clr is based on pip install pythonnet and not pip install clr which will bring something related to colors.
After getting your error, I was looking for this Pythonnet error and have found this answer, which led me to the final solution:
import clr
# We assume Newport.CONEXCC.CommandInterface.dll is copied to our folder
clr.AddReference("Newport.CONEXCC.CommandInterface")
from CommandInterfaceConexCC import *
instrument="COM4"
print('Instrument Key=>', instrument)
myCC = ConexCC()
ret = myCC.OpenInstrument(instrument)
print('OpenInstrument => ', ret)
response = 0
errString = ''
result, response, errString = myCC.SR_Get(1, response, errString)
print('Positive SW Limit: result=%d,response=%.2f,errString=\'%s\''%(result,response,errString))
myCC.CloseInstrument()
And here is the result I've got:
Instrument Key=> COM4
OpenInstrument => 0
Positive SW Limit: result=0,response=25.00,errString=''�
For Conex-CC serial connections are possible using both pyvisa
import pyvisa
rm = pyvisa.ResourceManager()
inst = rm.open_resource('ASRL6::INSTR',baud_rate=921600, write_termination='\r\n',read_termination='\r\n')
pos = inst.query('01PA?').strip()
and serial
import serial
serial = serial.Serial(port='com6',baudrate=921600,bytesize=8,parity='N',stopbits=1,xonxoff=True)
serial.write('01PA?'.encode('ascii'))
serial.read_until(b'\r\n')
All the commands are according to the manual