xyphro / UsbGpib

Versatile, cheap and portable USB to GPIB converter (USBTMC class based)
MIT License
288 stars 51 forks source link

Support for older GPIB protocols / instruments #41

Closed deepfryed closed 10 months ago

deepfryed commented 1 year ago

I seem to have trouble talking to an older HP3245A that has command set predating SCPI.

On connecting, I immediately get an error "Expected command header or assignment." and then the first device write and read kind of works but I suspect the atmega FW gets into a bad state subsequently (a simple ruby example below)

irb(main):013:0> port = File.open("/dev/usbtmc0", File::RDWR | File::NONBLOCK)
=> #<File:/dev/usbtmc0>
irb(main):014:0> port.write("ID?\r\n"); p port.readpartial(8); 1
"HP3245\r\n"
=> 1
irb(main):015:0> port.write("ID?\r\n"); p port.readpartial(8); 1
Traceback (most recent call last):
        3: from /home/bharanee/.asdf/installs/ruby/2.5.1/bin/irb:11:in `<main>'
        2: from (irb):15
        1: from (irb):15:in `readpartial'
Errno::ETIMEDOUT (Connection timed out @ io_getpartial - /dev/usbtmc0)

I can take a look at the fw code and try debugging unless you have some pointers and I'm missing something very obvious.

xyphro commented 1 year ago

Hi!

What you experience is from the automatic ibstrument identification feature. You can turn it off - check readme.md.

Best regards,

Kai

xyphro commented 1 year ago

I had a quick look at the manual. Is cr+lf termination supported by the instrument? I have old HP instruments that expect EOI only. Which means that CRLF should not be sent if my assumption is true.

But first disable auto id, this ensures that no unsupported rubbish is sent on power on of the instrument. I want to have auto id turned off default wise in future releases to avoid such issues - it only works with more recent instruments.

Ps: seeing that the instrument returns CRLF, I think it DOES likely require CRLF for commands too.

deepfryed commented 1 year ago

But first disable auto id, this ensures that no unsupported rubbish is sent on power on of the instrument.

I don't have NI libraries or pyvisa installed. Can I send those sequences to the raw tmc device as a binary sequence ?

device.write("\xa1\x40\x00\x00!0001")  
xyphro commented 1 year ago

Likely not, because it does not get a usb control transfer I suspect.

xyphro commented 1 year ago

It is possible to change also a value directly in avr eeprom, but I am on the road and cannot look it up right now

deepfryed commented 1 year ago

I'm going to try updating the firmware with the hack below, will let you know how that goes

423   bool id_enabled = false;
 424   if (id_enabled && eeprom_read_byte(104) != 0x01)
 425   {
 426     /* found a responsive GPIB address, now setup USB descriptor with *IDN? or ID? command response */
 427     eeprom_busy_wait();
 428     prevaddr = eeprom_read_byte((uint8_t*)0); /* read previous gpib address */
 429     if (identifyGpibDevice())
 430     { /* received a string over GPIB => Store it in EEPROM, if it changed */
 431       uint8_t *pdat, i;
deepfryed commented 1 year ago

ok that worked, along with '\n' read termination. I hard coded those in the fw , but will need to work out how to set that from CLI without pyvisa. Thanks for your help :+1:

deepfryed commented 1 year ago

I might have spoken too soon, I'll have to play with it (some of the responses are multi-line , it seems to break there)

Edit: IDN? returns a multi line string. When trying to read from the usbtmc device after the last line has been sent, the read times out and I have to unplug / reset the device. I'll poke around tomorrow and see what I can find.

xyphro commented 1 year ago

Interesting, never saw a multiline idn response. But if you use the default eoi termination, will it get split into a single read value or multiple.

Maybe it is required to do then one write followed by 3 reads.

Maybe there is also nothing wrong with the avr firmware and the instruments gpib command processor is just expecting 3 reads?

xyphro commented 1 year ago

Interesting, never saw a multiline idn response. But if you use the default eoi termination, will it get split into a single read value or multiple.

Maybe it is required to do then one write followed by 3 reads.

Question: does ID? Command work for you now?

deepfryed commented 1 year ago

Question: does ID? Command work for you now?

Yes it does, the only issue is trying to read from the tmc device when the gpib interface is not sending any more bytes. Once it times out I have to unplug / reset the avr - something I can try fixing if it's an actual issue.

xyphro commented 1 year ago

Can you check if your instrument does send EOI termination by observing the eoi line? If not it should work[*] by setting the /n termination in the eeprom (note if you hack the fw directly you might need to change it at multiple places if I remember correctly).

[*] expectation wise, but have also myself an instrument in use without eoi and only /n termination.

xyphro commented 1 year ago

Your issue might be related to the other one beeing on top of the issue list. I think I found something with that older instruments can do a pushback to gpib masters which I did not put in my code. Without this it might happen that the gpib adapter sends data too quick leading to skipped bytes. Busy with it and will share an update here in the thread once done.

xyphro commented 1 year ago

The funny thing is that I wrote it propperly in the comment of my gpib tx code but did not implement it like that :-/

xyphro commented 1 year ago

As you can compile yourself. Can you change this line in gpib.c (function gpib_tx): while ( (NRFD_STATE == 0) && !timedout); / wait until ready for data (NRFD to get high) /

to: while ( !((NDAC_STATE == 0) && (NRFD_STATE != 0)) && !timedout); / wait until ready for data (NRFD to get high) /

and see if ID? query now works consistently?

deepfryed commented 1 year ago

kind of, everything works until I hit a timeout error because of trying to read when data is not expected from the gpib interface. I'll take a closer look at gpib.c later today

irb(main):001:0> port = File.open("/dev/usbtmc0", File::RDWR | File::NONBLOCK)
=> #<File:/dev/usbtmc0>
irb(main):002:0> port.write("ID?\r\n")
=> 5
irb(main):003:0> port.readline
=> "HP3245\r\n"
irb(main):004:0> port.write("ID?\r\n")
=> 5
irb(main):005:0> port.readline
=> "HP3245\r\n"
irb(main):006:0> port.write("IDN?\r\n")
=> 6
irb(main):007:0> port.readline
=> "HEWLETT PACKARD\r\n"
irb(main):008:0> port.readline
=> "3245           \r\n"
irb(main):009:0> port.readline
=> "0              \r\n"
irb(main):010:0> port.readline
=> "2843           \r\n"
irb(main):011:0> port.readline
Errno::ETIMEDOUT (Connection timed out @ io_fillbuf - fd:9 /dev/usbtmc0)
irb(main):012:0> port.write("ID?\r\n")
=> 5
irb(main):013:0> port.readline
Errno::ETIMEDOUT (Connection timed out @ io_fillbuf - fd:9 /dev/usbtmc0)
deepfryed commented 1 year ago

I suspect it might be related to https://github.com/pyvisa/pyvisa-py/issues/177#issue-399263439

deepfryed commented 1 year ago

re. setting those eeprom bits from command line without visa, should this work ?

#!/usr/bin/env python3

import usb.core
import usb.util

# find our device
dev = usb.core.find(idVendor=0x03eb, idProduct=0x2065)

dev.detach_kernel_driver(0)
usb.util.claim_interface(dev, 0)
dev.reset()

dev.set_configuration()

dev.ctrl_transfer(0xa1, 0x40, 0x0000, 0x0000, b'!0001') # no auto-id
dev.ctrl_transfer(0xa1, 0x40, 0x0000, 0x0000, b'!0101') # use '\n' 

doesn't seem to work for me (I have removed the hard coded hacks in the fw and re-flashed it)

deepfryed commented 1 year ago

Tried this as well, no luck :laughing: this is the limit of my knowledge after a quick scan through usbmadesimple and pyusb

$ python3
Python 3.8.2 (default, Oct 11 2020, 20:10:53) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import usb.core
>>> import usb.util
>>> dev = usb.core.find(idVendor=0x03eb, idProduct=0x2065)
>>> dev.detach_kernel_driver(0)
>>> usb.util.claim_interface(dev, 0)
>>> dev.reset()
>>> [if_out, if_in] = dev[0].interfaces()[0].endpoints()
>>> dev.ctrl_transfer(0xa1, 0x40, 0x0000, 0x0000, 5)
array('B', [1])
>>> if_out
<ENDPOINT 0x3: Bulk OUT>
>>> if_out.write(b'!0001', 10)
5
xyphro commented 1 year ago

You can use also pyvisa-py package which does inplement it low level too.

deepfryed @.***> schrieb am So., 22. Okt. 2023, 07:27:

Tried this as well, no luck 😆 this is the limit of my knowledge after a quick scan through usbmadesimple and pyusb

$ python3 Python 3.8.2 (default, Oct 11 2020, 20:10:53) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information.

import usb.core import usb.util dev = usb.core.find(idVendor=0x03eb, idProduct=0x2065) dev.detach_kernel_driver(0) usb.util.claim_interface(dev, 0) dev.reset() [if_out, if_in] = dev[0].interfaces()[0].endpoints() dev.ctrl_transfer(0xa1, 0x40, 0x0000, 0x0000, 1, 10) array('B', [1]) if_out <ENDPOINT 0x3: Bulk OUT> if_out.write(b'!0001', 10) 5 if_out.write(b'!0101', 10) 5

— Reply to this email directly, view it on GitHub https://github.com/xyphro/UsbGpib/issues/41#issuecomment-1773999585, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFXEGFV7U6G6M45FSW6GFTYASVFFAVCNFSM6AAAAAA6J4HANKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTONZTHE4TSNJYGU . You are receiving this because you commented.Message ID: @.***>

xyphro commented 1 year ago

After a timeout a usbtmc visa implementation does special things to unstall (with a control transfer). This might not happen with direct device access.

xyphro commented 1 year ago

Just looked at the usbtmc kernel driver code.

It has ioctls implemented for timeout reaction handling but does not call them itself. usbtmc_ioctl_abort_bulk_in_tag Is one of them.

If a timeout occurs it can happen due to multiple sources: on usb level and gpib level. After a timeout the host side usbtmc driver does not know the state. Is there a response present on bulk endpoint or not. Visa sends for that reason those abort requests to bring the device to a known state (cleared bulk endpoints). The usbtmc kernel mode driver does not seem to do this automatically but seems to rely on the upper level stack to handle this.

deepfryed @.***> schrieb am So., 22. Okt. 2023, 07:27:

Tried this as well, no luck 😆 this is the limit of my knowledge after a quick scan through usbmadesimple and pyusb

$ python3 Python 3.8.2 (default, Oct 11 2020, 20:10:53) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information.

import usb.core import usb.util dev = usb.core.find(idVendor=0x03eb, idProduct=0x2065) dev.detach_kernel_driver(0) usb.util.claim_interface(dev, 0) dev.reset() [if_out, if_in] = dev[0].interfaces()[0].endpoints() dev.ctrl_transfer(0xa1, 0x40, 0x0000, 0x0000, 1, 10) array('B', [1]) if_out <ENDPOINT 0x3: Bulk OUT> if_out.write(b'!0001', 10) 5 if_out.write(b'!0101', 10) 5

— Reply to this email directly, view it on GitHub https://github.com/xyphro/UsbGpib/issues/41#issuecomment-1773999585, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFXEGFV7U6G6M45FSW6GFTYASVFFAVCNFSM6AAAAAA6J4HANKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTONZTHE4TSNJYGU . You are receiving this because you commented.Message ID: @.***>

deepfryed commented 1 year ago

You can use also pyvisa-py package which does inplement it low level too.

The pure python version doesn't implement it, which is why I'm trying to do this through the usb package. I've tried installing NI visa lib in the past, but it was a cluster$% of dependency hell on debian.

Don't fancy going down that route again and librevisa doesn't implement the control transfer features. In theory that example snippet above should work, probably almost there just need to work out what I'm missing.

>>> inst.control_in(0xa1, 0x40, 0x0000, 0x0000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/bharanee/.asdf/installs/python/3.8.2/lib/python3.8/site-packages/pyvisa/resources/usb.py", line 91, in control_in
    return self.visalib.usb_control_in(
  File "/home/bharanee/.asdf/installs/python/3.8.2/lib/python3.8/site-packages/pyvisa/highlevel.py", line 2615, in usb_control_in
    raise NotImplementedError
NotImplementedError
caiser01 commented 1 year ago

The pure python version doesn't implement it, which is why I'm trying to do this through the usb package. I've tried installing NI visa lib in the past, but it was a cluster$% of dependency hell on debian.

You might try Rohde & Schwarz VISA: https://www.rohde-schwarz.com/us/applications/r-s-visa-application-note_56280-148812.html

We've been using it for testing another issue: https://github.com/xyphro/UsbGpib/issues/38

It's a good VISA implementation that is far less of a pain to have installed than NI VISA. I've personally been using it on Win10, macOS, and Linux (RaspiOS) without issue and it doesn't feel like it's taken over my machine like NI always did.

deepfryed commented 1 year ago

Thanks @caiser01 It installed fine on my debian (sid) but have trouble using the backend

#!/usr/bin/env python3
import pyvisa
rm = pyvisa.ResourceManager('/usr/lib/librsvisa.so@ivi')
print(rm.list_resources())
inst = rm.open_resource('USB0::0x03EB::0x2065::HP3245::INSTR')
inst.write_termination = '\r\n'
inst.read_termination = '\r\n'
inst.write('ID?')
print(inst.read())

Get an error

pyvisa.errors.VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.

Am I missing something ? Using it from the usbtmc interface directly seems to work

caiser01 commented 1 year ago

@deepfryed, I had this exact problem when I was running the previous April 2022 firmware on the adapter and using R&S VISA. Have you upgraded to the latest October 2023 version? I would try that first. I would also open RsVisaTraceTool and start it recording so you can sniff the VISA function calls as they happen.

Alternatively, you could try testing queries with RsVisaTester but either way, make sure the adapter is running the latest firmware without any modifications first.

deepfryed commented 1 year ago

Gave that a go, no luck. Updated to 5efb91bcf9d9ad7f5ea0c4f2ea8c108cf3f86731 and removed all customisations.

Tried it with RSVisaTester and logged the trace, still having trouble.

RsVisa.log Screenshot at 2023-10-24 18-31-29

deepfryed commented 1 year ago

and no luck with python either, end of the day not a big issue. It'd be great to get the eeprom bit twiddling working. I can manually hardcode the values in FW and use the direct usbtmc interface meanwhile.

$ python3
Python 3.8.2 (default, Oct 11 2020, 20:10:53) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvisa
>>> rm = pyvisa.ResourceManager('/usr/lib/librsvisa.so@ivi')
>>> instr = rm.open_resource('USB0::0x03EB::0x2065::HP3245::INSTR')
>>> instr.control_in(0xa1, 0x40, 0x0000, 0x0000)
Traceback (most recent call last):
 <snip>

  handle_return_value
    raise errors.VisaIOError(rv)
pyvisa.errors.VisaIOError: VI_ERROR_IO (-1073807298): Could not perform operation because of I/O error.
caiser01 commented 1 year ago

@deepfryed and @xyphro, it looks as though there's an issue with sending the USB control bytes with R&S VISA in linux. I just tested this and it gives a warning VI_WARN_QUEUE_OVERFLOW when I try to do control_in() from python with R&S VISA under RaspiOS (debian bullseye), and the adapter actually hangs up and has to be unplugged/re-plugged to recover. It works as expected with R&S VISA on Windows and macOS; no issues.

@deepfryed, did you try testing any commands that are not queries from RsVisaTester? This would avoid a read timeout. Looks like there's a BEEP command that might be good for simple testing.

You might also want to have a look at the END command for your instrument. If your instrument is suppressing the EOI signal by default, that could be contributing to some of the behaviors you're seeing as well. Having the EOI signal turned on might also help with detecting the true end of the multiline response from your instrument's IDN? command.

xyphro commented 1 year ago

Can try during next days, but will need to prioritize between the 2 issues. I can loook forst into control byte topic, this is likely quicker solved with a usb trace if you dont mind.

Will do the debugging with wireshark on a pi capturing usb traffic - tomorrow arround this point of time. Never used r&s visa on a pi, but hope it will be easy to get running.

deepfryed commented 1 year ago

@caiser01 yes, the writes work with rsvisatester.

@xyphro tried getting a usb trace for the following python snippet, attached is the pcap file. Hope that helps.

>>> import pyvisa
>>> rm = pyvisa.ResourceManager('/usr/lib/librsvisa.so@ivi')
>>> rm.list_resources()
('USB0::0x03EB::0x2065::HP3245::INSTR', 'ASRL0::INSTR')
>>> instr = rm.open_resource('USB0::0x03EB::0x2065::HP3245::INSTR')
>>> instr.write_termination = '\r\n'
>>> instr.write('BEEP')
6
>>> instr.control_in(0xa1, 0x40, 0x0000, 0x0000)

gpibusb.pcap.gz

xyphro commented 1 year ago

grafik

xyphro commented 1 year ago

All fine under windows. But: On the PI with pyvisa and R&S Visa I get a control OUT transfer when I request a control IN transfer. No surprise that it does not work.

Who is guilty now? PyVisa or R&S Visa? Will check.

xyphro commented 1 year ago

I am 90% confident that this is a R&S Visa issue for the Raspberry PI port. Why? The RSVisaTraceTool shows under both Windows and RPI a viUsbControlIn. And not a viUsbControlOut.

Will file an issue towards R&S.

xyphro commented 1 year ago

I lost now several minutes of lifetime just to find out that neither Gloris nor the normal support page of R&S offers an option to file PRs for R&S Visa. I contacted however now a direct contact to report that issue by mail. Let's see if they pick it up.

caiser01 commented 1 year ago

Sorry they make it difficult. Hopefully they'll see your email. It's probably an easy fix for them.

xyphro commented 1 year ago

Your instrument supports EOI termination - check arround page 8-8.

However, the page 8-12 is missing which shows how to potentially enable it.

On my HP DMM i need to send always: END,ALWAYS/r/n After connection

To get queries like ID? Working. In the gpib fw I use standard EOI termination.

xyphro commented 1 year ago

Sorry they make it difficult. Hopefully they'll see your email. It's probably an easy fix for them.

Received today an email that the PR was received and forwarded internally for handling. So it seems to go forward in the right way.

xyphro commented 1 year ago

I got very good support from R&S team and we figured out what happens.

What happens is the following:

libusb does convert control in transfers (so transfers which go from the device to the PC) with 0 bytes specified as length to a control out transfer. Windows IVI driver keeps it a control in transfer.

So this is one of the fameous cases where you can end up in religious discussions: From USB standard perspective a read of 0 bytes is OK (with my interpretation of the standard). If it makes sense to do a read of 0 bytes: NO :-) But if you will ask 10 people you will hear 5-10 different opinions, so let's not go there :-)

How to address: The correct syntax to use is: VM.control_in(0xa1, 0x40, 0, 0, 1);

The last ",1" is added and specifies that 1 byte should be read. This is then also inline with the pulse indicator control transfer described in the USB TMC standard (base document).

I tested it and it works on the PI with R&S Visa. No need to update R&S Visa, my firmware or anything else. Just add ",1". I updated readme.md to reflect his addition.

deepfryed commented 1 year ago

yes, that works as expected. cheers!

xyphro commented 10 months ago

Closing for now. In case sonething os left to do, let me know/reopen or create a new issue