Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3204985
Add comprehensive Stepper motor class with multiple API approaches
pjbRPF Oct 16, 2025
b97feaf
Add documentation for MotionSensor and Stepper classes
pjbRPF Oct 17, 2025
44d6aad
Add comprehensive Stepper motor class with multiple API approaches
pjbRPF Oct 16, 2025
b1c6db4
Add documentation for MotionSensor and Stepper classes
pjbRPF Oct 17, 2025
80abbeb
Remove duplicate MotionSensor documentation
pjbRPF Oct 17, 2025
995ee20
Merge branch 'dev' into feature/stepper
pjbRPF Oct 17, 2025
ca8ca7f
Stepper updates after review
pjbRPF Nov 25, 2025
278369b
Merge feature/stepper - keep local changes
pjbRPF Nov 25, 2025
d878743
update to changelog
pjbRPF Nov 25, 2025
274bd66
Merge branch 'dev' into feature/stepper
pjbRPF Nov 25, 2025
1807c97
Fix: DigitalInputDevice bounce_time not detecting fast signals
pjbRPF Nov 25, 2025
858d0f5
Merge branch 'feature/stepper' of https://github.com/RaspberryPiFound…
pjbRPF Nov 25, 2025
ebff9b2
updated changelog
pjbRPF Nov 25, 2025
7db134e
dates, version numering, code owners
pjbRPF Nov 25, 2025
b5120a5
Update setup.py
pjbRPF Nov 26, 2025
02ad6da
update changelog
pjbRPF Nov 26, 2025
b4ec837
Merge branch 'dev' into feature/stepper
pjbRPF Nov 26, 2025
28a7185
Fix: Remove positional-only parameters for MicroPython compatibility
pjbRPF Nov 26, 2025
f921000
updated to ensure tests pass
pjbRPF Nov 26, 2025
76d612f
Merge branch 'dev' into feature/stepper
pjbRPF Nov 27, 2025
9ebd8c5
Merge branch 'dev' into feature/stepper
pjbRPF Nov 28, 2025
90e61af
Remove StepperMotor alias for Stepper class
pjbRPF Nov 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ Servo
:inherited-members:
:members:

Stepper
-------

.. autoclass:: Stepper
:show-inheritance:
:inherited-members:
:members:

Motor
-----

Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Change log

.. currentmodule:: picozero

0.6.0 - 2025-11-26
-----------

+ Introduced ``Stepper`` class for stepper motors

0.5.2 - 2025-11-26
-----------

Expand Down
87 changes: 50 additions & 37 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,75 @@
#
import os
import sys

# sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
on_rtd = os.environ.get("READTHEDOCS", None) == "True"


# Mock out certain modules while building documentation
class Mock:
__all__ = []
def __init__(self, *args, **kw): pass
def __call__(self, *args, **kw): return Mock()
def __mul__(self, other): return Mock()
def __and__(self, other): return Mock()
def __bool__(self): return False
def __nonzero__(self): return False

def __init__(self, *args, **kw):
pass

def __call__(self, *args, **kw):
return Mock()

def __mul__(self, other):
return Mock()

def __and__(self, other):
return Mock()

def __bool__(self):
return False

def __nonzero__(self):
return False

@classmethod
def __getattr__(cls, name):
if name in ('__file__', '__path__'):
return '/dev/null'
if name in ("__file__", "__path__"):
return "/dev/null"
else:
return Mock()

sys.modules['machine'] = Mock()
sys.modules['micropython'] = Mock()

sys.modules["machine"] = Mock()
sys.modules["micropython"] = Mock()

# add the ticks_ms function to time (as it is in micropython)
import time
setattr(time, 'ticks_ms', lambda x: None)
setattr(time, 'ticks_us', lambda x: None)

setattr(time, "ticks_ms", lambda x: None)
setattr(time, "ticks_us", lambda x: None)

# -- Project information -----------------------------------------------------

project = 'picozero'
copyright = '2025, Raspberry Pi Foundation'
author = 'Raspberry Pi Foundation'
project = "picozero"
copyright = "2025, Raspberry Pi Foundation"
author = "Raspberry Pi Foundation"

# The full version, including alpha/beta/rc tags
release = '0.5.0'
release = "0.6.0"


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx'
]
extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.intersphinx"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]


# -- Options for HTML output -------------------------------------------------
Expand All @@ -79,26 +92,26 @@ def __getattr__(cls, name):
# a list of builtin themes.
#
if on_rtd:
html_theme = 'sphinx_rtd_theme'
#html_theme_options = {}
html_theme = "sphinx_rtd_theme"
# html_theme_options = {}
html_sidebars = {
'**': [
'globaltoc.html',
'relations.html',
'searchbox.html',
"**": [
"globaltoc.html",
"relations.html",
"searchbox.html",
],
}
else:
html_theme = 'alabaster'
#html_theme_options = {}
#html_sidebars = {}
html_theme = "alabaster"
# html_theme_options = {}
# html_sidebars = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]

# -- Autodoc configuration ------------------------------------------------

autodoc_member_order = 'groupwise'
autodoc_default_flags = ['members']
autodoc_member_order = "groupwise"
autodoc_default_flags = ["members"]
129 changes: 129 additions & 0 deletions docs/examples/stepper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from picozero import Stepper
from time import sleep

stepper = Stepper((1, 2, 3, 4))

print(f"Starting position: {stepper.step_count} steps, {stepper.angle:.1f} degrees")
sleep(0.5)
print()

print("Using step() with default clockwise direction:")
stepper.step(10)
print(f"Position after step(10): {stepper.step_count} steps")
sleep(0.5)
print()

print("step(20, 1) - clockwise:")
stepper.step(20, 1)
print(f"Position: {stepper.step_count} steps")
sleep(0.5)
print()

print("step(15, -1) - counter-clockwise:")
stepper.step(15, -1)
print(f"Position: {stepper.step_count} steps")
sleep(0.5)
print()

print("step(5, 'cw') - short string abbreviation:")
stepper.step(5, "cw")
print(f"Position: {stepper.step_count} steps")
sleep(0.5)
print()

print("step(10, 1) - numeric:")
stepper.step(10, 1)
print(f"Position: {stepper.step_count} steps")
print()

print("step(10, 'cw') - string abbreviation:")
stepper.step(10, "cw")
print(f"Position: {stepper.step_count} steps")
sleep(0.5)
print()

print("step(10, 'clockwise') - full string:")
stepper.step(10, "clockwise")
print(f"Position: {stepper.step_count} steps")
sleep(0.5)
print()


print("turn(90, 'cw') - 90 degrees CW:")
stepper.turn(90, "cw")
print(f"Position: {stepper.step_count} steps, {stepper.angle:.1f} degrees")
sleep(0.5)
print()

print("turn(45, 'ccw') - 45 degrees CCW:")
stepper.turn(45, "ccw")
print(f"Position: {stepper.step_count} steps, {stepper.angle:.1f} degrees")
sleep(0.5)
print()

print("rotate(1, 'cw') - 1 full rotation CW:")
stepper.rotate(1, "cw")
print(f"Position: {stepper.step_count} steps, {stepper.angle:.1f} degrees")
sleep(0.5)
print()

print("rotate(0.5, 'ccw') - half rotation CCW:")
stepper.rotate(0.5, "ccw")
print(f"Position: {stepper.step_count} steps, {stepper.angle:.1f} degrees")
sleep(0.5)
print()

print("Resetting position to home (0 steps, 0°)...")
stepper.reset_position()
print(f"After reset: {stepper.step_count} steps, {stepper.angle:.1f} degrees")
sleep(0.5)
print()

print()
print("Final demonstration - all methods achieve the same result:")
for i, (method_name, method_call) in enumerate(
[
("Default direction", lambda: stepper.step(5)),
("Numeric direction", lambda: stepper.step(5, 1)),
("String direction", lambda: stepper.step(5, "cw")),
],
1,
):
print(f"{i}. {method_name}: 5 steps clockwise")
method_call()

print(f"All methods reached: {stepper.step_count} steps total")

# Advanced methods demonstration
print()
print("=== ADVANCED METHODS ===")
print()

stepper.reset_position()
print("step_to(100, 'cw') - move to absolute step 100 clockwise:")
stepper.step_to(100, "cw")
print(f"Position: {stepper.step_count} steps")
sleep(0.5)
print()

stepper.reset_position()
print("turn_to(180, 'cw') - turn to 180 degrees clockwise:")
stepper.turn_to(180, "cw")
print(f"Position: {stepper.angle:.1f}° ({stepper.step_count} steps)")
sleep(0.5)
print()

print("set_speed(60) - set speed to 60 RPM:")
stepper.set_speed(60)
print(f"Step delay now: {stepper.step_delay:.6f} seconds")
sleep(0.5)
print()

print("step(20, 'cw') - step 20 times at 60 RPM:")
stepper.step(20, "cw")
print(f"Position: {stepper.step_count} steps")
print()

# Turn off motor when done
stepper.off()
stepper.close()
Loading