Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Philip Abbet <[email protected]>
  • Loading branch information
dcarron-idiap authored and Kanma committed Dec 18, 2024
0 parents commit 7b575c1
Show file tree
Hide file tree
Showing 12 changed files with 938 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2024 Idiap Research Institute <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause

__pycache__
*.egg-info
build
31 changes: 31 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: 2024 Idiap Research Institute <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause

default:
image: python:3.12
tags:
- docker
- linux

stages:
- qa
- build
- test

quality:
stage: qa
script:
- pip install pre-commit
- pre-commit run --all-files

build-job:
stage: build
script:
- pip install . --no-cache

test-job:
stage: test
script:
- pip install .
- python3 -m unittest discover tests
26 changes: 26 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2024 Idiap Research Institute <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-ast
- id: check-case-conflict
- id: check-docstring-first
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
- repo: https://github.com/fsfe/reuse-tool
rev: v4.0.3
hooks:
- id: reuse
162 changes: 162 additions & 0 deletions ArduinoPyTwisterFirmware/ArduinoPyTwisterFirmware.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: 2024 Idiap Research Institute <[email protected]>
//
// SPDX-FileContributor: Michael Liebling <[email protected]>
//
// SPDX-License-Identifier: BSD-3-Clause

/*
Firmware for Arduino connected to a Sparkfun stepper motor driver
that interacts with a computer over a serial port and via
the Python library arduino_twister.
*/

const char FIRMWARE_NAME[] = "ArduinoPyTwister";
const char VERSION[] = "0.1";

/* Acceptable instruction codes */
const String RESET_CODE = "RES";
const String FIRMWARE_CODE = "FIR";
const String VERSION_CODE = "VER";
const unsigned char TURN_CODE_PREFIX = 'T';

/* Error and Acknowledgement codes (single byte) */
const unsigned char ACK = 0;
const unsigned char ERR = 1;

/* How Sparkfun Easy Driver stepper motor driver
connectors are connected to Arduino */
const unsigned short PIN_ENABLE = 2;
const unsigned short PIN_DIR = 3;
const unsigned short PIN_STEP = 4;

/* How long to wait between rotation steps */
const unsigned short stepDelayMs = 2; //ms
/* How long to wait after rotation is complete */
const unsigned short stopDelayMs = 200; //ms

void reset()
{ /* Reset Easy Driver pins to default states */
digitalWrite(PIN_STEP, LOW);
digitalWrite(PIN_DIR, LOW);
digitalWrite(PIN_ENABLE, LOW);
}

void setup()
{ /* This function is run automatically when Arduino is powered up */
pinMode(PIN_STEP, OUTPUT);
pinMode(PIN_DIR, OUTPUT);
pinMode(PIN_ENABLE, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
reset();
//Serial.begin(19200);
Serial.begin(9600);
}

void loop()
{ /* This function loops over to accept instructions over the serial port
Properly formatted commands are three characters long. If the first
character is TURN_CODE_PREFIX the last two characters are interpreted as
the number of steps and rotation direction direction.
*/
char received_command[3];
if (Serial.available() >= 3)
{ /* All commands are made of three characters; as soon as buffer contains
three characters, we read themm in. */
received_command[0] = Serial.read();
received_command[1] = Serial.read();
received_command[2] = Serial.read();
/* DEBUG
Serial.print("Received:");
Serial.println(received_command);
DEBUG */
/* Next we interpret the commands */
if (String(received_command)==RESET_CODE){ // Reset Arduino
reset();
}else if (String(received_command)==FIRMWARE_CODE){
/* Return the firmware name */
//Serial.println("Firmware");
Serial.println(FIRMWARE_NAME);
} else if(String(received_command)==VERSION_CODE){
/* Return the firmare version number */
Serial.println(VERSION);
} else if (received_command[0]==TURN_CODE_PREFIX){
/* Rotate the twister */
int16_t steps = rotation_code_to_int16(received_command);
/* DEBUG
Serial.print("Rotating by ");
Serial.print(steps,DEC);
Serial.println(" steps.");
DEBUG */
rotate_by_steps(steps);
Serial.write(ACK);
}else{
/* The command is unrecognized. */
//Serial.write(ERR);
/* DEBUG
Serial.println("Unrecognized command.");
DEBUG */
Serial.write(ERR);
}
}
}

int16_t rotation_code_to_int16(char* rotation_command ){
/*
Extract the last two bytes of a 3-byte rotation command
and convert them to a signed integer that represents
the number of steps to take in the rotation. The first
byte or the 3-byte rotation command, which contains the
rotation code that serves to identify the instruction,
is ignored by this function.
The sign corresponds to the direction of the rotation.
Returns: steps as an int16_t (signed 2-byte integer)
*/
uint8_t steps_b[2];
steps_b[0] = rotation_command[1];
steps_b[1] = rotation_command[2];
int16_t steps = steps_b[0] | (int16_t)steps_b[1] << 8;
return(steps);
}

void rotate_by_steps(int steps)
{
/*
Instruct the stepper motor to turn by
`steps` times 0.225° degrees.
Rotation table for Sparkfun Easy Driver
in eigth-step mode:
1 step: 1.8°/8 = 0.2250°
8 steps: 1.8°
1 step: 1.8°/8 = 0.2250°
8 steps: 1.8°
steps angle # steps # steps
increment in 180° in 360°
1 0.225° 800 1600
8 1.8° 100 200
16 3.6° 50 100
80 18° 10 20
100 22.5° 8 16
200 45° 4 8
400 90° 2 4
800 180° 1 2
*/
if (steps < 0)
{
digitalWrite(PIN_DIR, HIGH);
steps = -steps;
}
else
{
digitalWrite(PIN_DIR, LOW);
}
for (int i = 0; i < steps; i++)
{
digitalWrite(PIN_STEP, HIGH);
delay(stepDelayMs);
digitalWrite(PIN_STEP, LOW);
delay(stepDelayMs);
}
delay(stopDelayMs);
}
11 changes: 11 additions & 0 deletions LICENSES/BSD-3-Clause.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Copyright (c) <year> <owner>.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
118 changes: 118 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<!--
SPDX-FileCopyrightText: 2024 Idiap Research Institute <[email protected]>
SPDX-License-Identifier: BSD-3-Clause
-->

# arduino_pytwister

## Description

This Python library allows interacting with a stepper motor that rotates a stage. The stepper motor is connected to a Sparkfun driver which is itself connected to an Arduino microcontroller.

## Installation

arduino_pytwister has been developed for Python 3.

```bash
conda create -n envname python>=3.7
conda activate envname
pip install .
```

On Linux, add yourself to the `dialout`group
```bash
sudo usermod -a -G dialout $USER
```

### Arduino

arduino_pytwister expects to interact with an Arduino Uno. The latter is connected to an [EasyDriver Stepper Motor Driver - ROB-12779 by SparkFun Electronics](https://www.sparkfun.com/products/12779).

The wiring is as follows:

![EasyDriverHookup](https://cdn.sparkfun.com/assets/learn_tutorials/2/4/1/EasyDriverHookup_bb2.png)

Connect the arduino to the computer USB and to a power source.

Open the `ArduinoPyTwisterFirmware/ArduinoPyTwisterFirmware.ino` with the [Arduino IDE software](https://www.arduino.cc/en/software) and upload it to the Arduino.

## Usage

The package provides the following key functions:

1. `get_comport_list`: wrapper function to identify available serial ports
2. `get_arduino_ports`: function to identify serial ports linked to connected arduinos
3. `scan_list_for_arduinopytwister`: function to identify the serial port linked to a connected arduino with the correct `ArduinoPyTwisterFirmware.ino` code uploaded.
4. `get_serial_object_to_arduino`: function to obtain a serial object that can be used to communicate with the arduino
5. `rotate_by_steps`: the function to instruct rotation

An example of a simple interation would be:

```python
import pytwister as pt
port_list = pt.arduino.get_comport_list()
arduino_ports = pt.arduino.get_arduino_ports(port_list)
twister_port = pt.arduino.scan_list_for_arduinopytwister(arduino_ports)
ser = pt.arduino.get_serial_object_to_arduino(twister_port)
pt.arduino.rotate_by_steps(ser, -200)
pt.arduino.rotate_by_steps(ser, 200)
```

The number of steps can be any integer in the range [-32768,32767].

Each step corresponds to 0.225° (degrees).

The `Twister` convenience class gives a high-level control interface.
An example of the same simple iteration would be:

```python
import pytwister as pt
twister = pt.Twister()
twister.rotate_rel(45)
twister.rotate_abs(0)
```

Conversion table for Sparkfun Easy Driver in eigth-step mode:

| steps | angle increment |
| ----: | :---: |
| 1 | 1.8°/8 =0.225° |
| 8 | 1.8° |
| 16 | 3.6° |
| 80 | 18° |
| 100 | 22.5° |
| 200 | 45° |
| 400 | 90° |
| 800 | 180° |
| 1600 | 360° |

## Support

This software is provided as-is.

## Contributing

Extensions should be accompanied by a test and clear function/class headers. Tests are gathered in `test_arduino_twister.py`

```python
python3 -m unittest discover tests
```

The tests are run in "dummy" mode and there is currently no requirement that an arduino is connected.

## Authors and acknowledgment

Michael Liebling
Idiap Research Institute
April 2023

Francois Marelli
Idiap Research Institute
April 2023

inspired by related projects (µmanager, etc.)

## License

BSD-3
Loading

0 comments on commit 7b575c1

Please sign in to comment.