# devices.thorlabs:BSC103


The Thorlabs BSC103 motor controller device class.


Usage

``` python
devices.thorlabs:BSC103(
    interface,
    conf=None,
    debug=0,
)
```


Could also be used for other devices using the APT protocol with little to no change. Note that multi channel operation is implemented to a certain degree, but not completely and without documentation, as it is not used in the BSC103. Multi card devices are fully implemented.


## Attributes


`debug: int`  
The debugging mode. If byte 0x01 is set, debug information is written to a log file using the logging module. If byte 0x02 is set, debug information is written to console. 0x03 activates both.

`drive_config: dict`  
Contains the configuration of the device/motor.

`mb: int`  
The address of the motherboard, as specified in the config file.

`drives: list of int`  
A list of the addresses of the connected drives.

`ndrives: int`  
The number of connected drives.

`uStepsPerStep: int`  
The ratio of (internal) micro steps to actual stepper motor steps. Specific to the used controller.


## Classes

| Name | Description |
|----|----|
| [message](#message) | Data structure for communication using the APT protocol. |

------------------------------------------------------------------------


### message


Data structure for communication using the APT protocol.


Usage

``` python
message(
    msgID,
    payload,
    dst,
    src=1,
    respLen=0,
)
```


Messages (packets) consist of a 6 byte header (2b message ID, 2b payload, 1b destination address, 1b source address) sometimes followed by a variable length data packet. If the destination is bitwise OR'd with 0x08, a data packet will follow, with the length specified in the payload section. If not, the payload contains two 1b parameters. Little endian convention is normally used for conversion between bytes and integers. Further information on the message structure can be obtained from the APT protocol documentation.


#### Attributes


`msgID: int`  
The message ID, which usually specifies the used command.

`dst: int`  
The destination address.

`src: int`  
The source address.

`respLen: int`  
The expected length of the response, if there is one.

`param1, param2: int`  
The parameters, if the message is header only.

`data: bytes`  
The data appended to the message.

`datalen: int`  
The length of the data packet.

`longPacket: bool`  
True if a data packet follows the header.


#### Methods

| Name | Description |
|----|----|
| [__init__()](#__init__) | Construct a message object. |
| [fromBytes()](#fromBytes) | Create a message object from (read) bytes. |
| [getBytes()](#getBytes) | Return the raw bytes of a BSC103.message. |


##### \_\_init\_\_()


Construct a message object.


Usage

``` python
__init__(
    msgID,
    payload,
    dst,
    src=1,
    respLen=0,
)
```


###### Parameters


`msgID: int`  
The message ID, which usually specifies the used command.

`payload: bytes or tuple`  
If the payload is of bytes type, a long packet is created automatically. If not, it should be a tuple of two integers containing the parameters.

`dst: int`  
The destination address.

`src: int = ``1`  
The source address, by default 0x01

`respLen: int = ``0`  
The expected response length. If no response is expected, set to 0. Default is 0.


##### fromBytes()


Create a message object from (read) bytes.


Usage

``` python
fromBytes(
    bytearr,
    respLen=0,
)
```


###### Parameters


`bytearr: bytes`  
The bytes to create the message from.

`respLen: int = ``0`  
The expected response length. If no response is expected, set to 0. Default is 0.


###### Returns


`BSC103.message`  
The created message.


##### getBytes()


Return the raw bytes of a BSC103.message.


Usage

``` python
getBytes()
```


###### Returns


`bytes`  
The raw bytes representation of the message.


## Methods

| Name | Description |
|----|----|
| [ReqResp()](#ReqResp) | Send a packet to the device, and return the response. |
| [__init__()](#__init__) | Initialize a new BSC103 device. |
| [bytestostr()](#bytestostr) | Format a bytes type into a string of its hexadecimal representation. |
| [enableAxis()](#enableAxis) | Set the state of an axis to either en- or disabled. |
| [getAxisStatus()](#getAxisStatus) | Get information about the status of a drive. |
| [getHWInfo()](#getHWInfo) | Get basic hardware information from a controller. |
| [getHomeParams()](#getHomeParams) | Read the home parameters from a drive. |
| [getLimSwitchParams()](#getLimSwitchParams) | Read the limit switch settings from a drive. |
| [getVelParams()](#getVelParams) | Read the velocity parameters from a drive. |
| [home()](#home) | Home a drive. |
| [homeMult()](#homeMult) | Home multiple drives at once. |
| [id()](#id) | Get device identification information. |
| [initDrives()](#initDrives) | Initialize communication, set drive settings, enable multiple drives. |
| [initializeComm()](#initializeComm) | Initialize communication with the controller. |
| [mmto_uSteps()](#mmto_uSteps) | Convert millimeter to the number of micro steps. |
| [moveAbs()](#moveAbs) | Move a drive to certain position on the axis. |
| [moveAbsMult()](#moveAbsMult) | Move multiple drives to a point of the coordinate system. |
| [moveRel()](#moveRel) | Move a drive a certain distance relative to the current position. |
| [moveRelMult()](#moveRelMult) | Move multiple drives relative to their current position. |
| [open()](#open) | Open the connection to the device and clear the input buffer. |
| [parseMotorStatusPckt()](#parseMotorStatusPckt) | Extract information from a motor status packet. |
| [printDebug()](#printDebug) | Print a debug string to console or log file. |
| [readPacket()](#readPacket) | Read an entire packet from the serial port input buffer. |
| [setDriveSettings()](#setDriveSettings) | Set drive according to basic config file settings. |
| [setHomeParams()](#setHomeParams) | Set the home parameters for a drive. |
| [setLimSwitchParams()](#setLimSwitchParams) | Write the limit switch settings to a drive. |
| [setVelParams()](#setVelParams) | Set the velocity parameters for a drive. |
| [stop()](#stop) | Stop any motion of the drive. |
| [uStepsto_mm()](#uStepsto_mm) | Convert the number of micro steps to millimeter. |
| [write()](#write) | Write data to the serial port. |

------------------------------------------------------------------------


### ReqResp()


Send a packet to the device, and return the response.


Usage

``` python
ReqResp(msg)
```


If the debug option is turned on, check if the length of the response matches the expected length, as some command have a response length attribute leftover from when the read method was used instead of the readPacket method. Also check if the response ID is the request ID incremented by 1, as this is usually the case except for movement commands. Display a warning in both cases, this is not totally accurate, but sometimes useful.


#### Parameters


`msg: BSC103.message`  
The Request to be written. Note that the system will wait for a response, so make sure that the request will actually be answered by the device (Certain commands do not provoke a response).


#### Returns


`BSC103.message`  
The received response.


------------------------------------------------------------------------


### \_\_init\_\_()


Initialize a new BSC103 device.


Usage

``` python
__init__(
    interface,
    conf=None,
    debug=0,
)
```


#### Parameters


`interface: str`  
The serial port where the device is located.

`conf: dict = None`  
Contains the configuration of the device/motor in question, example follows: {"name": "Thorlabs DRV001", "mb address": 17, "drive addresses": \[33, 34, 35\], "backlash dist": 0.01, "ccw hard limit": 1, "ccw soft limit": 1.0, "cw hard limit": 3, "cw soft limit": 3.0, "soft limit mode": 1, "def accn": 1.0, "def max vel": 2.0, "def min vel": 0.0, "home dir": 2, "home limit switch": 1, "home vel": 1.0, "home zero offset": 3, "jog accn": 0.5, "jog max vel": 1.0, "jog min vel": 0.0, "jog mode": 2, "jog step size": 0.05, "jog stop mode": 2, "power rest" : 20, "power mov" : 100, "pitch": 0.5, "steps per rev": 200, "units": 1}

`debug: int = ``0`  
The debugging mode, by default 0x00 If byte 0x01 is set, debug information is written to a log file using the logging module. If byte 0x02 is set, debug information is written to console. 0x03 activates both.


------------------------------------------------------------------------


### bytestostr()


Format a bytes type into a string of its hexadecimal representation.


Usage

``` python
bytestostr(bytearr)
```


#### Parameters


`bytearr: bytes`  
The bytes to convert to a string.


#### Returns


`str`  
Hexadecimal representation of the bytes.


------------------------------------------------------------------------


### enableAxis()


Set the state of an axis to either en- or disabled.


Usage

``` python
enableAxis(
    dst,
    channel=1,
    status=True,
)
```


#### Parameters


`dst: int`  
The destination address.

`channel: int = ``1`  
The channel number, by default 0x01

`status: bool = ``True`  
If true, enable the axis, else disable it. Default is True.


------------------------------------------------------------------------


### getAxisStatus()


Get information about the status of a drive.


Usage

``` python
getAxisStatus(
    dst,
    channel=1,
)
```


The return format is specified in parseMotorStatusPckt.


#### Parameters


`dst: int`  
The destination address.

`channel: int = ``1`  
The channel number, by default 0x01


#### Returns


`dict`  
Motor status information as specified in parseMotorStatusPckt.


------------------------------------------------------------------------


### getHWInfo()


Get basic hardware information from a controller.


Usage

``` python
getHWInfo(dst=17)
```


Not all information is parsed in this function, refer to the documentation.


#### Parameters


`dst: int = ``17`  
The destination address, by default 0x11


#### Returns


`tuple`  
A tuple containing:

- SerialNmbr: int - The serial number.
- ModelNmbr: str - The model number.
- Notes: str - Information string from the device, usually contains the name.
- nChannels: int - The number of channels the device can use.


------------------------------------------------------------------------


### getHomeParams()


Read the home parameters from a drive.


Usage

``` python
getHomeParams(
    dst,
    channel,
)
```


#### Parameters


`dst: int`  
The destination address.

`channel: int`  
The channel number.


#### Returns


`tuple`  
A tuple containing:

- homeDir: bool - True if homing in positive direction, else false.
- limSwitch: bool - Limit switch settings, see APT protocol documentation for more information.
- homeVel: float - The homing velocity in mm/s.
- offsetDistance: float - The distance from the limit switch in mm, to where the drive moves after homing.


------------------------------------------------------------------------


### getLimSwitchParams()


Read the limit switch settings from a drive.


Usage

``` python
getLimSwitchParams(
    dst,
    channel,
)
```


See the APT protocol documentation for more information.


#### Parameters


`dst: int`  
The destination address.

`channel: int`  
The channel number.


#### Returns


`tuple`  
A tuple containing:

- CWHardLim : int - Clockwise hard limit
- CCWHardLim : int - Counter-clockwise hard limit
- CWSoftLim : int - Clockwise soft limit
- CCWSoftLim : int - Counter-clockwise soft limit
- SoftLimMode : int - Soft limit mode


------------------------------------------------------------------------


### getVelParams()


Read the velocity parameters from a drive.


Usage

``` python
getVelParams(
    dst,
    channel,
)
```


#### Parameters


`dst: int`  
The destination address.

`channel: int`  
The channel number.


#### Returns


`tuple`  
A tuple containing:

- minVel : float - The minimum velocity in mm/s, usually zero.
- acc : float - The acceleration in mm/s².
- maxVel : float - The maximum velocity in mm/s.


------------------------------------------------------------------------


### home()


Home a drive.


Usage

``` python
home(
    dst,
    channel=1,
    noResp=False,
)
```


#### Parameters


`dst: int`  
The destination address.

`channel: int = ``1`  
The channel number, by default 0x01

`noResp: bool = ``False`  
If True, do not wait for a response. Useful if moving multiple drives at once. Default is False.


#### Returns


`str`  
Home status:

- 'homed': if the drive was already homed, or if the method waited for a response.
- 'homing': if the drive is homing, but the method did not wait for a response.
- 'not homed': if something went wrong.


------------------------------------------------------------------------


### homeMult()


Home multiple drives at once.


Usage

``` python
homeMult(
    axis=None,
    channel=1,
)
```


#### Parameters


`axis: list of bool = None`  
A list of boolean values, with a length equal to self.ndrives. The order of the drives is specified in self.drives. If a certain value is set to true, home the drive. If set to None, all drives get homed. Default is None.

`channel: int = ``1`  
The channel number, by default 0x01


#### Returns


`dict`  
A dictionary mapping drive addresses (in hex) to home status strings:

- 'homed': if the drive was already homed, or if the method waited for a response.
- 'homing': if the drive is homing, but the method did not wait for a response.
- 'not homed': if something went wrong.


------------------------------------------------------------------------


### id()


Get device identification information.


Usage

``` python
id()
```


#### Returns


`str`  
Combined model number and notes information.


------------------------------------------------------------------------


### initDrives()


Initialize communication, set drive settings, enable multiple drives.


Usage

``` python
initDrives(
    axis=None,
    channel=1,
)
```


#### Parameters


`axis: list of bool = None`  
List of boolean values, with a length equal to self.ndrives. The order of the drives is specified in self.drives. If a certain value is set to true, initialize the drive. If set to None, all drives get initialized. Default is None.

`channel: int = ``1`  
The channel number, by default 0x01


------------------------------------------------------------------------


### initializeComm()


Initialize communication with the controller.


Usage

``` python
initializeComm(dst)
```


This message must be sent to the motherboard and all drives as part of the initialisation process.


#### Parameters


`dst: int`  
The destination address.


------------------------------------------------------------------------


### mmto_uSteps()


Convert millimeter to the number of micro steps.


Usage

``` python
mmto_uSteps(mm)
```


The conversion happens based on the drive configuration, and the value is rounded to the nearest integer.


#### Parameters


`mm: float`  
Distance in millimeters to convert.


#### Returns


`int`  
Number of microsteps.


------------------------------------------------------------------------


### moveAbs()


Move a drive to certain position on the axis.


Usage

``` python
moveAbs(
    dst,
    dist,
    channel=1,
    noResp=False,
)
```


#### Parameters


`dst: int`  
The destination address.

`dist: float`  
The position to which the drive should move.

`channel: int = ``1`  
(Default value = 0x01)

`noResp: bool = ``False`  
(Default value = False) If True, do not wait for a response. Useful if moving multiple drives at once.


#### Returns


`motorStatusPacket`  
Motor status as specified in the parser function.


------------------------------------------------------------------------


### moveAbsMult()


Move multiple drives to a point of the coordinate system.


Usage

``` python
moveAbsMult(
    distArr,
    channel=1,
)
```


#### Parameters


`distArr: [float]`  
List of the coordinates in mm, lenght should be equal to self.ndrives. Order of the drives is specified in self.drives.

`channel: int = ``1`  
(Default value = 0x01)


#### Returns


`{`  

`hex(drive Address): motorStatusPacket`  
Motor status as specified in the parser function.

`}`  


------------------------------------------------------------------------


### moveRel()


Move a drive a certain distance relative to the current position.


Usage

``` python
moveRel(
    dst,
    dist,
    channel=1,
    noResp=False,
)
```


#### Parameters


`dst: int`  
The destination address.

`dist: float`  
The distance in mm which the drive should move by.

`channel: int = ``1`  
(Default value = 0x01)

`noResp: bool = ``False`  
(Default value = False) If True, do not wait for a response. Useful if moving multiple drives at once.


#### Returns


`motorStatusPacket`  
Motor status as specified in the parser function.


------------------------------------------------------------------------


### moveRelMult()


Move multiple drives relative to their current position.


Usage

``` python
moveRelMult(
    distArr,
    channel=1,
)
```


#### Parameters


`distArr: [float]`  
List of the distances in mm, lenght should be equal to self.ndrives. Order of the drives is specified in self.drives.

`channel: int = ``1`  
(Default value = 0x01)


#### Returns


`{`  

`hex(drive address): motorStatusPacket`  
Motor status as specified in the parser function.

`}`  


------------------------------------------------------------------------


### open()


Open the connection to the device and clear the input buffer.


Usage

``` python
open()
```


------------------------------------------------------------------------


### parseMotorStatusPckt()


Extract information from a motor status packet.


Usage

``` python
parseMotorStatusPckt(packet)
```


Those packets have an ID of either 0x0481 or 0x0464. If the ID does not match, print a warning if debug is enabled. This method has not been fully tested yet, so use with caution.


#### Parameters


`packet: BSC103.message`  
The packet to parse.


#### Returns


`dict`  
A dictionary containing motor status information:

- channel: int - Channel number
- position: float - The current position of the drive in millimeters.
- encCount: int - Not used on the installed drives, as they have no encoder.
- switches: str - Hexadecimal representation of the switch status. For further information, refer to the APT protocol documentation.
- ismoving: int - The sign indicates the direction, + is forward. 1 indicates normal, 2 indicates jogging movement. 0 means the drive is stationary.
- motor connected: bool - True if a motor is attached to the drive. (not working properly)
- home status: str - One of {'not homed', 'homeing', 'homed'}


------------------------------------------------------------------------


### printDebug()


Print a debug string to console or log file.


Usage

``` python
printDebug(
    string,
    color="INFO",
    warning=False,
)
```


The writing location is decided based on the debug attribute of the class.


#### Parameters


`string: str`  
The string containing the debug information.

`color: (W, R, WARN, INFO) = ``"W"`  
Affects the color of the output printed to console. If W or R, it is printed in front of the output. Default is 'INFO'.

`warning: bool = ``False`  
If true, the message is logged as a warning. Default is False.


------------------------------------------------------------------------


### readPacket()


Read an entire packet from the serial port input buffer.


Usage

``` python
readPacket()
```


#### Returns


`BSC103.message`  
The read packet.


------------------------------------------------------------------------


### setDriveSettings()


Set drive according to basic config file settings.


Usage

``` python
setDriveSettings(
    dst,
    channel=1,
)
```


This includes home, velocity and limit switch settings as well as power settings and the backlash correction. This method should always be called before attempting to move an axis.


#### Parameters


`dst: int`  
The destination address.

`channel: int = ``1`  
The channel number, by default 0x01


------------------------------------------------------------------------


### setHomeParams()


Set the home parameters for a drive.


Usage

``` python
setHomeParams(
    dst,
    channel,
    homedir,
    limSwitch,
    homeVel,
    offsetDist,
)
```


#### Parameters


`dst: int`  
The destination address.

`channel: int`  
The channel number.

`homedir: bool`  
True if homing in positive direction, else false.

`limSwitch: bool`  
Limit switch settings, see APT protocol documentation for more information.

`homeVel: float`  
The homing velocity in mm/s.

`offsetDist: float`  
The distance from the limit switch in mm, to where the drive moves after homing.


------------------------------------------------------------------------


### setLimSwitchParams()


Write the limit switch settings to a drive.


Usage

``` python
setLimSwitchParams(
    dst,
    channel,
    CWHardLim,
    CCWHardLim,
    CWSoftLim,
    CCWSoftLim,
    SoftLimMode,
)
```


See the APT protocol documentation for more information.


#### Parameters


`dst: int`  
The destination address.

`channel: int`  
The channel number.

`CWHardLim: int`  
Clockwise hard limit setting.

`CCWHardLim: int`  
Counter-clockwise hard limit setting.

`CWSoftLim: int`  
Clockwise soft limit setting.

`CCWSoftLim: int`  
Counter-clockwise soft limit setting.

`SoftLimMode: int`  
Soft limit mode setting.


------------------------------------------------------------------------


### setVelParams()


Set the velocity parameters for a drive.


Usage

``` python
setVelParams(
    dst,
    channel,
    minVel,
    acc,
    maxVel,
)
```


#### Parameters


`dst: int`  
The destination address.

`channel: int`  
The channel number.

`minVel: float`  
The minimum velocity in mm/s, usually zero.

`acc: float`  
The acceleration in mm/s².

`maxVel: float`  
The maximum velocity in mm/s.


------------------------------------------------------------------------


### stop()


Stop any motion of the drive.


Usage

``` python
stop(
    dst,
    channel=1,
    mode=2,
)
```


Not fully tested, so use with caution!


#### Parameters


`dst: int`  
The destination address.

`channel: int = ``1`  
(Default value = 0x01)

`mode: int = ``2`  
(Default value = 0x02) The stop mode. Set to 0x01 to stop immediately, or to 0x02 to stop in a controlled (profiled) manner


------------------------------------------------------------------------


### uStepsto_mm()


Convert the number of micro steps to millimeter.


Usage

``` python
uStepsto_mm(usteps)
```


As specified in the drive configuration.


#### Parameters


`usteps: int`  
The number of micro steps to convert.


#### Returns


`float`  
The equivalent distance in millimeters.


------------------------------------------------------------------------


### write()


Write data to the serial port.


Usage

``` python
write(data)
```


#### Parameters


`data: bytes or BSC103.message`  
The data to be written. If data is of type BSC103.message, it is converted into a bytes type first.


#### Returns


`int`  
The number of written bytes.
