├── tools ├── __init__.py └── protocol_import.py ├── images └── pololu_tic.png ├── pytic ├── __init__.py ├── drivers │ └── x64 │ │ ├── libusbp-1.dll │ │ └── libpololu-tic-1.dll ├── pytic_structures.py ├── pytic_protocol.py └── pytic.py ├── MANIFEST.in ├── config └── config.yml ├── setup.py ├── CONTRIBUTING.md ├── LICENSE.txt └── README.md /tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/pololu_tic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenInstitute/pytic/HEAD/images/pololu_tic.png -------------------------------------------------------------------------------- /pytic/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Object-Oriented Python Wrapper for Pololu Tic C-API 3 | ''' 4 | from .pytic import * -------------------------------------------------------------------------------- /pytic/drivers/x64/libusbp-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenInstitute/pytic/HEAD/pytic/drivers/x64/libusbp-1.dll -------------------------------------------------------------------------------- /pytic/drivers/x64/libpololu-tic-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AllenInstitute/pytic/HEAD/pytic/drivers/x64/libpololu-tic-1.dll -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pytic/drivers/x64/*.dll 2 | include pytic/drivers/x64/*.lib 3 | include config/*.yml 4 | include README.md 5 | include CONTRIBUTING.md 6 | include LICENSE.txt -------------------------------------------------------------------------------- /tools/protocol_import.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | ''' 4 | Requires user to remove header and extra new-lines in-between entries 5 | ''' 6 | 7 | 8 | pfile = open("tic_protocol.h", 'r') # Copy file to leave original unaltered 9 | ofile = open('pytic_protocol.py', 'w') 10 | preader = csv.reader(pfile, delimiter=' ') 11 | ofile.write("tic_constant = {\n") 12 | 13 | for r in preader: 14 | ofile.write('\t\'' + r[1] + '\': ' + r[2] + ',\n') 15 | 16 | ofile.write('}') 17 | ofile.close() 18 | pfile.close() -------------------------------------------------------------------------------- /config/config.yml: -------------------------------------------------------------------------------- 1 | tic_settings: 2 | product: TIC_PRODUCT_T825 3 | auto_clear_driver_error: True 4 | ignore_err_line_high: True 5 | serial_crc_enabled: False 6 | command_timeout: 0 7 | max_speed: 180000000 # pulses/s * 10^-4 8 | starting_speed: 0 # pulses/s * 10^-4 9 | max_accel: 9000000 # pulses/s^2 * 10^-2 10 | max_decel: 9000000 # pulses/s^2 * 10^-2 11 | step_mode: TIC_STEP_MODE_MICROSTEP16 12 | current_limit: 640 # mA (see table in Pololu manual for acceptable values) 13 | decay_mode: TIC_DECAY_MODE_T825_FAST 14 | pin_settings: 15 | - pin_num: TIC_PIN_NUM_RX 16 | func: TIC_PIN_FUNC_USER_INPUT 17 | pullup: True 18 | analog: False -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | __author__ = "Daniel Castelli" 2 | from setuptools import setup 3 | from setuptools.command.install import install as _install 4 | import subprocess 5 | 6 | class install(_install): 7 | def pre_install_script(self): 8 | pass 9 | 10 | def post_install_script(self): 11 | pass 12 | 13 | def run(self): 14 | self.pre_install_script() 15 | 16 | _install.run(self) 17 | 18 | self.post_install_script() 19 | 20 | with open('README.md') as f: 21 | long_description = f.read() 22 | 23 | setup( 24 | name = "PyTic", 25 | version = "0.0.5", 26 | author = "Daniel Castelli", 27 | author_email = "danc@alleninstitute.org", 28 | description = "An object-oriented Python wrapper for Pololu Tic Stepper Controllers.", 29 | long_description=long_description, 30 | long_description_content_type='text/markdown', 31 | license = "Allen Institute Software License", 32 | keywords = "PyTic Pololu Tic Stepper Controller Wrapper", 33 | url = "https://github.com/AllenInstitute/pytic", 34 | packages = ['pytic'], 35 | install_requires = ['PyYAML'], 36 | include_package_data=True, 37 | cmdclass = {'install': install}, 38 | ) 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Allen Institute Contribution Agreement 2 | 3 | This document describes the terms under which you may make “Contributions” — 4 | which may include without limitation, software additions, revisions, bug fixes, configuration changes, 5 | documentation, or any other materials — to any of the projects owned or managed by the Allen Institute. 6 | If you have questions about these terms, please contact us at terms@alleninstitute.org. 7 | 8 | You certify that: 9 | 10 | • Your Contributions are either: 11 | 12 | 1. Created in whole or in part by you and you have the right to submit them under the designated license 13 | (described below); or 14 | 2. Based upon previous work that, to the best of your knowledge, is covered under an appropriate 15 | open source license and you have the right under that license to submit that work with modifications, 16 | whether created in whole or in part by you, under the designated license; or 17 | 18 | 3. Provided directly to you by some other person who certified (1) or (2) and you have not modified them. 19 | 20 | • You are granting your Contributions to the Allen Institute under the terms of the [2-Clause BSD license](https://opensource.org/licenses/BSD-2-Clause) 21 | (the “designated license”). 22 | 23 | • You understand and agree that the Allen Institute projects and your Contributions are public and that 24 | a record of the Contributions (including all metadata and personal information you submit with them) is 25 | maintained indefinitely and may be redistributed consistent with the Allen Institute’s mission and the 26 | 2-Clause BSD license. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Allen Institute Software License - This software license is the 2-clause BSD license 2 | plus a third clause that prohibits redistribution for commercial purposes without further permission. 3 | 4 | Copyright (c) 2018. Allen Institute. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 7 | following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the 10 | following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 13 | following disclaimer in the documentation and/or other materials provided with the distribution. 14 | 15 | 3. Redistributions for commercial purposes are not permitted without the Allen Institute's written permission. 16 | For purposes of this license, commercial purposes is the incorporation of the Allen Institute's software into 17 | anything for which you will charge fees or other compensation. Contact terms@alleninstitute.org for commercial 18 | licensing opportunities. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 26 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /pytic/pytic_structures.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from .pytic_protocol import tic_constant as t_const 3 | 4 | class libusbp_generic_interface(Structure): 5 | _fields_ = [("interface_number", c_uint8), 6 | ("device_instance_id", c_char_p), 7 | ("filename", c_char_p)] 8 | 9 | class libusbp_generic_handle(Structure): 10 | # untested type, no HANDLE or WIN_INTERFACE_HANDLE type 11 | _fields_ = [("file_handle", c_ulong), 12 | ("winusb_handle", c_ulong)] 13 | 14 | class tic_device(Structure): 15 | _fields_ = [("usb_interface", POINTER(libusbp_generic_interface)), 16 | ("serial_number", c_char_p), 17 | ("os_id", c_char_p), 18 | ("firmware_version", c_uint16), 19 | ("product", c_uint8)] 20 | 21 | class tic_handle(Structure): 22 | _fields_ = [('usb_handle', POINTER(libusbp_generic_handle)), 23 | ('device', POINTER(tic_device)), 24 | ('cached_firmware_version_string', c_char_p)] 25 | 26 | class pin_settings(Structure): 27 | _fields_ = [('func', c_uint8), 28 | ('pullup', c_bool), 29 | ('analog', c_bool), 30 | ('polarity', c_bool)] 31 | 32 | class tic_settings(Structure): 33 | _fields_ = [('product', c_uint8), 34 | ('control_mode', c_uint8), 35 | ('never_sleep', c_bool), 36 | ('disable_safe_start', c_bool), 37 | ('ignore_err_line_high', c_bool), 38 | ('auto_clear_driver_error', c_bool), 39 | ('soft_error_response', c_uint8), 40 | ('soft_error_position', c_int32), 41 | ('serial_baud_rate', c_uint32), 42 | ('serial_device_number', c_uint8), 43 | ('command_timeout', c_uint16), 44 | ('serial_crc_enabled', c_bool), 45 | ('serial_response_delay', c_uint8), 46 | ('low_vin_timeout', c_uint16), 47 | ('low_vin_shutoff_voltage', c_uint16), 48 | ('low_vin_startup_voltage', c_uint16), 49 | ('high_vin_shutoff_voltage', c_uint16), 50 | ('vin_calibration', c_int16), 51 | ('rc_max_pulse_period', c_uint16), 52 | ('rc_bad_signal_timeout', c_uint16), 53 | ('rc_consecutive_good_pulses', c_uint8), 54 | ('input_averaging_enabled', c_uint8), 55 | ('input_hysteresis', c_uint16), 56 | ('input_error_min', c_uint16), 57 | ('input_error_max', c_uint16), 58 | ('input_scaling_degree', c_uint8), 59 | ('input_invert', c_bool), 60 | ('input_min', c_uint16), 61 | ('input_neutral_min', c_uint16), 62 | ('input_neutral_max', c_uint16), 63 | ('input_max', c_uint16), 64 | ('output_min', c_int32), 65 | ('output_max', c_int32), 66 | ('encoder_prescaler', c_uint32), 67 | ('encoder_postscaler', c_uint32), 68 | ('encoder_unlimited', c_bool), 69 | ('pin_settings', pin_settings * t_const['TIC_CONTROL_PIN_COUNT']), 70 | ('current_limit', c_uint32), 71 | ('current_limit_during_error', c_int32), 72 | ('step_mode', c_uint8), 73 | ('decay_mode', c_int8), 74 | ('starting_speed', c_uint32), 75 | ('max_speed', c_uint32), 76 | ('max_decel', c_uint32), 77 | ('max_accel', c_uint32), 78 | ('invert_motor_direction', c_bool)] 79 | 80 | class pin_info(Structure): 81 | _fields_ = [('analog_reading', c_uint16), 82 | ('digital_reading', c_bool), 83 | ('pin_state', c_uint8)] 84 | 85 | class tic_variables(Structure): 86 | _fields_ = [('product', c_uint8), 87 | ('operation_state', c_uint8), 88 | ('energized', c_bool), 89 | ('position_uncertain', c_bool), 90 | ('error_status', c_uint16), 91 | ('errors_occurred', c_uint32), 92 | ('planning_mode', c_uint8), 93 | ('target_position', c_int32), 94 | ('target_velocity', c_int32), 95 | ('starting_speed', c_uint32), 96 | ('max_speed', c_uint32), 97 | ('max_decel', c_uint32), 98 | ('max_accel', c_uint32), 99 | ('current_position', c_int32), 100 | ('current_velocity', c_int32), 101 | ('acting_target_position', c_int32), 102 | ('time_since_last_step', c_uint32), 103 | ('device_reset', c_uint8), 104 | ('vin_voltage', c_uint16), 105 | ('up_time', c_uint32), 106 | ('encoder_position', c_int32), 107 | ('rc_pulse_width', c_uint16), 108 | ('step_mode', c_uint8), 109 | ('current_limit_code', c_uint8), 110 | ('decay_mode', c_uint8), 111 | ('input_state', c_uint8), 112 | ('input_after_averaging', c_uint16), 113 | ('input_after_hysteresis', c_uint16), 114 | ('input_after_scaling', c_int32), 115 | ('pin_info', pin_info * t_const['TIC_CONTROL_PIN_COUNT'])] 116 | 117 | class tic_error(Structure): 118 | _fields_ = [('do_not_free', c_bool), 119 | ('message', c_char_p), 120 | ('code_count', c_size_t), 121 | ('code_array', POINTER(c_uint32))] 122 | -------------------------------------------------------------------------------- /pytic/pytic_protocol.py: -------------------------------------------------------------------------------- 1 | tic_constant = { 2 | # not in protocol.h 3 | 'TIC_PRODUCT_T825': 1, 4 | 'TIC_PRODUCT_T834': 2, 5 | 'TIC_PRODUCT_T500': 3, 6 | # in protocol.h 7 | 'TIC_VENDOR_ID': 0x1FFB, 8 | 'TIC_PRODUCT_ID_T825': 0x00B3, 9 | 'TIC_PRODUCT_ID_T834': 0x00B5, 10 | 'TIC_PRODUCT_ID_T500': 0x00BD, 11 | 'TIC_FIRMWARE_MODIFICATION_STRING_INDEX': 4, 12 | 'TIC_OPERATION_STATE_RESET': 0, 13 | 'TIC_OPERATION_STATE_DEENERGIZED': 2, 14 | 'TIC_OPERATION_STATE_SOFT_ERROR': 4, 15 | 'TIC_OPERATION_STATE_WAITING_FOR_ERR_LINE': 6, 16 | 'TIC_OPERATION_STATE_STARTING_UP': 8, 17 | 'TIC_OPERATION_STATE_NORMAL': 10, 18 | 'TIC_MISC_FLAGS1_ENERGIZED': 0, 19 | 'TIC_MISC_FLAGS1_POSITION_UNCERTAIN': 1, 20 | 'TIC_ERROR_INTENTIONALLY_DEENERGIZED': 0, 21 | 'TIC_ERROR_MOTOR_DRIVER_ERROR': 1, 22 | 'TIC_ERROR_LOW_VIN': 2, 23 | 'TIC_ERROR_KILL_SWITCH': 3, 24 | 'TIC_ERROR_REQUIRED_INPUT_INVALID': 4, 25 | 'TIC_ERROR_SERIAL_ERROR': 5, 26 | 'TIC_ERROR_COMMAND_TIMEOUT': 6, 27 | 'TIC_ERROR_SAFE_START_VIOLATION': 7, 28 | 'TIC_ERROR_ERR_LINE_HIGH': 8, 29 | 'TIC_ERROR_SERIAL_FRAMING': 16, 30 | 'TIC_ERROR_SERIAL_RX_OVERRUN': 17, 31 | 'TIC_ERROR_SERIAL_FORMAT': 18, 32 | 'TIC_ERROR_SERIAL_CRC': 19, 33 | 'TIC_ERROR_ENCODER_SKIP': 20, 34 | 'TIC_INPUT_STATE_NOT_READY': 0, 35 | 'TIC_INPUT_STATE_INVALID': 1, 36 | 'TIC_INPUT_STATE_HALT': 2, 37 | 'TIC_INPUT_STATE_POSITION': 3, 38 | 'TIC_INPUT_STATE_VELOCITY': 4, 39 | 'TIC_RESPONSE_DEENERGIZE': 0, 40 | 'TIC_RESPONSE_HALT_AND_HOLD': 1, 41 | 'TIC_RESPONSE_DECEL_TO_HOLD': 2, 42 | 'TIC_RESPONSE_GO_TO_POSITION': 3, 43 | 'TIC_PIN_NUM_SCL': 0, 44 | 'TIC_PIN_NUM_SDA': 1, 45 | 'TIC_PIN_NUM_TX': 2, 46 | 'TIC_PIN_NUM_RX': 3, 47 | 'TIC_PIN_NUM_RC': 4, 48 | 'TIC_PLANNING_MODE_OFF': 0, 49 | 'TIC_PLANNING_MODE_TARGET_POSITION': 1, 50 | 'TIC_PLANNING_MODE_TARGET_VELOCITY': 2, 51 | 'TIC_RESET_POWER_UP': 0, 52 | 'TIC_RESET_BROWNOUT': 1, 53 | 'TIC_RESET_RESET_LINE': 2, 54 | 'TIC_RESET_WATCHDOG': 4, 55 | 'TIC_RESET_SOFTWARE': 8, 56 | 'TIC_RESET_STACK_OVERFLOW': 16, 57 | 'TIC_RESET_STACK_UNDERFLOW': 32, 58 | 'TIC_PIN_STATE_HIGH_IMPEDANCE': 0, 59 | 'TIC_PIN_STATE_PULLED_UP': 1, 60 | 'TIC_PIN_STATE_OUTPUT_LOW': 2, 61 | 'TIC_PIN_STATE_OUTPUT_HIGH': 3, 62 | 'TIC_MIN_ALLOWED_BAUD_RATE': 200, 63 | 'TIC_MAX_ALLOWED_BAUD_RATE': 115385, 64 | 'TIC_DEFAULT_COMMAND_TIMEOUT': 1000, 65 | 'TIC_MAX_ALLOWED_COMMAND_TIMEOUT': 60000, 66 | 'TIC_MAX_ALLOWED_CURRENT': 3968, 67 | 'TIC_MAX_ALLOWED_CURRENT_T825': 3968, 68 | 'TIC_MAX_ALLOWED_CURRENT_T834': 3456, 69 | 'TIC_MAX_ALLOWED_CURRENT_T500': 3093, 70 | 'TIC_MAX_ALLOWED_CURRENT_CODE_T500': 32, 71 | 'TIC_CURRENT_LIMIT_UNITS_MA': 32, 72 | 'TIC_MIN_ALLOWED_ACCEL': 100, 73 | 'TIC_MAX_ALLOWED_ACCEL': 0x7FFFFFFF, 74 | 'TIC_MAX_ALLOWED_SPEED': 500000000, 75 | 'TIC_MAX_ALLOWED_ENCODER_PRESCALER': 0x7FFFFFFF, 76 | 'TIC_MAX_ALLOWED_ENCODER_POSTSCALER': 0x7FFFFFFF, 77 | 'TIC_SPEED_UNITS_PER_HZ': 10000, 78 | 'TIC_ACCEL_UNITS_PER_HZ2': 100, 79 | 'TIC_CONTROL_MODE_SERIAL': 0, 80 | 'TIC_CONTROL_MODE_STEP_DIR': 1, 81 | 'TIC_CONTROL_MODE_RC_POSITION': 2, 82 | 'TIC_CONTROL_MODE_RC_SPEED': 3, 83 | 'TIC_CONTROL_MODE_ANALOG_POSITION': 4, 84 | 'TIC_CONTROL_MODE_ANALOG_SPEED': 5, 85 | 'TIC_CONTROL_MODE_ENCODER_POSITION': 6, 86 | 'TIC_CONTROL_MODE_ENCODER_SPEED': 7, 87 | 'TIC_SCALING_DEGREE_LINEAR': 0, 88 | 'TIC_SCALING_DEGREE_QUADRATIC': 1, 89 | 'TIC_SCALING_DEGREE_CUBIC': 2, 90 | 'TIC_STEP_MODE_FULL': 0, 91 | 'TIC_STEP_MODE_HALF': 1, 92 | 'TIC_STEP_MODE_MICROSTEP1': 0, 93 | 'TIC_STEP_MODE_MICROSTEP2': 1, 94 | 'TIC_STEP_MODE_MICROSTEP4': 2, 95 | 'TIC_STEP_MODE_MICROSTEP8': 3, 96 | 'TIC_STEP_MODE_MICROSTEP16': 4, 97 | 'TIC_STEP_MODE_MICROSTEP32': 5, 98 | 'TIC_DECAY_MODE_MIXED': 0, 99 | 'TIC_DECAY_MODE_SLOW': 1, 100 | 'TIC_DECAY_MODE_FAST': 2, 101 | 'TIC_DECAY_MODE_MODE3': 3, 102 | 'TIC_DECAY_MODE_MODE4': 4, 103 | 'TIC_DECAY_MODE_T825_MIXED': 0, 104 | 'TIC_DECAY_MODE_T825_SLOW': 1, 105 | 'TIC_DECAY_MODE_T825_FAST': 2, 106 | 'TIC_DECAY_MODE_T834_MIXED50': 0, 107 | 'TIC_DECAY_MODE_T834_SLOW': 1, 108 | 'TIC_DECAY_MODE_T834_FAST': 2, 109 | 'TIC_DECAY_MODE_T834_MIXED25': 3, 110 | 'TIC_DECAY_MODE_T834_MIXED75': 4, 111 | 'TIC_DECAY_MODE_T500_AUTO': 0, 112 | 'TIC_PIN_PULLUP': 7, 113 | 'TIC_PIN_ANALOG': 6, 114 | 'TIC_PIN_FUNC_POSN': 0, 115 | 'TIC_PIN_FUNC_MASK': 0x0F, 116 | 'TIC_PIN_FUNC_DEFAULT': 0, 117 | 'TIC_PIN_FUNC_USER_IO': 1, 118 | 'TIC_PIN_FUNC_USER_INPUT': 2, 119 | 'TIC_PIN_FUNC_POT_POWER': 3, 120 | 'TIC_PIN_FUNC_SERIAL': 4, 121 | 'TIC_PIN_FUNC_RC': 5, 122 | 'TIC_PIN_FUNC_ENCODER': 6, 123 | 'TIC_PIN_FUNC_KILL_SWITCH': 7, 124 | 'TIC_CMD_SET_TARGET_POSITION': 0xE0, 125 | 'TIC_CMD_SET_TARGET_VELOCITY': 0xE3, 126 | 'TIC_CMD_HALT_AND_SET_POSITION': 0xEC, 127 | 'TIC_CMD_HALT_AND_HOLD': 0x89, 128 | 'TIC_CMD_RESET_COMMAND_TIMEOUT': 0x8C, 129 | 'TIC_CMD_DEENERGIZE': 0x86, 130 | 'TIC_CMD_ENERGIZE': 0x85, 131 | 'TIC_CMD_EXIT_SAFE_START': 0x83, 132 | 'TIC_CMD_ENTER_SAFE_START': 0x8F, 133 | 'TIC_CMD_RESET': 0xB0, 134 | 'TIC_CMD_CLEAR_DRIVER_ERROR': 0x8A, 135 | 'TIC_CMD_SET_MAX_SPEED': 0xE6, 136 | 'TIC_CMD_SET_STARTING_SPEED': 0xE5, 137 | 'TIC_CMD_SET_MAX_ACCEL': 0xEA, 138 | 'TIC_CMD_SET_MAX_DECEL': 0xE9, 139 | 'TIC_CMD_SET_STEP_MODE': 0x94, 140 | 'TIC_CMD_SET_CURRENT_LIMIT': 0x91, 141 | 'TIC_CMD_SET_DECAY_MODE': 0x92, 142 | 'TIC_CMD_GET_VARIABLE': 0xA1, 143 | 'TIC_CMD_GET_VARIABLE_AND_CLEAR_ERRORS_OCCURRED': 0xA2, 144 | 'TIC_CMD_GET_SETTING': 0xA8, 145 | 'TIC_CMD_SET_SETTING': 0x13, 146 | 'TIC_CMD_REINITIALIZE': 0x10, 147 | 'TIC_CMD_START_BOOTLOADER': 0xFF, 148 | 'TIC_CMD_GET_DEBUG_DATA': 0x20, 149 | 'TIC_VAR_OPERATION_STATE': 0x00, 150 | 'TIC_VAR_MISC_FLAGS1': 0x01, 151 | 'TIC_VAR_ERROR_STATUS': 0x02, 152 | 'TIC_VAR_ERRORS_OCCURRED': 0x04, 153 | 'TIC_VAR_PLANNING_MODE': 0x09, 154 | 'TIC_VAR_TARGET_POSITION': 0x0A, 155 | 'TIC_VAR_TARGET_VELOCITY': 0x0E, 156 | 'TIC_VAR_STARTING_SPEED': 0x12, 157 | 'TIC_VAR_MAX_SPEED': 0x16, 158 | 'TIC_VAR_MAX_DECEL': 0x1A, 159 | 'TIC_VAR_MAX_ACCEL': 0x1E, 160 | 'TIC_VAR_CURRENT_POSITION': 0x22, 161 | 'TIC_VAR_CURRENT_VELOCITY': 0x26, 162 | 'TIC_VAR_ACTING_TARGET_POSITION': 0x2A, 163 | 'TIC_VAR_TIME_SINCE_LAST_STEP': 0x2E, 164 | 'TIC_VAR_DEVICE_RESET': 0x32, 165 | 'TIC_VAR_VIN_VOLTAGE': 0x33, 166 | 'TIC_VAR_UP_TIME': 0x35, 167 | 'TIC_VAR_ENCODER_POSITION': 0x39, 168 | 'TIC_VAR_RC_PULSE_WIDTH': 0x3D, 169 | 'TIC_VAR_ANALOG_READING_SCL': 0x3F, 170 | 'TIC_VAR_ANALOG_READING_SDA': 0x41, 171 | 'TIC_VAR_ANALOG_READING_TX': 0x43, 172 | 'TIC_VAR_ANALOG_READING_RX': 0x45, 173 | 'TIC_VAR_DIGITAL_READINGS': 0x47, 174 | 'TIC_VAR_PIN_STATES': 0x48, 175 | 'TIC_VAR_STEP_MODE': 0x49, 176 | 'TIC_VAR_CURRENT_LIMIT': 0x4A, 177 | 'TIC_VAR_DECAY_MODE': 0x4B, 178 | 'TIC_VAR_INPUT_STATE': 0x4C, 179 | 'TIC_VAR_INPUT_AFTER_AVERAGING': 0x4D, 180 | 'TIC_VAR_INPUT_AFTER_HYSTERESIS': 0x4F, 181 | 'TIC_VAR_INPUT_AFTER_SCALING': 0x51, 182 | 'TIC_VARIABLES_SIZE': 0x55, 183 | 'TIC_SETTING_NOT_INITIALIZED': 0x00, 184 | 'TIC_SETTING_CONTROL_MODE': 0x01, 185 | 'TIC_SETTING_NEVER_SLEEP': 0x02, 186 | 'TIC_SETTING_DISABLE_SAFE_START': 0x03, 187 | 'TIC_SETTING_IGNORE_ERR_LINE_HIGH': 0x04, 188 | 'TIC_SETTING_SERIAL_BAUD_RATE_GENERATOR': 0x05, 189 | 'TIC_SETTING_SERIAL_DEVICE_NUMBER': 0x07, 190 | 'TIC_SETTING_AUTO_CLEAR_DRIVER_ERROR': 0x08, 191 | 'TIC_SETTING_COMMAND_TIMEOUT': 0x09, 192 | 'TIC_SETTING_SERIAL_CRC_ENABLED': 0x0B, 193 | 'TIC_SETTING_LOW_VIN_TIMEOUT': 0x0C, 194 | 'TIC_SETTING_LOW_VIN_SHUTOFF_VOLTAGE': 0x0E, 195 | 'TIC_SETTING_LOW_VIN_STARTUP_VOLTAGE': 0x10, 196 | 'TIC_SETTING_HIGH_VIN_SHUTOFF_VOLTAGE': 0x12, 197 | 'TIC_SETTING_VIN_CALIBRATION': 0x14, 198 | 'TIC_SETTING_RC_MAX_PULSE_PERIOD': 0x16, 199 | 'TIC_SETTING_RC_BAD_SIGNAL_TIMEOUT': 0x18, 200 | 'TIC_SETTING_RC_CONSECUTIVE_GOOD_PULSES': 0x1A, 201 | 'TIC_SETTING_INVERT_MOTOR_DIRECTION': 0x1B, 202 | 'TIC_SETTING_INPUT_ERROR_MIN': 0x1C, 203 | 'TIC_SETTING_INPUT_ERROR_MAX': 0x1E, 204 | 'TIC_SETTING_INPUT_SCALING_DEGREE': 0x20, 205 | 'TIC_SETTING_INPUT_INVERT': 0x21, 206 | 'TIC_SETTING_INPUT_MIN': 0x22, 207 | 'TIC_SETTING_INPUT_NEUTRAL_MIN': 0x24, 208 | 'TIC_SETTING_INPUT_NEUTRAL_MAX': 0x26, 209 | 'TIC_SETTING_INPUT_MAX': 0x28, 210 | 'TIC_SETTING_OUTPUT_MIN': 0x2A, 211 | 'TIC_SETTING_INPUT_AVERAGING_ENABLED': 0x2E, 212 | 'TIC_SETTING_INPUT_HYSTERESIS': 0x2F, 213 | 'TIC_SETTING_CURRENT_LIMIT_DURING_ERROR': 0x31, 214 | 'TIC_SETTING_OUTPUT_MAX': 0x32, 215 | 'TIC_SETTING_SWITCH_POLARITY_MAP': 0x36, 216 | 'TIC_SETTING_ENCODER_POSTSCALER': 0x37, 217 | 'TIC_SETTING_SCL_CONFIG': 0x3B, 218 | 'TIC_SETTING_SDA_CONFIG': 0x3C, 219 | 'TIC_SETTING_TX_CONFIG': 0x3D, 220 | 'TIC_SETTING_RX_CONFIG': 0x3E, 221 | 'TIC_SETTING_RC_CONFIG': 0x3F, 222 | 'TIC_SETTING_CURRENT_LIMIT': 0x40, 223 | 'TIC_SETTING_STEP_MODE': 0x41, 224 | 'TIC_SETTING_DECAY_MODE': 0x42, 225 | 'TIC_SETTING_STARTING_SPEED': 0x43, 226 | 'TIC_SETTING_MAX_SPEED': 0x47, 227 | 'TIC_SETTING_MAX_DECEL': 0x4B, 228 | 'TIC_SETTING_MAX_ACCEL': 0x4F, 229 | 'TIC_SETTING_SOFT_ERROR_RESPONSE': 0x53, 230 | 'TIC_SETTING_SOFT_ERROR_POSITION': 0x54, 231 | 'TIC_SETTING_ENCODER_PRESCALER': 0x58, 232 | 'TIC_SETTING_ENCODER_UNLIMITED': 0x5C, 233 | 'TIC_SETTING_KILL_SWITCH_MAP': 0x5D, 234 | 'TIC_SETTING_SERIAL_RESPONSE_DELAY': 0x5E, 235 | 'TIC_SETTINGS_SIZE': 0x5F, 236 | 'TIC_BAUD_RATE_GENERATOR_FACTOR': 12000000, 237 | 'TIC_MAX_USB_RESPONSE_SIZE': 128, 238 | 'TIC_INPUT_NULL': 0xFFFF, 239 | 'TIC_CONTROL_PIN_COUNT': 5, 240 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # PyTic v0.0.4 3 | 4 | ![pololu tic](images/pololu_tic.png) 5 | 6 | --- 7 | 8 | ## Introduction 9 | 10 | `PyTic` is an object-oriented Python wrapper for the Pololu Tic stepper driver series. The wrapper interacts with the stepper driver device using the API described in the [Pololu-Tic-Software][pololu_tic_software] GitHub page using the ctypes library. The device comunication protocol is USB. 11 | 12 | --- 13 | 14 | ## Installation 15 | 16 | ### Prerequisites 17 | 18 | `PyTic` requires the [Tic Software and Drivers for Windows][tic_drivers_win] msi provided by Pololu to be installed as a prerequisite. Other operating system drivers can be found on the [Pololu Tic Resources][tic_resources], but are not currently supported by this Python package. 19 | 20 | 21 | ### Pip Install 22 | 23 | To install the `PyTic` package on a Windows machine equipped with Python 2.7 or higher, run the following `pip` command: 24 | 25 | ```console 26 | C:\> pip install pytic 27 | ``` 28 | 29 | * Note: Only Windows x64 machines are supported at this time. 30 | 31 | --- 32 | 33 | 34 | ## Package Architecture 35 | 36 | `PyTic` encompasses almost all functionality present in the original C-API with some additional features. The __Pololu Tic Stepper Driver__ is represented in Python using the `pytic.PyTic()` object. 37 | 38 | ```console 39 | ---------------------------------- 40 | | Package Relation Tree | 41 | ---------------------------------- 42 | 43 | PyTic [Tic Object] 44 | |-- Settings [Structure Interface Object] 45 | |- Pin Settings [Structure Interface Object] [List] 46 | |-- Variables [Structure Interface Object] 47 | |- Pin Info [Structure Interface Object] [List] 48 | |-- Logger [Notification] 49 | 50 | PyTic_Protocol [Module] 51 | |-- Tic Constants [Dictionary] 52 | ``` 53 | 54 | ### PyTic Protocol (C-Constants Dictionary) 55 | 56 | 57 | The __Pololu Tic C-API__ uses `CAPS_DEFINED_CONSTANTS` for setting many of its parameters that represent an integer value. These contants set parameters such as pin function, step mode, etc. The `PyTic` package auto-imports these values from the [tic_protocol.h][tic_protocol_h] header file and stores them in a Python dictionary named `tic_constants` in the `pytic_protocol` module. See [Using Settings](#using_settings) in the [Example Code](#example_code) section to see how to use this dictionary in contect. 58 | 59 | ### Error Handling 60 | 61 | All __Pololu Tic C-API__ functions when dynamically imported into `PyTic` are wrapped in a higher-order function error handler called `TED()`, short for __[T]ic [E]rror [D]ecoder__. `TED()` will make all Tic wrapped functions return 0 from a successful call and 1 from a call that generated an error. In addition, `TED()` performs low-level bit mask decoding and writes the enumerated error value to the `PyTic` object internal log. This log can be output the ther terminal or file using the standard [logging][logging_lib] library. 62 | 63 | --- 64 | 65 | ## Example Code 66 | 67 | 68 | Outlined in this section are several examples of how to use `PyTic` to control a __Pololu Tic Stepper Driver__. The objective of this section is to show the `PyTic` syntax used to implement the __Pololu Tic Stepper Driver C-API__ as opposed to detail each of the available functions. For a full list of commands, settings, and variable information please refer to either the [Pololu Tic Manual][pololu_tic_manual], the [Pololu Tic C-API][tic_h], or this package's source code. 69 | 70 | ### Simple Program 71 | 72 | 73 | The simple program below demonstrates how to connect to a __Pololu Tic Stepper Driver__ device over USB and move to several positions after the previous position has been reached. 74 | 75 | ```python 76 | import pytic 77 | from time import sleep 78 | 79 | # - Initialization ------------------------------------------- 80 | 81 | tic = pytic.PyTic() 82 | 83 | # Connect to first available Tic Device serial number over USB 84 | serial_nums = tic.list_connected_device_serial_numbers() 85 | tic.connect_to_serial_number(serial_nums[0]) 86 | 87 | # Load configuration file and apply settings 88 | tic.settings.load_config('path\\to\\config.yml') 89 | tic.settings.apply() 90 | 91 | # - Motion Command Sequence ---------------------------------- 92 | 93 | # Zero current motor position 94 | tic.halt_and_set_position(0) 95 | 96 | # Energize Motor 97 | tic.energize() 98 | tic.exit_safe_start() 99 | 100 | # Move to listed positions 101 | positions = [1000, 2000, 3000, 0] 102 | for p in positions: 103 | tic.set_target_position(p) 104 | while tic.variables.current_position != tic.variables.target_position: 105 | sleep(0.1) 106 | 107 | # De-energize motor and get error status 108 | tic.enter_safe_start() 109 | tic.deenergize() 110 | print(tic.variables.error_status) 111 | ``` 112 | 113 | * Note: Modified settings will not take effect until `PyTic.settings.apply()` method is called. This is to avoid unnecessary writes to non-volitile memory. 114 | 115 | ### Using Settings 116 | 117 | 118 | The `PyTic.settings` structure interface object is used to alter device settings stored in non-volitile memory. As detailed above in [PyTic Protocol](#pytic_protocol), some of these settings have enumerated constants to maintain a user-friendly interaction. The code sample below demonstrates how to interact with `PyTic.settings` using the `tic_constant` dictionary. To avoid unnecissary writes to non-volitile memory, the `PyTic.settings.apply()` function must be called for the new settings to take effect. 119 | 120 | ```python 121 | 122 | # ... assume PyTic object initialized and connected to device as 'tic' 123 | 124 | # Load Tic Constant Dictionary 125 | tc = pytic.pytic_protocol.tic_constant 126 | 127 | # Modify individual properties of composite settings object 128 | tic.settings.product = tc['TIC_PRODUCT_T825'] 129 | tic.settings.step_mode = tc['TIC_STEP_MODE_MICROSTEP16'] 130 | 131 | # Turn the Serial RX Pin into a generic digital user input 132 | pin = tc['TIC_PIN_NUM_RX'] 133 | tic.settings.pin_setting[pin].func = tc['TIC_PIN_FUNC_USER_INPUT'] 134 | tic.settings.pin_setting[pin].pullup = True 135 | 136 | # Required to burn new settings to Tic non-volitile memory 137 | tic.settings.apply() 138 | 139 | ``` 140 | 141 | 142 | 150 | 151 | --- 152 | ## Logging 153 | 154 | `PyTic` uses the `logging` package to display Tic status messages. The default logging level is `logging.DEBUG`. For less verbose logging, set `PyTic.log_level = logging.CRITICAL`. The log name is `PyTic` for users that would like to have a parent-object handle the logging information. 155 | 156 | 157 | --- 158 | ## Example YAML Configuration File 159 | 160 | `PyTic` settings can be set invidually using the `PyTic.settings` structure interface in the script or all-at-once using a YAML config file and the `PyTic.settings.load_config('\\path\\to\\config.yml')` function. Here is an example YAML config file with some usage notes, 161 | 162 | ```yaml 163 | tic_settings: # required header for load_config fcn. 164 | product: TIC_PRODUCT_T825 165 | auto_clear_driver_error: True # ** These 4 settings ** 166 | ignore_err_line_high: True # ** were experimentally ** 167 | serial_crc_enabled: False # ** determined to stabalize ** 168 | command_timeout: 0 # ** device performance ** 169 | max_speed: 180000000 # pulses/s * 10^-4 170 | starting_speed: 0 # pulses/s * 10^-4 171 | max_accel: 9000000 # pulses/s^2 * 10^-2 172 | max_decel: 9000000 # pulses/s^2 * 10^-2 173 | step_mode: TIC_STEP_MODE_MICROSTEP16 174 | current_limit: 640 # mA, Only select values acceptable, See notes. 175 | decay_mode: TIC_DECAY_MODE_T825_FAST 176 | pin_settings: # Ex. Modifying Default Pin Fcn. 177 | - pin_num: TIC_PIN_NUM_RX 178 | func: TIC_PIN_FUNC_USER_INPUT 179 | pullup: True 180 | analog: False 181 | # - pin_id: TIC_PIN_NUM_TX # ... modifying a 2nd pin ... 182 | # func: TIC_PIN_FUNC_USER_INPUT 183 | # polarity: True 184 | # analog: False 185 | ``` 186 | 187 | Notes: 188 | * `CAPS_DEFINED_CONSTANTS` are keys for the `tic_constant` dictionary located in `pytic_protocol.py`. Refer to section [Using Settings](#using_settings) for more details on the dictionary and its use. 189 | * `current_limit` only accepts select values detailed in the [Pololu Tic Manual][pololu_tic_manual] 190 | 191 | 192 | --- 193 | 194 | ## Dependencies 195 | 196 | Dependencies include the following, 197 | 198 | * PyYAML 199 | 200 | --- 201 | 202 | ## Level of Support Notice 203 | 204 | This code is currently not supported. It is being released to the community AS IS without any guarantee of support. The community is welcome to submit issues, but should not expect an active response. 205 | 206 | --- 207 | 208 | ## External Resources 209 | 210 | External resources include the following, 211 | 212 | * [logging Library][logging_lib] 213 | * [Pololu-Tic-Software GitHub][pololu_tic_software] 214 | * [Pololu Tic Manual][pololu_tic_manual] 215 | * [Pololu Tic Resources][tic_resources] 216 | * [tic.h][tic_h] 217 | * [tic_protocol.h][tic_protocol_h] 218 | * [Tic Software and Drivers for Windows][tic_drivers_win] 219 | 220 | [pololu_tic_software]: https://github.com/pololu/pololu-tic-software 221 | [pololu_tic_manual]: https://www.pololu.com/docs/0J71 222 | [logging_lib]: https://docs.python.org/3/library/logging.html 223 | [tic_protocol_h]: https://github.com/pololu/pololu-tic-software/blob/a75c204a2255554e21cc5351c528d930ba5d2c38/include/tic_protocol.h 224 | [tic_drivers_win]: https://www.pololu.com/file/0J1325/pololu-tic-1.6.2-win.msi 225 | [tic_resources]:https://www.pololu.com/product/3131/resources 226 | [tic_h]: https://github.com/pololu/pololu-tic-software/blob/master/include/tic.h -------------------------------------------------------------------------------- /pytic/pytic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import yaml 4 | from ctypes import * 5 | from time import sleep 6 | from .pytic_protocol import tic_constant as tc 7 | from .pytic_structures import * 8 | from functools import wraps, partial 9 | import logging 10 | 11 | # [T]ic [E]rror [D]ecoder 12 | def TED(func): 13 | @wraps(func) 14 | def func_wrapper(*args, **kwargs): 15 | _e_p = func(*args, **kwargs) 16 | if bool(_e_p): 17 | _e = cast(_e_p, POINTER(tic_error)) 18 | _logger = logging.getLogger('PyTic') 19 | _logger.error(_e.contents.message) 20 | return 1 21 | else: 22 | return 0 23 | return func_wrapper 24 | 25 | class PyTic(object): 26 | def __init__(self, log_file=None): 27 | self._load_drivers() 28 | self._logger = self._initialize_logger() 29 | self.device = None 30 | self.handle = None 31 | self.settings = None 32 | self.variables = None 33 | self._commands = [('set_target_position', c_int32), 34 | ('set_target_velocity', c_int32), 35 | ('halt_and_set_position', c_int32), 36 | ('halt_and_hold', None), 37 | ('reset_command_timeout', None), 38 | ('deenergize', None), 39 | ('energize', None), 40 | ('exit_safe_start', None), 41 | ('enter_safe_start', None), 42 | ('reset', None), 43 | ('clear_driver_error', None), 44 | ('set_max_speed', c_uint32), 45 | ('set_starting_speed', c_uint32), 46 | ('set_max_accel', c_uint32), 47 | ('set_max_decel', c_uint32), 48 | ('set_step_mode', c_uint8), 49 | ('set_current_limit', c_uint32), 50 | ('set_current_limit_code', c_uint8), 51 | ('set_decay_mode', c_uint8)] 52 | self._create_tic_command_attributes() 53 | 54 | def _initialize_logger(self): 55 | # - Logging - 56 | self._log_level = logging.DEBUG 57 | _logger = logging.getLogger('PyTic') 58 | _logger.setLevel(self._log_level) 59 | _formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 60 | # Console Logging 61 | _ch = logging.StreamHandler() 62 | _ch.setLevel(self._log_level) 63 | _ch.setFormatter(_formatter) 64 | _logger.addHandler(_ch) 65 | return _logger 66 | 67 | @property 68 | def log_level(self): 69 | return self._log_level 70 | 71 | @log_level.setter 72 | def log_level(self, level): 73 | self._log_level = level 74 | self._logger.setLevel(level) 75 | 76 | def _load_drivers(self): 77 | # Driver Locations (x64) 78 | file_path = os.path.dirname(os.path.abspath(__file__)) 79 | #file_path = file_path[:-len('pytic')] 80 | self.usblib = windll.LoadLibrary(file_path+"\\drivers\\x64\\libusbp-1.dll") 81 | self.ticlib = windll.LoadLibrary(file_path+"\\drivers\\x64\\libpololu-tic-1.dll") 82 | 83 | def _create_tic_command_attributes(self): 84 | for c in self._commands: 85 | if bool(c[1]): 86 | setattr(self.__class__, c[0], partial(self._tic_command_with_value, c[0], c[1])) 87 | else: 88 | setattr(self.__class__, c[0], partial(self._tic_command, c[0])) 89 | 90 | @TED 91 | def _tic_command(self, cmd_name): 92 | e_p = getattr(self.ticlib,'tic_'+ cmd_name)(byref(self.handle)) 93 | return e_p 94 | 95 | @TED 96 | def _tic_command_with_value(self, cmd_name, c_type, value): 97 | if 'TIC' in str(value): 98 | value = tc[value] 99 | e_p = getattr(self.ticlib,'tic_'+ cmd_name)(byref(self.handle), c_type(value)) 100 | return e_p 101 | 102 | @TED 103 | def _list_connected_devices(self): 104 | self._devcnt = c_size_t(0) 105 | self._dev_pp = POINTER(POINTER(tic_device))() 106 | e_p = self.ticlib.tic_list_connected_devices(byref(self._dev_pp), byref(self._devcnt)) 107 | return e_p 108 | 109 | @TED 110 | def _tic_handle_open(self): 111 | handle_p = POINTER(tic_handle)() 112 | e_p = self.ticlib.tic_handle_open(byref(self.device), byref(handle_p)) 113 | self.handle = handle_p[0] 114 | return e_p 115 | 116 | def list_connected_device_serial_numbers(self): 117 | self._list_connected_devices() 118 | tic_list = [] 119 | if not self._devcnt.value: 120 | print("No Tic devices connected.") 121 | for i in range(0, self._devcnt.value): 122 | ticdev = self._dev_pp[0][i] 123 | tic_list.append(ticdev.serial_number.decode('utf-8')) 124 | # print("Tic Device #: {0}, Serial #: {1}".format(i, sn)) 125 | return tic_list 126 | 127 | def connect_to_serial_number(self, serial_number): 128 | self._list_connected_devices() 129 | for i in range(0, self._devcnt.value): 130 | if serial_number == self._dev_pp[0][i].serial_number.decode('utf-8'): 131 | self.device = self._dev_pp[0][i] 132 | self._tic_handle_open() 133 | self.variables = PyTic_Variables(self.handle, (self.usblib, self.ticlib)) 134 | self.settings = PyTic_Settings(self.handle, (self.usblib, self.ticlib), self.variables.product) 135 | return 0 136 | if not self.device: 137 | self._logger.error("Serial number device not found.") 138 | return 1 139 | 140 | 141 | class PyTic_Variables(object): 142 | def __init__(self, device_handle, driver_handles): 143 | self.usblib, self.ticlib = driver_handles 144 | self._logger = logging.getLogger('PyTic') 145 | self._device_handle = device_handle 146 | self._tic_variables_p = POINTER(tic_variables)() 147 | self._tic_variables = tic_variables() 148 | 149 | self.pin_info = [] 150 | for i in range(0, tc['TIC_CONTROL_PIN_COUNT']): 151 | self.pin_info.append(type('pinfo_'+str(i), (object,), {})()) 152 | 153 | self._convert_structure_to_readonly_properties() 154 | 155 | def _convert_structure_to_readonly_properties(self): 156 | for field in tic_variables._fields_: 157 | if not field[0] == 'pin_info': 158 | prop = property(fget=partial(self._get_tic_readonly_property, field[0])) 159 | setattr(self.__class__, field[0], prop) 160 | 161 | for i in range(0, tc['TIC_CONTROL_PIN_COUNT']): 162 | for field in pin_info._fields_: 163 | prop = property(fget=partial(self._get_pin_readonly_property, field[0], i)) 164 | setattr(self.pin_info[i].__class__, field[0], prop) 165 | 166 | @TED 167 | def _update_tic_variables(self): 168 | e_p = self.ticlib.tic_get_variables(byref(self._device_handle), \ 169 | byref(self._tic_variables_p), c_bool(True)) 170 | self._tic_variables = self._tic_variables_p[0] 171 | return e_p 172 | 173 | def _get_tic_readonly_property(self, field, obj): 174 | self._update_tic_variables() 175 | value = getattr(self._tic_variables, field) 176 | if field == "error_status" or field == "error_occurred": 177 | self._convert_error_bitmask(value) 178 | return value 179 | 180 | def _get_pin_readonly_property(self, field, pin_num, obj): 181 | self._update_tic_variables() 182 | return getattr(self._tic_variables.pin_info[pin_num], field) 183 | 184 | def _convert_error_bitmask(self, e_bit_mask): 185 | ecodes = ["TIC_ERROR_INTENTIONALLY_DEENERGIZED", 186 | "TIC_ERROR_MOTOR_DRIVER_ERROR", 187 | "TIC_ERROR_LOW_VIN", 188 | "TIC_ERROR_KILL_SWITCH", 189 | "TIC_ERROR_REQUIRED_INPUT_INVALID", 190 | "TIC_ERROR_SERIAL_ERROR", 191 | "TIC_ERROR_COMMAND_TIMEOUT", 192 | "TIC_ERROR_SAFE_START_VIOLATION", 193 | "TIC_ERROR_ERR_LINE_HIGH", 194 | "TIC_ERROR_SERIAL_FRAMING", 195 | "TIC_ERROR_SERIAL_RX_OVERRUN", 196 | "TIC_ERROR_SERIAL_FORMAT", 197 | "TIC_ERROR_SERIAL_CRC", 198 | "TIC_ERROR_ENCODER_SKIP"] 199 | for code in ecodes: 200 | if ((e_bit_mask >> tc[code]) & 1): 201 | self._logger.error(code) 202 | 203 | 204 | class PyTic_Settings(object): 205 | def __init__(self, device_handle, driver_handles, product): 206 | self.usblib, self.ticlib = driver_handles 207 | self._logger = logging.getLogger('PyTic') 208 | self._device_handle = device_handle 209 | # local vs device - local settings on pc, device settings on tic 210 | self._local_settings = tic_settings() 211 | self._device_settings = tic_settings() 212 | self._device_settings_p = POINTER(tic_settings)() 213 | 214 | self.pin_settings = [] 215 | for i in range(0, tc['TIC_CONTROL_PIN_COUNT']): 216 | self.pin_settings.append(type('pset_'+str(i), (object,), {})()) 217 | 218 | self._convert_structure_to_properties() 219 | self.auto_apply = False 220 | 221 | if "TIC" in str(product): 222 | product = int(tc[product]) 223 | self._fill_with_defaults(product) 224 | 225 | def _convert_structure_to_properties(self): 226 | for field in tic_settings._fields_: 227 | if not field[0] == 'pin_settings': 228 | prop = property(fget=partial(self._get_tic_settings_from_device, field[0]), 229 | fset=partial(self._set_tic_settings_with_option, field[0])) 230 | setattr(self.__class__, field[0], prop) 231 | 232 | for i in range(0, tc['TIC_CONTROL_PIN_COUNT']): 233 | for field in pin_settings._fields_: 234 | prop = property(fget=partial(self._get_pin_settings_from_device, field[0], i), 235 | fset=partial(self._set_pin_settings_with_option, field[0], i)) 236 | setattr(self.pin_settings[i].__class__, field[0], prop) 237 | 238 | def _get_tic_settings_from_device(self, field, obj): 239 | self._pull_device_settings() 240 | return getattr(self._device_settings, field) 241 | 242 | def _set_tic_settings_with_option(self, field, obj, value): 243 | setattr(self._local_settings, field, value) 244 | if (self.auto_apply): 245 | self.apply() 246 | 247 | def _get_pin_settings_from_device(self, field, pin_num, obj): 248 | self._pull_device_settings() 249 | return getattr(self._device_settings.pin_settings[pin_num], field) 250 | 251 | def _set_pin_settings_with_option(self, field, pin_num, obj, value): 252 | setattr(self._local_settings.pin_settings[pin_num], field) 253 | if (self.auto_apply): 254 | self.apply() 255 | 256 | 257 | @TED 258 | def _pull_device_settings(self): 259 | e_p = self.ticlib.tic_get_settings(byref(self._device_handle), 260 | byref(self._device_settings_p)) 261 | self._device_settings = self._device_settings_p[0] 262 | return e_p 263 | 264 | @TED 265 | def _set_settings(self): 266 | e_p = self.ticlib.tic_set_settings(byref(self._device_handle), 267 | byref(self._local_settings)) 268 | return e_p 269 | 270 | def _fill_with_defaults(self, product): 271 | self._local_settings.product = product 272 | self.ticlib.tic_settings_fill_with_defaults(byref(self._local_settings)) 273 | 274 | def apply(self): 275 | self._settings_fix() 276 | self._set_settings() 277 | self._reinitialize() 278 | 279 | @TED 280 | def _settings_fix(self): 281 | warnings_p = POINTER(c_char_p)() 282 | e_p = self.ticlib.tic_settings_fix(byref(self._local_settings),warnings_p) 283 | if bool(warnings_p): 284 | for w in warnings_p: 285 | self._logger.warning(w) 286 | return e_p 287 | 288 | @TED 289 | def _reinitialize(self): 290 | e_p = self.ticlib.tic_reinitialize(byref(self._device_handle)) 291 | return e_p 292 | 293 | def load_config(self, config_file): 294 | with open(config_file, 'r') as ymlfile: 295 | if yaml.__version__.split('.')[:2] < ['5', '1']: 296 | cfg = yaml.load(ymlfile) 297 | else: 298 | cfg = yaml.load(ymlfile, yaml.SafeLoader) 299 | 300 | cfg_settings = cfg['tic_settings'] 301 | 302 | tic_settings_list = [] 303 | for setting in tic_settings._fields_: 304 | tic_settings_list.append(setting[0]) 305 | 306 | for setting in cfg_settings: 307 | if setting in tic_settings_list: 308 | if setting == 'pin_settings': 309 | for pin in cfg_settings['pin_settings']: 310 | i = tc[pin['pin_num']] 311 | if 'func' in pin: 312 | self._local_settings.pin_settings[i].func = tc[pin['func']] 313 | if 'pullup' in pin: 314 | self._local_settings.pin_settings[i].pullup = pin['pullup'] 315 | if 'analog' in pin: 316 | self._local_settings.pin_settings[i].analog = pin['analog'] 317 | if 'polarity' in pin: 318 | self._local_settings.pin_settings[i].polarity = pin['polarity'] 319 | else: 320 | if 'TIC' in str(cfg_settings[setting]): 321 | value = tc[cfg_settings[setting]] 322 | else: 323 | value = cfg_settings[setting] 324 | setattr(self._local_settings, setting, value) 325 | 326 | if (self.auto_apply): 327 | self.apply() 328 | 329 | if __name__ == '__main__': 330 | 331 | tic = PyTic() 332 | print(tic.list_connected_device_serial_numbers()) 333 | --------------------------------------------------------------------------------