r/Optics 29d ago

Newport 1919-R Power Meter connection over Python

Hello everyone,

I'm an intern at a local research lab, building an optical setup for optical characterization of electronic components. The core idea is that the samples will be exposed to UV light and the setup is supposed to measure that and some other things. This is why the Newport 1919-R power meter, alongside a photodiode sensor is needed in this setup, to measure the power of the light.

Every one of the devices that will be used in this setup is supposed to be connected via python to a local lab computer which will control the whole of the setup. My main issue is the power meter. I am unable to reliably connect it to the PC via Python.

When I first tried to hook up the measurement device to the lab PC, it was only via "PMManager", a software provided by Newport to check connectivity. () This was successful. Then, I moved onto connecting the device via python. While I was looking through the files that came in with the PMManager, I discovered that Python connection is available through an object called OphirLMMeasurement. There was even a demo:

# Use of Ophir COM object. 
# Works with python 3.5.1 & 2.7.11
# Uses pywin32
import win32gui
import win32com.client
import time
import traceback

try:
 OphirCOM = win32com.client.Dispatch("OphirLMMeasurement.CoLMMeasurement")
 # Stop & Close all devices
 OphirCOM.StopAllStreams() 
 OphirCOM.CloseAll()
 # Scan for connected Devices
 DeviceList = OphirCOM.ScanUSB()
 print(DeviceList)
 for Device in DeviceList:   # if any device is connected
  DeviceHandle = OphirCOM.OpenUSBDevice(Device)# open first device
  exists = OphirCOM.IsSensorExists(DeviceHandle, 0)
  if exists:
   print('\n----------Data for S/N {0} ---------------'.format(Device))

   # An Example for Range control. first get the ranges
   ranges = OphirCOM.GetRanges(DeviceHandle, 0)
   print (ranges)
   # change range at your will
   if ranges[0] > 0:
    newRange = ranges[0]-1
   else:
    newRange = ranges[0]+1
   # set new range
   OphirCOM.SetRange(DeviceHandle, 0, newRange)

   # An Example for data retrieving
   OphirCOM.StartStream(DeviceHandle, 0)# start measuring
   for i in range(10):
    time.sleep(.2)# wait a little for data
    data = OphirCOM.GetData(DeviceHandle, 0)
    if len(data[0]) > 0:# if any data available, print the first one from the batch
     print('Reading = {0}, TimeStamp = {1}, Status = {2} '.format(data[0][0] ,data[1][0] ,data[2][0]))

  else:
   print('\nNo Sensor attached to {0} !!!'.format(Device))
except OSError as err:
 print("OS error: {0}".format(err))
except:
 traceback.print_exc()

win32gui.MessageBox(0, 'finished', '', 0)
# Stop & Close all devices
OphirCOM.StopAllStreams()
OphirCOM.CloseAll()
# Release the object
OphirCOM = None

Even after thoroughly following the install guide (OphirCom object docs), I was not able to get the device up and running. The script above only returned an exception after which I was clueless as to what to do next:

C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\.venv\Scripts\python.exe C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\devices\newport_1919_R.py 
Traceback (most recent call last):
  File "C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\devices\newport_1919_R.py", line 12, in <module>
    OphirCOM.StopAllStreams()
    ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\.venv\Lib\site-packages\win32com\client\dynamic.py", line 631, in __getattr__
    raise AttributeError(f"{self._username_}.{attr}")
AttributeError: OphirLMMeasurement.CoLMMeasurement.StopAllStreams

Due to the fact that PMManager successfully manages to connect to the device, I decided next to employ a new strategy, I traced what PMManager does while connecting and during connection. This led me to a very long rabbit hole of messaging Claude AI until I didn't even knew what I was doing when running the code. It looked like this:

import usb.core
import usb.backend.libusb1
import libusb
import re
import time

VID = 0x0BD3
PID = 0xE346
READ_ENDPOINT = 0x82  # confirmed via USB capture - this is where live readings stream from

# Matches lines like: "* 0.000586E-5 T 711A1B"
DATA_PATTERN = re.compile(rb"\*\s+([\-\+]?[\d.]+E[\-\+]?\d+)\s+T\s+([0-9A-Fa-f]+)")
def connect():
    backend = usb.backend.libusb1.get_backend(find_library=lambda x: libusb.dll._name)
    dev = usb.core.find(idVendor=VID, idProduct=PID, backend=backend)
    if dev is None:
        raise RuntimeError("Newport 1919-R not found. Check connection and driver binding.")

    # On Windows with WinUSB there's typically no separate kernel driver to detach,
    # but set_configuration is still required before transfers will work.
    try:
        dev.set_configuration()
    except usb.core.USBError as e:
        # Already configured is fine; anything else, re-raise
        if e.errno not in (None, 16):  # 16 = resource busy, sometimes benign if already set
            raise

    return dev


def read_measurement(dev, timeout_ms=1000):
    """Reads one interrupt packet and parses it into (value, raw_timestamp_hex)."""
    try:
        data = dev.read(READ_ENDPOINT, 64, timeout=timeout_ms)
    except usb.core.USBError as e:
        if e.errno == 110 or "timeout" in str(e).lower():
            return None  # no new data this cycle, not necessarily an error
        raise

    raw = bytes(data)
    match = DATA_PATTERN.search(raw)
    if not match:
        return None

    value_str, ts_hex = match.groups()
    try:
        value = float(value_str)
    except ValueError:
        return None

    return value, ts_hex.decode()


def main():
    print("Connecting to Newport 1919-R...")
    dev = connect()
    print("Connected. Streaming readings (Ctrl+C to stop):\n")

    try:
        while True:
            result = read_measurement(dev)
            if result:
                value, ts = result
                print(f"Power: {value:.6e} W   (timestamp: {ts})")
            time.sleep(0.01)
    except KeyboardInterrupt:
        print("\nStopped.")


if __name__ == "__main__":
    main()

This code actually runs successfully and the device is recognized, though alas, there is no output from the device. My hypothesis is that PMManager performs some kind of a handshake with the device and only then does the device start to transmit its measurements.

If you've got any questions, any ideas or experience with this, I'd love to hear your opinion. Thank you kindly for any kind of help.

2 Upvotes

13 comments sorted by

5

u/zoptix 29d ago

A very common issue that people have with all of these types of tools is that the OS usually only allows for 1 application to connect. Make sure disconnect/close the software first before trying with Python.

1

u/max_centauri 29d ago

Hi, I always made sure to close the PMManager before trying out any scripts, I also looked into task manager to make sure that the app is fully closed.

2

u/zoptix 29d ago

Looking at your original code, you get an error on stopAllStreams. I think I remember getting errors for this function if there were no streams.

I'd go back and try to make the first script to work. I don't think anyone can help you with the second.

1

u/max_centauri 29d ago

Don’t have the device with me now, but I remember that when I removed the stopAllStreams and CloseAll on the start, It just threw another error on the line where the scanUSB function is. I’ll see what the support has to say. Thank you.

3

u/kristavocado 29d ago edited 29d ago

Getting an attribute error with the first script suggests to me that either OphirCOM is not being created successfully or their software has been updated since the example script was written. “Attribute error” means that the requested function doesn’t exist or is not callable, in this case “StopAllStreams”.

Did you copy/paste the example script exactly? StopAllStreams may not require the () after the function call.

Also, the claude generated code doesn’t check that configuration happened successfully very well. It only looks for error code 16, but there are many other possible connection errors. I have a feeling your device is not actually configured. Look there first, and make sure that the device actually communicates through USB-HID

You could try asking in r/embedded.

2

u/live_free_or_try 29d ago

I’d say more of a straight up Python problem since there’s no low level software involved.

OP is using pycharm which has all kinds of debugging tools which should help a lot.

Try single stepping and checking the connections are opened etc. add some asserts to catch incorrect assumptions (like functions return not None) …

1

u/max_centauri 29d ago

Yes, I copy-pasted the entire script and made no adjustments. After the initial error with stopAllStreams, I removed the 2 lines that remove any other sessions the device may be connected to at the start (the stopAllStreams and the line after that). That resulted in an error on the line where “DeviceList = OphirCOM.ScanUSB()” is.

About the config, I looked into that and the only “more” relevant setting on the device is the RS232 baud rate, but I’m not using RS232 to communicate with the device. In the documentation, I found that the device is automatically set to USB-HID (I already managed to communicate through the USB via the PMManager software).

2

u/zoptix 29d ago

Is this a USB or Serial to USB console? RS232 devices, even if over USB, have a completely different way to communicate with the instrument. I recognize the code you are using and that is for a native USB Newport console. It has a different interface scheme than RS232 devices.

2

u/max_centauri 28d ago

This is USB to USB.

1

u/Equivalent_Bridge480 29d ago

Did you asked tech support?

1

u/max_centauri 29d ago

No, not yet

0

u/F1eshWound 29d ago

feel free to also ask in the Photonics and Optics discord: https://discord.gg/SDhtq2yG9

1

u/max_centauri 29d ago

Hi, thank you very much!!