Files
scanengine-3/tests/test_genesis_protocol.py
T
2026-02-09 14:40:34 -06:00

238 lines
7.3 KiB
Python
Executable File

#!/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()