diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 720d4db..ffd1522 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -6,5 +6,5 @@ jobs: call_workflow: uses: ./.github/workflows/Testbase.yml with: - python: '3.10' + python: '3.11' qt5: 'pyqt5' \ No newline at end of file diff --git a/.github/workflows/Testbase.yml b/.github/workflows/Testbase.yml index ed4517b..614121e 100644 --- a/.github/workflows/Testbase.yml +++ b/.github/workflows/Testbase.yml @@ -18,9 +18,9 @@ jobs: QT_DEBUG_PLUGINS: 1 steps: - name: Set up Python ${{ inputs.python }} - uses: actions/checkout@v3 + uses: actions/checkout@v4.2.2 - name: Install dependencies - uses: actions/setup-python@v4 + uses: actions/setup-python@v5.4.0 with: python-version: ${{ inputs.python }} - name: Install package @@ -29,7 +29,7 @@ jobs: python -m pip install --upgrade pip export QT_DEBUG_PLUGINS=1 pip install flake8 pytest pytest-cov pytest-qt pytest-xdist pytest-xvfb setuptools wheel numpy h5py ${{ inputs.qt5 }} toml - pip install pymodaq + pip install pymodaq pyqt5 pip install -e . - name: create local pymodaq folder and setting permissions run: | @@ -41,4 +41,4 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=src/pymodaq/resources/QtDesigner_Ressources,docs - name: Test with pytest run: | - pytest -n auto + pytest -n auto -k "not test_compatibility" \ No newline at end of file diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml new file mode 100644 index 0000000..7c601f5 --- /dev/null +++ b/.github/workflows/compatibility.yml @@ -0,0 +1,78 @@ +name: Compatibility with pymodaq (latest release) + +on: + workflow_call: + + pull_request: + + push: + branches: + - '*' + +concurrency: + # github.workflow: name of the workflow + # github.event.pull_request.number || github.ref: pull request number or branch name if not a pull request + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + # Cancel in-progress runs when a new workflow with the same group name is triggered + cancel-in-progress: true + +jobs: + tests: + continue-on-error: true + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "windows-latest"] + python-version: ["3.9", "3.10", "3.11", "3.12"] + qt-backend: ["pyqt5", "pyqt6", "pyside6"] + runs-on: ${{ matrix.os }} + env: + DISPLAY: ':99' + QT_DEBUG_PLUGINS: 1 + + steps: + - name: Set project name environment variable + run: | + echo "plugin_name=$(echo '${{ github.repository }}' | cut -d'/' -f2)" >> $GITHUB_ENV + + - name: Checkout the repo + uses: actions/checkout@v4.2.2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5.4.0 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest pytest-cov pytest-qt pytest-xvfb pytest-xdist setuptools wheel numpy h5py pymodaq ${{ matrix.qt-backend }} + pip install -e . + + # Create folder and set permissions on Ubuntu + - name: Create local pymodaq folder setup env (Linux) + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install -y libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-cursor0 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils libgl1 libegl1 + export QT_DEBUG_PLUGINS=1 + sudo mkdir -p /etc/.pymodaq + sudo chmod uo+rw /etc/.pymodaq + - name: Exporting debug variables (Windows) + if: runner.os == 'Windows' + run: | + set QT_DEBUG_PLUGINS=1 + + - name: Compatibility tests with ${{ matrix.os }} ${{ matrix.python-version }} ${{ matrix.qt-backend}} + run: | + pytest -vv -n 1 -k "test_compatibility" + + - name: Upload compatibility report + if: failure() + uses: actions/upload-artifact@v4.6.1 + with: + name: + path: 'import_report_tests_${{ env.plugin_name }}_None.txt' + if-no-files-found: error + + \ No newline at end of file diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 55326c5..78ca73f 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -13,25 +13,28 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4.2.2 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5.4.0 with: - python-version: '3.11' + python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel twine toml "pymodaq>=4.1.0" pyqt5 - - - name: create local pymodaq folder and setting permissions + pip install hatch hatchling toml twine + - name: Get history and tags for SCM versioning to work run: | - sudo mkdir /etc/.pymodaq - sudo chmod uo+rw /etc/.pymodaq - - - name: Build and publish + git branch + git fetch --prune --unshallow + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + hatch version + - name: Build + run: hatch build + - name: Check the build + run: twine check dist/* + - name: publish env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + HATCH_INDEX_USER: ${{ secrets.PYPI_USERNAME }} + HATCH_INDEX_AUTH: ${{ secrets.PYPI_PASSWORD }} run: | - python setup.py sdist bdist_wheel - twine upload dist/* + hatch publish \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 66a7ca5..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include README.rst -include LICENSE -graft src \ No newline at end of file diff --git a/hatch_build.py b/hatch_build.py new file mode 100644 index 0000000..f8302b7 --- /dev/null +++ b/hatch_build.py @@ -0,0 +1,10 @@ +from pathlib import Path +from hatchling.metadata.plugin.interface import MetadataHookInterface +from pymodaq_utils.resources.hatch_build_plugins import update_metadata_from_toml + +here = Path(__file__).absolute().parent + + +class PluginInfoTomlHook(MetadataHookInterface): + def update(self, metadata: dict) -> None: + update_metadata_from_toml(metadata, here) \ No newline at end of file diff --git a/plugin_info.toml b/plugin_info.toml deleted file mode 100644 index 1841bdd..0000000 --- a/plugin_info.toml +++ /dev/null @@ -1,24 +0,0 @@ -## To modify by developer(s) of the plugin - -[plugin-info] -SHORT_PLUGIN_NAME = 'arduino' #to be modified, for instance daqmx then rename the module name: -# (pymodaq_plugins_template become pymodaq_plugins_daqmx for instance) - -package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_arduino' #to modify -description = 'Set of instrument plugins implemented using an Arduino Board' - -author = 'Sebastien J. Weber' -author-email = 'sebastien.weber@cemes.fr' -license = 'MIT' - -[plugin-install] -#packages required for your plugin: -packages-required = ['pymodaq>=4.3.0', 'telemetrix', 'pyvisa', 'pyvisa-py'] - -[features] # defines the plugin features contained into this plugin -instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) -extensions = true # true if plugins contains dashboard extensions -models = false # true if plugins contains pid models or other models (optimisation...) -h5exporters = false # true if plugin contains custom h5 file exporters -scanners = false # true if plugin contains custom scan layout (daq_scan extensions) - diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e1a25f4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,65 @@ + +[features] # defines the plugin features contained into this plugin +instruments = true # true if plugin contains instrument classes (else false, notice the lowercase for toml files) +extensions = true # true if plugins contains dashboard extensions +models = false # true if plugins contains pid models +h5exporters = false # true if plugin contains custom h5 file exporters +scanners = false # true if plugin contains custom scan layout (daq_scan extensions) + +[urls] +package-url = 'https://github.com/PyMoDAQ/pymodaq_plugins_arduino' + +[project] +name = "pymodaq_plugins_arduino" #todo modify template by your plugin short name +description = 'Set of instrument plugins implemented using an Arduino Board.' +dependencies = [ + "pymodaq>=4.3.0", 'telemetrix', 'pyvisa', 'pyvisa-py' + #todo: list here all dependencies your package may have +] + +authors = [ + {name = "Sebastien J. Weber", email = "sebastien.weber@cemes.fr"}, + {name = "Jérémie Margueritat", email = "jeremie.margueritat@univ-lyon1.fr"}, + #todo: list here all authors of your plugin +] +maintainers = [ + {name = "Sebastien J. Weber", email = "sebastien.weber@cemes.fr"}, + {name = "Jérémie Margueritat", email = "jeremie.margueritat@univ-lyon1.fr"}, + #todo: list here all maintainers of your plugin +] + +# nottodo: leave everything below as is! + +dynamic = ["version", "urls", "entry-points"] +readme = "README.rst" +license = { file="LICENSE" } +requires-python = ">=3.8" + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Scientific/Engineering :: Human Machine Interfaces", + "Topic :: Scientific/Engineering :: Visualization", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: User Interfaces", +] + +[build-system] +requires = [ + "hatchling>=1.9.0", + "hatch-vcs", "toml", + "pymodaq_utils>=0.0.6", +] +build-backend = "hatchling.build" + +[tool.hatch.metadata.hooks.custom] + +[tool.hatch.version] +source = "vcs" diff --git a/setup.py b/setup.py deleted file mode 100644 index c85f63a..0000000 --- a/setup.py +++ /dev/null @@ -1,4 +0,0 @@ -from pymodaq.resources.setup_plugin import setup -from pathlib import Path - -setup(Path(__file__).parent) diff --git a/src/pymodaq_plugins_arduino/daq_move_plugins/daq_move_StepperMotor.py b/src/pymodaq_plugins_arduino/daq_move_plugins/daq_move_StepperMotor.py new file mode 100644 index 0000000..43b8b67 --- /dev/null +++ b/src/pymodaq_plugins_arduino/daq_move_plugins/daq_move_StepperMotor.py @@ -0,0 +1,151 @@ +from typing import Union, List, Dict, Optional +from pymodaq.control_modules.move_utility_classes import ( + DAQ_Move_base, comon_parameters_fun, main, DataActuatorType, DataActuator +) +from pymodaq_utils.utils import ThreadCommand # Object used to send info back to the main thread +from pymodaq_gui.parameter import Parameter +from pymodaq_plugins_arduino.hardware.arduino_telemetrix import Arduino +from pymodaq_plugins_arduino.utils import Config + +config = Config() + + +class DAQ_Move_StepperMotor(DAQ_Move_base): + """Plugin to control stepper motor using Arduino controller and PyMoDAQ. + + This object inherits all functionalities to communicate with PyMoDAQ’s DAQ_Move module through inheritance via + DAQ_Move_base. It makes a bridge between the DAQ_Move module and the Python wrapper of a particular instrument. + + Use the arduino_telemetrix wrapper to communicate with the Arduino Board. + It may work with up to 4 axes depending on the configuration. + It does not consider the daisy chain option: only one controller. + Tested with Arduino Uno and one motor NEMA17 (1 axis). + PyMoDAQ version during the test was PyMoDAQ==5.0.5. + The operating system used was Windows 11. + Telemetrix4arduino has to be uploaded on the Arduino board. + + Attributes + ---------- + controller: object + The particular object that allows communication with the hardware, in general, a Python wrapper around the + hardware library. + """ + is_multiaxes = True + _axis_names: Union[List[str], Dict[str, int]] = {'Stepper': config('stepper')} + _controller_units: Union[str, List[str]] = '' # steps + _epsilon: Union[float, List[float]] = 1 + data_actuator_type = DataActuatorType.float + + params = [ + { + 'title': 'Ports:', + 'name': 'com_port', + 'type': 'list', + 'value': config('com_port'), + 'limits': Arduino.COM_PORTS, + } + ] + comon_parameters_fun(is_multiaxes, axis_names=_axis_names, epsilon=_epsilon) + + def ini_attributes(self): + self.controller: Optional[Arduino] = None + + def get_actuator_value(self) -> float: + """Get the current value from the hardware with scaling conversion. + + Returns + ------- + float + The position obtained after scaling conversion. + """ + pos = self.controller.get_stepper_position() + pos = self.get_position_with_scaling(pos) + return pos + + def user_condition_to_reach_target(self) -> bool: + """Will be triggered for each end of move; abs, rel, or homing.""" + return self._move_done + + def close(self): + """Terminate the communication protocol.""" + self.controller.shutdown() + + def commit_settings(self, param: Parameter): + """Apply the consequences of a change of value in the detector settings. + + Parameters + ---------- + param: Parameter + A given parameter (within detector_settings) whose value has been changed by the user. + """ + pass + + def ini_stage(self, controller=None): + """Actuator communication initialization. + + Parameters + ---------- + controller: object + Custom object of a PyMoDAQ plugin (Slave case). None if only one actuator by controller (Master case). + + Returns + ------- + info: str + initialized: bool + False if initialization failed otherwise True. + """ + self.ini_stage_init(slave_controller=controller) # Useful when controller is slave + + if self.is_master: # Needed when controller is master + self.controller = Arduino( + com_port=self.settings['com_port'] + ) + self.controller.initialize_stepper_motor( + config('stepper', 'pins', 'pul_pin'), + config('stepper', 'pins', 'dir_pin'), + config('stepper', 'pins', 'ena_pin'), + ) # Pulse, direction, enable pins + + info = "Stepper motor connected with config file" + initialized = True # TODO: Replace with actual initialization check + return info, initialized + + def move_abs(self, value: DataActuator): + """Move the actuator to the absolute target defined by value. + + Parameters + ---------- + value: float + Value of the absolute target positioning. + """ + value = self.check_bound(value) # Apply bounds if user checked them + self.target_value = value + value = self.set_position_with_scaling(value) # Apply scaling if specified + self._move_done = self.controller.move_stepper_to_position(value) + self.emit_status(ThreadCommand('Update_Status', ['absolute move done'])) + + def move_rel(self, value: DataActuator): + """Move the actuator to the relative target actuator value defined by value. + + Parameters + ---------- + value: float + Value of the relative target positioning. + """ + value = self.check_bound(self.current_position + value) - self.current_position + self.target_value = value + self.current_position + value = self.set_position_relative_with_scaling(value) + self._move_done = self.controller.move_stepper_to_position(self.target_value) + self.emit_status(ThreadCommand('Update_Status', ['relative move done'])) + + def move_home(self): + """Call the reference method of the controller.""" + self.controller.move_stepper_to_position(0) # Move to home position (0) + self.emit_status(ThreadCommand('Update_Status', ['homing'])) + + def stop_motion(self): + """Stop the actuator and emit move_done signal.""" + pass + + +if __name__ == '__main__': + main(__file__) \ No newline at end of file diff --git a/src/pymodaq_plugins_arduino/hardware/arduino_telemetrix.py b/src/pymodaq_plugins_arduino/hardware/arduino_telemetrix.py index 24fa194..7d22b56 100644 --- a/src/pymodaq_plugins_arduino/hardware/arduino_telemetrix.py +++ b/src/pymodaq_plugins_arduino/hardware/arduino_telemetrix.py @@ -1,6 +1,5 @@ import numbers -from threading import Lock - +from threading import Lock, Event from pyvisa import ResourceManager from telemetrix import telemetrix @@ -27,7 +26,8 @@ def __init__(self, *args, **kwargs): 3: 0, 4: 0, 5: 0} # Initialized dictionary for 6 analog channels - + self.stepper_motor = None + @staticmethod def round_value(value): return max(0, min(255, int(value))) @@ -96,17 +96,69 @@ def servo_move_degree(self, pin: int, value: float): self.pin_values_output[pin] = value lock.release() + #Stepper Motor Methods + def initialize_stepper_motor(self, pulse_pin, direction_pin, enable_pin=7): + """ Initialize the stepper motor with the given pins """ + self.stepper_motor = self.set_pin_mode_stepper(interface=1, pin1=pulse_pin, pin2=direction_pin) + self.enable = enable_pin + self.set_pin_mode_digital_output(self.enable) # Set the enable pin as digital output + self.digital_write(self.enable , 1) # Disable the motor driver to avoid electrical consumption + self.stepper_set_current_position(self.stepper_motor, 0) # Set the current position to 0 + + def move_stepper_to_position(self, position: float, max_speed=200, acceleration=400): + """ Move the stepper motor to the specified position """ + if self.stepper_motor is None: + raise ValueError("Stepper motor not initialized. Call initialize_stepper_motor first.") + + # Set motor parameters + self.stepper_set_max_speed(self.stepper_motor, max_speed) + self.stepper_set_acceleration(self.stepper_motor, acceleration) + # Set the target position + self.stepper_move_to(self.stepper_motor, int(position)) + completion_event = Event() + def completion_callback(data): + """ Callback function to signal that the stepper motor has completed its movement """ + completion_event.set() + self.digital_write(self.enable, 0) + self.stepper_run(self.stepper_motor, completion_callback=completion_callback) + completion_event.wait() + self.digital_write(self.enable, 1) + return True + + def get_stepper_position(self): + """ Retrieve the current position of the stepper motor """ + position_event = Event() + def position_callback(data): + """ Callback function to retrieve the current position of the stepper motor """ + self.position = data[2] + position_event.set() + + self.stepper_get_current_position(self.stepper_motor, + current_position_callback=position_callback) + position_event.wait() + return self.position if __name__ == '__main__': import time - tele = Arduino('COM6') + tele = Arduino('COM10') + + # Test servo motor tele.set_pin_mode_servo(5, 100, 3000) time.sleep(.2) - tele.servo_write(5, 90) - time.sleep(1) - tele.servo_write(5, 00) + # Test stepper motor + tele.initialize_stepper_motor(pulse_pin=8, direction_pin=9, enable_pin=7) + tele.get_stepper_position() + time.sleep(0.2) + tele.move_stepper_to_position(500) # Move to position 2000 + time.sleep(0.2) + tele.get_stepper_position() + time.sleep(0.2) + tele.move_stepper_to_position(-500) # Move to position -2000 + time.sleep(0.2) + tele.get_stepper_position() + tele.shutdown() \ No newline at end of file diff --git a/src/pymodaq_plugins_arduino/hardware/stepper_absolute.py b/src/pymodaq_plugins_arduino/hardware/stepper_absolute.py new file mode 100644 index 0000000..0c42979 --- /dev/null +++ b/src/pymodaq_plugins_arduino/hardware/stepper_absolute.py @@ -0,0 +1,134 @@ +""" + Copyright (c) 2022 Alan Yorinks All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + Version 3 as published by the Free Software Foundation; either + or (at your option) any later version. + This library is distributed in the hope that it will be useful,f + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +""" +import sys +import time + +from telemetrix import telemetrix + +""" +Run a motor to an absolute position. Server will send a callback notification +when motion is complete. + +Motor used to test is a NEMA-17 size - 200 steps/rev, 12V 350mA. +And the driver is a TB6600 4A 9-42V Nema 17 Stepper Motor Driver. + +The driver was connected as follows: +VCC 12 VDC +GND Power supply ground +ENA- Not connected +ENA+ Not connected +DIR- GND +DIR+ GPIO Pin 23 ESP32 +PUL- ESP32 GND +PUL+ GPIO Pin 22 ESP32 +A-, A+ Coil 1 stepper motor +B-, B+ Coil 2 stepper motor + +""" + +# GPIO Pins +PULSE_PIN = 8 +DIRECTION_PIN = 9 + +# flag to keep track of the number of times the callback +# was called. When == 2, exit program +exit_flag = 0 + + +def the_callback(data): + global exit_flag + date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[2])) + print(f'Motor {data[1]} absolute motion completed at: {date}.') + exit_flag += 1 + + +def running_callback(data): + if data[1]: + print('The motor is running.') + else: + print('The motor IS NOT running.') + + +def step_absolute(the_board): + + global exit_flag + # create an accelstepper instance for a TB6600 motor drive + # if you are using a micro stepper controller board: + # pin1 = pulse pin, pin2 = direction + motor = the_board.set_pin_mode_stepper(interface=1, pin1=PULSE_PIN, + pin2=DIRECTION_PIN) + the_board.set_pin_mode_digital_output(7) + the_board.digital_write(7, 1) # enable the motor driver + + # if you are using a 28BYJ-48 Stepper Motor with ULN2003 + # comment out the line above and uncomment out the line below. + # motor = the_board.set_pin_mode_stepper(interface=4, pin1=5, pin2=4, pin3=14, + # pin4=12) + + # the_board.stepper_is_running(motor, callback=running_callback) + time.sleep(5) + the_board.digital_write(7, 0) + # set the max speed and acceleration + the_board.stepper_set_current_position(0, 0) + the_board.stepper_set_max_speed(motor, 400) + the_board.stepper_set_acceleration(motor, 800) + + # set the absolute position in steps + the_board.stepper_move_to(motor, 2000) + + # run the motor + print('Starting motor...') + the_board.stepper_run(motor, completion_callback=the_callback) + time.sleep(.2) + #the_board.stepper_is_running(motor, callback=running_callback) + #time.sleep(.2) + while exit_flag == 0: + time.sleep(.2) + + the_board.stepper_set_current_position(0, 0) + the_board.stepper_set_max_speed(motor, 400) + the_board.stepper_set_acceleration(motor, 800) + # set the absolute position in steps + print('Running motor in opposite direction') + the_board.stepper_move_to(motor, -2000) + + the_board.stepper_run(motor, completion_callback=the_callback) + time.sleep(.2) + ##the_board.stepper_is_running(motor, callback=running_callback) + #time.sleep(.2) + + # keep application running + while exit_flag < 10: + try: + time.sleep(.2) + except KeyboardInterrupt: + the_board.shutdown() + sys.exit(0) + the_board.shutdown() + sys.exit(0) + + +# instantiate telemetrix +board = telemetrix.Telemetrix() +try: + # start the main function + step_absolute(board) + board.shutdown() +except KeyboardInterrupt: + board.shutdown() + sys.exit(0) \ No newline at end of file diff --git a/src/pymodaq_plugins_arduino/hardware/test_stepper_class.py b/src/pymodaq_plugins_arduino/hardware/test_stepper_class.py new file mode 100644 index 0000000..637cf7b --- /dev/null +++ b/src/pymodaq_plugins_arduino/hardware/test_stepper_class.py @@ -0,0 +1,62 @@ +import sys +import time +from telemetrix import telemetrix +from threading import Event + + +class StepperMotorController: + def __init__(self, pulse_pin, direction_pin, enable_pin=7): + self.pulse_pin = pulse_pin + self.direction_pin = direction_pin + self.enable_pin = enable_pin + self.board = telemetrix.Telemetrix() + self.motor = self.board.set_pin_mode_stepper(interface=1, pin1=self.pulse_pin, pin2=self.direction_pin) + self.completion_event = Event() + + # Initialize the enable pin + self.board.set_pin_mode_digital_output(self.enable_pin) + self.board.digital_write(self.enable_pin, 1) # Disbale the motor driver + + def the_callback(self, data): + self.completion_event.set() # Signal that the motion is complete + + def move_to_position(self, position, max_speed=400, acceleration=800): + # Set motor parameters + self.board.stepper_set_current_position(self.motor, 0) + self.board.stepper_set_max_speed(self.motor, max_speed) + self.board.stepper_set_acceleration(self.motor, acceleration) + + # Set the target position + self.board.stepper_move_to(self.motor, position) + + # Run the motor and wait for completion + print(f'Starting motor to move to position {position}...') + self.completion_event.clear() + self.board.digital_write(self.enable_pin, 0) # Enable the motor driver + self.board.stepper_run(self.motor, completion_callback=self.the_callback) + self.completion_event.wait() # Block until the motion is complete + self.board.digital_write(self.enable_pin, 1) + + def shutdown(self): + self.board.shutdown() + # Disable the motor driver + + +if __name__ == "__main__": + try: + # Create an instance of the StepperMotorController + stepper = StepperMotorController(pulse_pin=8, direction_pin=9) + + # Move to the first position + stepper.move_to_position(2000) + + # Move to the opposite position + print('Running motor in opposite direction...') + stepper.move_to_position(-2000) + + # Shutdown the board + stepper.shutdown() + + except KeyboardInterrupt: + stepper.shutdown() + sys.exit(0) \ No newline at end of file diff --git a/src/pymodaq_plugins_arduino/resources/config_template.toml b/src/pymodaq_plugins_arduino/resources/config_template.toml index 13b207c..c66549b 100644 --- a/src/pymodaq_plugins_arduino/resources/config_template.toml +++ b/src/pymodaq_plugins_arduino/resources/config_template.toml @@ -17,4 +17,11 @@ cols = 16 rows = 2 [servo] -pin = 3 \ No newline at end of file +pin = 3 + +[stepper] +name = 0 +[stepper.pins] +ena_pin = 7 +pul_pin = 8 +dir_pin = 9 \ No newline at end of file