#!/usr/bin/env python3 """ Genesis Laser Protocol Test Script =================================== Simple command-line script to test NXP I2C-over-serial communication with the Genesis SLM MX 532 laser. Usage: python test_genesis_protocol.py [port] [baudrate] Example: python test_genesis_protocol.py /dev/ttyUSB0 9600 """ import sys import time import serial from typing import Optional class NXPProtocolTester: """Simple NXP I2C protocol tester""" NXP_START = 0x53 NXP_STOP = 0x50 def __init__(self, port: str = "/dev/ttyUSB0", baudrate: int = 9600): self.port = serial.Serial( port=port, baudrate=baudrate, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1.0 ) time.sleep(0.1) print(f"Connected to {port} at {baudrate} baud") def send_packet(self, i2c_addr: int, cmd: bytes, data: bytes = b''): """Send NXP I2C write packet""" if not isinstance(cmd, bytes): cmd = bytes([cmd]) if not isinstance(data, bytes): data = bytes(data) length = len(cmd) + len(data) packet = bytes([self.NXP_START, i2c_addr, length]) + cmd + data + bytes([self.NXP_STOP]) print(f"TX: {' '.join(f'{b:02x}' for b in packet)}") self.port.write(packet) def read_packet(self, i2c_addr_write: int, cmd: bytes, cmd_len: int, data_len: int) -> Optional[bytes]: """Send NXP I2C read packet and return data""" if not isinstance(cmd, bytes): cmd = bytes([cmd]) i2c_addr_read = i2c_addr_write | 0x01 packet_write = bytes([self.NXP_START, i2c_addr_write, cmd_len]) + cmd packet_read = bytes([self.NXP_START, i2c_addr_read, data_len, self.NXP_STOP]) packet = packet_write + packet_read print(f"TX: {' '.join(f'{b:02x}' for b in packet)}") self.port.write(packet) time.sleep(0.05) response = self.port.read(data_len) if response: print(f"RX: {' '.join(f'{b:02x}' for b in response)}") return response else: print("RX: (no response)") return None def test_x9119_current(self, value: int = 100): """Test X9119 current control""" print(f"\n=== Testing X9119 Current Control (value={value}) ===") # X9119 current control at 0x52 # Command 0xa0, 2 bytes data (10-bit value) value = max(0, min(1023, value)) msb = (value >> 8) & 0x03 lsb = value & 0xFF self.send_packet(0x52, bytes([0xa0]), bytes([msb, lsb])) print("Current command sent") def test_pca9555_shutter(self, state: bool): """Test PCA9555 shutter control""" print(f"\n=== Testing PCA9555 Shutter Control (state={'OPEN' if state else 'CLOSED'}) ===") # Read current output port 0 value print("Reading current port value...") current = self.read_packet(0x4a, bytes([0x02]), 1, 1) if current and len(current) == 1: current_value = current[0] print(f"Current port value: 0x{current_value:02x}") # Modify bit 0 (shutter) if state: new_value = current_value | 0x01 else: new_value = current_value & ~0x01 print(f"New port value: 0x{new_value:02x}") # Write back self.send_packet(0x4a, bytes([0x02]), bytes([new_value])) print("Shutter command sent") else: print("Failed to read current port value") def test_ads7828_current_reading(self): """Test ADS7828 current reading""" print(f"\n=== Testing ADS7828 Current Reading ===") # ADS7828 at 0x90/0x91 # Command byte: 0x80 (single-ended) | (channel << 4) | 0x0c (internal ref) # Reading channel 0 cmd_byte = 0x80 | (0 << 4) | 0x0c data = self.read_packet(0x90, bytes([cmd_byte]), 1, 2) if data and len(data) == 2: value = (data[0] << 8) | data[1] value = (value >> 4) & 0x0FFF print(f"Current reading: {value} counts (0x{value:03x})") else: print("Failed to read current") def test_pca9555_read_ports(self): """Test reading all PCA9555 I/O expander ports""" print(f"\n=== Testing PCA9555 Port Reads ===") devices = [ (0x4a, "Main DIO (0x14a)"), (0x48, "PS Glue (0x148)"), (0x44, "Head DIO (0x144)"), (0x40, "LDD Control (0x140)"), ] for addr, name in devices: print(f"\n{name}:") for port in range(8): data = self.read_packet(addr, bytes([port]), 1, 1) if data and len(data) == 1: print(f" Register 0x{port:02x}: 0x{data[0]:02x} (0b{data[0]:08b})") else: print(f" Register 0x{port:02x}: read failed") time.sleep(0.05) def close(self): """Close serial port""" self.port.close() print("\nConnection closed") def main(): """Main test function""" # Parse command line arguments port = sys.argv[1] if len(sys.argv) > 1 else "/dev/ttyUSB0" baudrate = int(sys.argv[2]) if len(sys.argv) > 2 else 9600 try: # Create tester tester = NXPProtocolTester(port, baudrate) # Menu while True: print("\n" + "="*60) print("Genesis Laser Protocol Test Menu") print("="*60) print("1. Test X9119 current control (set to 100)") print("2. Test PCA9555 shutter OPEN") print("3. Test PCA9555 shutter CLOSE") print("4. Test ADS7828 current reading") print("5. Test read all PCA9555 ports") print("6. Set current to 0 (safe state)") print("7. Emergency stop (shutter close + current 0)") print("8. Custom X9119 current value") print("0. Exit") print("="*60) choice = input("Enter choice: ").strip() if choice == "1": tester.test_x9119_current(100) elif choice == "2": tester.test_pca9555_shutter(True) elif choice == "3": tester.test_pca9555_shutter(False) elif choice == "4": tester.test_ads7828_current_reading() elif choice == "5": tester.test_pca9555_read_ports() elif choice == "6": print("\n=== Setting current to 0 ===") tester.test_x9119_current(0) elif choice == "7": print("\n=== EMERGENCY STOP ===") tester.test_pca9555_shutter(False) time.sleep(0.1) tester.test_x9119_current(0) print("Emergency stop complete") elif choice == "8": try: value = int(input("Enter current value (0-1023): ")) tester.test_x9119_current(value) except ValueError: print("Invalid value") elif choice == "0": break else: print("Invalid choice") tester.close() except Exception as e: print(f"Error: {e}") import traceback traceback.print_exc() if __name__ == "__main__": main()