I was working in the macro programming language and I had my friend Claude take a quick look at some maintenance logs. Below is the logic and some tips for implementing into PyLabRobot.
I didn’t make the changes/additions myself because I’m not all that comfortable with it at this point.
Hope this helps!
Hamilton STARlet Weekly Maintenance Process Analysis
PyLabRobot Conversion Project Overview
Date: 2025-01-14
System: Hamilton Microlab STARlet (Serial: XXXX)
Software: Phoenix 4.5.0.7977 / STARlet 4.5.0.5217
Firmware: C0 Master 7.6S, P1/P2 Channels 4.0S, X0 Drive 1.4S
Executive Summary
This document analyzes the Hamilton STARlet weekly maintenance process to enable PyLabRobot implementation. The maintenance involves system initialization, user-guided cleaning procedures, pressure tightness testing, and liquid level detection verification.
System Architecture
Hardware Configuration
- Main Controller (C0): Master module controlling system coordination
- Pipetting Channels (P1/P2): Two 1000μl channels with pressure sensors
- X-Drive (X0): Horizontal positioning system
- Teaching Needles: Special maintenance tools for pressure testing
Communication Protocol
- High-Level: Hamilton Phoenix software commands
- Low-Level: Direct firmware commands via USB/CAN protocol
- Command Format: ModuleCommand + ID + Parameters (e.g.,
C0RQid0101
)
Maintenance Workflow Analysis
1. System Initialization (16:24:38-16:24:48)
# Equivalent PyLabRobot sequence:
device_enumeration()
firmware_version_check()
channel_configuration_verification()
system_status_query()
Hamilton Firmware Commands:
# Device identification and status
< C0RQid0101 # Request status
> C0RQid0101rq0000 # Response: OK
< C0RIid0103 # Read instrument info
> C0RIid0103er00/00si2017-01-31snXXXX # Serial number and date
< C0RMid0105 # Read module configuration
> C0RMid0105er00/00kb01kp02 C00000 X00000 P10000 P20000 # Module status
< P1RFid0107 # Read P1 firmware version
> P1RFid0107rf4.0S j 2022-03-16 # Firmware version response
< P2RFid0109 # Read P2 firmware version
> P2RFid0109rf4.0S j 2022-03-16 # Firmware version response
< C0QMid0104 # Query system configuration
> C0QMid0104er00/00ka000001xt30xa30xw08000xl01xr00xm03500xx11400ys090xu3540xv3700yu0800kl360kc0yx0060ke00000000xn00xo00ym6065kr0km360
Key Commands:
C0RI
- Read instrument info (serial number)
C0RM
- Read module configuration
P1RF
/P2RF
- Read firmware versions
C0QM
- Query system configuration
2. User Interaction Prompts (16:24:48-16:25:00)
# User guidance sequence:
prompt_remove_carriers()
prompt_clean_deck()
prompt_empty_tip_waste()
confirm_teaching_needles_installed()
Hamilton User Interaction Examples:
# Initial maintenance confirmation
2025-06-03 16:24:48> SYSTEM : Message Box - start; <Weekly maintenance - deck and tip waste>
Available button(s): <Yes>, <No>, Default button: <Yes>
Message: <Do you want to execute the weekly maintenance for deck and waste?>
2025-06-03 16:24:50> SYSTEM : Message Box - complete; <Yes (6)>
# Carrier removal prompt
2025-06-03 16:24:52> SYSTEM : Message Box - start; <Weekly maintenance - carriers>
Available button(s): <OK>, <Cancel>, Default button: <OK>
Message: <Remove all carriers from the deck, clean all carriers and press OK to continue.
(See Operator's Manual for use of cleaning, disinfecting and decontaminating fluids.)
Cancel will abort the weekly maintenance.>
2025-06-03 16:24:53> SYSTEM : Message Box - complete; <OK (1)>
# Deck cleaning prompt
2025-06-03 16:24:53> SYSTEM : Message Box - start; <Weekly maintenance - deck>
Available button(s): <OK>, Default button: <OK>
Message: <Open the front cover, clean the deck and press OK to continue.
(See Operator's Manual for use of cleaning, disinfecting and decontaminating fluids.)>
2025-06-03 16:24:54> SYSTEM : Message Box - complete; <OK (1)>
# Tip waste cleaning prompt
2025-06-03 16:24:55> SYSTEM : Message Box - start; <Weekly maintenance - tip waste>
Available button(s): <OK>, Default button: <OK>
Message: <Open the front cover, empty and clean the tip waste and press OK to continue.
(See Operator's Manual for use of cleaning, disinfecting and decontaminating fluids.)>
2025-06-03 16:24:56> SYSTEM : Message Box - complete; <OK (1)>
# Tightness test confirmation
2025-06-03 16:25:00> SYSTEM : Message Box - start; <Weekly Maintenance - Tightness Check 1000μl Channel>
Available button(s): <Yes>, <No>, Default button: <Yes>
Message: <Do you want to execute the 1000μl Channel tightness check?
Make sure that 2 1000μl teaching needles are installed.>
2025-06-03 16:25:01> SYSTEM : Message Box - complete; <Yes (6)>
User Actions Required:
- Remove all carriers from deck
- Clean deck surfaces
- Empty and clean tip waste
- Install 2x 1000μl teaching needles
3. Pressure Tightness Testing (16:25:01-16:26:31)
# Pressure testing sequence:
teaching_needle_pickup()
baseline_pressure_calibration() # BP command
positive_pressure_test() # DS + RP commands
negative_pressure_test() # DS + RP commands
pressure_tolerance_validation()
teaching_needle_ejection()
Hamilton Firmware Commands:
# Teaching needle pickup and baseline calibration
< PXAAid0208dp20000 # Position channels for needle pickup
> PXAAid0208er00 # Acknowledge
< PXBPid0212bp0 # Baseline pressure calibration
> PXBPid0212er00 # Calibration complete
# Positive pressure test
< P1DSid0213ds01250dt1 # Dosing steps: 1250 steps, dispense direction
> P1DSid0213er00 # Movement complete
< P1RPid0214 # Read pressure channel 1
> P1RPid0214rp+4082 # Pressure reading: +4082 Pascal
< P2DSid0215ds01250dt1 # Channel 2 dosing steps
> P2DSid0215er00 # Movement complete
< P2RPid0216 # Read pressure channel 2
> P2RPid0216rp+4057 # Pressure reading: +4057 Pascal
# Wait 20 seconds, then re-read for leak test
< P1RPid0219 # Read pressure after delay
> P1RPid0219rp+4077 # Pressure after 20s: +4077 Pascal (5 Pa loss)
< P2RPid0220 # Read pressure channel 2
> P2RPid0220rp+4052 # Pressure after 20s: +4052 Pascal (5 Pa loss)
# Negative pressure test
< P1DSid0228ds00800dt0 # Dosing steps: 800 steps, aspirate direction
> P1DSid0228er00 # Movement complete
< P1RPid0229 # Read pressure
> P1RPid0229rp-4370 # Negative pressure: -4370 Pascal
< P2DSid0230ds00800dt0 # Channel 2 aspirate
> P2DSid0230er00 # Movement complete
< P2RPid0231 # Read pressure
> P2RPid0231rp-4365 # Negative pressure: -4365 Pascal
# Leak test after 20 seconds
< P1RPid0234 # Read pressure after delay
> P1RPid0234rp-4345 # Pressure after 20s: -4345 Pascal (25 Pa loss)
< P2RPid0235 # Read pressure channel 2
> P2RPid0235rp-4340 # Pressure after 20s: -4340 Pascal (25 Pa loss)
Key Parameters:
- Target Pressures: ±4000 Pascal range
- Tolerance: <1500 Pascal pressure loss over 20 seconds
- Commands:
BP
- Baseline pressure calibration
DS
- Dosing drive steps (dt0=aspirate, dt1=dispense)
RP
- Read pressure values
4. cLLD Testing (16:26:37-16:27:01)
# Capacitive liquid level detection test:
teaching_needle_pickup()
clld_initialization() # AL command
measurement_setup() # JM command
level_detection_test() # ZL + RN + KZ commands
clld_validation()
teaching_needle_ejection()
Hamilton Firmware Commands:
# cLLD initialization and setup
< PXALid0245 # Liquid level detection initialization
> PXALid0245er00 # Initialization complete
< PXJMid0246 # Measurement mode setup
> PXJMid0246er00/00 # Setup complete
# Level detection test sequence
< PXZLid0247 # Z-level detection command
> PXZLid0247er70 # Error 70: Expected for air detection
< PXRNid0248 # Needle contact detection
> PXRNid0248rn0 # Contact status: 0 (no contact)
< PXZLid0249 # Retry Z-level detection
> PXZLid0249er00 # Success: Level detected
< PXRNid0250 # Needle contact verification
> PXRNid0250rn1 # Contact status: 1 (contact detected)
< PXKZid0251 # Capacitance measurement
> PXKZid0251er00/00 # Measurement complete
# Second channel cLLD test
< PXJMid0252 # Channel 2 measurement setup
> PXJMid0252er00/00 # Setup complete
< PXZLid0253 # Z-level detection
> PXZLid0253er70 # Expected air detection error
< PXRNid0254 # Contact detection
> PXRNid0254rn0 # No contact initially
< PXZLid0255 # Retry detection
> PXZLid0255er00 # Level found
< PXRNid0256 # Final contact check
> PXRNid0256rn1 # Contact confirmed
< PXKZid0257 # Final capacitance measurement
> PXKZid0257er00/00 # Measurement successful
Key Commands:
AL
- Liquid level detection initialization
JM
- Measurement mode setup
ZL
- Z-level detection with error handling
RN
- Needle contact detection
KZ
- Capacitance measurement
5. System Cleanup (16:27:01-16:27:24)
# Cleanup sequence:
return_to_safe_positions()
channel_spreading()
system_shutdown()
maintenance_completion_log()
Hamilton Cleanup Commands:
# System position reset
< C0RUid0117 # Return to safe positions
> C0RUid0117er00/00ru00950 08000 30000 30000 # Position confirmation
# Channel cleanup and spreading
2025-06-03 16:27:22> Microlab STARlet : Clean up instrument - progress; Move up 1000ul channels
2025-06-03 16:27:22> Microlab STARlet : Clean up instrument - progress; Spreading channels
# Final system status
< C0AV # System availability check
> C0AVer00/00 # System ready
# Maintenance completion
2025-06-03 16:27:04> SYSTEM : Message Box - start; <Weekly maintenance - successful>
Available button(s): <OK>, Default button: <OK>
Message: <Weekly maintenance execute successfully completed>
2025-06-03 16:27:20> SYSTEM : Message Box - complete; <OK (1)>
6. Teaching Needle Management
# Teaching needle pickup sequence
2025-06-03 16:25:01> Microlab STARlet : 1000ul Channel Tip Pick Up (Single Step) - start;
2025-06-03 16:25:21> Microlab STARlet : 1000ul Channel Tip Pick Up (Single Step) - complete;
> channel 1: teachingNeedleBlock, 1 > channel 2: teachingNeedleBlock, 2
# Teaching needle ejection sequence
2025-06-03 16:25:45> Microlab STARlet : 1000ul Channel Tip Eject (Single Step) - start;
2025-06-03 16:25:52> Microlab STARlet : 1000ul Channel Tip Eject (Single Step) - complete;
> channel 1: teachingNeedleBlock, 1 > channel 2: teachingNeedleBlock, 2
Critical Implementation Requirements
1. Command Translation Table
Hamilton Command |
Function |
Example |
PyLabRobot Equivalent |
C0RI |
Read instrument info |
C0RIid0103 → er00/00si2017-01-31snXXXX |
device.get_serial_number() |
P1RP /P2RP |
Read pressure |
P1RPid0214 → rp+4082 |
channel.read_pressure() |
P1DS /P2DS |
Dosing steps |
P1DSid0213ds01250dt1 → er00 |
channel.move_plunger(steps=1250, direction='dispense') |
PXBPbp0 |
Baseline pressure |
PXBPid0212bp0 → er00 |
channel.calibrate_pressure_baseline() |
PXAA |
Initialize position |
PXAAid0208dp20000 → er00 |
channel.initialize(position=20000) |
PXAL |
cLLD initialization |
PXALid0245 → er00 |
channel.initialize_liquid_detection() |
PXJM |
Measurement mode |
PXJMid0246 → er00/00 |
channel.set_measurement_mode() |
PXZL |
Z-level detection |
PXZLid0247 → er70 |
channel.detect_level() |
PXRN |
Needle contact |
PXRNid0248 → rn0 |
channel.check_needle_contact() |
PXKZ |
Capacitance measure |
PXKZid0251 → er00/00 |
channel.measure_capacitance() |
2. Pressure Testing Specifications
- Positive Pressure: Target ~4000 Pa, tolerance ±200 Pa
- Negative Pressure: Target ~-4000 Pa, tolerance ±200 Pa
- Leak Test: <1500 Pa loss over 20 seconds
- Stabilization: 2-3 second delays between measurements
3. Error Handling Requirements
- Pressure range validation (max ±10000 Pa)
- Teaching needle presence verification
- Timeout handling for pressure stabilization
- User abort capability with safe recovery
Hamilton Error Handling Examples:
# Normal successful response
< P1RPid0214
> P1RPid0214rp+4082 # Success: pressure value returned
# Error response format
< PXZLid0247 # Z-level detection
> PXZLid0247er70 # Error 70: Expected for air detection
# Communication error
< C0RQid0101
> C0RQid0101er99/99 # Error 99: Communication failure
# Timeout handling
< P1DSid0213ds01250dt1
> [no response after timeout] # Requires retry or abort
# USB connection status
! 15:30:56.836 8AF#8000#00: USB Info: CLOSE USB_READ_THREAD
! 15:31:13.392 8AF#8000#00: USB Info: CLOSE USB_READ_THREAD
Error Code Meanings:
er00/00
- Success, no errors
er70
- Expected cLLD error (air detection)
er99/99
- Communication failure
- No response - Timeout, requires recovery
4. Timing Requirements
- Pressure Stabilization: 2-3 seconds between measurements
- Leak Testing: 20-second monitoring periods
- User Timeouts: Configurable prompt timeouts
- System Delays: Hardware-specific settling times
PyLabRobot Implementation Strategy
Phase 1: Core Infrastructure
- Hamilton firmware command abstraction layer
- Device enumeration and configuration system
- Error handling and recovery procedures
- Basic communication protocol implementation
Phase 2: Maintenance Procedures
- User interaction and prompting system
- Teaching needle management
- Pressure testing implementation
- cLLD testing procedures
Phase 3: Validation and Testing
- Test framework development
- Tolerance validation system
- Comprehensive logging and tracing
- Performance verification against Hamilton reference
Implementation Examples
Hamilton Command Interface
class HamiltonSTARlet:
def __init__(self, device_id: str = "XXXX"):
self.device_id = device_id
self.channels = {1: HamiltonChannel(1), 2: HamiltonChannel(2)}
def send_command(self, command: str, timeout: float = 5.0) -> str:
"""Send Hamilton firmware command and return response"""
# Implementation would interface with PyLabRobot backend
response = self._send_raw_command(command, timeout)
return self._parse_response(response)
def get_system_info(self) -> dict:
"""Get system configuration like Hamilton C0RI command"""
response = self.send_command("C0RIid0001")
# Parse: er00/00si2017-01-31snXXXX
return self._parse_system_info(response)
class HamiltonChannel:
def __init__(self, channel_id: int):
self.channel_id = channel_id
def read_pressure(self) -> int:
"""Read pressure like Hamilton P1RP command"""
cmd = f"P{self.channel_id}RPid{self._get_next_id()}"
response = self.send_command(cmd)
# Parse: rp+4082 -> return 4082
return self._parse_pressure(response)
def move_plunger(self, steps: int, direction: str) -> bool:
"""Move plunger like Hamilton P1DS command"""
dt_value = "1" if direction == "dispense" else "0"
cmd = f"P{self.channel_id}DSid{self._get_next_id()}ds{steps:05d}dt{dt_value}"
response = self.send_command(cmd)
return response.startswith("er00")
Pressure Testing Implementation
class PressureTightnessTest:
def __init__(self, device: HamiltonSTARlet):
self.device = device
self.tolerance_pa = 1500 # Max 1500 Pa loss allowed
async def run_tightness_test(self) -> dict:
"""Execute complete pressure tightness test"""
results = {}
# Step 1: Teaching needle pickup
await self._pickup_teaching_needles()
# Step 2: Baseline calibration
await self._calibrate_baseline_pressure()
try:
# Step 3: Positive pressure test
pos_results = await self._test_positive_pressure()
results.update(pos_results)
# Step 4: Negative pressure test
neg_results = await self._test_negative_pressure()
results.update(neg_results)
finally:
# Step 5: Teaching needle ejection
await self._eject_teaching_needles()
return self._validate_results(results)
async def _test_positive_pressure(self) -> dict:
"""Test positive pressure like Hamilton sequence"""
results = {}
for channel_id in [1, 2]:
channel = self.device.channels[channel_id]
# Build pressure: P1DSid0213ds01250dt1
await channel.move_plunger(steps=1250, direction="dispense")
# Read initial pressure: P1RPid0214 -> rp+4082
initial_pressure = await channel.read_pressure()
# Wait 20 seconds for stabilization
await asyncio.sleep(20.0)
# Read final pressure: P1RPid0219 -> rp+4077
final_pressure = await channel.read_pressure()
# Calculate pressure loss
pressure_loss = abs(initial_pressure - final_pressure)
results[f"channel_{channel_id}_positive"] = {
"initial_pressure": initial_pressure,
"final_pressure": final_pressure,
"pressure_loss": pressure_loss,
"pass": pressure_loss < self.tolerance_pa
}
return results
User Interaction System
class MaintenanceUserInterface:
def __init__(self):
self.logger = logging.getLogger(__name__)
async def prompt_maintenance_start(self) -> bool:
"""Replicate Hamilton weekly maintenance start prompt"""
message = (
"Weekly maintenance - deck and tip waste\n\n"
"Do you want to execute the weekly maintenance for deck and waste?"
)
return await self._show_yes_no_dialog(
title="Weekly Maintenance",
message=message,
default="Yes"
)
async def prompt_clean_deck(self) -> bool:
"""Replicate Hamilton deck cleaning prompt"""
message = (
"Open the front cover, clean the deck and press OK to continue.\n"
"(See Operator's Manual for use of cleaning, disinfecting and "
"decontaminating fluids.)"
)
return await self._show_ok_dialog(
title="Weekly maintenance - deck",
message=message
)
async def prompt_teaching_needles(self) -> bool:
"""Confirm teaching needles are installed"""
message = (
"Do you want to execute the 1000μl Channel tightness check?\n\n"
"Make sure that 2 1000μl teaching needles are installed."
)
return await self._show_yes_no_dialog(
title="Weekly Maintenance - Tightness Check 1000μl Channel",
message=message,
default="Yes"
)
Key Challenges and Considerations
1. Hardware Abstraction
- Direct firmware command translation to PyLabRobot APIs
- Pressure sensor calibration and reading
- Teaching needle handling mechanisms
2. Timing Sensitivity
- Pressure stabilization requirements
- Hardware response delays
- Real-time user interaction handling
3. Safety and Validation
- Pressure range limits and safety checks
- Teaching needle presence verification
- System state validation and recovery
4. User Experience
- Clear maintenance instructions and prompts
- Progress indication during long procedures
- Error reporting and troubleshooting guidance
Success Metrics
- Functional Equivalence: All Hamilton maintenance procedures replicated
- Performance: Timing within ±10% of Hamilton reference
- Reliability: >99% successful completion rate
- Safety: Zero hardware damage incidents during testing
- Usability: Clear user guidance and error reporting
Next Steps
- Complete command mapping documentation
- Implement basic device communication layer
- Develop pressure testing procedures
- Create comprehensive test framework
- Validate against Hamilton reference behavior
This analysis provides the foundation for implementing Hamilton STARlet weekly maintenance procedures in PyLabRobot, ensuring functional equivalence while maintaining safety and reliability standards.