From 31f1abc22c8f7d03a34ecee7ca20943486e75832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Fri, 10 Jul 2026 01:15:50 -0400 Subject: [PATCH 1/4] Add Coherent Verdi-G (HOPS-supply) laser driver VerdiGDevice is a LaserSourceDevice (OnOff / Shutter / Power / Interlock) for a Coherent laser on a HOPS ("High Output Power Supply") supply; the lab head reports as a Genesis CX-Vis (G532). The supply is not serial: its FTDI FT2232 (0x0403:0x6010) is driven as bit-banged I2C, with the power DAC, ADC, shutter/enable GPIO, and the head identity/calibration EEPROM all on one I2C bus. VerdiGDevice drives that bus through an interchangeable HOPSInterface, selected with interface="auto" (native first, then the DLL; or "native"/"dll"/an instance): - HOPSNativeInterface (hopsnative.py): pure-Python pyftdi I2C, no DLL (macOS/Linux). Hardware-confirmed end to end on the lab unit (identity, on/off, shutter, remote, power setpoint, temperature). interlock()/faults() raise HOPSInterface.NotSupported until the ?FF decode is reverse-engineered. - HOPSDLLInterface (hopsdll.py): Coherent's CohrHOPS.dll (ASCII command set; Windows/Linux), with the CohrHOPS ctypes binding. Tests in testVerdiG.py: debug, native-on-mock, and read-modify-write always run; the hardware class skips when no laser is reachable. scratch-hops/ (vendor DLLs and reverse-engineering prototypes) is gitignored. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01841beMNdCw3zYf6fdBUoy7 --- .gitignore | 3 + CHANGELOG.md | 22 ++ hardwarelibrary/sources/__init__.py | 5 + hardwarelibrary/sources/hopsdll.py | 244 ++++++++++++++++++++ hardwarelibrary/sources/hopsnative.py | 250 +++++++++++++++++++++ hardwarelibrary/sources/verdig.py | 311 ++++++++++++++++++++++++++ hardwarelibrary/tests/testVerdiG.py | 179 +++++++++++++++ 7 files changed, 1014 insertions(+) create mode 100644 hardwarelibrary/sources/hopsdll.py create mode 100644 hardwarelibrary/sources/hopsnative.py create mode 100644 hardwarelibrary/sources/verdig.py create mode 100644 hardwarelibrary/tests/testVerdiG.py diff --git a/.gitignore b/.gitignore index 64b23ca..e6f93af 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,6 @@ cython_debug/ .DS_Store stellarnet.py stellarnet.hex + +# Coherent HOPS vendor DLLs, capture logs, and local reversing prototypes (not for VCS) +scratch-hops/ diff --git a/CHANGELOG.md b/CHANGELOG.md index c1c6353..af8892e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,28 @@ API changes can land even when the minor version is unchanged. ## [Unreleased] +### Added +- `VerdiGDevice` (and `DebugVerdiGDevice`): a laser-source driver for the Coherent + "HOPS" (High Output Power Supply) laser -- Genesis heads / Verdi G-C, e.g. the + lab Genesis CX-Vis (head `G532`). A HOPS supply is not a serial device: its + FTDI FT2232 (`0x0403:0x6010`) is driven as bit-banged I2C, with power DAC, ADC, + shutter/enable GPIO, and the head identity/calibration EEPROM all on one I2C + bus (see `manuals/Coherent-HOPS-*`). `VerdiGDevice` combines `OnOffControl`, + `ShutterControl`, `PowerControl`, and `InterlockControl`, and drives the bus + through an interchangeable `HOPSInterface`: + - `HOPSNativeInterface` (`sources/hopsnative.py`): **pure-Python** pyftdi I2C, + no DLL (macOS/Linux). Hardware-confirmed end to end on the lab unit + (identity, on/off, shutter, remote, power setpoint, temperature). Its + `interlock()`/`faults()` raise `HOPSInterface.NotSupported` until the `?FF` + decode is reverse-engineered. + - `HOPSDLLInterface` (`sources/hopsdll.py`): Coherent's `CohrHOPS.dll` (ASCII + command set; Windows/Linux). Read + `REM`/`PCMD` write paths hardware- + confirmed; `KSWCMD`/`SHCMD` per the DLL spec, not yet exercised. + Selection: `VerdiGDevice(interface="auto")` tries native first, then the DLL; + pass `"native"`/`"dll"`/an interface instance to force one. Protocol and I2C + decode in `manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md` and + `manuals/Coherent-HOPS-3-I2C-Wire-Protocol.md`. + ## [1.4.0] - 2026-07-08 ### Added diff --git a/hardwarelibrary/sources/__init__.py b/hardwarelibrary/sources/__init__.py index d6a10c9..e0adb0c 100644 --- a/hardwarelibrary/sources/__init__.py +++ b/hardwarelibrary/sources/__init__.py @@ -10,3 +10,8 @@ MillenniaEv25Device, DebugMillenniaEv25Device, MillenniaDevice, DebugMillenniaDevice, ) +from .verdig import ( + VerdiGDevice, DebugVerdiGDevice, HOPSInterface, DebugHOPSInterface, +) +from .hopsnative import HOPSNativeInterface, HOPSNativeI2C, MockHOPSBus +from .hopsdll import HOPSDLLInterface diff --git a/hardwarelibrary/sources/hopsdll.py b/hardwarelibrary/sources/hopsdll.py new file mode 100644 index 0000000..9655527 --- /dev/null +++ b/hardwarelibrary/sources/hopsdll.py @@ -0,0 +1,244 @@ +"""HOPSDLLInterface: the HOPS transport backed by Coherent's CohrHOPS.dll. + +Implements the transport-agnostic HOPSInterface (see verdig.py) by sending the +DLL's ASCII command set over its binary/I2C transport. Windows/Linux only; +raises HOPSInterface.Unavailable where the DLL cannot load (e.g. macOS). +""" + +import ctypes +import os +import sys +from enum import IntEnum + +from .verdig import HOPSInterface + +MAX_DEVICES = 20 +MAX_STRLEN = 100 +_DLL_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) + + +class HOPSStatus(IntEnum): + OK = 0 + INVALID_HANDLE = -1 + INVALID_HEAD = -2 + INVALID_COMMAND = -3 + INVALID_DATA = -4 + I2C_ERROR = -5 + USB_ERROR = -6 + FTCI2C_DLL_FILE_NOT_FOUND = -100 + FTCI2C_DLL_FUNCTION_NOT_FOUND = -101 + FTCI2C_DLL_EXCEPTION = -102 + NXP_ERROR = -200 + RS232_ERROR = -300 + THREAD_ERROR = -400 + OTHER_ERROR = -999 + + +class HOPSError(Exception): + def __init__(self, function, status): + self.function = function + self.status = status + try: + name = HOPSStatus(status).name + except ValueError: + name = "UNKNOWN" + super().__init__("{0} failed: {1} ({2})".format(function, status, name)) + + +class CohrHOPS: + """Thin ctypes binding to CohrHOPS.dll (the 6 exported functions).""" + + def __init__(self, dllDirectory=_DLL_DIRECTORY): + if not sys.platform.startswith(("win", "linux")): + raise HOPSInterface.Unavailable( + "CohrHOPS.dll is Windows/Linux only; this is {0}.".format(sys.platform)) + self.dll = self._loadLibrary(dllDirectory) + self._declareSignatures() + + @staticmethod + def _loadLibrary(dllDirectory): + if sys.platform.startswith("win"): + if hasattr(os, "add_dll_directory") and os.path.isdir(dllDirectory): + os.add_dll_directory(dllDirectory) + os.environ["PATH"] = dllDirectory + os.pathsep + os.environ.get("PATH", "") + name = "CohrHOPS.dll" + else: + name = "libcohrhops.so" + path = os.path.join(dllDirectory, name) + if not os.path.exists(path): + raise HOPSInterface.Unavailable( + "{0} not found in {1}. Provide CohrHOPS.dll and CohrFTCI2C.dll " + "(matching your Python's bitness).".format(name, dllDirectory)) + try: + return ctypes.CDLL(path) + except OSError as error: + raise HOPSInterface.Unavailable( + "Could not load {0}: {1} (often a 32/64-bit mismatch or a missing " + "CohrFTCI2C.dll).".format(path, error)) + + def _declareSignatures(self): + handleArray = ctypes.c_uint64 * MAX_DEVICES + self._handleArrayType = handleArray + self.dll.CohrHOPS_GetDLLVersion.argtypes = [ctypes.c_char_p] + self.dll.CohrHOPS_CheckForDevices.argtypes = [ + handleArray, ctypes.POINTER(ctypes.c_uint32), + handleArray, ctypes.POINTER(ctypes.c_uint32), + handleArray, ctypes.POINTER(ctypes.c_uint32)] + self.dll.CohrHOPS_InitializeHandle.argtypes = [ctypes.c_uint64, ctypes.c_char_p] + self.dll.CohrHOPS_SendCommand.argtypes = [ + ctypes.c_uint64, ctypes.c_char_p, ctypes.c_char_p] + self.dll.CohrHOPS_Close.argtypes = [ctypes.c_uint64] + for fn in ("GetDLLVersion", "CheckForDevices", "InitializeHandle", "SendCommand", "Close"): + getattr(self.dll, "CohrHOPS_" + fn).restype = ctypes.c_int32 + + @staticmethod + def _check(function, status): + if status != HOPSStatus.OK: + raise HOPSError(function, status) + + def version(self) -> str: + buffer = ctypes.create_string_buffer(MAX_STRLEN) + self._check("CohrHOPS_GetDLLVersion", self.dll.CohrHOPS_GetDLLVersion(buffer)) + return buffer.value.decode(errors="replace") + + def checkForDevices(self) -> list: + connected = self._handleArrayType() + added = self._handleArrayType() + removed = self._handleArrayType() + nConnected = ctypes.c_uint32(0) + nAdded = ctypes.c_uint32(0) + nRemoved = ctypes.c_uint32(0) + self._check("CohrHOPS_CheckForDevices", self.dll.CohrHOPS_CheckForDevices( + connected, ctypes.byref(nConnected), added, ctypes.byref(nAdded), + removed, ctypes.byref(nRemoved))) + handles = [connected[i] for i in range(nConnected.value)] + handles += [added[i] for i in range(nAdded.value)] + return sorted(set(h for h in handles if h)) + + def initializeHandle(self, handle: int) -> str: + headType = ctypes.create_string_buffer(MAX_STRLEN) + self._check("CohrHOPS_InitializeHandle", + self.dll.CohrHOPS_InitializeHandle(ctypes.c_uint64(handle), headType)) + return headType.value.decode(errors="replace") + + def sendCommand(self, handle: int, command: str) -> str: + response = ctypes.create_string_buffer(MAX_STRLEN) + self._check("CohrHOPS_SendCommand", self.dll.CohrHOPS_SendCommand( + ctypes.c_uint64(handle), command.encode(), response)) + return response.value.decode(errors="replace").strip() + + def close(self, handle: int): + self._check("CohrHOPS_Close", self.dll.CohrHOPS_Close(ctypes.c_uint64(handle))) + + +class HOPSDLLInterface(HOPSInterface): + """HOPS transport over CohrHOPS.dll (ASCII command set).""" + + name = "dll" + powerSetpointTolerance = 0.02 + + # ?FF fault bitmask; bits 0x0020/0x0100/0x0300 relate to interlocks. + faultBits = { + 0x0008: "Main TEC error", + 0x0010: "LBO/BRF temperature not OK", + 0x0020: "Interlock fault", + 0x0100: "Shutter error", + 0x0200: "Glue board error", + 0x0800: "LDD at current limit", + } + interlockFaultBit = 0x0020 + + diagnosticReads = { + "headHours": ("?HH", float), "current": ("?C", float), + "currentLimit": ("?CLIM", float), "powerLimit": ("?PLIM", float), + "mainTemperature": ("?TMAIN", float), "shgTemperature": ("?TSHG", float), + "brfTemperature": ("?TBRF", float), "etalonTemperature": ("?TETA", float), + "fanSpeed": ("?FAN", int), "mode": ("?CMODE", int), + } + + def __init__(self, serialNumber=None, dllDirectory=_DLL_DIRECTORY): + self.serialNumber = serialNumber + self.dllDirectory = dllDirectory + self.lib = None + self.handle = None + + def open(self): + self.lib = CohrHOPS(self.dllDirectory) # raises HOPSInterface.Unavailable if no DLL + handles = self.lib.checkForDevices() + if not handles: + raise HOPSInterface.Unavailable("CohrHOPS found no HOPS device on USB.") + self.handle = self._selectHandle(handles) + self.lib.initializeHandle(self.handle) + + def _selectHandle(self, handles): + if self.serialNumber is None: + return handles[0] + for handle in handles: + self.lib.initializeHandle(handle) + if self.lib.sendCommand(handle, "?HID") == self.serialNumber: + return handle + raise HOPSInterface.Unavailable("No HOPS head with serial {0}".format(self.serialNumber)) + + def close(self): + if self.lib is not None and self.handle is not None: + self.lib.close(self.handle) + self.lib = None + self.handle = None + + def _query(self, command) -> str: + return self.lib.sendCommand(self.handle, command) + + def identity(self) -> dict: + return { + "model": self._query("?LASERMODEL"), + "headType": self._query("?HTYPE"), + "serialNumber": self._query("?HID"), + "maxPower": float(self._query("?PLIM")), + } + + def getPower(self) -> float: + return float(self._query("?P")) + + def powerSetpoint(self) -> float: + return float(self._query("?PCMD")) + + def setPower(self, power: float): + self.lib.sendCommand(self.handle, "PCMD={0:.4f}".format(power)) + echoed = self.powerSetpoint() + if abs(echoed - power) > self.powerSetpointTolerance: + raise HOPSError("PCMD setpoint confirm", HOPSStatus.INVALID_DATA) + + def emissionOn(self) -> bool: + return self._query("?KSWCMD") == "1" + + def setEmission(self, on: bool): + self.lib.sendCommand(self.handle, "KSWCMD={0}".format(1 if on else 0)) + + def shutterOpen(self) -> bool: + return self._query("?SH") == "1" + + def setShutter(self, isOpen: bool): + self.lib.sendCommand(self.handle, "SHCMD={0}".format(1 if isOpen else 0)) + + def remoteOn(self) -> bool: + return self._query("?REM") == "1" + + def setRemote(self, on: bool): + self.lib.sendCommand(self.handle, "REM={0}".format(1 if on else 0)) + + def mainTemperature(self) -> float: + return float(self._query("?TMAIN")) + + def faults(self) -> list: + reply = self._query("?FF") + code = int(reply, 16) if reply else 0 + return [text for bit, text in self.faultBits.items() if code & bit] + + def interlockOk(self) -> bool: + reply = self._query("?FF") + code = int(reply, 16) if reply else 0 + return (code & self.interlockFaultBit) == 0 + + def diagnostics(self) -> dict: + return {name: cast(self._query(command)) + for name, (command, cast) in self.diagnosticReads.items()} diff --git a/hardwarelibrary/sources/hopsnative.py b/hardwarelibrary/sources/hopsnative.py new file mode 100644 index 0000000..cfa94e5 --- /dev/null +++ b/hardwarelibrary/sources/hopsnative.py @@ -0,0 +1,250 @@ +"""HOPSNativeInterface: the HOPS transport in pure Python over pyftdi I2C. + +Implements the transport-agnostic HOPSInterface (see verdig.py) by driving the +HOPS supply's I2C bus directly through the FT2232 with pyftdi -- no CohrHOPS.dll, +no Windows. Reverse-engineered from sniffed I2C traffic (see +manuals/Coherent-HOPS-3-I2C-Wire-Protocol.md and Coherent-HOPS-3-I2C-Circuit.svg): + + - EEPROM 0x52 (2-byte pointer 0x01XX): identity/calibration (head type 0x00, + head ID 0x10, board rev 0x60). + - ADC 0x48: power at reg 0xE4, main temperature at reg 0x94 (2 bytes, BE). + - I/O expander 0x25 (PCA9555-style): input port 0x00, output 0x02, config 0x06; + bit0 shutter (1=open), bit3 remote (active-low, 0=on), bit5 emission enable. + - DAC 0x29 reg 0xA0: 16-bit power setpoint. + +Gaps (not yet reverse-engineered), so these raise HOPSInterface.NotSupported / +NotCalibrated: the full ?FF fault/interlock decode, and the ADC power-read scale +(only 0 counts = 0 W is known). Main-temperature uses a two-point cal valid +~32-40 C. On Ventura pyftdi claims the FT2232 without unloading Apple's VCP +driver; run with libusb reachable. +""" + +import struct + +from .verdig import HOPSInterface + + +class HOPSNativeI2C: + """Register-level access to the HOPS I2C devices over a pyftdi-style bus. + + The bus exposes get_port(address) with .exchange(out, readlen) (read: write + pointer then read) and .write(out) (write only). HOPSNativeI2C.open() builds + the real pyftdi bus; tests inject a mock with the same interface. + """ + + EEPROM = 0x52 + EEPROM_PAGE = 0x01 + ADC = 0x48 + GPIO = 0x25 + DAC = 0x29 + GPIO_IN = 0x00 + GPIO_OUT = 0x02 + GPIO_CFG = 0x06 + + def __init__(self, bus): + self.bus = bus + + @classmethod + def open(cls, url, frequency=10000): + from pyftdi.ftdi import Ftdi + from pyftdi.i2c import I2cController + try: + Ftdi.add_custom_product(0x0403, 0x6010, "HOPS") + except Exception: + pass + controller = I2cController() + controller.force_clock_mode(True) # FT2232C/D has no 3-phase clock + controller.configure(url, frequency=frequency) + return cls(controller) + + def close(self): + closer = getattr(self.bus, "close", None) + if closer is not None: + closer() + + def readEepromByte(self, register) -> int: + return self.bus.get_port(self.EEPROM).exchange( + [self.EEPROM_PAGE, register & 0xFF], 1)[0] + + def readEepromString(self, base, maxLength=32) -> str: + out = bytearray() + for offset in range(maxLength): + byte = self.readEepromByte(base + offset) + if byte == 0: + break + out.append(byte) + return out.decode("ascii", "replace") + + def readEepromFloat(self, base) -> float: + return struct.unpack(">f", bytes(self.readEepromByte(base + i) for i in range(4)))[0] + + def readAdc(self, register) -> int: + data = self.bus.get_port(self.ADC).exchange([register], 2) + return (data[0] << 8) | data[1] + + def readGpioBit(self, bit) -> int: + port = self.bus.get_port(self.GPIO).exchange([self.GPIO_IN], 1)[0] + return (port >> bit) & 0x01 + + def writeGpioBit(self, bit, value): + # Read-modify-write: make the pin an output (0 in config) and drive it, + # leaving other bits untouched. Writes use write() -- pyftdi rejects + # exchange(out, readlen=0) with "Nothing to read". + gpio = self.bus.get_port(self.GPIO) + config = gpio.exchange([self.GPIO_CFG], 1)[0] & ~(1 << bit) + gpio.write([self.GPIO_CFG, config]) + output = gpio.exchange([self.GPIO_OUT], 1)[0] + output = (output | (1 << bit)) if value else (output & ~(1 << bit)) + gpio.write([self.GPIO_OUT, output]) + + def writeDac(self, register, code): + code &= 0xFFFF + self.bus.get_port(self.DAC).write([register, (code >> 8) & 0xFF, code & 0xFF]) + + +class HOPSNativeInterface(HOPSInterface): + """HOPS transport over native pyftdi I2C (no DLL).""" + + name = "native" + defaultUrl = "ftdi://ftdi:0x6010:FTV5L9CA/1" + + HEAD_TYPE_REG = 0x00 + HEAD_ID_REG = 0x10 + POWER_REG = 0xE4 + TMAIN_REG = 0x94 + SHUTTER_BIT = 0 + REMOTE_BIT = 3 # active-low + ENABLE_BIT = 5 + DAC_POWER_REG = 0xA0 + dacCountsPerWatt = 102.0 # provisional (PCMD=0.5 W -> 0x0033), refine later + + # Main-temperature two-point cal (NTC-like): raw 1696<->32.222, 1432<->39.773. + _a, _b = (1696, 32.222), (1432, 39.773) + tmainSlope = (_b[1] - _a[1]) / (_b[0] - _a[0]) + tmainOffset = _a[1] - tmainSlope * _a[0] + + headCatalog = {"G532": ("Genesis CX-Vis", 7.344)} + + class NotCalibrated(HOPSInterface.NotSupported): + pass + + def __init__(self, url=None, serialNumber=None, bus=None): + self.url = url or self.defaultUrl + self.serialNumber = serialNumber + self._bus = bus # injectable for tests (a MockHOPSBus) + self.i2c = None + self._setpointWatts = 0.0 + self.powerReadCountsPerWatt = None # set after an emission calibration + + def open(self): + try: + self.i2c = HOPSNativeI2C(self._bus) if self._bus is not None else HOPSNativeI2C.open(self.url) + except HOPSInterface.Unavailable: + raise + except Exception as error: + raise HOPSInterface.Unavailable( + "Could not open the HOPS FT2232 over pyftdi/libusb: {0}".format(error)) from error + + def close(self): + if self.i2c is not None: + self.i2c.close() + self.i2c = None + + def identity(self) -> dict: + headType = self.i2c.readEepromString(self.HEAD_TYPE_REG) + model, maxPower = self.headCatalog.get(headType, (headType, None)) + return { + "model": model, + "headType": headType, + "serialNumber": self.i2c.readEepromString(self.HEAD_ID_REG), + "maxPower": maxPower, + } + + def getPower(self) -> float: + raw = self.i2c.readAdc(self.POWER_REG) + if raw == 0: + return 0.0 + if self.powerReadCountsPerWatt is None: + raise HOPSNativeInterface.NotCalibrated( + "power ADC read is uncalibrated (raw={0}); set powerReadCountsPerWatt " + "after calibrating against a meter during emission.".format(raw)) + return raw / self.powerReadCountsPerWatt + + def powerSetpoint(self) -> float: + return self._setpointWatts # DAC is write-only; report last commanded + + def setPower(self, power: float): + self.i2c.writeDac(self.DAC_POWER_REG, int(round(power * self.dacCountsPerWatt))) + self._setpointWatts = power + + def emissionOn(self) -> bool: + return self.i2c.readGpioBit(self.ENABLE_BIT) == 1 + + def setEmission(self, on: bool): + self.i2c.writeGpioBit(self.ENABLE_BIT, 1 if on else 0) + + def shutterOpen(self) -> bool: + return self.i2c.readGpioBit(self.SHUTTER_BIT) == 1 + + def setShutter(self, isOpen: bool): + self.i2c.writeGpioBit(self.SHUTTER_BIT, 1 if isOpen else 0) + + def remoteOn(self) -> bool: + return self.i2c.readGpioBit(self.REMOTE_BIT) == 0 # active-low + + def setRemote(self, on: bool): + self.i2c.writeGpioBit(self.REMOTE_BIT, 0 if on else 1) + + def mainTemperature(self) -> float: + return self.tmainSlope * self.i2c.readAdc(self.TMAIN_REG) + self.tmainOffset + + # interlockOk() and faults() inherit HOPSInterface.NotSupported (the ?FF + # decode was not reverse-engineered natively; to be fixed later). + + def diagnostics(self) -> dict: + return {"mainTemperature": self.mainTemperature()} + + +class MockHOPSBus: + """In-memory pyftdi-style I2C bus for tests. Seeded to the lab G532 snapshot.""" + + def __init__(self): + self.eeprom = {} + self._seed(0x00, "G532") + self._seed(0x10, "VH5359") + self._seed(0x60, "DE") + self.adc = {0xE4: 0x0000, 0x94: 1556} + self.gpio = {0x00: 0x00, 0x02: 0x00, 0x06: 0xFF} + self.dacCode = None + + def _seed(self, base, text): + for offset, char in enumerate(text.encode("ascii")): + self.eeprom[base + offset] = char + self.eeprom[base + len(text)] = 0x00 + + def get_port(self, address): + return MockHOPSBus._Port(self, address) + + class _Port: + def __init__(self, bus, address): + self.bus = bus + self.address = address + + def exchange(self, out, readlen=0): + out = list(out) + if self.address == HOPSNativeI2C.EEPROM: + return bytes([self.bus.eeprom.get(out[1], 0x00)]) + if self.address == HOPSNativeI2C.ADC: + value = self.bus.adc.get(out[0], 0) + return bytes([(value >> 8) & 0xFF, value & 0xFF]) + if self.address == HOPSNativeI2C.GPIO: + source = 0x02 if out[0] == 0x00 else out[0] # input mirrors output + return bytes([self.bus.gpio.get(source, 0x00)]) + return b"" + + def write(self, out): + out = list(out) + if self.address == HOPSNativeI2C.GPIO: + self.bus.gpio[out[0]] = out[1] + elif self.address == HOPSNativeI2C.DAC: + self.bus.dacCode = (out[1] << 8) | out[2] diff --git a/hardwarelibrary/sources/verdig.py b/hardwarelibrary/sources/verdig.py new file mode 100644 index 0000000..d17807e --- /dev/null +++ b/hardwarelibrary/sources/verdig.py @@ -0,0 +1,311 @@ +"""VerdiGDevice: one laser-source driver for a Coherent HOPS supply, over an +interchangeable transport. + +A HOPS ("High Output Power Supply") laser (Genesis heads, Verdi G/C) is not a +serial device: its FT2232 is driven as bit-banged I2C, and all control -- power +DAC, ADC, shutter/enable GPIO, and the head's identity/calibration EEPROM across +the umbilical -- hangs off that one I2C bus (see manuals/Coherent-HOPS-*). So a +driver must speak that bus, either through Coherent's CohrHOPS.dll or natively. + +VerdiGDevice holds one HOPSInterface and delegates every capability hook to it: + + - HOPSNativeInterface (hopsnative.py): pure-Python pyftdi I2C, no DLL (macOS/Linux) + - HOPSDLLInterface (hopsdll.py): Coherent's CohrHOPS.dll (Windows/Linux) + +Selection: VerdiGDevice(interface="auto") tries native first, then the DLL; pass +"native"/"dll" to force one, or an interface instance directly. The debug device +uses an in-memory DebugHOPSInterface and needs neither hardware nor pyftdi/DLL. + +The lab unit reports as a Genesis CX-Vis (head G532). InterlockControl is part of +the contract, but the native interface cannot decode faults yet, so on the native +transport interlock()/faults() raise HOPSInterface.NotSupported (to be fixed once +the ?FF decode is reverse-engineered). +""" + +from abc import ABC, abstractmethod + +from ..physicaldevice import PhysicalDevice +from .lasersourcedevice import LaserSourceDevice +from .capabilities import OnOffControl, ShutterControl, PowerControl, InterlockControl + + +class HOPSInterface(ABC): + """Transport-agnostic contract VerdiGDevice drives. Implemented by + HOPSDLLInterface and HOPSNativeInterface.""" + + name = "interface" + + class Unavailable(Exception): + """This transport cannot be opened on this host (no DLL, no pyftdi, no + device). Used by VerdiGDevice's auto-selection to fall through.""" + + class NotSupported(Exception): + """This transport cannot perform the requested operation (e.g. native + interlock/fault decode).""" + + @abstractmethod + def open(self): ... + + @abstractmethod + def close(self): ... + + @abstractmethod + def identity(self) -> dict: + """{model, headType, serialNumber, maxPower (or None)}.""" + + @abstractmethod + def getPower(self) -> float: ... + + @abstractmethod + def powerSetpoint(self) -> float: ... + + @abstractmethod + def setPower(self, power: float): ... + + @abstractmethod + def emissionOn(self) -> bool: ... + + @abstractmethod + def setEmission(self, on: bool): ... + + @abstractmethod + def shutterOpen(self) -> bool: ... + + @abstractmethod + def setShutter(self, isOpen: bool): ... + + @abstractmethod + def remoteOn(self) -> bool: ... + + @abstractmethod + def setRemote(self, on: bool): ... + + @abstractmethod + def mainTemperature(self) -> float: ... + + def interlockOk(self) -> bool: + raise HOPSInterface.NotSupported( + "interlock state is not available on the {0} interface".format(self.name)) + + def faults(self) -> list: + raise HOPSInterface.NotSupported( + "fault decode is not available on the {0} interface".format(self.name)) + + def diagnostics(self) -> dict: + return {} + + +class VerdiGDevice(LaserSourceDevice, OnOffControl, ShutterControl, PowerControl, + InterlockControl): + """Coherent Verdi-G laser on a HOPS supply (the lab head reports as a Genesis + CX-Vis, G532), driven through an interchangeable HOPSInterface -- native + pyftdi I2C or CohrHOPS.dll. See the module docstring.""" + + classIdVendor = 0x0403 # FTDI FT2232 the HOPS supply enumerates as + classIdProduct = 0x6010 + + def __init__(self, interface="auto", url=None, serialNumber=None, + idProduct=None, idVendor=None): + super().__init__(serialNumber=serialNumber, idProduct=idProduct, idVendor=idVendor) + self._interfaceChoice = interface + self.url = url + self.interface = None + self.laserModel = None + self.headType = None + self.headSerialNumber = None + self.maxPower = None + + def _interfaceCandidates(self): + choice = self._interfaceChoice + if isinstance(choice, HOPSInterface): + return [lambda: choice] + serial = None if self.serialNumber in (None, ".*") else self.serialNumber + + def native(): + from .hopsnative import HOPSNativeInterface + return HOPSNativeInterface(url=self.url, serialNumber=serial) + + def dll(): + from .hopsdll import HOPSDLLInterface + return HOPSDLLInterface(serialNumber=serial) + + table = {"auto": [native, dll], "native": [native], "dll": [dll]} + if choice not in table: + raise PhysicalDevice.UnableToInitialize( + "interface must be 'auto', 'native', 'dll', or a HOPSInterface") + return table[choice] + + def doInitializeDevice(self): + errors = [] + for makeInterface in self._interfaceCandidates(): + interface = makeInterface() + try: + interface.open() + except HOPSInterface.Unavailable as error: + errors.append("{0}: {1}".format(interface.name, error)) + continue + self.interface = interface + break + if self.interface is None: + raise PhysicalDevice.UnableToInitialize( + "No HOPS interface could be opened. " + " | ".join(errors)) + try: + identity = self.interface.identity() + self.laserModel = identity.get("model") + self.headType = identity.get("headType") + self.headSerialNumber = identity.get("serialNumber") + self.maxPower = identity.get("maxPower") + self.interface.setRemote(True) + except Exception as error: + self.interface.close() + self.interface = None + raise PhysicalDevice.UnableToInitialize(error) from error + + def doShutdownDevice(self): + if self.interface is not None: + try: + self.interface.setRemote(False) + finally: + self.interface.close() + self.interface = None + + # OnOffControl (emission enable) + def doTurnOn(self): + self.interface.setEmission(True) + + def doTurnOff(self): + self.interface.setEmission(False) + + def doGetOnOffState(self) -> bool: + return self.interface.emissionOn() + + # ShutterControl + def doOpenShutter(self): + self.interface.setShutter(True) + + def doCloseShutter(self): + self.interface.setShutter(False) + + def doGetShutterState(self) -> bool: + return self.interface.shutterOpen() + + # PowerControl + def doSetPower(self, power: float): + upper = self.maxPower if self.maxPower is not None else float("inf") + if not (0.0 <= power <= upper): + raise ValueError("power {0} W outside [0, {1}] W".format(power, self.maxPower)) + self.interface.setPower(power) + + def doGetPower(self) -> float: + return self.interface.getPower() + + # InterlockControl (native raises HOPSInterface.NotSupported, by design) + def doGetInterlockState(self) -> bool: + return self.interface.interlockOk() + + # Extra, non-capability helpers delegated to the interface + def powerSetpoint(self) -> float: + return self.interface.powerSetpoint() + + def mainTemperature(self) -> float: + return self.interface.mainTemperature() + + def faults(self) -> list: + return self.interface.faults() + + def diagnostics(self) -> dict: + return self.interface.diagnostics() + + def remoteControlIsOn(self) -> bool: + return self.interface.remoteOn() + + def doGetStatusUserInfo(self) -> dict: + info = { + "power": self.doGetPower(), + "setpoint": self.powerSetpoint(), + "isLaserOn": self.doGetOnOffState(), + "isShutterOpen": self.doGetShutterState(), + "remoteControl": self.remoteControlIsOn(), + "mainTemperature": self.mainTemperature(), + } + try: + info["interlockOk"] = self.doGetInterlockState() + info["faults"] = self.faults() + except HOPSInterface.NotSupported: + info["interlockOk"] = None + info["faults"] = None + return info + + +class DebugHOPSInterface(HOPSInterface): + """In-memory HOPS transport for tests -- needs neither pyftdi nor the DLL. + Fully capable (implements interlock/faults) so VerdiGDevice's whole contract can + be exercised. Seeded to the lab Genesis CX-Vis (G532).""" + + name = "debug" + + def __init__(self): + self.emission = False + self.shutter = False + self.remote = False + self.setpoint = 0.0 + self.tmain = 36.0 + self.activeFaults = [] + self.opened = False + + def open(self): + self.opened = True + + def close(self): + self.opened = False + + def identity(self) -> dict: + return {"model": "Genesis CX-Vis", "headType": "G532", + "serialNumber": "VH5359", "maxPower": 7.344} + + def getPower(self) -> float: + return self.setpoint if (self.emission and self.shutter) else 0.0 + + def powerSetpoint(self) -> float: + return self.setpoint + + def setPower(self, power: float): + self.setpoint = power + + def emissionOn(self) -> bool: + return self.emission + + def setEmission(self, on: bool): + self.emission = bool(on) + + def shutterOpen(self) -> bool: + return self.shutter + + def setShutter(self, isOpen: bool): + self.shutter = bool(isOpen) + + def remoteOn(self) -> bool: + return self.remote + + def setRemote(self, on: bool): + self.remote = bool(on) + + def mainTemperature(self) -> float: + return self.tmain + + def interlockOk(self) -> bool: + return not any(f in self.activeFaults for f in ("Interlock fault",)) + + def faults(self) -> list: + return list(self.activeFaults) + + def diagnostics(self) -> dict: + return {"mainTemperature": self.tmain} + + +class DebugVerdiGDevice(VerdiGDevice): + classIdVendor = 0xFFFF + classIdProduct = 0xFFF4 + + def __init__(self, serialNumber="debug"): + super().__init__(interface=DebugHOPSInterface(), serialNumber=serialNumber) diff --git a/hardwarelibrary/tests/testVerdiG.py b/hardwarelibrary/tests/testVerdiG.py new file mode 100644 index 0000000..c570b87 --- /dev/null +++ b/hardwarelibrary/tests/testVerdiG.py @@ -0,0 +1,179 @@ +import env +import unittest + +from hardwarelibrary.physicaldevice import PhysicalDevice, DeviceState +from hardwarelibrary.sources.verdig import ( + VerdiGDevice, DebugVerdiGDevice, HOPSInterface, DebugHOPSInterface) +from hardwarelibrary.sources.hopsnative import ( + HOPSNativeInterface, HOPSNativeI2C, MockHOPSBus) +from hardwarelibrary.sources.capabilities import ( + OnOffControl, ShutterControl, PowerControl, InterlockControl, WavelengthControl) +from hardwarelibrary.sources.lasersourcedevice import LaserSourceDevice + +# Point at a real Verdi-G / HOPS laser on this host to exercise the hardware class. +NATIVE_URL = "ftdi://ftdi:0x6010:FTV5L9CA/1" + + +class TestVerdiGWithDebugInterface(unittest.TestCase): + def setUp(self): + self.laser = DebugVerdiGDevice() + self.laser.initializeDevice() + + def tearDown(self): + if self.laser.state == DeviceState.Ready: + self.laser.shutdownDevice() + + def testAdvertisesFullCapabilitySet(self): + self.assertIsInstance(self.laser, LaserSourceDevice) + self.assertEqual(set(self.laser.capabilities()), + {OnOffControl, ShutterControl, PowerControl, InterlockControl}) + self.assertFalse(self.laser.hasCapability(WavelengthControl)) + + def testReadsIdentity(self): + self.assertEqual(self.laser.laserModel, "Genesis CX-Vis") + self.assertEqual(self.laser.headType, "G532") + self.assertEqual(self.laser.headSerialNumber, "VH5359") + self.assertAlmostEqual(self.laser.maxPower, 7.344, places=3) + + def testInitEnablesRemoteShutdownDisablesIt(self): + self.assertTrue(self.laser.remoteControlIsOn()) + interface = self.laser.interface + self.laser.shutdownDevice() + self.assertFalse(interface.remoteOn()) + + def testTurnOnOff(self): + self.laser.turnOn() + self.assertTrue(self.laser.isLaserOn()) + self.laser.turnOff() + self.assertFalse(self.laser.isLaserOn()) + + def testShutterRoundTrip(self): + self.laser.openShutter() + self.assertTrue(self.laser.isShutterOpen()) + self.laser.closeShutter() + self.assertFalse(self.laser.isShutterOpen()) + + def testSetPowerAndSetpoint(self): + self.laser.setPower(3.0) + self.assertAlmostEqual(self.laser.powerSetpoint(), 3.0, places=3) + + def testActualPowerNeedsEnableAndShutter(self): + self.laser.setPower(2.0) + self.assertEqual(self.laser.power(), 0.0) + self.laser.turnOn() + self.laser.openShutter() + self.assertAlmostEqual(self.laser.power(), 2.0, places=3) + + def testRejectsOutOfRangePower(self): + with self.assertRaises(ValueError): + self.laser.setPower(self.laser.maxPower + 1.0) + with self.assertRaises(ValueError): + self.laser.setPower(-0.1) + + def testInterlockAndFaults(self): + self.assertTrue(self.laser.interlock()) + self.assertEqual(self.laser.faults(), []) + self.laser.interface.activeFaults = ["Interlock fault"] + self.assertFalse(self.laser.interlock()) + self.assertIn("Interlock fault", self.laser.faults()) + + def testStatusUserInfo(self): + self.laser.turnOn(); self.laser.openShutter(); self.laser.setPower(1.5) + info = self.laser.doGetStatusUserInfo() + self.assertAlmostEqual(info["power"], 1.5, places=3) + self.assertTrue(info["isLaserOn"]) + self.assertTrue(info["isShutterOpen"]) + self.assertTrue(info["remoteControl"]) + self.assertTrue(info["interlockOk"]) + + +class TestInterfaceSelection(unittest.TestCase): + def testPassingAnInterfaceInstanceUsesIt(self): + interface = DebugHOPSInterface() + laser = VerdiGDevice(interface=interface) + laser.initializeDevice() + self.assertIs(laser.interface, interface) + laser.shutdownDevice() + + def testUnknownInterfaceChoiceRaises(self): + laser = VerdiGDevice(interface="banana") + with self.assertRaises(PhysicalDevice.UnableToInitialize): + laser.initializeDevice() + + +class TestNativeInterfaceOnMock(unittest.TestCase): + def setUp(self): + self.interface = HOPSNativeInterface(bus=MockHOPSBus()) + self.laser = VerdiGDevice(interface=self.interface) + self.laser.initializeDevice() + + def tearDown(self): + if self.laser.state == DeviceState.Ready: + self.laser.shutdownDevice() + + def testNativeReadsIdentity(self): + self.assertEqual(self.laser.headType, "G532") + self.assertEqual(self.laser.laserModel, "Genesis CX-Vis") + + def testNativeShutterAndEnableViaBits(self): + self.laser.openShutter() + self.assertTrue(self.laser.isShutterOpen()) + self.laser.turnOn() + self.assertTrue(self.laser.isLaserOn()) + + def testNativeSetPowerWritesDac(self): + self.laser.setPower(3.0) + self.assertEqual(self.interface._bus.dacCode, + int(round(3.0 * self.interface.dacCountsPerWatt))) + + def testNativeTemperatureCalibration(self): + self.interface._bus.adc[0x94] = 1696 + self.assertAlmostEqual(self.laser.mainTemperature(), 32.222, places=2) + + def testNativeInterlockRaisesNotSupported(self): + with self.assertRaises(HOPSInterface.NotSupported): + self.laser.interlock() + with self.assertRaises(HOPSInterface.NotSupported): + self.laser.faults() + + def testNativePowerUncalibratedNonzeroRaises(self): + self.interface._bus.adc[0xE4] = 1234 + with self.assertRaises(HOPSInterface.NotSupported): # NotCalibrated subclass + self.laser.power() + + +class TestHOPSNativeI2CReadModifyWrite(unittest.TestCase): + def testWriteGpioBitPreservesOtherBits(self): + bus = MockHOPSBus() + i2c = HOPSNativeI2C(bus) + bus.gpio[0x02] = 0b00000101 + i2c.writeGpioBit(5, 1) + self.assertEqual(bus.gpio[0x02], 0b00100101) + i2c.writeGpioBit(0, 0) + self.assertEqual(bus.gpio[0x02], 0b00100100) + + +class TestVerdiGHardware(unittest.TestCase): + def setUp(self): + self.laser = VerdiGDevice(interface="native", url=NATIVE_URL) + try: + self.laser.initializeDevice() + except PhysicalDevice.UnableToInitialize: + self.skipTest("No native Verdi-G / HOPS laser reachable via pyftdi on this host") + + def tearDown(self): + if self.laser.state == DeviceState.Ready: + self.laser.shutdownDevice() + + def testReadsIdentity(self): + self.assertIsNotNone(self.laser.headType) + + def testReadsTemperature(self): + self.assertIsInstance(self.laser.mainTemperature(), float) + + def testReadsShutterState(self): + self.assertIn(self.laser.isShutterOpen(), (True, False)) + + +if __name__ == "__main__": + unittest.main() From 01443dc07729f25c078fc57ae5616364ccd94b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Fri, 10 Jul 2026 01:15:50 -0400 Subject: [PATCH 2/4] Add Coherent HOPS reverse-engineering documentation Reconstructed by intercepting CohrHOPS.dll and decoding its I2C traffic. Three numbered docs to read in order, plus the bus diagram: 1. Overview & runbook -- the macOS-writes / Windows-runs / Parallels workflow used to reach the laser, and how to reproduce it. 2. USB / CohrHOPS.dll command protocol. 3. I2C wire protocol (device addresses, registers, calibration) and the bus diagram (SVG + PNG) -- what the native pyftdi transport replays. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01841beMNdCw3zYf6fdBUoy7 --- .../Coherent-HOPS-1-Overview-and-Runbook.md | 290 ++++++++++++++++++ .../Coherent-HOPS-2-USB-and-DLL-Protocol.md | 116 +++++++ .../manuals/Coherent-HOPS-3-I2C-Circuit.png | Bin 0 -> 147021 bytes .../manuals/Coherent-HOPS-3-I2C-Circuit.svg | 135 ++++++++ .../Coherent-HOPS-3-I2C-Wire-Protocol.md | 147 +++++++++ 5 files changed, 688 insertions(+) create mode 100644 hardwarelibrary/manuals/Coherent-HOPS-1-Overview-and-Runbook.md create mode 100644 hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md create mode 100644 hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Circuit.png create mode 100644 hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Circuit.svg create mode 100644 hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Wire-Protocol.md diff --git a/hardwarelibrary/manuals/Coherent-HOPS-1-Overview-and-Runbook.md b/hardwarelibrary/manuals/Coherent-HOPS-1-Overview-and-Runbook.md new file mode 100644 index 0000000..523d19f --- /dev/null +++ b/hardwarelibrary/manuals/Coherent-HOPS-1-Overview-and-Runbook.md @@ -0,0 +1,290 @@ +# Runbook: controlling the Coherent "Verdi-G" / Genesis HOPS laser + +> HOPS docs, read in order: **1. Overview & Runbook** -> 2. USB/DLL Protocol -> 3. I2C Wire Protocol. + +How we got a lab "Verdi-G" laser under computer control, end to end, and how to +do it again. Written so a future session (human or AI) can reproduce it without +rediscovering the dead ends. + +> **Use at your own risk — reverse-engineered without Coherent.** Everything here +> was obtained by reverse-engineering the Windows-only, proprietary `CohrHOPS.dll`: +> intercepting its calls and dumping the parameters, so a pure-Python macOS/Linux +> version could be written without the DLL. Thanks to +> https://github.com/AllenNeuralDynamics/coherent-lasers for the critical starting +> point. + +**Design.** Everything goes through the HOPS (High Output Power Supply) — which +appears to be a general supply Coherent reuses across many systems — so the driver +is a single `VerdiGDevice` backed by either transport: the DLL (`HOPSDLLInterface`) +or pure-Python pyftdi I2C (`HOPSNativeInterface`). `VerdiGDevice` picks one and is +otherwise unaware of the details. + +For the protocol details see `Coherent-HOPS-2-USB-and-DLL-Protocol.md`; for the driver see +`hardwarelibrary/sources/verdig.py` (`VerdiGDevice`) and its interfaces +`hopsnative.py` / `hopsdll.py`. This file is the *workflow*. + +--- + +## TL;DR + +1. The lab laser labelled "Verdi-G" is actually a **Coherent Genesis CX-Vis** + (head type `G532`, 532 nm) on a **HOPS (High Output Power Supply)**. It is **not a serial + device**: it enumerates as an FTDI FT2232 but is driven as bit-banged I2C, so + nothing answers over a COM port at any baud. +2. The only sane way to talk to it is **Coherent's `CohrHOPS.dll`**, which + exposes an ASCII command API over that binary transport. +3. `CohrHOPS.dll` is **Windows-only**, and the lab control machine is a **Mac**. + The working arrangement: **Claude Code (on macOS) writes the code**, drops it + into a folder that a **Parallels Windows VM** sees, and the DLL is **run in + Windows** (via PowerShell, because the VM had no Python). Output files sync + back to macOS, so Claude reads the results and iterates. +4. The DLL and Coherent's full software are **public** — links below. + +--- + +## What the laser actually is + +Query it and it tells you (this is the real recorded output): + +``` +?LASERMODEL -> Genesis CX-Vis +?HTYPE -> G532 +?HID -> VH5359 (head serial) +CohrHOPS.dll version: 2.0.7 +``` + +USB identity of the supply: FTDI **FT2232**, `VID 0x0403 / PID 0x6010`, +manufacturer `Coherent`, product string `HOPS Power Supply`, adaptor serial +`FTV5L9CA`. On macOS it appears as `/dev/cu.usbserial-FTV5L9CA0` and `...CA1`. + +## Why it can't be driven as a serial port + +The FT2232 enumerates as a virtual COM port, but the HOPS firmware does **not** +run it as a UART. It is driven in bit-banged / MPSSE **I2C** to an NXP micro- +controller in the supply, and the command set is exchanged as **binary frames**. +Consequence, confirmed on the bench with the laser powered and lasing: it returns +**zero bytes** to every serial probe — all bauds 9600–921600, every +databit/parity/stopbit combo, every line terminator — and the FTDI never even +flags a framing error (the RX line never toggles). Do not waste time on pyserial; +it is structurally impossible. + +## The software: CohrHOPS.dll — where to get it + +Coherent's own DLL, headers, demo, and full software package are mirrored +publicly by the Allen Institute's `coherent-lasers` project: + +- **Repo:** https://github.com/AllenNeuralDynamics/coherent-lasers +- **Release with the binaries (v0.1.0):** + https://github.com/AllenNeuralDynamics/coherent-lasers/releases/tag/v0.1.0 + - `CohrHOPS.dll` (64-bit): + https://github.com/AllenNeuralDynamics/coherent-lasers/releases/download/v0.1.0/CohrHOPS.dll + - `CohrFTCI2C.dll` (64-bit, dependency): + https://github.com/AllenNeuralDynamics/coherent-lasers/releases/download/v0.1.0/CohrFTCI2C.dll + - `OPLS.Software.V3.7.1.zip` (359 MB — Coherent's full package, contains the + `CohrHopsDemo` with `main.c`, both 32- and 64-bit DLLs, and `.pdb` debug + symbols): + https://github.com/AllenNeuralDynamics/coherent-lasers/releases/download/v0.1.0/OPLS.Software.V3.7.1.zip +- **Header (API):** + https://raw.githubusercontent.com/AllenNeuralDynamics/coherent-lasers/main/src/coherent_lasers/genesis_mx/hops/CohrHOPS.h +- **Genesis command tables (read/write command strings):** + https://raw.githubusercontent.com/AllenNeuralDynamics/coherent-lasers/main/src/coherent_lasers/genesis_mx/commands.py + +Coherent also distributes the same DLL on the **install CD/USB shipped with the +laser** and, in principle, via Coherent Support (their public Resources page is +JS-gated with no direct download). The GitHub mirror above is the reliable source. + +Copies used in this work are kept locally in the repo at `scratch-hops/` +(`CohrHOPS.dll`, `CohrFTCI2C.dll`, `CohrHOPS.h`, `CohrHopsDemo_main.c`). These are +**vendor binaries — do not commit them** to the repo; treat `scratch-hops/` as a +local stash and gitignore/remove it before a PR. + +Bitness matters: match the DLL to whatever runs it. The standalone release-asset +`CohrHOPS.dll` is **x86-64** (use it with 64-bit PowerShell/Python); the copy +inside `OPLS/CohrHopsDemo/Release` is 32-bit x86. + +--- + +## The workflow that worked (macOS + Parallels Windows) + +### Roles + +- **macOS (Claude Code):** writes all the code and probe scripts; cannot run the + DLL. Places deliverables in a folder the VM can see; reads back the results. +- **Windows VM (Parallels):** runs `CohrHOPS.dll` against the USB-attached laser. +- **Human:** does the few GUI actions the Parallels *Standard* edition blocks + from the command line (start the VM, route the USB device, double-click a file). + +### Why these exact choices + +- **Parallels Standard edition** blocks host→guest automation: `prlctl start` and + `prlctl exec` return *"available only in Parallels Desktop Pro/Business."* So + the Mac side cannot start the VM or run commands inside it. (Pro, ~US $120/yr, + would unlock full automation — not required.) +- **Shared Profile** (Parallels setting, was already on) maps the Mac home into + the guest: the Mac `~/Desktop` appears in Windows as `C:\Mac\Home\Desktop`, and + files the guest writes there sync back to macOS. This is the data bridge. +- **No Python in the guest** — Windows only had the Microsoft Store `python.exe` + *stub* (prints "Python was not found"). So we drive the DLL with **PowerShell + P/Invoke** (`Add-Type` + `DllImport`), which is always present on Windows. + +### Step by step + +1. **macOS (Claude):** build a package folder on the Mac Desktop, e.g. + `~/Desktop/VerdiHOPS/python64/`, containing the 64-bit `CohrHOPS.dll` + + `CohrFTCI2C.dll`, a PowerShell probe (`hops_probe.ps1`), and a launcher + (`RUN_ME.bat`). Because Shared Profile is on, it appears in Windows at + `C:\Mac\Home\Desktop\VerdiHOPS\python64`. +2. **Human:** start the **Windows 11** VM in Parallels. +3. **Human:** route the laser's USB to the guest — Parallels menu bar > + **Devices > USB & Bluetooth > Coherent "HOPS Power Supply"**. When routed it + disappears from macOS (`/dev/cu.usbserial-FTV5L9CA*` gone). Leave the + FieldMaster power meter (also FTDI) on the Mac. +4. **Human:** in Windows, open that folder and **double-click `RUN_ME.bat`**. +5. **macOS (Claude):** read `hops_probe_output.txt` (and `run_log.txt`) from + `~/Desktop/VerdiHOPS/python64/`. Iterate: edit the script on the Mac, have the + human re-run, read again. + +The launcher (`RUN_ME.bat`) runs the probe under PowerShell so no Python is +needed: + +```bat +@echo off +pushd "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "hops_probe.ps1" +popd +echo Press any key to close... +pause >nul +``` + +The probe loads the DLL with P/Invoke, discovers the device, initializes the +handle, sends read-only queries, and writes the results to a file: + +```powershell +Add-Type -TypeDefinition @" +using System; using System.Runtime.InteropServices; using System.Text; +public static class Cohr { + [DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern bool SetDllDirectory(string p); + [DllImport("CohrHOPS.dll", CallingConvention=CallingConvention.StdCall)] + public static extern int CohrHOPS_CheckForDevices([Out] ulong[] c, out uint nc, [Out] ulong[] a, out uint na, [Out] ulong[] r, out uint nr); + [DllImport("CohrHOPS.dll", CallingConvention=CallingConvention.StdCall)] + public static extern int CohrHOPS_InitializeHandle(ulong h, StringBuilder head); + [DllImport("CohrHOPS.dll", CallingConvention=CallingConvention.StdCall)] + public static extern int CohrHOPS_SendCommand(ulong h, string cmd, StringBuilder resp); + [DllImport("CohrHOPS.dll", CallingConvention=CallingConvention.StdCall)] + public static extern int CohrHOPS_Close(ulong h); +} +"@ +[Cohr]::SetDllDirectory($PSScriptRoot) | Out-Null +$c=New-Object 'ulong[]' 20; $a=New-Object 'ulong[]' 20; $r=New-Object 'ulong[]' 20 +[uint32]$nc=0;[uint32]$na=0;[uint32]$nr=0 +[void][Cohr]::CohrHOPS_CheckForDevices($c,[ref]$nc,$a,[ref]$na,$r,[ref]$nr) +$h = if ($nc) {$c[0]} else {$a[0]} +$head=New-Object System.Text.StringBuilder 100 +[void][Cohr]::CohrHOPS_InitializeHandle($h,$head) # $head -> "G532" +$resp=New-Object System.Text.StringBuilder 100 +[void][Cohr]::CohrHOPS_SendCommand($h,"?LASERMODEL",$resp) # -> "Genesis CX-Vis" +[void][Cohr]::CohrHOPS_Close($h) +``` + +The full read-only probe (`hops_probe.ps1`) and the safe write-path validation +(`hops_writetest.ps1`, exercises `REM=`/`PCMD=` then restores, never opens the +shutter) live in `~/Desktop/VerdiHOPS/python64/`. + +--- + +## Gotchas we hit (and the fixes) + +- **`.bat` window flashes and closes / does nothing.** From a Parallels shared + folder the script runs from a UNC path (`\\Mac\...`) where `cd /d` silently + fails. Fix: use `pushd "%~dp0"` (maps a temp drive for UNC), log everything to + a file, and end with `pause` so the window never vanishes unseen. +- **"Python not found."** `where python` found only `...WindowsApps\python.exe`, + the Microsoft Store *alias stub*. Fix: don't rely on Python — use PowerShell + P/Invoke (always present). +- **`BadImageFormat` / DLL won't load.** 32/64-bit mismatch, or `CohrFTCI2C.dll` + / `FTD2XX.dll` missing. Fix: use 64-bit DLLs with 64-bit PowerShell; ensure the + FTDI **CDM driver** (provides `FTD2XX.dll`) is installed (Windows usually + auto-installs it when the device is routed). +- **No device found by the DLL.** The USB was not routed to the VM. Fix: route it + in the Parallels Devices > USB menu; confirm it left macOS. +- **The FT2232 is bus-powered**, so its COM ports appear even with the laser + supply switched off — port presence does not mean the laser is on. +- **Don't confuse the ports.** On the lab Mac: `usbmodem48F8834E39561` is a + Millennia eV (STM32 `0x0483:0x5740`, `*IDN?` -> Spectra_Physics), `FTFDLOTS` is + the FieldMaster meter, `FTV5L9CA0/1` is this Genesis/HOPS supply. + +--- + +## The DLL API and command set (essentials) + +`CohrHOPS.dll` exports 6 `extern "C"` functions (ctypes/P-Invoke friendly): + +| Function | Purpose | +|---|---| +| `CohrHOPS_GetDLLVersion(char*)` | version string | +| `CohrHOPS_CheckForDevices(conn,&nc, add,&na, rem,&nr)` | USB discovery -> handles | +| `CohrHOPS_InitializeHandle(handle, char* headOut)` | detect head; returns e.g. `G532` | +| `CohrHOPS_SendCommand(handle, char* cmd, char* respOut)` | one ASCII command -> ASCII reply | +| `CohrHOPS_Close(handle)` | release | +| `CohrHOPS_OpenSerialPort(port, &handle)` | for RS-232 HOPS supplies (not used here) | + +Command vocabulary (Genesis/HOPS): reads `?HID ?HTYPE ?LASERMODEL ?HH ?P ?PCMD +?PLIM ?C ?CLIM ?SH ?KSW ?KSWCMD ?REM ?CMODE ?INT ?FF ?TMAIN ?TSHG ?TBRF ?TETA`; +writes take a `CMD=` suffix: `PCMD=` (power setpoint), `KSWCMD=<0|1>` (software +switch = emission enable), `SHCMD=<0|1>` (shutter), `CMODECMD`, plus `REM=<0|1>` +(remote control — must be `1` before writes take effect). Faults: `?FF` is a hex +bitmask (`0x0020`=interlock, `0x0200`=glue board, etc.). Full table in +`Coherent-HOPS-2-USB-and-DLL-Protocol.md`. + +**Emission model** (no single on/off): needs hardware keyswitch (`?KSW`=1) + +cleared interlock + `REM=1` + `KSWCMD=1` + `PCMD>0`; the mechanical shutter +`SHCMD` gates the beam. + +--- + +## The driver + +`hardwarelibrary/sources/verdig.py` — `VerdiGDevice` (+ `DebugVerdiGDevice`), one +laser-source driver combining `OnOffControl` / `ShutterControl` / `PowerControl` +/ `InterlockControl`. It drives the bus through an interchangeable +`HOPSInterface`, chosen with `VerdiGDevice(interface="auto")` (native first, then +DLL), or forced with `"native"` / `"dll"` / an instance: + +- `hopsnative.py` — `HOPSNativeInterface`: pure-Python pyftdi I2C, no DLL + (macOS/Linux). Hardware-confirmed end to end on the lab unit; `interlock()` / + `faults()` raise `HOPSInterface.NotSupported` until the `?FF` decode is done. +- `hopsdll.py` — `HOPSDLLInterface`: Coherent's `CohrHOPS.dll` (ASCII command + set; Windows/Linux), with the ctypes binding `CohrHOPS`. + +Tests: `hardwarelibrary/tests/testVerdiG.py` (debug + native-on-mock always +run; a hardware class skips when no laser is reachable). + +--- + +## Verified on hardware (2026-07-09, lab Genesis CX-Vis) + +- Read path (identity, power, interlock/faults, all temperatures/hours): OK. +- Write path `REM=1` and `PCMD=0.5` -> read back `?PCMD`=0.498 (head quantizes + to ~2 mW, inside the driver's tolerance), `?P` stayed 0 (no emission, shutter + closed), then restored. So the DLL interface's REM/PCMD/confirm logic is proven. +- **Not yet exercised on hardware (safety):** `KSWCMD` (emission enable) and + `SHCMD` (shutter open). Verify with the beam blocked and interlock cleared. +- Note: at probe time `?FF`=`0x0220` — an active **interlock fault** (+ glue-board + bit); the laser will not emit until that is cleared. + +--- + +## Reproduce from scratch — checklist + +1. Download `CohrHOPS.dll` + `CohrFTCI2C.dll` (64-bit) from the v0.1.0 release + link above. Put them in `~/Desktop/VerdiHOPS/python64/` on the Mac with the + probe scripts from this repo's package. +2. Start the Parallels Windows VM. (Standard edition: do it in the GUI.) +3. Route the Coherent "HOPS Power Supply" USB device to the VM. Ensure the FTDI + CDM driver is installed in Windows. +4. In Windows, open `C:\Mac\Home\Desktop\VerdiHOPS\python64` and double-click + `RUN_ME.bat` (read-only probe) — confirm `?LASERMODEL` / head type. +5. Read `hops_probe_output.txt` back on the Mac. +6. For control, either run PowerShell that calls the DLL (as above) in the guest, + or run `VerdiGDevice(interface="dll")` (this library) inside the guest with a real Python + + the DLLs. For emission, first clear the interlock and block the beam. diff --git a/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md b/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md new file mode 100644 index 0000000..78f0649 --- /dev/null +++ b/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md @@ -0,0 +1,116 @@ +# Coherent HOPS USB Protocol (Genesis / Verdi G-C) + +> HOPS docs, read in order: 1. Overview & Runbook -> **2. USB/DLL Protocol** -> 3. I2C Wire Protocol. + +How to talk to a Coherent "HOPS" (High Output Power Supply) over USB. This is the transport +used by the Genesis OPSL heads and the **Verdi G- and C-series** -- *not* the +DB-9 ASCII RS-232 protocol of the classic Verdi V-series (a different product +line, not covered here or by this library). + +## Why a HOPS supply looks dead over serial + +A HOPS supply enumerates as an **FTDI FT2232** (`0x0403:0x6010`, USB manufacturer +`Coherent`, product `HOPS Power Supply`), so the OS attaches an FTDI VCP driver +and creates two `/dev/cu.usbserial-*` (or `COMn`) ports. **They answer nothing.** +The FT2232 is not run as a UART: Coherent drives it in **bit-banged / MPSSE I2C** +mode. The supply's **NXP microcontroller** sits on that I2C bus, and the laser +command set is exchanged as **binary frames** on the bus. So no baud rate, parity, +or terminator will ever elicit a reply on the COM port -- verified on the bench: a +powered, lasing Verdi G returned zero bytes across every baud 9600-921600, all +databit/parity/stopbit combinations, and every terminator, with the FTDI never +even flagging a framing error (the RX line never toggles). + +This was established from Coherent's own `CohrHOPS.dll` and its debug symbols +(`.pdb`), which are public in Coherent's OPLS software package (the `CohrHopsDemo` +folder) and mirrored in the AllenNeuralDynamics `coherent-lasers` GitHub release. + +## The stack + +``` +your code + -> CohrHOPS.dll ASCII command API; parses ASCII, builds binary frames + -> CohrFTCI2C.dll hardware-I2C path --. + -> (NXP bit-bang) software-I2C path --+--> FTD2XX.dll -> FT2232 -> I2C bus -> NXP uC -> laser head +``` + +The DLL's `.pdb` reveals an `NXP` class implementing I2C by hand over FTDI GPIO +(`SetClockLineHigh/Low`, `SetDataLineHigh/Low`, `GetDataLine`, `Start`, `Stop`, +`SendByte`, `GetByte`, `MasterAck`, `GetSlaveAck`, `ReadRegister`, +`WriteRegister`), and an `I2C` class for the hardware-assisted FTCI2C path. **Do +not reimplement this blindly**: stray writes on that bus can land on the head's +EEPROM/registers and damage the laser. Use the DLL. + +## The DLL API + +`extern "C"`, `__stdcall`, exported without name mangling -- ctypes-friendly. All +functions return `0` (OK) or a negative status; string buffers are 100 bytes. + +| Function | Purpose | +|---|---| +| `CohrHOPS_GetDLLVersion(char* version)` | DLL version string | +| `CohrHOPS_CheckForDevices(conn,&nConn, added,&nAdd, removed,&nRem)` | USB discovery; returns handle arrays | +| `CohrHOPS_OpenSerialPort(const char* port, HANDLE* handle)` | for RS-232 HOPS supplies | +| `CohrHOPS_InitializeHandle(HANDLE, char* headTypeOut)` | detect head; returns head-type string, or `INVALID_HEAD` | +| `CohrHOPS_SendCommand(HANDLE, char* cmd, char* resp)` | one ASCII command/query -> ASCII response | +| `CohrHOPS_Close(HANDLE)` | release the handle | + +Status codes: `0 OK`, `-1 INVALID_HANDLE`, `-2 INVALID_HEAD`, `-3 INVALID_COMMAND`, +`-4 INVALID_DATA`, `-5 I2C_ERROR`, `-6 USB_ERROR`, `-100..-102 FTCI2C_DLL_*`, +`-200 NXP_ERROR`, `-300 RS232_ERROR`, `-400 THREAD_ERROR`, `-999 OTHER_ERROR`. + +Call order (from Coherent's `CohrHopsDemo/main.c`): +`CheckForDevices` -> `InitializeHandle` -> `SendCommand("?HID")` -> `Close`. + +## Command vocabulary (HOPS/Genesis) + +Queries: `?HTYPE`, `?LASERMODEL`, `?HID`, `?HBDREV`, `?P` (power), `?SH` (shutter), +`?KSW` (keyswitch), `?CMODE`, `?PLIM`, `?CLIM`, `?POWERUNITS`, `?FAN`, `?INT`, +`?ETAD`, `?MAIND`, `?TBRF`, ... Writable settings take a `CMD` suffix: `?PCMD` +(set power), `?SHCMD` (shutter), `?KSWCMD`, `?CMODECMD`, `?TMAINCMD`, etc. This is +distinct from the V-series `?P`/`?L`/`?S`/`P:`/`L:`/`S:` set. + +## Getting the DLLs + +Not committed here (vendor binaries, Windows-only, bitness-specific). Obtain from: + +- the **install media that shipped with the laser** (the Verdi-G-capable build), or +- Coherent's public **OPLS software** package (`CohrHopsDemo` folder has + `CohrHOPS.dll`, `CohrFTCI2C.dll`, `main.c`, and `.pdb` symbols), or +- the AllenNeuralDynamics **`coherent-lasers`** GitHub release assets. + +Place `CohrHOPS.dll` and `CohrFTCI2C.dll` next to +`hardwarelibrary/sources/verdig.py`, matching your Python's bitness (the OPLS +`CohrHopsDemo/Release` DLL is 32-bit x86; the standalone release-asset build is +x86-64). + +## Using it from this library + +`VerdiGDevice` drives the laser; the DLL is one of its interchangeable transports +(`HOPSDLLInterface`), the native pyftdi I2C path (`HOPSNativeInterface`) the +other. Normally you just let it pick: + +```python +from hardwarelibrary.sources.verdig import VerdiGDevice + +laser = VerdiGDevice(interface="auto") # native first, then the DLL +laser.initializeDevice() +print(laser.laserModel, laser.headType, laser.headSerialNumber) +laser.setPower(0.5); laser.turnOn(); laser.openShutter() +laser.shutdownDevice() +``` + +To force the DLL transport, use `VerdiGDevice(interface="dll")`. The low-level +ctypes binding to `CohrHOPS.dll` is `CohrHOPS` in `sources/hopsdll.py`; on macOS +constructing the DLL interface raises `HOPSInterface.Unavailable` (no DLL for the +platform), so `interface="auto"` falls through to native. + +## Open question for the Verdi G specifically + +The public `CohrHOPS.dll` builds inspected so far list only **Genesis** head types +(`Genesis CX-UV/CX-Vis`, `Genesis MX-MTM/STM`, `Invalid head`) and contain **no +"Verdi" strings**. So `InitializeHandle` may return `INVALID_HEAD` for a Verdi G, +meaning the Verdi-capable CohrHOPS build is the one on the laser's own CD. One run +of the snippet above on a Windows host with the laser attached settles it: if +`initializeHandle` succeeds and `?LASERMODEL` names the Verdi, this DLL works; if +it raises `INVALID_HEAD`, use the laser's shipped DLL instead. Either way the API +and this wrapper are unchanged -- only the DLL file differs. diff --git a/hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Circuit.png b/hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Circuit.png new file mode 100644 index 0000000000000000000000000000000000000000..bd24a305ee3e80c323d3d5530e7e47a3468e7c86 GIT binary patch literal 147021 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�Sl<0m+vGyQe)*?@=ew( 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 + + + Coherent HOPS / Verdi-G (Genesis CX-Vis, G532) — I2C control bus + Reconstructed from the I2C traffic captured via the CohrFTCI2C.dll logging proxy. Logical bus, not a verified board schematic. + + + + POWER SUPPLY (HOPS) + + LASER HEAD + Genesis CX-Vis / G532 + + + + umbilical / head connector + + + + USB Host + (Mac) + + USB + + + + FTDI FT2232C/D + USB 0x0403:0x6010 + "HOPS Power Supply" + S/N FTV5L9CA + Ch A → MPSSE I2C + Ch B → 2nd port (unused) + force non-3-phase clock (C/D) + + AD0 → SCL + AD1+AD2 → SDA + + + + + + + + + SCL + SDA + + + + +V + + + + + + + + + R₁ pull-ups + + + + + + + + DAC + 0x29 + power setpoint + reg 0xA0, 16-bit + written by PCMD + 0.5 W → 0x0033 + + + + + + + + ADC + 0x48 + power & temperatures + power: reg 0xE4 + TMAIN: reg 0x94 + read by ?P, ?TMAIN + + + + + + + + I/O expanders + 0x20 0x22 0x24 0x25 + (PCA9555-style, x4) + shutter (SHCMD) + keyswitch, remote (REM) + enable (KSWCMD), faults + + + + + + + + EEPROM + 0x52 + reg 0x01XX (2-byte) + head type: G532 + head ID: VH5359 + calibration floats + identity + cal, in head + + + + Confirmed from capture: + 7-bit addresses, registers, and that FT2232 channel A carries the I2C traffic. + Inferred (standard wiring): + SCL=AD0 and SDA=AD1+AD2 (tied), the pull-ups, and the head EEPROM sitting across the umbilical. + Part types: + EEPROM / ADC / DAC / GPIO inferred from register behavior; exact part numbers unknown. + Clock: I2C runs ~10 kHz (CohrHOPS divisor 599). TMAIN sensor is NTC-like (raw falls as temperature rises). + diff --git a/hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Wire-Protocol.md b/hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Wire-Protocol.md new file mode 100644 index 0000000..061f8c9 --- /dev/null +++ b/hardwarelibrary/manuals/Coherent-HOPS-3-I2C-Wire-Protocol.md @@ -0,0 +1,147 @@ +# HOPS I2C protocol — decoded from a live capture + +> HOPS docs, read in order: 1. Overview & Runbook -> 2. USB/DLL Protocol -> **3. I2C Wire Protocol**. + +> **Use at your own risk — reverse-engineered without Coherent.** This was +> obtained by intercepting `CohrHOPS.dll`'s calls and dumping their parameters, to +> write a pure-Python driver without the proprietary, Windows-only DLL. Addresses +> and registers are confirmed from the capture; wiring and part types are inferred. + +Decoded from `ftci2c_capture_readonly.log` (captured 2026-07-09 via the logging +`CohrFTCI2C.dll` proxy, read-only queries only) on the lab Genesis CX-Vis (G532). +This is the wire protocol underneath `CohrHOPS.dll` — what a native `pyftdi` +driver would replay. See `Coherent-HOPS-1-Overview-and-Runbook.md` for how the capture +was made, and `Coherent-HOPS-3-I2C-Circuit.svg` for a bus diagram of the devices +below (logical, reconstructed from this capture — not a verified board schematic). + +## Transport + +FTCI2C setup once at open: `I2C_InitDevice(divisor=599)`, `I2C_SetMode(mode=1)` +(STANDARD_MODE). Every access is a standard I2C "write register pointer, then +read N bytes" (`I2C_Read`, rType=2 = BLOCK_READ). In the FTCI2C control buffer, +byte 0 is the device address with the write bit; the remaining control bytes are +the register pointer written before the repeated-start read. + +The HOPS supply is **not one chip** — it's several I2C devices on one bus: + +| 7-bit addr | Ctrl byte 0 (W) | Role | Register addressing | +|---|---|---|---| +| 0x52 | 0xA4 | **Config/calibration + identity EEPROM** | 2-byte pointer `0x01 0xXX`, 1 byte per read | +| 0x48 | 0x90 | **ADC** (power, temperatures — raw counts) | 1-byte register, reads 2 bytes | +| 0x20 | 0x40 | status/GPIO port | 1-byte register (0x00/0x01), 1 byte | +| 0x22 | 0x44 | status/GPIO port | 1-byte register (0x00/0x01), 1 byte | +| 0x24 | 0x48 | status/GPIO port (keyswitch) | 1-byte register, 1 byte | +| 0x25 | 0x4A | status/GPIO port (shutter) | 1-byte register, 1 byte | + +## EEPROM 0x52 (identity + calibration), page 0x01 + +Strings are read byte-by-byte until a NUL: + +| Reg | Field | Bytes | Value | +|---|---|---|---| +| 0x0100.. | head type (`?HTYPE`) | `47 35 33 32 00` | "G532" | +| 0x0110.. | head ID (`?HID`) | `56 48 35 33 35 39 00` | "VH5359" | +| 0x0160.. | board rev (`?HBDREV`) | `44 45 00` | "DE" | + +Calibration constants are 4-byte **IEEE-754 big-endian** floats: + +| Reg | Bytes | Float | +|---|---|---| +| 0x0120 | `44 95 40 00` | 1194 | +| 0x0124 | `45 CA D0 00` | 6490 | +| 0x0128 | `43 73 00 00` | 243 | +| 0x012C | `46 29 E8 00` | 10874 | +| 0x0180 | `C0 8B 4F 35` | -4.35342 | +| 0x0184 | `3F E7 EF A0` | 1.812 | +| 0x0188 | `C2 30 C3 89` | -44.191 | + +(Blocks repeat at 0x0130/0x0140/0x0150 and 0x0184/0x0188/0x018C — per-channel +gain/offset sets.) **These are the head calibration values.** A stray I2C write +to 0x52 corrupts them — this is the concrete reason not to poke the bus blind. + +## Live signals — ADC 0x48 (raw counts, converted with the cal constants) + +| `?` command | I2C read | raw | note | +|---|---|---|---| +| `?P` (power) | dev 0x48, reg 0xE4, 2 bytes | `00 00` | 0 (laser off); scaled to W via cal | +| `?TMAIN` | dev 0x48, reg 0x94, 2 bytes | `06 A0` | raw ADC; scaled to °C (see below) | + +**TMAIN calibration** (two DLL-correlated points, captured 2026-07-09): +raw 1696 ↔ 32.222 °C and raw 1432 ↔ 39.773 °C — an **inverse** (NTC-like) +relationship. Two-point linear fit: **`T(°C) = -0.028602 · raw + 80.7315`**, +good over ~32–40 °C. (A one-point-through-origin guess is wrong — the slope is +negative. Widen with more points, or use the EEPROM cal floats, for a larger +range.) The **power** ADC read only has the (0 counts → 0 W) point so far; its +non-zero scale needs actual emission to calibrate. + +So `CohrHOPS` reads all EEPROM cal floats at init, then converts ADC counts to +watts/°C. A native driver must reproduce that scaling (or read the cal block and +apply the same gain/offset). + +## Status / faults — GPIO ports 0x20/0x22/0x24/0x25 + +Single-byte port reads whose bits the DLL combines: + +| `?` command | reads | raw | +|---|---|---| +| `?SH` (shutter) | dev 0x25 reg 0x00 | `EC` | +| `?KSW` (keyswitch) | dev 0x24 reg 0x00 | `94` | +| `?FF` (fault) | dev 0x20 reg 0x01/0x00, 0x24 reg 0x01/0x00, 0x22 reg 0x01/0x00 | `DC D5 FA 94 0F 15` | + +The DLL masks specific bits out of these port bytes (e.g. `?SH`->0, `?KSW`->1 on +this run). The exact bit map needs a few more captures with the shutter/keyswitch +toggled to pin down which bit is which. + +## Write protocol (captured 2026-07-09, beam blocked) + +Writes go to two devices that were not touched by reads: + +### Power setpoint — DAC at 0x29 (ctrl byte 0x52), register 0xA0 + +`PCMD=` writes a 16-bit big-endian code to device 0x29, register 0xA0 +(`I2C_Write` wType=2 / PAGE_WRITE, 2 data bytes): + +| `PCMD` | wdata | +|---|---| +| 0.5 W | `00 33` (=51) | +| 0.0 W | `00 00` | + +~102 counts/W through the origin from these two points (single non-zero point, +so the offset/exact scale should be confirmed with a couple more setpoints, or +computed from the EEPROM cal floats). This is **not** the cal EEPROM (0x52) — so +setting power never risks the calibration. + +### Discrete controls — GPIO expander at 0x25 (ctrl byte 0x4A) + +A PCA9555-style 16-bit I/O expander: register **0x02 = output port**, **0x06 = +configuration/direction**. Each command writes 0x06 (make the pin an output) then +0x02 (drive it). The DLL uses a read-modify-write shadow, so a native driver +should read 0x02/0x06, flip only the target bit, and write back (never blast a +whole byte). + +Output-port 0x02 bit map (derived from the deltas): + +| Bit | Mask | Control | Meaning | +|---|---|---|---| +| 0 | 0x01 | shutter (`SHCMD`) | 1 = open, 0 = closed (only `SHCMD=0` captured; `=1` inferred) | +| 3 | 0x08 | remote (`REM`) | **active-low**: 0 = remote on, 1 = off | +| 5 | 0x20 | software switch (`KSWCMD`, emission enable) | 1 = on, 0 = off | + +Captured output-port values: `REM=1`->E4, `KSWCMD=0`->C4, `KSWCMD=1`->E4, +`SHCMD=0`->E4 (bit0 already 0), `REM=0`->EC. + +All writes returned status 0, and the run restored REM/PCMD/KSWCMD and left the +shutter closed (verified in the final-state read). + +## Status of the reverse-engineering + +- **Reads: mapped.** Identity/EEPROM reads are trivially replayable; ADC and GPIO + reads are captured, pending the count->unit scaling and the status bit map. +- **Writes: captured** (see "Write protocol" above). Power = DAC 0x29 reg 0xA0; + shutter/remote/enable = GPIO expander 0x25 bits 0/3/5. Remaining refinement: + a few more `PCMD` points to nail the DAC scale/offset, and confirming `SHCMD=1` + (shutter open) — deliberately not captured to keep the beam blocked. +- A native `pyftdi` driver is therefore feasible: pyftdi's `I2cController` does + exactly "write pointer, read N bytes" on these addresses. The remaining work is + the ADC scaling, the status bit map, and a write capture — not more transport + reverse-engineering. From 1a4e7211b4f6750894f579ce22620e1a949dcb5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Fri, 10 Jul 2026 01:26:31 -0400 Subject: [PATCH 3/4] Document the driver's implemented ASCII command set in HOPS doc 2 Add a "Commands implemented by the driver" section to Coherent-HOPS-2-USB-and-DLL-Protocol.md: the exact ASCII queries, diagnostics, and settings HOPSDLLInterface exchanges through CohrHOPS.dll, each with its description and return value, plus the ?FF fault-bit decode. Notes that the native pyftdi transport maps the same operations onto I2C. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01841beMNdCw3zYf6fdBUoy7 --- .../Coherent-HOPS-2-USB-and-DLL-Protocol.md | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md b/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md index 78f0649..00db0c8 100644 --- a/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md +++ b/hardwarelibrary/manuals/Coherent-HOPS-2-USB-and-DLL-Protocol.md @@ -69,6 +69,72 @@ Queries: `?HTYPE`, `?LASERMODEL`, `?HID`, `?HBDREV`, `?P` (power), `?SH` (shutte (set power), `?SHCMD` (shutter), `?KSWCMD`, `?CMODECMD`, `?TMAINCMD`, etc. This is distinct from the V-series `?P`/`?L`/`?S`/`P:`/`L:`/`S:` set. +## Commands implemented by the driver + +These are the ASCII commands `HOPSDLLInterface` actually exchanges through +`CohrHOPS.dll`. The native pyftdi transport (`HOPSNativeInterface`) maps the same +operations onto I2C register reads/writes instead of ASCII (see +`Coherent-HOPS-3-I2C-Wire-Protocol.md`). + +Queries (read) -- prefixed `?`, return an ASCII value: + +| Command | Description | Returns | +|---|---|---| +| `?LASERMODEL` | Laser/head model name | string, e.g. `Genesis CX-Vis` | +| `?HTYPE` | Head type code | string, e.g. `G532` | +| `?HID` | Head serial / ID | string, e.g. `VH5359` | +| `?PLIM` | Maximum (limit) output power | float W, e.g. `7.344` | +| `?P` | Actual output power (post-shutter) | float W, e.g. `0.000` | +| `?PCMD` | Power setpoint (commanded) | float W, e.g. `0.100` | +| `?KSWCMD` | Software switch / emission-enable state | `0` off / `1` on | +| `?SH` | Shutter state | `0` closed / `1` open | +| `?REM` | Remote-control state | `0` off / `1` on | +| `?TMAIN` | Main (baseplate) temperature | float °C | +| `?FF` | Fault bitmask | hex string, e.g. `0220` (see below) | + +Diagnostics queries (read) -- returned by `diagnostics()`: + +| Command | Description | Returns | +|---|---|---| +| `?HH` | Head operating hours | float | +| `?C` | Diode current | float A | +| `?CLIM` | Current limit | float A | +| `?TSHG` | SHG crystal temperature | float °C | +| `?TBRF` | BRF temperature | float °C | +| `?TETA` | Etalon temperature | float °C | +| `?FAN` | Fan speed/state | int | +| `?CMODE` | Control mode | int (`0`/`1`) | + +(`?PLIM` and `?TMAIN` are reused here too.) + +Settings (write) -- `NAME=value`; the response is an ack (empty/echoed) the +driver mostly ignores, except `setPower`, which reads `?PCMD` back to confirm the +value within tolerance: + +| Command | Description | Driver method | +|---|---|---| +| `PCMD=` | Set power setpoint, e.g. `PCMD=0.1000` | `setPower()` | +| `KSWCMD=<0\|1>` | Software switch / emission enable | `turnOn()` / `turnOff()` | +| `SHCMD=<0\|1>` | Shutter (`1` open, `0` close) | `openShutter()` / `closeShutter()` | +| `REM=<0\|1>` | Remote control (`1` on) | set on at init, off at shutdown | + +`?FF` fault bits decoded by `faults()` / `interlockOk()`: + +| Bit | Meaning | +|---|---| +| `0x0008` | Main TEC error | +| `0x0010` | LBO/BRF temperature not OK | +| `0x0020` | Interlock fault | +| `0x0100` | Shutter error | +| `0x0200` | Glue-board error | +| `0x0800` | LDD at current limit | + +`interlockOk()` is true when the interlock bit (`0x0020`) is clear; `faults()` +returns the names of the set bits (e.g. `0x0220` = interlock + glue-board). `?FF`, +`interlockOk()`, and the diagnostics are DLL-only; on the native pyftdi transport +they raise `HOPSInterface.NotSupported` (the `?FF` decode is not yet +reverse-engineered natively). + ## Getting the DLLs Not committed here (vendor binaries, Windows-only, bitness-specific). Obtain from: From aba7193678f072015d69c02a4a6787105e40b52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20C=C3=B4t=C3=A9?= Date: Fri, 10 Jul 2026 10:29:32 -0400 Subject: [PATCH 4/4] Add Verdi-G / HOPS monitor examples Two read-only status monitors under examples/verdig/ (stream once per second, Ctrl-C to stop; neither takes remote control, so the front panel stays usable): - temperature_monitor_native.py: main temperature over native pyftdi I2C (no DLL). - status_monitor_dll.py: full status (four servo temperatures, power, shutter, emission, interlock/faults) over Coherent's CohrHOPS.dll. Plus a short README. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01841beMNdCw3zYf6fdBUoy7 --- examples/verdig/README.md | 16 ++++++ examples/verdig/status_monitor_dll.py | 53 +++++++++++++++++++ examples/verdig/temperature_monitor_native.py | 40 ++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 examples/verdig/README.md create mode 100644 examples/verdig/status_monitor_dll.py create mode 100644 examples/verdig/temperature_monitor_native.py diff --git a/examples/verdig/README.md b/examples/verdig/README.md new file mode 100644 index 0000000..badd320 --- /dev/null +++ b/examples/verdig/README.md @@ -0,0 +1,16 @@ +# Verdi-G / HOPS examples + +Read-only monitors for a Coherent HOPS-supply laser (Genesis / Verdi-G). Both +stream once per second and stop on Ctrl-C. Neither takes remote control, so the +front-panel wheel keeps working while they run. + +- `temperature_monitor_native.py` — main temperature over **native pyftdi I2C** + (no DLL). macOS/Linux; needs pyftdi + libusb and the laser on this host. +- `status_monitor_dll.py` — full status (all four servo temperatures, power, + shutter, emission, interlock/faults) over **Coherent's `CohrHOPS.dll`**. + Windows/Linux; needs the DLLs (see `../../hardwarelibrary/manuals/` + `Coherent-HOPS-2-USB-and-DLL-Protocol.md`). + +For the full driver and the two transports, see +`hardwarelibrary/sources/verdig.py` (`VerdiGDevice`) and the `Coherent-HOPS-*` +docs under `hardwarelibrary/manuals/`. diff --git a/examples/verdig/status_monitor_dll.py b/examples/verdig/status_monitor_dll.py new file mode 100644 index 0000000..6496a47 --- /dev/null +++ b/examples/verdig/status_monitor_dll.py @@ -0,0 +1,53 @@ +"""Stream full Verdi-G / HOPS status over Coherent's CohrHOPS.dll (Windows/Linux). + +Read-only: it does not change the laser state or take remote control (the front +panel stays usable). Unlike the native transport, the DLL path also reads all +four thermal-servo temperatures and the interlock/fault state. + +Setup: place CohrHOPS.dll and CohrFTCI2C.dll (matching your Python's bitness) +next to hardwarelibrary/sources/hopsdll.py, or pass dllDirectory=... below. + +Run: python examples/verdig/status_monitor_dll.py [count] +""" +import datetime +import sys +import time + +from hardwarelibrary.sources.hopsdll import HOPSDLLInterface + + +def main(): + count = int(sys.argv[1]) if len(sys.argv) > 1 else None + interface = HOPSDLLInterface() # or HOPSDLLInterface(dllDirectory=r"C:\dlls") + interface.open() + + identity = interface.identity() + print("Connected via CohrHOPS.dll: {0} (head {1}, s/n {2}, max {3:.3f} W)".format( + identity["model"], identity["headType"], identity["serialNumber"], + identity["maxPower"])) + + read = 0 + try: + while count is None or read < count: + diag = interface.diagnostics() # includes the four servo temperatures + temps = ("main={mainTemperature:.1f} SHG={shgTemperature:.1f} " + "BRF={brfTemperature:.1f} etalon={etalonTemperature:.1f}").format(**diag) + faults = interface.faults() + interlock = "ok" if interface.interlockOk() else "FAULT" + stamp = datetime.datetime.now().strftime("%H:%M:%S") + print("{0} {1} C | P={2:.3f} W shutter={3} emission={4} interlock={5}{6}".format( + stamp, temps, interface.getPower(), + "open" if interface.shutterOpen() else "closed", + "ON" if interface.emissionOn() else "off", interlock, + (" faults=" + ",".join(faults)) if faults else "")) + read += 1 + if count is None or read < count: + time.sleep(1.0) + except KeyboardInterrupt: + print() + finally: + interface.close() + + +if __name__ == "__main__": + main() diff --git a/examples/verdig/temperature_monitor_native.py b/examples/verdig/temperature_monitor_native.py new file mode 100644 index 0000000..50b9b81 --- /dev/null +++ b/examples/verdig/temperature_monitor_native.py @@ -0,0 +1,40 @@ +"""Stream the Verdi-G / HOPS main temperature over native pyftdi I2C (no DLL). + +Read-only: it does not change the laser state or take remote control (so the +front panel stays usable). Works on macOS/Linux with the laser attached and +pyftdi + libusb available. + +Run: python examples/verdig/temperature_monitor_native.py [count] +On macOS, put the directory that holds libusb-1.0 on the loader path, e.g.: + DYLD_LIBRARY_PATH=/opt/homebrew/lib python examples/verdig/temperature_monitor_native.py +""" +import datetime +import sys +import time + +from hardwarelibrary.sources.hopsnative import HOPSNativeInterface + + +def main(): + count = int(sys.argv[1]) if len(sys.argv) > 1 else None + interface = HOPSNativeInterface() # or HOPSNativeInterface(url="ftdi://...") + interface.open() + print("Verdi-G / HOPS main-temperature monitor (Ctrl-C to stop). " + "Native calibration valid ~32-40 C.") + read = 0 + try: + while count is None or read < count: + celsius = interface.mainTemperature() + stamp = datetime.datetime.now().strftime("%H:%M:%S") + print("{0} TMAIN = {1:6.2f} C".format(stamp, celsius)) + read += 1 + if count is None or read < count: + time.sleep(1.0) + except KeyboardInterrupt: + print() + finally: + interface.close() + + +if __name__ == "__main__": + main()