Wrapper class to make Python GPIB scripts portable - Page 1
Forum section: electronics / general

author: e61_phil
author_rank: Frequent Contributor
timestamp: November 20, 2018, 06:47:18 am
content: Hello,I think there are mainly to methods to use GPIB from python. Some people uses a Raspberry Pi with GPIB under LINUX (like TiN) and other uses PyVISA running on a windows machine (like me).I think we should think about some kind of wrapper class which encapsulates the low level GPIB stuff. This way one only needs to change the class to use a script from TiN under Windows for example.To build something like this we should collect the used functions with GPIB. With PyVISA I use the following methods:- object.read()- object.write()- object.query()- object.timeout = <timeout in ms> (should be a method like the rest, I think)In addition to objects from instruments one can also create an object from the GPIB interface itself in PyVISA. That is needed if one wants to trigger multiple instruments via GPIB at the same time (GET). This looks like that:Code:rm = visa.ResourceManager()intf = rm.open_resource('GPIB0::INTFC')hp3458A1 = rm.open_resource("GPIB0::23::INSTR")hp3458A2 = rm.open_resource("GPIB0::22::INSTR")intf.group_execute_trigger(hp3458A1, hp3458A2)How are such things done in the Rapsberry Pi world?And what is missing here?

author: z01z
author_rank: Regular Contributor
timestamp: November 20, 2018, 09:06:09 am
content: The logfile path and the needed imports are also different.I'm using something like this:Code:runOnPi = TrueK617_gpib_addr = 17if runOnPi:import Gpibinst = Gpib.Gpib(0,K617_gpib_addr)LogFile = '/pub/logs/K617_log.txt'else:import visarm = visa.ResourceManager()inst = rm.open_resource('GPIB0::{0:d}::INSTR'.format(K617_gpib_addr))LogFile = 'd/tmp/logs/K617_log.txt'Then just do inst.read/write/etc.The OS detection can also be automated, e.g.https://stackoverflow.com/questions/1854/python-what-os-am-i-running-on.I had a problem with the timeout handling on Win, but the details became blurry by now.Another issue can be python 2 vs 3.

author: e61_phil
author_rank: Frequent Contributor
timestamp: November 20, 2018, 10:38:14 am
content: Hmm, the Python 2 vs 3 issue could be a real problem.Your code is nice if you only uses one instrument, but it doesn't scale for many instruments.I'm also not sure how to parse the GPIB address, because PyVISA is complete VISA and not only GPIB. My Scope and my funcgen are connected via ethernet and USB. This is also done by VISA. Therefore I would go the other way around with the identfier.

author: z01z
author_rank: Regular Contributor
timestamp: November 20, 2018, 11:05:27 am
content: Quote from: e61_phil on November 20, 2018, 10:38:14 amIt is, if you want to mix them, there are countless web pages describing the differences. You can of course stick to just one version.TiN's scripts were for 2.x, with which I started playing on a Win machine with 3.x. So far I've found these to be a problem, plus print() must be used.Code:if sys.version=="3": raw_input=inputif sys.version=="3": open_permission="w"else: open_permission="wb"QuoteYou can of course duplicate the lines, just as you've showed in the first post. You could also create a function for initializing an instrument, if you want a wrapper.It's even more problematic, if they are different kinds. For that some kind of instrument class library would also be nice. There's PyMeasure but it would need effort to extend the list of supported instruments.QuoteI was only using GPIB, I haven't investigated that area unfortunately.

author: e61_phil
author_rank: Frequent Contributor
timestamp: November 20, 2018, 11:45:06 am
content: Quote from: z01z on November 20, 2018, 11:05:27 amAll programmers I know are always saying "don't repeat yourself". Copy&paste isn't the way one should do that.I was thinking about a class which encapsulates all this stuff. In the end one have to write someting like this:Code:import gpibwrappergpib = gpibwrapper.main('rpi')hp3458A = gpib.open("GPIB0::23::INSTR")K617 = gpib.open("GPIB0::17::INSTR")...hp3458A.write("END ALWAYS")...If you want to go to a windows/PyVISA machine the only thing to do is to change the first line against:Code:gpib = gpibwrapper.main('pyvisa')

author: TiN
author_rank: Super Contributor
timestamp: November 20, 2018, 11:45:19 am
content: I voted "No" because if you doing python scripts, it's not such a problem to change two lines of code to support either linux-gpib interface or vxi or visa. I prefer to install minimum possible of stuff to get gear working, so I don't use megabytes of PyVISA either. So original binds are already simple enough not to make yet another object that you need to support/maintain and test, when it essentially wraps gpib.write with gpibwrapper.write.It's useful in some cases (I did this in my calkit app, because I needed simulator for all GPIB commands, instead of writing to hardware for test. Idea to source 1000V into 10mV nanovoltmeter due to software bug isn't that interesting). Doing extra instance to wrap few commands just for datalogging feels excessive.However some time ago I moved from linux-gpib (and NI GPIB-USB-HS dongles) to vxi (using Agilent E5810A's VXI/LAN GPIB gateway) which I now enjoy greatly, as I can run python app on any device within network and talk to multiple GPIB networks. VXI bind is simple library too, and platform independent by design.Made a guide about it too. E5810A's pop up on ebay from time to time for similar prices of the USB GPIB dongles.Also I don't use windows machine for logging, as often experiments run for days/weeks and I log environment stats from I2C sensors for each data sample. RAW I2C access via Python in Windows would be ...em.... uneasy. I also do use device classes so each instrument can have own interface/address/type and I just type in main app:Initialize and load modulesCode:import impdmm1 = imp.load_source('hp3458', 'devices/hp3458.py') # Load support for HP3458Admm2 = imp.load_source('k2002' , 'devices/k2002.py') # Load support for K2002dmm3 = imp.load_source('k2002' , 'devices/k2002.py') # Load support for K2002dmm4 = imp.load_source('f8508a', 'devices/f8508a.py') # Load support for 8508Admm5 = imp.load_source('k182m' , 'devices/k182m.py') # Load support for K182MConfigure meters with GPIB address numbers and function+range:Code:dmm1 = dmm.dmm_meter(2,0,"3458B")dmm2 = dmm.scpi_meter(6,0,"2002-6")dmm3 = dmm.scpi_meter(4,0,"2002-4")dmm4 = dmm.flk_meter(5,0,"8508")dmm5 = nvm.k182m_meter(18,0,"182")dmm1.set_dcv_range(10)dmm1.acal_dcv()dmm2.set_ohmf_range(100000)dmm3.set_dci_range(1e-3)dmm4.set_ohm_range(200e3)dmm5.set_dcv_range(3e-3)And read data by simpleCode:meas_val = dmm1.get_data()meas_val2 = dmm3.get_data()meas_val3 = dmm3.get_data()meas_val4 = dmm4.get_data()meas_val5a = dmm5.get_data()

author: e61_phil
author_rank: Frequent Contributor
timestamp: November 20, 2018, 11:53:01 am
content: @Tin: you don't need to install PyVISA to use such a wrapper. The only thing that add to your sources would be the wrapper class file. The advantage of such a wrapper would be that you don't need to change any lines in your code if you move to the next gpib framework next time. Such a wrapper wouldn't be limited to two systems. Any changes in the low level stuff is at one place only.The other stufff you described is one layer higher. That has nothing to do with encapsulating the gpib stuff. I also use such things for the calibration scripts.As an example calibrating OHMs of a Solartron 7150 with the Fluke 5450A:Code:import visaimport DMM_Solartron_7150import CAL_Fluke_5450Aimport cal_runrm = visa.ResourceManager()S7150 = DMM_Solartron_7150.DMM(rm.open_resource("GPIB1::3::INSTR"))F5450A = CAL_Fluke_5450A.CAL(rm.open_resource("GPIB1::19::INSTR"))#F5450A Resistorscalpoints = ["0", "1", "1.9", "10", "19", "100", "190", "1e3", "1.9e3","10e3", "19e3", "100e3", "190e3","1e6", "1.9e6", "10e6", "19e6"]runs = 100S7150.set_mode("OHM")cal_run.cal(F5450A, S7150, calpoints, runs)S7150.close()F5450A.close()The DMM class takes care about peculiarities of the meters. The 7150 returns OHMs always in kOhm for example:Code:def get_reading(self):reading = float(self.visa.read())if self.mode == "OHM":reading *= 1000 #Werte werden in kOhm ausgegebenreturn readingbtw: Why don't you use lists, if you have numbered DMMs and values?

author: TiN
author_rank: Super Contributor
timestamp: November 20, 2018, 12:05:06 pm
content: e61_phil, somebody above mentioned use non-GPIB hardware, like ethernet-device, hence my reply on that side.QuoteI often use post-processing separate math on values, having lists make it less human readable IMO. So I do it monkey way, instead of polished programmer-way.

author: e61_phil
author_rank: Frequent Contributor
timestamp: November 20, 2018, 12:11:48 pm
content: Quote from: TiN on November 20, 2018, 12:05:06 pmYes, and that is another plus for such a wrapper. As long PyVISA can handle all VISA stuff, the linux-gpib can only handle gpib. But there is stuff on Linux for USBTMC and LXI for sure. One could put this also into such a class and address the instruments like one would do it with VISA. Internally the class than knows not to use gpib for the USBTMC device but USBTMC. For the programmer everything looks and feels the same.Quote from: TiN on November 20, 2018, 12:05:06 pmIs dmm1 more readable than dmm?In this case you could use dictionaries. That would be iterable like a list and is addressable like dmm["K2002"] for example.But I know the issues. Often times one do it the fast way and that stays forever then

author: TiN
author_rank: Super Contributor
timestamp: November 20, 2018, 12:19:07 pm
content: And then end up with something like dmm["3458A-3"], dmm["3458A-4"], dmm["3458A-2"]. Line those fellas up together, and I'll run off the horizontal line space even on 4K monitor.I guess you know by now why none of my ugly code end up on github or so

author: alm
author_rank: Super Contributor
timestamp: November 20, 2018, 12:35:21 pm
content: Quote from: TiN on November 20, 2018, 11:45:19 amPyVISA is 400 kiB on my computer. Hardly a big deal, even for an embedded Linux system like a Raspberry PI.Quote from: TiN on November 20, 2018, 11:45:19 amThat seems like a textbook application of a wrapper / abstraction to me.I'm not a big fan of reinventing wheels, even if the existing wheel is not exactly what I want. So I use PyVISA for VXI-11/GPIB access, and Python-IVI to wrap instruments so I can do:Code:dmm = agilent3458A('TCPIP::...::GPIB:21::INSTR')dmm.measurement_function = 'dc_volts'dmm.range = 10result = dmm.measurement.read(0)Granted, this abstraction will be overkill for many applications, but not enough reason to write something from scratch for me. I've done the "I only need one line difference between GPIB and RS-232" thing, including a wrapper class for RS-232 to behave like GPIB. But I still had to special case things like binary transfers with some "if RS-232 ...", and it was annoying.