when'd i last commit this pos?
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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!
|
||||
Reference in New Issue
Block a user