when'd i last commit this pos?

This commit is contained in:
Thomas Ales [M S E]
2026-02-09 14:40:34 -06:00
parent fc43fbe4b0
commit 23f6331ba2
94 changed files with 14427 additions and 12178 deletions
+300
View File
@@ -0,0 +1,300 @@
# BBD203 Connection Guide
## Quick Start - Connecting Your BBD203 Controller
This guide explains how to connect your ThorLabs BBD203 motor controller to nueScan using the simplified serial number method.
---
## Step 1: Physical Connection
1. **Connect USB Cable**
- Connect the USB cable from your BBD203 controller to your computer
- Wait for Windows/Linux to recognize the device
- No special drivers needed (uses standard FTDI USB-Serial)
2. **Power On Controller**
- Ensure BBD203 is powered on
- Front panel should be lit
---
## Step 2: Find Serial Number
The serial number is printed on a label on your BBD203 controller.
**Common Locations:**
- Back panel of the controller
- Side panel
- Original packaging
**Format:**
- Usually 8 digits (e.g., `83123456`)
- May include letters (e.g., `83A12345`)
**Example Label:**
```
ThorLabs BBD203
S/N: 83123456
```
---
## Step 3: Connect in nueScan
### Using the GUI
1. **Launch nueScan**
```bash
python -m nuescan
```
2. **Enter Serial Number**
- Locate the "ThorLABS MLS Stage Serial:" field at the top of the window
- Type your serial number (e.g., `83123456`)
3. **Click Connect**
- Click the "Connect" button next to the serial field
- Wait 1-2 seconds for connection
4. **Success!**
- If successful, you'll see a confirmation dialog
- Button changes to "Disconnect"
- All 3 motor channels are now enabled
### Programmatic Connection
```python
from hardware.thorlabs_stage import ThorLabsStage
# Create stage instance
stage = ThorLabsStage(encoder_counts_per_mm=20000)
# Connect by serial number
success = stage.connect('83123456')
if success:
print("Connected! Ready to home axes.")
else:
print("Connection failed.")
```
---
## Troubleshooting
### "Device not found" Error
**Problem:** Connection fails with "Could not find BBD203 with serial number..."
**Solutions:**
1. **Check USB Connection**
- Ensure USB cable is fully inserted
- Try a different USB port
- Try a different USB cable
2. **Verify Serial Number**
- Double-check the serial number on the controller label
- Ensure no typos (0 vs O, 1 vs I, etc.)
3. **List Available Devices**
- The error dialog will show all detected ThorLabs devices
- Check if your device appears with a different serial number
- If no devices shown, check USB connection and drivers
4. **Windows: Check Device Manager**
- Open Device Manager
- Look under "Ports (COM & LPT)"
- Should see "USB Serial Port (COMx)" with FTDI in description
- If device shows with "!" icon, driver issue
5. **Linux: Check Permissions**
```bash
# Check if device is detected
lsusb | grep -i ftdi
# Check serial ports
ls -l /dev/ttyUSB*
# Add user to dialout group (may require logout)
sudo usermod -a -G dialout $USER
```
### Connection Succeeds but No Response
**Problem:** Connection successful but motors don't respond
**Solutions:**
1. **Check Power**
- Verify motors are connected and powered
- Check motor power LEDs on BBD203 front panel
2. **Enable Channels**
- Channels should auto-enable on connection
- Check status indicators in UI
3. **Home Axes**
- Axes may need homing before movement
- Try homing each axis
### Multiple Controllers
**Problem:** You have multiple BBD203 controllers connected
**Solution:**
- Each controller has a unique serial number
- Connect to specific controller by entering its serial number
- Error dialog will show all connected devices
---
## What Happens During Connection
### Automatic Process
When you click "Connect", the following happens automatically:
1. **USB Enumeration**
- Scans all USB ports
- Finds ThorLabs devices (FTDI vendor ID: 0x0403)
- Matches your serial number
2. **Port Assignment**
- Determines the COM port (e.g., COM3, /dev/ttyUSB0)
- Opens serial connection at 115200 baud
3. **Controller Initialization**
- Requests hardware information
- Enables automatic status updates
- Enables all 3 motor channels
4. **Default Configuration**
- Sets velocity: 1.0 mm/s
- Sets acceleration: 5.0 mm/s²
- Starts position monitoring
### Status Updates
After connection:
- Position updates received every ~100ms
- Status bits monitored (homed, moving, errors)
- Move completion notifications enabled
---
## Advanced Configuration
### Custom Encoder Resolution
If you're using a stage with different encoder resolution:
```python
# Example: Stage with 2,000 counts/mm instead of default 20,000
stage = ThorLabsStage(encoder_counts_per_mm=2000)
stage.connect('83123456')
```
Common resolutions:
- **MLS203**: 20,000 counts/mm (default)
- **DDS220**: 2,000 counts/mm
- **Custom**: Check your stage specifications
### Direct Port Connection (Not Recommended)
If you need to connect to a specific port instead of using serial number:
```python
from hardware.bbd203_driver import BBD203Driver
driver = BBD203Driver()
driver.connect('COM3') # or '/dev/ttyUSB0' on Linux
```
---
## System Requirements
### Operating Systems
- ✅ Windows 7/8/10/11
- ✅ Linux (Ubuntu, Fedora, etc.)
- ✅ macOS (with FTDI driver)
### Dependencies
- Python 3.8+
- PySerial 3.5+
- PyQt6 6.4+
### USB Requirements
- USB 2.0 or higher
- FTDI USB-Serial drivers (usually automatic)
---
## Next Steps
After successful connection:
1. **Home the Axes**
- Required before first movement
- Establishes zero position reference
2. **Test Movement**
- Try small movements to verify operation
- Check position feedback in UI
3. **Configure Scan Parameters**
- Set scan area (X/Y start, delta)
- Set row spacing
- Configure velocity if needed
4. **Begin Scanning**
- All systems should show "Ready"
- Click "Begin Scan" to start
---
## Support
For issues with the BBD203 driver or connection:
1. **Check Debug Output**
- Console window shows connection details
- Look for "INFO:", "DEBUG:", and "ERROR:" messages
2. **Review Driver Documentation**
- `BBD203_DRIVER_README.md` - Complete driver reference
- `BBD203_Communications_Protocol.md` - Protocol details
3. **ThorLabs Support**
- For hardware issues: techsupport@thorlabs.com
- For driver/protocol questions: Review APT documentation
---
## Example Session
```
$ python -m nuescan
# UI appears
# Enter serial: 83123456
# Click Connect
# Console output:
INFO: Connecting to BBD203 with serial number 83123456
DEBUG: Found ThorLabs device - Serial: 83123456, Port: COM3
INFO: Found device 83123456 on port COM3
INFO: Connecting to BBD203 on COM3
INFO: Successfully connected to BBD203 on COM3
INFO: Stage connected and channels enabled
# Success dialog appears
# Button changes to "Disconnect"
# Ready to home and move!
```
---
*For detailed technical information, see `BBD203_DRIVER_README.md`*
@@ -0,0 +1,702 @@
# BBD203 Motor Controller - APT Communications Protocol
## Version 42.1 - Extracted Documentation
This document contains only the information relevant to the **BBD203 3-Channel Benchtop Brushless DC Motor Controller**, extracted from the complete Thorlabs APT Communications Protocol documentation.
---
## Table of Contents
1. [Introduction](#1-introduction)
2. [General Protocol Information](#2-general-protocol-information)
3. [BBD203 Specifications](#3-bbd203-specifications)
4. [Message Format](#4-message-format)
5. [BBD203 Applicable Messages](#5-bbd203-applicable-messages)
6. [Command Examples](#6-command-examples)
7. [Important Notes for BBD203](#7-important-notes-for-bbd203)
---
## 1. Introduction
The BBD203 is a 3-channel benchtop brushless DC motor controller that is part of Thorlabs' APT motion control system. This document describes the communication protocol used to control the BBD203 via USB or RS232 interfaces.
### 1.1 Device Overview
The BBD203 provides independent control of up to three brushless DC motors with the following key features:
- 3 independent motor channels
- USB and RS232 communication interfaces
- Closed-loop position and velocity control
- Encoder feedback support
- Digital I/O for triggering and synchronization
- Compatible with Thorlabs' APT software suite
### 1.2 Related Controllers
The BBD203 shares its protocol with other controllers in the BBD series:
- **BBD201** - 1 Channel Benchtop Brushless DC Motor Driver
- **BBD202** - 2 Channel Benchtop Brushless DC Motor Driver
- **BBD203** - 3 Channel Benchtop Brushless DC Motor Driver
---
## 2. General Protocol Information
### 2.1 Communication Format
All communications with the BBD203 use a binary message protocol. Messages consist of a 6-byte header followed by an optional data packet.
#### Header Structure (6 bytes):
| Byte | Description |
|------|------------|
| 0-1 | Message ID (16-bit, little-endian) |
| 2 | Data length (bytes) or parameter 1 |
| 3 | Data length MSB or parameter 2 |
| 4 | Destination |
| 5 | Source |
#### Destination Byte Values:
| Value | Description |
|-------|------------|
| 0x50 | Generic USB device |
| 0x11 | Rack controller (card slot unit) |
| 0x21 | Bay 1 / Channel 1 |
| 0x22 | Bay 2 / Channel 2 |
| 0x23 | Bay 3 / Channel 3 |
#### Source Byte Values:
| Value | Description |
|-------|------------|
| 0x01 | Host PC |
### 2.2 Channel Addressing
The BBD203 has three motor channels. When addressing specific channels:
- **Channel 1**: Use destination byte `0x21`
- **Channel 2**: Use destination byte `0x22`
- **Channel 3**: Use destination byte `0x23`
- **All channels**: Use destination byte `0x11`
**Important**: Although the BBD203 has 3 channels, each channel operates as an independent single-channel controller. In the data packet's channel identifier field, always use Channel 1 (`0x01 0x00`), and use the destination byte in the header to specify the physical channel.
### 2.3 Data Types
| Type | Size | Description |
|------|------|------------|
| char | 1 byte | 8-bit signed integer |
| short | 2 bytes | 16-bit signed integer (little-endian) |
| long | 4 bytes | 32-bit signed integer (little-endian) |
| word | 2 bytes | 16-bit unsigned integer (little-endian) |
| dword | 4 bytes | 32-bit unsigned integer (little-endian) |
---
## 3. BBD203 Specifications
### 3.1 Encoder and Position Scaling
For BBD203 controllers, position and velocity values are scaled based on encoder counts. The scaling formulas are:
#### Position Scaling:
```
POS_APT = EncCnt × Pos
```
Where:
- `POS_APT` = Position value to send/receive via APT protocol
- `EncCnt` = Encoder counts per unit (stage-specific)
- `Pos` = Position in real units (mm, degrees, etc.)
#### Velocity Scaling:
```
VEL_APT = EncCnt × T × 65536 × Vel
```
Where:
- `VEL_APT` = Velocity value to send/receive via APT protocol
- `T` = 102.4 × 10⁻⁶ seconds (controller sample time)
- `Vel` = Velocity in real units per second
#### Acceleration Scaling:
```
ACC_APT = EncCnt × T² × 65536 × Acc
```
Where:
- `ACC_APT` = Acceleration value to send/receive via APT protocol
- `T²` = (102.4 × 10⁻⁶)²
- `Acc` = Acceleration in real units per second²
### 3.2 Example Scaling Values
For a stage with 20,000 encoder counts per mm:
- Position of 10 mm = 200,000 counts
- Velocity of 1 mm/s = 134,218 APT units
- Acceleration of 1 mm/s² = 13.7 APT units
---
## 4. Message Format
### 4.1 Message Types
Messages are categorized into several types:
- **MOD** - Module control messages (identify, enable/disable)
- **HW** - Hardware information and control
- **MOT** - Motor control messages (move, velocity, position)
- **RACK** - Rack and bay status messages
### 4.2 Message Flow
1. **Command Messages**: Sent from host to controller
2. **Request Messages**: Request data from controller
3. **Get Messages**: Controller response with requested data
4. **Status Messages**: Unsolicited updates from controller
---
## 5. BBD203 Applicable Messages
### 5.1 Module Control Messages
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOD_IDENTIFY | 0x0223 | Host→Device | Flash front panel LEDs to identify unit |
| MGMSG_MOD_SET_CHANENABLESTATE | 0x0210 | Host→Device | Enable/disable a motor channel |
| MGMSG_MOD_REQ_CHANENABLESTATE | 0x0211 | Host→Device | Request channel enable state |
| MGMSG_MOD_GET_CHANENABLESTATE | 0x0212 | Device→Host | Get channel enable state response |
### 5.2 Hardware Control Messages
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_HW_DISCONNECT | 0x0002 | Host→Device | Disconnect from USB bus |
| MGMSG_HW_RESPONSE | 0x0080 | Device→Host | Response/error message |
| MGMSG_HW_RICHRESPONSE | 0x0081 | Device→Host | Detailed response with error info |
| MGMSG_HW_START_UPDATEMSGS | 0x0011 | Host→Device | Start automatic status updates |
| MGMSG_HW_STOP_UPDATEMSGS | 0x0012 | Host→Device | Stop automatic status updates |
| MGMSG_HW_REQ_INFO | 0x0005 | Host→Device | Request hardware information |
| MGMSG_HW_GET_INFO | 0x0006 | Device→Host | Hardware information response |
### 5.3 Rack Status Messages
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_RACK_REQ_BAYUSED | 0x0060 | Host→Device | Request which bays are occupied |
| MGMSG_RACK_GET_BAYUSED | 0x0061 | Device→Host | Bay occupation status |
| MGMSG_RACK_REQ_STATUSBITS | 0x0226 | Host→Device | Request rack status bits |
| MGMSG_RACK_GET_STATUSBITS | 0x0227 | Device→Host | Rack status bits response |
| MGMSG_RACK_SET_DIGOUTPUTS | 0x0228 | Host→Device | Set digital outputs |
| MGMSG_RACK_REQ_DIGOUTPUTS | 0x0229 | Host→Device | Request digital output states |
| MGMSG_RACK_GET_DIGOUTPUTS | 0x0230 | Device→Host | Digital output states |
### 5.4 Motor Control Messages - Basic
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOT_SET_POSCOUNTER | 0x0410 | Host→Device | Set position counter value |
| MGMSG_MOT_REQ_POSCOUNTER | 0x0411 | Host→Device | Request position counter |
| MGMSG_MOT_GET_POSCOUNTER | 0x0412 | Device→Host | Position counter value |
| MGMSG_MOT_SET_ENCCOUNTER | 0x0409 | Host→Device | Set encoder counter value |
| MGMSG_MOT_REQ_ENCCOUNTER | 0x040A | Host→Device | Request encoder counter |
| MGMSG_MOT_GET_ENCCOUNTER | 0x040B | Device→Host | Encoder counter value |
### 5.5 Motor Control Messages - Homing
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOT_SET_HOMEPARAMS | 0x0440 | Host→Device | Set homing parameters |
| MGMSG_MOT_REQ_HOMEPARAMS | 0x0441 | Host→Device | Request homing parameters |
| MGMSG_MOT_GET_HOMEPARAMS | 0x0442 | Device→Host | Homing parameters |
| MGMSG_MOT_MOVE_HOME | 0x0443 | Host→Device | Start homing sequence |
| MGMSG_MOT_MOVE_HOMED | 0x0444 | Device→Host | Homing completed |
### 5.6 Motor Control Messages - Movement
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOT_SET_MOVERELPARAMS | 0x0445 | Host→Device | Set relative move parameters |
| MGMSG_MOT_REQ_MOVERELPARAMS | 0x0446 | Host→Device | Request relative move parameters |
| MGMSG_MOT_GET_MOVERELPARAMS | 0x0447 | Device→Host | Relative move parameters |
| MGMSG_MOT_MOVE_RELATIVE | 0x0448 | Host→Device | Start relative move |
| MGMSG_MOT_SET_MOVEABSPARAMS | 0x0450 | Host→Device | Set absolute move parameters |
| MGMSG_MOT_REQ_MOVEABSPARAMS | 0x0451 | Host→Device | Request absolute move parameters |
| MGMSG_MOT_GET_MOVEABSPARAMS | 0x0452 | Device→Host | Absolute move parameters |
| MGMSG_MOT_MOVE_ABSOLUTE | 0x0453 | Host→Device | Start absolute move |
| MGMSG_MOT_MOVE_COMPLETED | 0x0464 | Device→Host | Move completed notification |
| MGMSG_MOT_MOVE_VELOCITY | 0x0457 | Host→Device | Start velocity move |
| MGMSG_MOT_MOVE_STOP | 0x0465 | Host→Device | Stop any motion |
| MGMSG_MOT_MOVE_STOPPED | 0x0466 | Device→Host | Motion stopped notification |
### 5.7 Motor Control Messages - Velocity Parameters
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOT_SET_VELPARAMS | 0x0413 | Host→Device | Set velocity parameters |
| MGMSG_MOT_REQ_VELPARAMS | 0x0414 | Host→Device | Request velocity parameters |
| MGMSG_MOT_GET_VELPARAMS | 0x0415 | Device→Host | Velocity parameters |
### 5.8 Motor Control Messages - Status
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOT_REQ_STATUSUPDATE | 0x0480 | Host→Device | Request status update |
| MGMSG_MOT_GET_STATUSUPDATE | 0x0481 | Device→Host | Status update |
| MGMSG_MOT_REQ_STATUSBITS | 0x0429 | Host→Device | Request status bits |
| MGMSG_MOT_GET_STATUSBITS | 0x042A | Device→Host | Status bits |
### 5.9 BBD-Specific Control Messages
| Message | Hex Code | Direction | Description |
|---------|----------|-----------|-------------|
| MGMSG_MOT_SET_DCPIDPARAMS | 0x04A0 | Host→Device | Set DC motor PID parameters |
| MGMSG_MOT_REQ_DCPIDPARAMS | 0x04A1 | Host→Device | Request DC motor PID parameters |
| MGMSG_MOT_GET_DCPIDPARAMS | 0x04A2 | Device→Host | DC motor PID parameters |
| MGMSG_MOT_SET_POSITIONLOOPPARAMS | 0x04D7 | Host→Device | Set position loop parameters |
| MGMSG_MOT_REQ_POSITIONLOOPPARAMS | 0x04D8 | Host→Device | Request position loop parameters |
| MGMSG_MOT_GET_POSITIONLOOPPARAMS | 0x04D9 | Device→Host | Position loop parameters |
| MGMSG_MOT_SET_MOTOROUTPUTPARAMS | 0x04DA | Host→Device | Set motor output parameters |
| MGMSG_MOT_REQ_MOTOROUTPUTPARAMS | 0x04DB | Host→Device | Request motor output parameters |
| MGMSG_MOT_GET_MOTOROUTPUTPARAMS | 0x04DC | Device→Host | Motor output parameters |
| MGMSG_MOT_SET_TRACKSETTLEDPARAMS | 0x04E0 | Host→Device | Set tracking/settled parameters |
| MGMSG_MOT_REQ_TRACKSETTLEDPARAMS | 0x04E1 | Host→Device | Request tracking/settled parameters |
| MGMSG_MOT_GET_TRACKSETTLEDPARAMS | 0x04E2 | Device→Host | Tracking/settled parameters |
| MGMSG_MOT_SET_PROFILEMODEPARAMS | 0x04E3 | Host→Device | Set profile mode parameters |
| MGMSG_MOT_REQ_PROFILEMODEPARAMS | 0x04E4 | Host→Device | Request profile mode parameters |
| MGMSG_MOT_GET_PROFILEMODEPARAMS | 0x04E5 | Device→Host | Profile mode parameters |
| MGMSG_MOT_SET_JOYSTICKPARAMS | 0x04E6 | Host→Device | Set joystick parameters |
| MGMSG_MOT_REQ_JOYSTICKPARAMS | 0x04E7 | Host→Device | Request joystick parameters |
| MGMSG_MOT_GET_JOYSTICKPARAMS | 0x04E8 | Device→Host | Joystick parameters |
| MGMSG_MOT_SET_CURRENTLOOPPARAMS | 0x04D4 | Host→Device | Set current loop parameters |
| MGMSG_MOT_REQ_CURRENTLOOPPARAMS | 0x04D5 | Host→Device | Request current loop parameters |
| MGMSG_MOT_GET_CURRENTLOOPPARAMS | 0x04D6 | Device→Host | Current loop parameters |
| MGMSG_MOT_SET_SETTLEDCURRENTLOOPPARAMS | 0x04E9 | Host→Device | Set settled current loop parameters |
| MGMSG_MOT_REQ_SETTLEDCURRENTLOOPPARAMS | 0x04EA | Host→Device | Request settled current loop parameters |
| MGMSG_MOT_GET_SETTLEDCURRENTLOOPPARAMS | 0x04EB | Device→Host | Settled current loop parameters |
| MGMSG_MOT_SET_STAGEAXISPARAMS | 0x04F0 | Host→Device | Set stage axis parameters |
| MGMSG_MOT_REQ_STAGEAXISPARAMS | 0x04F1 | Host→Device | Request stage axis parameters |
| MGMSG_MOT_GET_STAGEAXISPARAMS | 0x04F2 | Device→Host | Stage axis parameters |
| MGMSG_MOT_SET_TRIGGER | 0x0500 | Host→Device | Set trigger configuration |
| MGMSG_MOT_REQ_TRIGGER | 0x0501 | Host→Device | Request trigger configuration |
| MGMSG_MOT_GET_TRIGGER | 0x0502 | Device→Host | Trigger configuration |
---
## 6. Command Examples
### 6.1 Enable Channel 1
To enable channel 1 on the BBD203:
**Command bytes:**
```
TX: 10 02 01 01 21 01
```
**Breakdown:**
- `10 02` - MGMSG_MOD_SET_CHANENABLESTATE
- `01` - Enable channel (0x02 to disable)
- `01` - Channel 1
- `21` - Destination (Channel 1)
- `01` - Source (Host PC)
### 6.2 Home Channel 2
To initiate homing on channel 2:
**Command bytes:**
```
TX: 43 04 01 00 22 01
```
**Breakdown:**
- `43 04` - MGMSG_MOT_MOVE_HOME
- `01` - Channel identifier (always 0x01 for BBD203)
- `00` - Not used
- `22` - Destination (Channel 2)
- `01` - Source (Host PC)
### 6.3 Set Position Counter
To set the position counter for channel 1 to 10.0 mm (assuming 20,000 counts/mm):
**Command bytes:**
```
TX: 10 04 06 00 21 01 01 00 40 0D 03 00
```
**Breakdown:**
- `10 04` - MGMSG_MOT_SET_POSCOUNTER
- `06 00` - 6 byte data packet
- `21` - Destination (Channel 1)
- `01` - Source (Host PC)
- `01 00` - Channel 1 (in data packet)
- `40 0D 03 00` - Position = 200,000 counts (10 mm × 20,000)
### 6.4 Move Absolute
To move channel 3 to absolute position 50 mm:
**Command bytes:**
```
TX: 53 04 06 00 23 01 01 00 A0 86 01 00
```
**Breakdown:**
- `53 04` - MGMSG_MOT_MOVE_ABSOLUTE
- `06 00` - 6 byte data packet
- `23` - Destination (Channel 3)
- `01` - Source (Host PC)
- `01 00` - Channel 1 (in data packet, always 0x01 0x00)
- `A0 86 01 00` - Position = 1,000,000 counts (50 mm × 20,000)
### 6.5 Set Velocity Parameters
To set velocity parameters for channel 2 (max velocity = 5 mm/s, acceleration = 10 mm/s²):
**Command bytes:**
```
TX: 13 04 0E 00 22 01 01 00 00 00 8A 44 0A 00 89 00 00 00
```
**Breakdown:**
- `13 04` - MGMSG_MOT_SET_VELPARAMS
- `0E 00` - 14 byte data packet
- `22` - Destination (Channel 2)
- `01` - Source (Host PC)
- `01 00` - Channel 1 (in data packet)
- `00 00` - Min velocity (usually 0)
- `8A 44 0A 00` - Max velocity = 671,090 APT units (5 mm/s)
- `89 00 00 00` - Acceleration = 137 APT units (10 mm/s²)
### 6.6 Stop Motion
To immediately stop motion on all channels:
**Command bytes:**
```
TX: 65 04 01 01 11 01
```
**Breakdown:**
- `65 04` - MGMSG_MOT_MOVE_STOP
- `01` - Channel identifier
- `01` - Stop mode (0x01 = immediate, 0x02 = profiled)
- `11` - Destination (All channels)
- `01` - Source (Host PC)
### 6.7 Request Status Update
To request a status update from channel 1:
**Command bytes:**
```
TX: 80 04 01 00 21 01
```
**Breakdown:**
- `80 04` - MGMSG_MOT_REQ_STATUSUPDATE
- `01` - Channel identifier
- `00` - Not used
- `21` - Destination (Channel 1)
- `01` - Source (Host PC)
**Response format (20 bytes):**
```
RX: 81 04 14 00 01 00 [Channel] [Position-4bytes] [EncCount-4bytes] [StatusBits-4bytes]
```
### 6.8 Set Position Loop Parameters
To set position loop PID parameters for channel 1:
**Command bytes:**
```
TX: D7 04 1C 00 21 01 01 00 41 00 AF 00 80 38 01 00 [12 more bytes...]
```
**Data packet structure:**
- Bytes 0-1: Channel (0x01 0x00)
- Bytes 2-3: Proportional gain
- Bytes 4-5: Integral gain
- Bytes 6-9: Integral limit
- Bytes 10-13: Derivative gain
- Bytes 14-15: Derivative time
- Bytes 16-17: Loop rate
- Bytes 18-19: Output gain
- Bytes 20-23: Velocity feedforward gain
- Bytes 24-25: Acceleration feedforward gain
- Bytes 26-27: Position error limit
---
## 7. Important Notes for BBD203
### 7.1 Digital Output Configuration
On the BBD203, the digital output and trigger output share a common pin. Before using the digital output functionality, the trigger functionality must be disabled by calling the `MGMSG_MOT_SET_TRIGGER` message with appropriate parameters.
**To disable trigger and enable digital output:**
```
TX: 00 05 06 00 21 01 01 00 00 00 00 00
```
### 7.2 Multi-Channel Operation
Although the BBD203 has three channels, each channel operates as an independent single-channel controller. Important points:
- Always use Channel 1 (`0x01 0x00`) in the channel identifier field of data packets
- Use the destination byte (`0x21`, `0x22`, or `0x23`) in the header to specify the physical channel
- Each channel maintains its own parameters and status independently
### 7.3 Encoder Scaling
All position values must be scaled according to the encoder counts per unit of your specific motor and stage combination. Common encoder resolutions:
| Stage Type | Encoder Counts/mm | Notes |
|------------|-------------------|-------|
| MLS203 | 20,000 | Standard linear stage |
| DDS220 | 2,000 | Direct drive stage |
| Custom | Varies | Check motor specification |
### 7.4 Status Updates
After connecting to the BBD203, it is important to:
1. Call `MGMSG_HW_START_UPDATEMSGS` to enable automatic status updates
2. This ensures move completed and other status messages are received properly
3. Status updates can be disabled with `MGMSG_HW_STOP_UPDATEMSGS` when not needed
**Enable status updates:**
```
TX: 11 00 00 00 11 01
```
### 7.5 Error Handling
The BBD203 returns error messages via `MGMSG_HW_RESPONSE` (0x0080) or `MGMSG_HW_RICHRESPONSE` (0x0081). Common error conditions:
| Error | Description |
|-------|------------|
| Over current | Motor drawing excessive current |
| Following error | Position error exceeds limit |
| Limit switch | Hardware limit reached |
| Not homed | Attempting move before homing |
### 7.6 Trigger Configuration
The BBD203 supports hardware triggering for synchronized motion. Trigger modes:
| Mode | Value | Description |
|------|-------|------------|
| Disabled | 0x00 | No triggering |
| In/Out Relative Move | 0x01 | Trigger initiates relative move |
| In/Out Absolute Move | 0x02 | Trigger initiates absolute move |
| In/Out Home | 0x03 | Trigger initiates homing |
| In/Out Stop | 0x04 | Trigger stops motion |
| Out Only | 0x10 | Generate trigger output on move |
| Out Position | 0x11 | Trigger at specific position |
### 7.7 Profile Modes
The BBD203 supports different motion profile modes:
| Mode | Value | Description |
|------|-------|------------|
| Trapezoidal | 0x00 | Linear acceleration/deceleration |
| S-Curve | 0x02 | Smooth acceleration with jerk limiting |
### 7.8 Communication Best Practices
1. **Initialization Sequence:**
- Send `MGMSG_HW_REQ_INFO` to verify connection
- Enable required channels with `MGMSG_MOD_SET_CHANENABLESTATE`
- Start update messages with `MGMSG_HW_START_UPDATEMSGS`
- Home axes if required
2. **Movement Sequence:**
- Set velocity/acceleration parameters
- Clear any errors
- Send move command
- Wait for move completed message
3. **Shutdown Sequence:**
- Stop any motion with `MGMSG_MOT_MOVE_STOP`
- Disable channels if needed
- Send `MGMSG_HW_DISCONNECT` before closing port
---
## Additional Information
This document contains only the essential information for controlling the BBD203 motor controller. For complete protocol details, advanced features, and other Thorlabs motion control products, please refer to the full APT Communications Protocol documentation.
### Contact Information
**Thorlabs, Inc.**
- Website: www.thorlabs.com
- Technical Support: techsupport@thorlabs.com
---
*Document generated from Thorlabs APT Communications Protocol v42.1*
## Table of Contents
1. [Introduction](#1-introduction)
2. [General Protocol Information](#2-general-protocol-information)
3. [BBD203 Specifications](#3-bbd203-specifications)
4. [Message Format](#4-message-format)
5. [BBD203 Applicable Messages](#5-bbd203-applicable-messages)
6. [Command Examples](#6-command-examples)
7. [Important Notes](#7-important-notes)
---
## 1. Introduction
The BBD203 is a 3-channel benchtop brushless DC motor controller that is part of Thorlabs' APT motion control system. This document describes the communication protocol used to control the BBD203 via USB or RS232 interfaces.
### 1.1 Device Overview
The BBD203 provides independent control of up to three brushless DC motors with the following key features:
- 3 independent motor channels
- USB and RS232 communication interfaces
- Closed-loop position and velocity control
- Encoder feedback support
- Digital I/O for triggering and synchronization
- Compatible with Thorlabs' APT software suite
### 1.2 Device Information
- **Product Name**: BBD203 - 3 Channel Benchtop Brushless DC Motor Driver
- **Protocol Version**: 42.1
- **Communication**: Binary message protocol over USB/RS232
---
## 2. General Protocol Information
### 2.1 Communication Format
All communications with the BBD203 use a binary message protocol. Messages consist of a 6-byte header followed by an optional data packet.
#### Header Structure
| Byte | Description |
|------|-------------|
| 0-1 | Message ID (16-bit, little-endian) |
| 2 | Data length (bytes) or parameter 1 |
| 3 | Data length MSB or parameter 2 |
| 4 | Destination |
| 5 | Source |
#### Destination Byte Values
| Value | Description |
|-------|-------------|
| 0x50 | USB interface |
| 0x11 | All channels (unit) |
| 0x21 | Channel 1 (Bay 1) |
| 0x22 | Channel 2 (Bay 2) |
| 0x23 | Channel 3 (Bay 3) |
#### Source Byte Values
| Value | Description |
|-------|-------------|
| 0x01 | Host PC |
### 2.2 Channel Addressing
The BBD203 has three motor channels. When addressing specific channels:
- **Channel 1**: Use destination byte `0x21`
- **Channel 2**: Use destination byte `0x22`
- **Channel 3**: Use destination byte `0x23`
- **All channels**: Use destination byte `0x11`
**Important**: Although the BBD203 has three channels, each channel operates as an independent single-channel controller. In the data packet's channel identifier field, always use Channel 1 (0x01), and specify the physical channel using the destination byte in the header.
### 2.3 Data Types
| Type | Size | Description |
|------|------|-------------|
| char | 1 byte | 8-bit signed integer |
| short | 2 bytes | 16-bit signed integer |
| long | 4 bytes | 32-bit signed integer |
| word | 2 bytes | 16-bit unsigned integer |
| dword | 4 bytes | 32-bit unsigned integer |
All multi-byte values are transmitted in little-endian format.
---
## 3. BBD203 Specifications
### 3.1 Encoder and Position Scaling
For BBD203 controllers, position and velocity values are scaled based on encoder counts. The scaling depends on your specific motor and stage combination.
#### Position Scaling
```
POSAPT = EncCnt × Pos
```
Where:
- `POSAPT` = Position value to send/receive via APT protocol
- `EncCnt` = Encoder counts per unit (e.g., counts per mm)
- `Pos` = Actual position in real units
#### Velocity Scaling
```
VELAPT = EncCnt × T × 65536 × Vel
```
Where:
- `VELAPT` = Velocity value to send/receive via APT protocol
- `EncCnt` = Encoder counts per unit
- `T` = 102.4 × 10⁻⁶
- `Vel` = Actual velocity in real units per second
#### Acceleration Scaling
```
ACCAPT = EncCnt × T² × 65536 × Acc
```
Where:
- `ACCAPT` = Acceleration value to send/receive via APT protocol
- `EncCnt` = Encoder counts per unit
- `T` = 102.4 × 10⁻⁶
- `Acc` = Actual acceleration in real units per second²
### 3.2 Example Scaling Values
For a stage with 20,000 encoder counts per mm:
| Parameter | Real Value | APT Value |
|-----------|------------|-----------|
| Position | 10 mm | 200,000 |
| Position | 50 mm | 1,000,000 |
| Velocity | 1 mm/s | 134.218 |
| Acceleration | 1 mm/s² | 0.0137 |
---
## 4. Message Format
### 4.1 Message Categories
Messages are organized into the following categories:
- **MOD** - Module control messages (identify, enable/disable)
- **HW** - Hardware information and control
- **MOT** - Motor control messages (move, velocity, position)
- **RACK** - Rack and bay status messages
### 4.2 Message Direction
- **SET** - Host sends command with parameters to controller
- **REQ
+390
View File
@@ -0,0 +1,390 @@
# ThorLabs BBD203 Motor Controller Driver
## Overview
This directory contains a complete implementation of the ThorLabs BBD203 3-channel benchtop brushless DC motor controller driver. The driver implements the full APT (Advanced Positioning Technology) binary communications protocol as specified in the BBD203_Communications_Protocol.md document.
## Architecture
The BBD203 driver is split into three layers:
### 1. Protocol Layer (`bbd203_protocol.py`)
Low-level protocol implementation that handles:
- Binary message construction and parsing
- APT protocol message IDs and structures
- Unit conversions (mm ↔ encoder counts, velocity/acceleration scaling)
- Status bit definitions
**Key Classes:**
- `MessageID`: Enumeration of all APT message IDs
- `APTMessage`: Message builder and parser for binary protocol
- `APTProtocol`: High-level protocol interface with unit conversions
### 2. Driver Layer (`bbd203_driver.py`)
Complete driver implementation providing:
- Serial communication with automatic message reception thread
- 3-channel management (independent motor control)
- Blocking and non-blocking move operations
- Status monitoring with automatic updates
- Event callbacks for move/home completion
- Thread-safe operation
**Key Classes:**
- `BBD203Channel`: Represents state of a single motor channel
- `BBD203Driver`: Main driver class for controller communication
### 3. Stage Interface Layer (`thorlabs_stage.py`)
Application-specific wrapper that:
- Maps 3 motor channels to X/Y/Z axes
- Provides simplified API for stage control
- Integrates with the nueScan application
- Maintains compatibility with existing UI
**Channel Mapping:**
- Channel 1 → X-axis
- Channel 2 → Y-axis
- Channel 3 → Z-axis (optional)
## Features
### Communication
- Binary APT protocol over USB/RS232
- Baud rate: 115200 (configurable)
- Automatic message reception in background thread
- Command/response handling with proper timeout
### Motion Control
- Absolute positioning
- Relative moves
- Velocity control
- Immediate and profiled stops
- Configurable acceleration
### Position Feedback
- Real-time position updates (encoder counts)
- Position in mm (with configurable scaling)
- Status bit monitoring (homing, moving, errors, etc.)
### Homing
- Individual axis homing
- All-axes homing
- Blocking or non-blocking operation
- Completion callbacks
### Safety
- Interlock checking before moves
- Error detection and reporting
- Motion error monitoring
- Limit switch status
## Usage
### Connection Methods
The driver supports two connection methods:
#### Method 1: Connect by Serial Number (Recommended)
Similar to ThorLabs Kinesis library - automatically finds the USB device:
```python
from hardware.bbd203_driver import BBD203Driver
# Create driver instance
driver = BBD203Driver(encoder_counts_per_mm=20000)
# List available ThorLabs devices
devices = driver.list_thorlabs_devices()
for device in devices:
print(f"Serial: {device['serial']}, Port: {device['port']}")
# Connect by serial number (auto-finds the port)
driver.connect_by_serial('83123456') # Serial printed on controller
```
#### Method 2: Connect by Port Name
Direct connection to a specific port:
```python
# Connect to specific port
driver.connect('/dev/ttyUSB0') # or 'COM3' on Windows
```
### Basic Movement
```python
# Enable all channels
driver.enable_channel(1, True) # X-axis
driver.enable_channel(2, True) # Y-axis
driver.enable_channel(3, True) # Z-axis
# Home all channels (blocking)
driver.home_all_channels(wait=True, timeout=60)
# Set velocity parameters
driver.set_velocity_params(channel=1, max_vel_mm_s=5.0, accel_mm_s2=10.0)
# Move to absolute position (non-blocking)
driver.move_absolute(channel=1, position_mm=10.0, wait=False)
# Move to absolute position (blocking)
driver.move_absolute(channel=2, position_mm=25.0, wait=True, timeout=30)
# Move relative
driver.move_relative(channel=1, distance_mm=-5.0, wait=True)
# Stop motion
driver.stop(channel=1, immediate=True)
# Get position
pos = driver.get_position(channel=1)
print(f"Position: {pos} mm")
# Get detailed status
status = driver.get_channel_status(channel=1)
print(f"Enabled: {status['enabled']}")
print(f"Homed: {status['homed']}")
print(f"Moving: {status['moving']}")
# Disconnect
driver.disconnect()
```
### Using the Stage Interface
```python
from hardware.thorlabs_stage import ThorLabsStage
# Create stage controller
stage = ThorLabsStage(encoder_counts_per_mm=20000)
# List available devices
devices = stage.list_devices()
for device in devices:
print(f"BBD203 Serial: {device['serial']}")
# Connect by serial number (automatically enables all channels)
stage.connect('83123456') # Serial number from controller label
# Home all axes
stage.home_all_axes(wait=True)
# Move to position
stage.move_absolute(x=10.0, y=20.0, wait=True)
# Move relative
stage.move_relative(dx=5.0, dy=-2.5, wait=True)
# Get position
pos = stage.get_position()
print(f"X: {pos['x']} mm, Y: {pos['y']} mm, Z: {pos['z']} mm")
# Check status
status = stage.get_status()
print(f"Ready: {status['ready']}")
print(f"X Homed: {status['x_homed']}")
# Disconnect
stage.disconnect()
```
### Event Callbacks
```python
# Define callback function
def on_move_complete(channel):
print(f"Channel {channel} move completed!")
# Register callback
driver.register_move_complete_callback(1, on_move_complete)
# Start non-blocking move - callback will be called when complete
driver.move_absolute(channel=1, position_mm=50.0, wait=False)
```
## Configuration
### Encoder Scaling
The encoder resolution must be configured to match your specific motor/stage combination:
```python
# Example: MLS203 stage with 20,000 counts/mm
driver = BBD203Driver(encoder_counts_per_mm=20000)
# Example: Custom stage with 2,000 counts/mm
driver = BBD203Driver(encoder_counts_per_mm=2000)
```
Common encoder resolutions:
- **MLS203**: 20,000 counts/mm
- **DDS220**: 2,000 counts/mm
- **Custom**: Varies (check motor specifications)
### Velocity and Acceleration
Velocity and acceleration use the APT scaling formulas:
```
VEL_APT = EncCnt × 102.4e-6 × 65536 × Vel
ACC_APT = EncCnt × (102.4e-6)² × 65536 × Acc
```
The driver handles these conversions automatically:
```python
# Set velocity to 5 mm/s, acceleration to 10 mm/s²
driver.set_velocity_params(
channel=1,
max_vel_mm_s=5.0,
accel_mm_s2=10.0
)
```
## Integration with nueScan
The driver is integrated into nueScan through the `ThorLabsStage` wrapper class. The connection is simplified using serial number auto-detection:
### Connecting in the UI
1. **Find Serial Number**: Look at the label on your BBD203 controller (e.g., `83123456`)
2. **Enter Serial**: Type the serial number in the "ThorLABS MLS Stage Serial" field
3. **Connect**: Click "Connect" button
- Driver automatically finds the USB device
- All 3 channels are enabled
- Status updates begin
4. **Ready**: The controller is now ready to home and move axes
### Connection Process
When you click "Connect":
- The driver scans all USB ports for ThorLabs devices (FTDI VID: 0x0403)
- Finds the device matching your serial number
- Automatically uses the correct COM port
- Enables all channels (X/Y/Z axes)
- Sets default velocity parameters
### Troubleshooting Connection
If connection fails, a dialog will show:
- The serial number you entered
- List of all detected ThorLabs devices with their serial numbers
- Helps you identify the correct serial to use
## Protocol Details
### Message Structure
All APT messages consist of:
- 6-byte header (message ID, length, destination, source)
- Optional data packet (variable length)
### Destination Addressing
- `0x21`: Channel 1 (X-axis)
- `0x22`: Channel 2 (Y-axis)
- `0x23`: Channel 3 (Z-axis)
- `0x11`: All channels
- `0x50`: USB interface
### Status Bits
Key status bits monitored by the driver:
| Bit | Mask | Meaning |
|-----|------|---------|
| HOMING | 0x00000200 | Homing in progress |
| HOMED | 0x00000400 | Axis has been homed |
| TRACKING | 0x00001000 | Following target position |
| SETTLED | 0x00002000 | Position settled |
| MOTION_ERROR | 0x00004000 | Following error exceeded |
| MOTOR_ENABLED | 0x80000000 | Motor drive enabled |
| IN_MOTION_FORWARD | 0x00000010 | Moving forward |
| IN_MOTION_REVERSE | 0x00000020 | Moving reverse |
## Troubleshooting
### Connection Issues
**Problem:** Cannot connect to controller
**Solutions:**
- Verify COM port name is correct (`ThorLabsStage.list_available_ports()`)
- Check USB cable connection
- Verify no other software has the port open
- Try different baud rate (default: 115200)
- Check device permissions on Linux
### Homing Fails
**Problem:** Homing timeout or never completes
**Solutions:**
- Increase homing timeout parameter
- Check limit switches are functioning
- Verify motor is enabled
- Check for mechanical obstructions
- Review homing parameters (direction, velocity)
### Position Errors
**Problem:** Reported position doesn't match reality
**Solutions:**
- Verify `encoder_counts_per_mm` setting matches your stage
- Check encoder connections
- Reset position counter if needed: `driver.cmd_set_position_counter()`
- Verify stage is homed before moves
### Communication Errors
**Problem:** Commands not acknowledged or responses missing
**Solutions:**
- Increase serial timeout
- Check for message buffer overflow
- Verify automatic status updates are enabled
- Add delays between rapid commands
## Debug Output
The driver provides extensive debug output:
```
INFO: Messages about successful operations
DEBUG: Detailed command/response information
ERROR: Error conditions and failures
WARNING: Potential issues
```
Enable Python logging to capture all output:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
```
## Performance Notes
- Message processing runs in separate thread (no blocking)
- Typical command response time: 10-50ms
- Position updates: ~10Hz when status messages enabled
- Move completion detected via asynchronous message
- Thread-safe for concurrent channel operations
## References
- **Protocol Documentation**: `BBD203_Communications_Protocol.md`
- **APT Protocol Version**: 42.1
- **Product Manual**: Available from Thorlabs.com
- **Technical Support**: techsupport@thorlabs.com
## License
Copyright (C) 2025 Thomas Ales
Licensed under GNU General Public License v2.0
+161
View File
@@ -0,0 +1,161 @@
# Genesis SLM MX 532 Laser Control Application
## Overview
This application provides comprehensive control of the Genesis SLM MX 532 laser over serial port using the NXP I2C-over-serial protocol. It features a professional PyQt6 GUI with multiple tabs for basic controls, monitoring, advanced operations, and configuration.
## Features
### Tab 1: Basic Controls
- **Current Control**: Slider and numeric input for laser current (0-1023) with percentage display
- **Power Control**: Slider for power command setting
- **Digital Controls**: Toggle buttons for:
- Shutter (Open/Closed)
- Keyswitch (On/Off)
- Remote Enable (On/Off)
- Analog Input Enable (On/Off)
- Current Mode (On/Off)
- **Emergency Stop**: Red button that immediately closes shutter, sets current to 0, and disables keyswitch
### Tab 2: Monitoring
- Real-time sensor readings with auto-refresh capability (configurable interval)
- Displays:
- Actual current reading from ADC
- Interlock status (visual color-coded indicator)
- LDD (Laser Diode Driver) enable status
- Power supply glue input/output status
- Head DIO status
- Laser information display (model and wavelength)
### Tab 3: Advanced
- **Raw I2C Packet Sender**: Send custom I2C commands with hex input
- **Packet Capture Log**: Real-time log of all transmitted and received packets with timestamps
- Useful for debugging and development
### Tab 4: Configuration
- Serial port selection with auto-detection
- Baud rate configuration (default: 9600)
- Connect/Disconnect control
- DTR/RTS control line settings
- About section with protocol information
## Safety Features
1. **All controls disabled until connected** - Prevents accidental commands
2. **Shutter confirmation dialog** - Warns if opening shutter with current > 0
3. **Emergency stop** - Always enabled, immediately enters safe state
4. **Auto-safe state on disconnect** - Laser enters safe state when disconnecting
5. **Settings persistence** - Window geometry and last settings are saved
## Protocol Details
The application uses the NXP I2C-over-serial protocol with this packet format:
**Write packet:**
```
[0x53] [I2C_ADDR] [LENGTH] [COMMAND_BYTES] [DATA_BYTES] [0x50]
```
**Read packet:**
```
[0x53] [ADDR_WRITE] [CMD_LEN] [CMD] [0x53] [ADDR_READ] [DATA_LEN] [0x50]
```
### I2C Devices
- **X9119** (0x52/0x53) - Digital potentiometer for current control
- **PCA9555** (various addresses) - I/O expanders for digital control
- **ADS7828** (0x90/0x91) - ADC for sensor readings
- **AD5254** (0x58) - Digital potentiometer for limits
- **M24C64** (0xa4/0xa5) - EEPROM for configuration storage
## Installation
1. Ensure dependencies are installed:
```bash
pip install -r requirements.txt
```
2. Connect the laser to your computer via serial port (typically /dev/ttyUSB0 on Linux)
3. Run the application:
```bash
python genesis_laser_control.py
```
## Usage
1. **Launch the application**
```bash
python genesis_laser_control.py
```
2. **Connect to the laser:**
- Go to the "Configuration" tab
- Select the correct serial port from the dropdown
- Verify baud rate is set to 9600 (default)
- Click "Connect"
3. **Basic operation:**
- Enable remote control using the "Remote: DISABLED" button
- Enable keyswitch if required
- Adjust current using the slider
- Open shutter when ready (confirmation dialog will appear if current > 0)
4. **Monitoring:**
- Go to the "Monitoring" tab
- Enable "Auto-refresh" to continuously update readings
- Adjust refresh interval as needed (default: 500ms)
5. **Emergency stop:**
- Click the red "EMERGENCY STOP" button on any tab
- This immediately closes shutter, sets current to 0, and disables keyswitch
6. **Disconnecting:**
- Click "Disconnect" in the Configuration tab
- Laser will automatically enter safe state before disconnecting
## Troubleshooting
### Connection Issues
- Verify serial port permissions: `sudo usermod -a -G dialout $USER` (logout and login)
- Check cable connection and power
- Verify correct port in Configuration tab
- Try refreshing ports list
### Communication Errors
- Check packet log in Advanced tab for detailed TX/RX
- Verify baud rate is 9600
- Ensure no other programs are using the serial port
- Try power cycling the laser
### Interlock Faults
- Check physical interlock connections
- Verify interlock status in Monitoring tab
- Ensure all safety covers are in place
## Code Structure
The application is organized into several classes:
- **SerialComm**: Low-level serial port communication
- **I2CProtocol**: NXP packet construction and parsing
- **I2CDevices**: I2C device-specific functions (PCA9555, X9119, ADS7828)
- **LaserControl**: High-level laser control operations
- **MainWindow**: PyQt6 GUI and user interaction
## Development
To modify the application:
1. **Adding new I2C devices**: Extend the `I2CDevices` class
2. **Adding new controls**: Add methods to `LaserControl` and corresponding UI elements
3. **Custom commands**: Use the Raw I2C Packet Sender in the Advanced tab for testing
## License
See LICENSE file for details.
## Support
For issues or questions, please refer to the main project documentation.
+596
View File
@@ -0,0 +1,596 @@
# Helios Laser System Driver
## Overview
This directory contains a complete implementation of the Helios laser system driver. The driver implements the full RS-232 ASCII communications protocol as specified in the helios_comms_protocol.pdf document.
The Helios laser system is a pulsed solid-state laser with:
- Diode-pumped Nd:YAG/Nd:YLF laser head
- Q-switched operation
- Frequency control (16.7 kHz - 125 kHz)
- Current control (0-7000 mA)
- Power monitoring
- Temperature monitoring (4 sensors)
- External trigger capability
## Architecture
The Helios driver is split into three layers:
### 1. Protocol Layer (`helios_protocol.py`)
Low-level protocol implementation that handles:
- ASCII command construction
- Response parsing and validation
- Unit conversions (Hz ↔ ns, °C ↔ m°C)
- Parameter range validation
- Status register decoding
**Key Classes:**
- `HeliosCommand`: Command constants and builders
- `HeliosProtocol`: High-level protocol interface with validation
### 2. Driver Layer (`helios_driver.py`)
Complete driver implementation providing:
- RS-232 serial communication (9600 baud, 8N1)
- Thread-safe command/query operations
- Comprehensive status monitoring
- Temperature monitoring (pump, resonator, q-switch, power stage)
- Power monitoring
- Laser enable/disable control
- Pulse mode control (single, gating, continuous)
- Frequency and current control
**Key Classes:**
- `PulseMode`: Enumeration of pulse modes
- `HeliosStatus`: Status data structure
- `HeliosDriver`: Main driver class for laser communication
### 3. Integration Layer (`hardware/microscope.py`)
Application-specific integration that:
- Combines Helios with Genesis microscope systems
- Provides unified status monitoring
- Integrates with nueScan application
- Implements safety interlocks
- Provides emergency stop functionality
## Features
### Communication
- ASCII-based RS-232 protocol
- Baud rate: 9600, 8 data bits, no parity, 1 stop bit
- Commands terminated with carriage return (CR)
- Thread-safe operation with mutex locking
- Configurable timeout (default: 1 second)
### Laser Control
- Laser enable/disable
- Three pulse modes:
- Single pulse (one pulse per trigger)
- Continuous gating (pulse train while triggered)
- Continuous pulsing (free-running)
- Frequency control (16.7 kHz to 125 kHz)
- Diode current control (0-7000 mA)
### Monitoring
- Real-time power measurement (mW)
- Four temperature sensors:
- Pump diode temperature
- Resonator temperature
- Q-switch temperature
- Power stage temperature
- Operation hours counter
- Comprehensive status register
- Error detection
### Safety
- Temperature monitoring with warnings
- Error status detection
- Laser enable/disable control
- Emergency stop capability
- Integration with system interlocks
## Usage
### Connection Methods
The driver supports connection to a specific COM port:
```python
from hardware.helios_driver import HeliosDriver
# Create driver instance
driver = HeliosDriver(timeout=1.0)
# List available COM ports
ports = HeliosDriver.list_available_ports()
for port in ports:
print(f"Available port: {port}")
# Connect to specific port
driver.connect('COM5') # or '/dev/ttyUSB0' on Linux
# Get device information
print(f"Controller S/N: {driver.get_controller_serial()}")
print(f"Head S/N: {driver.get_head_serial()}")
```
### Basic Laser Control
```python
# Set frequency (in Hz)
driver.set_frequency_hz(10000) # 10 kHz
# Set diode current (in mA)
driver.set_current_ma(500) # 500 mA
# Set pulse mode
from hardware.helios_driver import PulseMode
driver.set_pulse_mode(PulseMode.CONTINUOUS_PULSING)
# Enable laser
driver.set_laser_enable(True)
# Check if laser is enabled
if driver.is_laser_enabled():
print("Laser is ON")
# Disable laser
driver.set_laser_enable(False)
# Disconnect
driver.disconnect()
```
### Monitoring
```python
# Get current power
power_mw = driver.get_power_mw()
print(f"Output power: {power_mw} mW")
# Get temperatures (in Celsius)
temps = driver.get_all_temperatures()
print(f"Pump: {temps['pump_temp_c']:.1f}°C")
print(f"Resonator: {temps['resonator_temp_c']:.1f}°C")
print(f"Q-switch: {temps['qswitch_temp_c']:.1f}°C")
print(f"Power stage: {temps['power_stage_temp_c']:.1f}°C")
# Get operation hours
hours = driver.get_operation_hours()
print(f"Operation time: {hours} hours")
# Get comprehensive status
status = driver.get_status()
print(f"Connected: {status['connected']}")
print(f"Laser enabled: {status['laser_enabled']}")
print(f"Frequency: {status['frequency_hz']} Hz")
print(f"Current: {status['current_ma']} mA")
print(f"Power: {status['power_mw']} mW")
print(f"Has errors: {status['has_errors']}")
```
### Status Updates
```python
# Manually update status from hardware
driver.update_status()
# Status is automatically updated on each get_status() call
status = driver.get_status()
# Access cached values without querying hardware
freq = driver.get_frequency_hz() # Returns last read value
current = driver.get_current_ma() # Returns last read value
```
### Using Through Microscope Controller
The Helios driver is integrated into the application through the `MicroscopeController`:
```python
from hardware.microscope import MicroscopeController
# Create controller
microscope = MicroscopeController()
# Connect Helios
microscope.connect_helios('COM5')
# Apply settings from dialog
settings = {
'com_port': 'COM5',
'frequency_hz': 10000,
'current_ma': 500
}
microscope.apply_helios_settings(settings)
# Enable laser
microscope.helios_enable_laser(True)
# Get status
status = microscope.get_helios_status()
print(f"Helios ready: {status['ready']}")
print(f"Power: {status['power_mw']} mW")
# Disable laser
microscope.helios_enable_laser(False)
# Disconnect
microscope.disconnect_helios()
```
## Configuration
### Frequency Control
The Helios laser operates by setting the pulse period in nanoseconds. The driver automatically converts between frequency (Hz) and period (ns):
```python
# Set frequency in Hz (driver converts to period in ns)
driver.set_frequency_hz(10000) # 10 kHz → 100,000 ns period
# Valid frequency range: 16.7 kHz to 125 kHz
# Valid period range: 8000 ns to 60000 ns
```
Conversion formulas:
```
Period (ns) = 1,000,000,000 / Frequency (Hz)
Frequency (Hz) = 1,000,000,000 / Period (ns)
```
### Current Control
The diode current controls the laser output power:
```python
# Set current in milliamps
driver.set_current_ma(500) # 500 mA
# Valid range: 0 to 7000 mA
```
**Important:** Higher currents produce more power but also more heat. Monitor temperatures when operating at high current.
### Pulse Modes
Three pulse modes are available:
```python
from hardware.helios_driver import PulseMode
# Single pulse mode (one pulse per trigger)
driver.set_pulse_mode(PulseMode.SINGLE_PULSE)
# Continuous gating mode (pulse train while triggered)
driver.set_pulse_mode(PulseMode.CONTINUOUS_GATING)
# Continuous pulsing mode (free-running)
driver.set_pulse_mode(PulseMode.CONTINUOUS_PULSING)
```
**Mode Descriptions:**
- **Single Pulse (LDG=0)**: One pulse generated per external trigger
- **Continuous Gating (LDG=1)**: Pulse train while external trigger is high
- **Continuous Pulsing (LDG=2)**: Free-running at set frequency (default)
### Temperature Monitoring
The driver monitors four temperature sensors:
```python
# Individual temperatures
pump_temp = driver.query_pump_temp_c()
resonator_temp = driver.query_resonator_temp_c()
qswitch_temp = driver.query_qswitch_temp_c()
power_stage_temp = driver.query_power_stage_temp_c()
# All temperatures at once
temps = driver.get_all_temperatures()
```
**Temperature Ranges:**
- Normal operation: < 50°C
- Warning threshold: > 60°C
- Critical threshold: > 70°C
## Integration with nueScan
The Helios driver is integrated into nueScan through the settings dialog and microscope controller.
### Configuration in UI
1. **Open Helios Settings**
- Click "Helios Device Settings" button in main window
2. **Configure Parameters**
- **COM Port**: Select from dropdown (automatically populated)
- **Frequency**: Enter in Hz (16,666 - 125,000 Hz)
- **Current**: Enter in mA (0 - 7000 mA)
3. **Apply Settings**
- Click OK to apply and connect
- Settings are validated before sending to hardware
### Settings Dialog Integration
The `HeliosDialog` class provides:
- Automatic COM port enumeration
- Input validation with range checking
- User-friendly error messages
- Settings persistence
```python
# Dialog usage (called from main window)
from dialogs.helios_dialog import HeliosDialog
dialog = HeliosDialog(parent=self)
if dialog.exec() == QDialog.DialogCode.Accepted:
settings = dialog.get_settings() # Returns None if validation fails
if settings:
self.microscope.apply_helios_settings(settings)
```
### Validation Rules
The dialog validates all inputs before accepting:
**Frequency Validation:**
- Range: 16,666 Hz to 125,000 Hz
- Reason: Hardware period limit of 8000-60000 ns
- Error message shows entered value and valid range
**Current Validation:**
- Range: 0 to 7000 mA
- Reason: Maximum diode current rating
- Error message shows entered value and valid range
**COM Port Validation:**
- Must select valid port from list
- Cannot accept "No ports found" placeholder
- Error message prompts to check connections
## Protocol Details
### Command Format
All commands follow the format:
```
COMMAND [value]<CR>
```
Where:
- `COMMAND` is a 3-letter mnemonic (e.g., LDO, LDF, LDS)
- `[value]` is optional numeric parameter
- `<CR>` is carriage return (0x0D)
### Command Set
| Command | Parameter | Description |
|---------|-----------|-------------|
| LDO | 0/1 | Laser enable (0=off, 1=on) |
| LDG | 0/1/2 | Pulse mode (0=single, 1=gating, 2=continuous) |
| LDF | 8000-60000 | Pulse period in nanoseconds |
| LDS | 0-7000 | Diode current in milliamps |
| LDP | - | Query output power (mW) |
| LDPT | - | Query pump temperature (m°C) |
| LDRT | - | Query resonator temperature (m°C) |
| LDQT | - | Query q-switch temperature (m°C) |
| LDPST | - | Query power stage temperature (m°C) |
| LDSR | - | Query status register |
| LDOH | - | Query operation hours |
| LDCSN | - | Query controller serial number |
| LDHSN | - | Query head serial number |
### Response Format
Responses are numeric values terminated with `<CR>`:
```
12345<CR>
```
**Exception:** Serial numbers are returned as strings:
```
SN12345678<CR>
```
### Status Register
The status register (LDSR) returns a 16-bit value with error flags:
| Bit | Mask | Meaning |
|-----|------|---------|
| 0 | 0x0001 | Pump temperature error |
| 1 | 0x0002 | Resonator temperature error |
| 2 | 0x0004 | Q-switch temperature error |
| 3 | 0x0008 | Power stage temperature error |
| 4 | 0x0010 | Diode current error |
| 5 | 0x0020 | Interlock open |
| 6 | 0x0040 | Over-power condition |
| 7 | 0x0080 | Under-voltage condition |
A status of 0 indicates no errors.
### Set and Verify Pattern
For critical parameters, the driver uses a set-and-verify pattern:
```python
def _set_and_verify(self, set_cmd: bytes, query_cmd: bytes, expected: str) -> bool:
# Send set command
self._serial.write(set_cmd)
time.sleep(0.05) # Allow hardware to process
# Query back the value
self._serial.write(query_cmd)
response = self._read_response()
# Verify it matches
return response.strip() == expected.strip()
```
This ensures commands are executed correctly and hardware state matches software state.
## Troubleshooting
### Connection Issues
**Problem:** Cannot connect to laser
**Solutions:**
- Verify COM port name is correct (`HeliosDriver.list_available_ports()`)
- Check RS-232 cable connection
- Verify laser controller is powered on
- Try different COM port
- Check device permissions on Linux (`sudo usermod -a -G dialout $USER`)
### Communication Errors
**Problem:** Commands fail or no response
**Solutions:**
- Verify baud rate is 9600 (default)
- Check cable for proper null-modem configuration if needed
- Increase timeout: `driver = HeliosDriver(timeout=2.0)`
- Check for CR line termination (0x0D)
- Verify no other software has port open
### Frequency/Current Not Updating
**Problem:** Settings don't change on hardware
**Solutions:**
- Check return value of `set_frequency_hz()` and `set_current_ma()`
- Verify parameters are in valid range
- Check status register for errors: `driver.query_status_register()`
- Ensure laser is not in error state
- Try power cycling the controller
### Temperature Warnings
**Problem:** High temperature readings
**Solutions:**
- Check ventilation around laser head and controller
- Reduce diode current if at maximum
- Allow longer cool-down between operations
- Clean air filters if present
- Check for blocked cooling fans
### Laser Won't Enable
**Problem:** `set_laser_enable(True)` fails or laser stays off
**Solutions:**
- Check interlock connections (bit 5 of status register)
- Verify all interlocks are closed
- Check for error flags in status register
- Ensure parameters (frequency, current) are set
- Check external enable switch if present
- Review safety interlock documentation
### Status Register Errors
**Problem:** Status register shows error bits set
**Solutions:**
- Decode status register: `HeliosProtocol.decode_status_register(value)`
- Address specific error conditions:
- Temperature errors: Improve cooling
- Current error: Reduce current setting
- Interlock open: Check safety connections
- Over-power: Reduce current
- Under-voltage: Check power supply
## Debug Output
The driver provides extensive debug output:
```
INFO: Informational messages about operations
DEBUG: Detailed command/response information
WARNING: Potential issues (high temp, errors)
ERROR: Operation failures
```
Enable Python logging to capture all output:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
```
Example debug output:
```
INFO: Connecting to Helios laser on COM5
DEBUG: Sending command: b'LDCSN\r'
DEBUG: Received response: SN12345678
INFO: Successfully connected to Helios on COM5
DEBUG: Sending command: b'LDF 100000\r'
DEBUG: Verifying frequency setting...
INFO: Frequency set to 10000.0 Hz (period: 100000 ns)
```
## Performance Notes
- Command response time: 50-100ms typical
- Temperature queries: ~100ms per sensor
- Status register query: ~50ms
- All queries are synchronous (blocking)
- Thread-safe for concurrent access (mutex protected)
- Set-and-verify adds ~50ms overhead for reliability
## Safety Considerations
### Laser Safety
- **Class 4 Laser**: Hazardous to eyes and skin
- Always verify laser is disabled before opening beam paths
- Use appropriate laser safety eyewear
- Follow all facility laser safety procedures
- Ensure proper interlock connections
### Thermal Management
- Monitor temperatures during operation
- Allow adequate cool-down between high-power operations
- Ensure proper ventilation
- Do not block cooling vents
### Electrical Safety
- Verify proper grounding
- Use shielded cables for trigger/status connections
- Follow proper ESD procedures when servicing
## Hardware Connections
### Utility Connector (9-pin D-Sub)
The utility connector provides external control:
| Pin | Signal | Description |
|-----|--------|-------------|
| 1 | GND | Ground |
| 2 | Laser Disable | Input: Pull low to disable laser |
| 3 | External Trigger | Input: Rising edge triggers pulse |
| 4 | Status Out | Output: High when ready |
| 5 | GND | Ground |
| 6-9 | NC | Not connected |
Trigger specifications:
- Input: TTL/CMOS compatible
- Minimum pulse width: 100ns
- Maximum frequency: Limited by pulse mode setting
## References
- **Protocol Documentation**: `helios_comms_protocol.pdf`
- **RS-232 Standard**: EIA/TIA-232
- **Integration Guide**: `SETUP.md`
- **Connection Guide**: See main window Helios settings dialog
## License
Copyright (C) 2025 Thomas Ales
Licensed under GNU General Public License v2.0
@@ -0,0 +1,554 @@
# Genesis SLM MX 532 Laser Control - PyQt6 Implementation Guide
## Protocol Overview
The laser uses NXP I2C-over-serial protocol on /dev/ttyUSB0 at 9600 8N1.
### Packet Format
**Write:**
```
[0x53] [I2C_ADDR_WRITE] [LENGTH] [COMMAND_BYTES] [DATA_BYTES] [0x50]
```
**Read:**
```
[0x53] [ADDR_WRITE] [CMD_LEN] [CMD] [0x53] [ADDR_READ] [DATA_LEN] [0x50]
```
## I2C Device Map
| Device | Address | Write | Read | Purpose |
|--------|---------|-------|------|---------|
| X9119 (current) | 0x152 | 0x52 | 0x53 | Laser current control |
| X9119 (photo) | 0x152 | 0x52 | 0x53 | Power feedback control |
| PCA9555 (PS DIO) | 0x140 | 0x40 | 0x41 | Power supply digital I/O |
| PCA9555 (Head DIO) | 0x144 | 0x44 | 0x45 | Head digital I/O |
| PCA9555 (PS Glue In) | 0x148 | 0x48 | 0x49 | Power supply glue logic input |
| PCA9555 (PS Glue Out) | 0x14a | 0x4a | 0x4b | Power supply glue logic output |
| AD5254 (current limit) | 0x158 | 0x58 | 0x59 | Current limit potentiometer |
| AD5254 (photo limit) | 0x158 | 0x58 | 0x59 | Photo limit potentiometer |
| ADS7828 (ADC) | 0x190 | 0x90 | 0x91 | Analog sensor readings |
| M24C64 (EEPROM) | 0x2a4 | 0xa4 | 0xa5 | Configuration storage |
| STM32 (MCU) | 0x2ae | 0xae | 0xaf | Microcontroller |
| DS1682 (hour meter) | 0x1d6 | 0xd6 | 0xd7 | Hour meter |
## Control Commands (from SendI2cCommand)
### SET Commands with Vtable Offsets
| Command | Offset | Device | Details |
|---------|--------|--------|---------|
| PCMD= | 0xf8 | X9119 @ 0x152 | Power command, cmd 0xa0, 0-1023 |
| PMEM= | 0x100 | X9119 @ 0x152 | Power memory (writes to NV) |
| CCMD= | 0xe8 | X9119 @ 0x152 | Current command, cmd 0xa0, 0-1023 |
| SHCMD= | 0x54 | PCA9555 @ 0x14a | Shutter, port 0, bit 0x01 |
| KSWCMD= | 0x60 | PCA9555 @ 0x14a | Keyswitch, port 0, bit 0x20 |
| CMODECMD= | 0x84 | PCA9555 @ 0x14a | Current mode, port 0, bit 0x04 |
| ANACMD= | 0x70 | PCA9555 @ 0x14a | Analog enable, port 0, bit 0x10 |
| REM= | 0x7c | PCA9555 @ 0x14a | Remote enable, port 0, bit 0x08 |
### QUERY Commands with Vtable Offsets
| Command | Offset | Returns |
|---------|--------|---------|
| ?TMAINCMD | 0xdc | Main temperature command |
| ?MAIND | 0xe0 | Main diode current |
| ?PSGLUEIN | 0x44 | PS glue input (PCA9555 @ 0x148) |
| ?PSGLUEOUT | 0x48 | PS glue output (PCA9555 @ 0x14a) |
| ?HEADDIO | 0x4c | Head DIO (PCA9555 @ 0x144) |
| ?WAVELENGTH | 0x14 | Wavelength reading |
| ?LASERMODEL | 0x10 | Model string |
| ?POWERUNITS | 0x110 | Power units string |
## Device-Specific Commands
### X9119 Digital Potentiometer
- **Write wiper:** Command 0xa0, 2 bytes (10-bit value 0-1023)
- **Valid range:** 0-1023
- **Error message:** "Valid code range is 0-1023"
### PCA9555 I/O Expander Registers
| Register | Address | Purpose |
|----------|---------|---------|
| Input Port 0 | 0x00 | Read input state |
| Input Port 1 | 0x01 | Read input state |
| Output Port 0 | 0x02 | Write output state |
| Output Port 1 | 0x03 | Write output state |
| Polarity Inv 0 | 0x04 | Invert polarity |
| Polarity Inv 1 | 0x05 | Invert polarity |
| Config Port 0 | 0x06 | Direction (0=out, 1=in) |
| Config Port 1 | 0x07 | Direction (0=out, 1=in) |
### PCA9555 @ 0x14a (Power Supply Glue Out) Bit Assignments
| Bit | Mask | Function |
|-----|------|----------|
| 0 | 0x01 | Shutter command |
| 2 | 0x04 | Current mode command |
| 3 | 0x08 | Remote enable command |
| 4 | 0x10 | Analog input enable |
| 5 | 0x20 | Keyswitch command |
### ADS7828 ADC Commands
- **Current actual:** Channel 0x84
- **Photo actual (alt):** Channel 0xe4
- **Main temp actual:** Channel 0x94
## Read Strategies (Critical!)
The DLL uses three different read strategies:
### 1. ReadOne - Single Read
```python
def i2c_read_one(addr, cmd, data_len):
"""Single read, no filtering. Use for digital I/O."""
return nxp_read(addr, cmd, 1 if cmd <= 0xFF else 2, data_len)
```
**Use for:** Digital I/O states (PCA9555 reads)
### 2. ReadMatchTwo - Match Until Consistent
```python
def i2c_read_match_two(addr, cmd, data_len, max_attempts=5):
"""Read until 2 values match (up to 5 attempts). Most reliable."""
readings = []
for attempt in range(max_attempts):
value = nxp_read(addr, cmd, 1 if cmd <= 0xFF else 2, data_len)
readings.append(value)
if readings.count(value) >= 2:
return value
time.sleep(0.01)
return readings[0]
```
**Use for:** EEPROM reads, configuration values
### 3. ReadDiscardHighLow - Median Filter
```python
def i2c_read_discard_high_low(addr, cmd, data_len):
"""Read 3 times, return median. Filters noise."""
readings = []
for _ in range(3):
value = nxp_read(addr, cmd, 1 if cmd <= 0xFF else 2, data_len)
readings.append(value)
time.sleep(0.01)
return sorted(readings)[1] # Median
```
**Use for:** ADC readings (current, temperature, voltage)
## Implementation Requirements
### Core Protocol Functions
```python
def nxp_write(i2c_addr_write, cmd, data, data_len):
"""
Build NXP I2C write packet.
Args:
i2c_addr_write: I2C device address (write bit cleared)
cmd: Command byte(s) - int for 1 byte, int for 2 bytes
data: Data value to write
data_len: 1, 2, or 4 bytes
Packet: [0x53][addr][len][cmd...][data...][0x50]
"""
pass
def nxp_read(i2c_addr_write, cmd, cmd_len, data_len):
"""
Build NXP I2C read packet.
Args:
i2c_addr_write: I2C device address (write bit cleared)
cmd: Command byte(s)
cmd_len: 1 or 2 bytes
data_len: Number of bytes to read
Packet: [0x53][addr][cmd_len][cmd...][0x53][addr|0x01][data_len][0x50]
Returns: Bytes read from device
"""
pass
def pca9555_read_port(addr, port):
"""Read PCA9555 port register (0x00-0x07)."""
return i2c_read_one(addr, port, 1)
def pca9555_write_port(addr, port, value):
"""Write PCA9555 port register."""
nxp_write(addr, port, value, 1)
def pca9555_set_bit(addr, port, bitmask, state):
"""
Set or clear specific bit on PCA9555.
Reads current value, modifies bit, writes back.
"""
current = pca9555_read_port(addr, 0x02 + port) # Output port
if state:
new_value = current | bitmask
else:
new_value = current & ~bitmask
pca9555_write_port(addr, 0x02 + port, new_value)
def x9119_write_wiper(addr, value):
"""Set X9119 wiper position (0-1023)."""
if value > 1023:
raise ValueError("X9119 value must be 0-1023")
nxp_write(addr, 0xa0, value, 2)
def ads7828_read(addr, channel):
"""Read ADS7828 ADC channel."""
return i2c_read_discard_high_low(addr, channel, 2)
```
### High-Level Control Functions
```python
def set_current(value):
"""CCMD= - Set laser current (0-1023)."""
x9119_write_wiper(0x52, value)
def set_power_cmd(value):
"""PCMD= - Set power command (0-1023)."""
x9119_write_wiper(0x52, value) # Same device, different context
def set_shutter(state):
"""SHCMD= - Control shutter (True=open, False=closed)."""
pca9555_set_bit(0x4a, 0, 0x01, state)
def set_keyswitch(state):
"""KSWCMD= - Control keyswitch (True=on, False=off)."""
pca9555_set_bit(0x4a, 0, 0x20, state)
def set_remote_enable(state):
"""REM= - Enable remote control (True=enabled)."""
pca9555_set_bit(0x4a, 0, 0x08, state)
def set_analog_enable(state):
"""ANACMD= - Enable analog input (True=enabled)."""
pca9555_set_bit(0x4a, 0, 0x10, state)
def set_current_mode(state):
"""CMODECMD= - Set current mode (True=enabled)."""
pca9555_set_bit(0x4a, 0, 0x04, state)
def get_current_actual():
"""Read actual current from ADC."""
raw = ads7828_read(0x90, 0x84)
# TODO: Scale based on AmpsFullscale
return raw * 0.000244140625 # Placeholder scaling
def get_main_temp():
"""Read main crystal temperature."""
return ads7828_read(0x90, 0x94)
def get_interlock_status():
"""Check interlock status."""
value = pca9555_read_port(0x48, 0x00) # Input port 0
return bool(value & 0x01)
def get_ldd_enable():
"""Check if laser diode driver is enabled."""
value = pca9555_read_port(0x40, 0x00)
return not bool(value & 0x01) # Inverted logic
```
## GUI Structure
### Tab 1: Basic Controls
- **Current Control**
- Slider: 0-1023
- Spinbox: Numeric input
- Label: Percentage (0-100%)
- Button: Set to 0 (quick disable)
- **Power Command**
- Slider: 0-1023
- Spinbox: Numeric input
- **Digital Controls (Toggle Buttons)**
- Shutter (Open/Closed) - Red when open
- Keyswitch (On/Off)
- Remote Enable (On/Off)
- Analog Input (On/Off)
- Current Mode (On/Off)
- **Emergency Stop**
- Large red button
- Always enabled (even when disconnected)
- Actions: Close shutter, current=0, keyswitch=off
- **Status Display**
- Actual current reading (live)
- Connection status indicator
### Tab 2: Monitoring
- **Real-Time Readings** (auto-refresh 500ms)
- Current Actual: [value] A
- Main Temperature: [value] °C
- SHG Temperature: [value] °C
- Interlock Status: [OK/FAULT] (colored indicator)
- LDD Enable: [ON/OFF]
- **System Info** (read once on connect)
- Laser Model: [from EEPROM]
- Wavelength: [value] nm
- Serial Number: [from EEPROM]
- **Controls**
- Refresh Rate slider (100ms - 2000ms)
- Enable/Disable auto-refresh checkbox
- Manual refresh button
### Tab 3: Advanced
- **Raw I2C Interface**
- Device Address (hex): [input]
- Command (hex): [input]
- Data (hex): [input]
- Data Length: [dropdown: 1/2/4 bytes]
- Buttons: [Write] [Read]
- Response display (hex)
- **Packet Monitor**
- Scrolling log of all TX/RX
- Format: `[HH:MM:SS.mmm] TX: 53 52 02 a0 01 00 50`
- Clear button
- Export to file button
- **EEPROM Tools**
- Warning label (red): "Caution: Can damage laser!"
- Read Location (hex): [input]
- Read Length: [input]
- [Read] button
- Data display
- Write protection checkbox
### Tab 4: Configuration
- **Serial Port**
- Dropdown: Auto-detect available ports
- Baud Rate: [dropdown: 9600 default]
- Data Bits: 8 (fixed)
- Parity: None (fixed)
- Stop Bits: 1 (fixed)
- **Connection**
- [Connect]/[Disconnect] button (toggle)
- DTR checkbox
- RTS checkbox
- Status: [Connected/Disconnected]
- **Debug Options**
- Show raw sensor values (before filtering) checkbox
- Packet timeout (ms): [input]
- Retry count: [input]
## Safety Features
### Pre-Flight Checks
Before opening shutter, verify:
1. Remote enable is ON
2. Keyswitch is ON
3. Interlock is OK
4. Current is set to safe value
5. Show confirmation dialog
### Automatic Safety
- All controls disabled until serial connected
- Emergency stop always enabled
- Auto-disable on serial errors
- Auto-close shutter on disconnect
- Set current to 0 on disconnect
- Watchdog timer - if no commands for 5 seconds, safe state
### Error Handling
- Catch all serial exceptions
- Display errors in status bar
- Log errors with timestamps
- Retry logic for critical operations
- Timeout handling (default 1 second)
## Code Organization
### Class Structure
```
LaserControlApp (QMainWindow)
├── SerialComm (QObject)
│ ├── Methods: connect(), disconnect(), write_packet(), read_packet()
│ └── Signals: connected, disconnected, error, data_received
│
├── I2CProtocol (QObject)
│ ├── Low-level: nxp_write(), nxp_read()
│ ├── Device-specific: pca9555_*, x9119_*, ads7828_*
│ └── Read strategies: read_one(), read_match_two(), read_discard_high_low()
│
├── LaserControl (QObject)
│ ├── High-level: set_current(), set_shutter(), etc.
│ ├── Monitoring: get_current_actual(), get_temps(), etc.
│ └── Safety: emergency_stop(), safe_state(), pre_flight_check()
│
└── UI Tabs
├── BasicControlTab (QWidget)
├── MonitoringTab (QWidget)
├── AdvancedTab (QWidget)
└── ConfigTab (QWidget)
```
### Constants (at top of file)
```python
# I2C Addresses (7-bit, shifted)
ADDR_X9119_CURRENT = 0x52
ADDR_PCA9555_PS_DIO = 0x40
ADDR_PCA9555_HEAD_DIO = 0x44
ADDR_PCA9555_PS_GLUE_IN = 0x48
ADDR_PCA9555_PS_GLUE_OUT = 0x4a
ADDR_AD5254 = 0x58
ADDR_ADS7828 = 0x90
ADDR_EEPROM = 0xa4
ADDR_STM32 = 0xae
# PCA9555 Bit Masks (0x14a)
BIT_SHUTTER = 0x01
BIT_CURRENT_MODE = 0x04
BIT_REMOTE_ENABLE = 0x08
BIT_ANALOG_ENABLE = 0x10
BIT_KEYSWITCH = 0x20
# X9119 Commands
CMD_X9119_WRITE_WIPER = 0xa0
# ADS7828 Channels
CHAN_CURRENT_ACTUAL = 0x84
CHAN_PHOTO_ACTUAL = 0xe4
CHAN_MAIN_TEMP = 0x94
# Serial Protocol
NXP_START_BYTE = 0x53
NXP_STOP_BYTE = 0x50
```
## Styling
Use QSS for professional appearance:
```python
STYLESHEET = """
QMainWindow {
background-color: #2b2b2b;
}
QPushButton {
background-color: #3c3c3c;
color: white;
border: 1px solid #555;
padding: 5px;
border-radius: 3px;
}
QPushButton:hover {
background-color: #4c4c4c;
}
QPushButton:pressed {
background-color: #2c2c2c;
}
QPushButton#emergency {
background-color: #cc0000;
font-weight: bold;
font-size: 14pt;
}
QPushButton#emergency:hover {
background-color: #ff0000;
}
QLabel#status_ok {
color: #00ff00;
font-weight: bold;
}
QLabel#status_warning {
color: #ffaa00;
font-weight: bold;
}
QLabel#status_error {
color: #ff0000;
font-weight: bold;
}
QSlider::groove:horizontal {
background: #3c3c3c;
height: 8px;
}
QSlider::handle:horizontal {
background: #5c5c5c;
width: 16px;
margin: -4px 0;
border-radius: 8px;
}
"""
```
## Testing Checklist
Before Monday demo:
- [ ] Serial connection/disconnection works
- [ ] Current slider sets X9119 correctly
- [ ] Shutter opens/closes (verify with read-back)
- [ ] Emergency stop works from any state
- [ ] Sensor readings update (current, temp)
- [ ] Error handling doesn't crash app
- [ ] All safety interlocks functional
- [ ] Confirm dialog before shutter open
- [ ] Auto-disable on disconnect
- [ ] Packet monitor shows correct data
## README Section for Top of File
```python
"""
Genesis SLM MX 532 Laser Control Application
=============================================
This application controls a Genesis SLM MX 532 laser via I2C-over-serial protocol.
PROTOCOL OVERVIEW:
- Serial: /dev/ttyUSB0 @ 9600 8N1
- Protocol: NXP I2C tunneled over serial
- Packet format: [0x53][addr][len][cmd][data][0x50]
SAFETY WARNINGS:
- Always close shutter before adjusting current
- Verify interlock status before opening shutter
- Use emergency stop if anything looks wrong
- Never bypass safety interlocks
USAGE:
1. Connect serial port in Configuration tab
2. Enable Remote and Keyswitch
3. Set current to desired level
4. Open shutter (with confirmation)
5. Monitor temperature and current
6. Close shutter when done
For more information, see protocol documentation.
Author: [Your Name]
Date: [Date]
License: [License]
"""
```
## Notes for Implementation
- All I2C addresses are 7-bit and need write bit cleared (& 0xFE) for writes
- Read addresses are write address | 0x01
- Multi-byte data is big-endian
- Always validate inputs before sending to hardware
- Use type hints throughout
- Add docstrings to all functions
- Log all I2C transactions for debugging
- Include unit tests for packet construction
- Make sure emergency stop can interrupt any operation
---
**CRITICAL:** Test thoroughly before Monday! The laser is expensive - safety first!
Binary file not shown.
Binary file not shown.
Binary file not shown.