Skip to content

Commit

Permalink
Merge branch 'MrHarcombe-phase-enable'
Browse files Browse the repository at this point in the history
  • Loading branch information
waveform80 committed Feb 20, 2018
2 parents 1214da7 + a14b87c commit 8d93860
Show file tree
Hide file tree
Showing 7 changed files with 463 additions and 2 deletions.
14 changes: 14 additions & 0 deletions docs/api_boards.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ Robot
:inherited-members:
:members:

PhaseEnableRobot
================

.. autoclass:: PhaseEnableRobot
:inherited-members:
:members:

Ryanteck MCB Robot
==================

Expand All @@ -122,6 +129,13 @@ CamJam #3 Kit Robot
:inherited-members:
:members:

Pololu DRV8835 Robot
====================

.. autoclass:: PololuDRV8835Robot
:inherited-members:
:members:

Energenie
=========

Expand Down
8 changes: 7 additions & 1 deletion docs/api_output.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ Motor
=====

.. autoclass:: Motor(forward, backward, \*, pwm=True, pin_factory=None)
:members: forward, backward, stop
:members: forward, backward, reverse, stop

PhaseEnableMotor
================

.. autoclass:: PhaseEnableMotor(phase, enable, \*, pwm=True, pin_factory=None)
:members: forward, backward, reverse, stop

Servo
=====
Expand Down
3 changes: 3 additions & 0 deletions gpiozero/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
LED,
Buzzer,
Motor,
PhaseEnableMotor,
Servo,
AngularServo,
RGBLED,
Expand All @@ -89,6 +90,8 @@
Robot,
RyanteckRobot,
CamJamKitRobot,
PhaseEnableRobot,
PololuDRV8835Robot,
Energenie,
)
from .other_devices import (
Expand Down
142 changes: 142 additions & 0 deletions gpiozero/boards.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
RGBLED,
Buzzer,
Motor,
PhaseEnableMotor,
)
from .threads import GPIOThread
from .devices import Device, CompositeDevice
Expand Down Expand Up @@ -1362,6 +1363,147 @@ def __init__(self, pin_factory=None):
)


class PhaseEnableRobot(SourceMixin, CompositeDevice):
"""
Extends :class:`CompositeDevice` to represent a dual-motor robot based
around a Phase/Enable motor board.
This class is constructed with two tuples representing the phase
(direction) and enable (speed) pins of the left and right controllers
respectively. For example, if the left motor's controller is connected to
GPIOs 12 and 5, while the right motor's controller is connected to GPIOs 13
and 6 so the following example will drive the robot forward::
from gpiozero import PhaseEnableRobot
robot = PhaseEnableRobot(left=(5, 12), right=(6, 13))
robot.forward()
:param tuple left:
A tuple of two GPIO pins representing the phase and enable inputs
of the left motor's controller.
:param tuple right:
A tuple of two GPIO pins representing the phase and enable inputs
of the right motor's controller.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
"""

def __init__(self, left=None, right=None, pin_factory=None):
super(PhaseEnableRobot, self).__init__(
left_motor=PhaseEnableMotor(*left, pin_factory=pin_factory),
right_motor=PhaseEnableMotor(*right, pin_factory=pin_factory),
_order=('left_motor', 'right_motor'),
pin_factory=pin_factory
)

@property
def value(self):
"""
Returns a tuple of two floating point values (-1 to 1) representing the
speeds of the robot's two motors (left and right). This property can
also be set to alter the speed of both motors.
"""
return super(PhaseEnableRobot, self).value

@value.setter
def value(self, value):
self.left_motor.value, self.right_motor.value = value

def forward(self, speed=1):
"""
Drive the robot forward by running both motors forward.
:param float speed:
Speed at which to drive the motors, as a value between 0 (stopped)
and 1 (full speed). The default is 1.
"""
self.left_motor.forward(speed)
self.right_motor.forward(speed)

def backward(self, speed=1):
"""
Drive the robot backward by running both motors backward.
:param float speed:
Speed at which to drive the motors, as a value between 0 (stopped)
and 1 (full speed). The default is 1.
"""
self.left_motor.backward(speed)
self.right_motor.backward(speed)

def left(self, speed=1):
"""
Make the robot turn left by running the right motor forward and left
motor backward.
:param float speed:
Speed at which to drive the motors, as a value between 0 (stopped)
and 1 (full speed). The default is 1.
"""
self.right_motor.forward(speed)
self.left_motor.backward(speed)

def right(self, speed=1):
"""
Make the robot turn right by running the left motor forward and right
motor backward.
:param float speed:
Speed at which to drive the motors, as a value between 0 (stopped)
and 1 (full speed). The default is 1.
"""
self.left_motor.forward(speed)
self.right_motor.backward(speed)

def reverse(self):
"""
Reverse the robot's current motor directions. If the robot is currently
running full speed forward, it will run full speed backward. If the
robot is turning left at half-speed, it will turn right at half-speed.
If the robot is currently stopped it will remain stopped.
"""
self.left_motor.value = -self.left_motor.value
self.right_motor.value = -self.right_motor.value

def stop(self):
"""
Stop the robot.
"""
self.left_motor.stop()
self.right_motor.stop()


class PololuDRV8835Robot(PhaseEnableRobot):
"""
Extends :class:`PhaseEnableRobot` for the `Pololu DRV8835 Dual Motor Driver
Kit`_.
The Pololu DRV8835 pins are fixed and therefore there's no need to specify
them when constructing this class. The following example drives the robot
forward::
from gpiozero import PololuDRV8835Robot
robot = PololuDRV8835Robot()
robot.forward()
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
.. _Pololu DRV8835 Dual Motor Driver Kit: https://www.pololu.com/product/2753
"""

def __init__(self, pin_factory=None):
super(PololuDRV8835Robot, self).__init__(
(5, 12), (6, 13), pin_factory=pin_factory
)


class _EnergenieMaster(SharedMixin, CompositeOutputDevice):
def __init__(self, pin_factory=None):
self._lock = Lock()
Expand Down
117 changes: 116 additions & 1 deletion gpiozero/output_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ def _blink_device(
class Motor(SourceMixin, CompositeDevice):
"""
Extends :class:`CompositeDevice` and represents a generic motor
connected to a bi-directional motor driver circuit (i.e. an `H-bridge`_).
connected to a bi-directional motor driver circuit (i.e. an `H-bridge`_).
Attach an `H-bridge`_ motor controller to your Pi; connect a power source
(e.g. a battery pack or the 5V pin) to the controller; connect the outputs
Expand Down Expand Up @@ -945,6 +945,121 @@ def stop(self):
self.backward_device.off()


class PhaseEnableMotor(SourceMixin, CompositeDevice):
"""
Extends :class:`CompositeDevice` and represents a generic motor connected
to a Phase/Enable motor driver circuit; the phase of the driver controls
whether the motor turns forwards or backwards, while enable controls the
speed with PWM.
The following code will make the motor turn "forwards"::
from gpiozero import PhaseEnableMotor
motor = PhaseEnableMotor(12, 5)
motor.forward()
:param int phase:
The GPIO pin that the phase (direction) input of the motor driver chip
is connected to.
:param int enable:
The GPIO pin that the enable (speed) input of the motor driver chip is
connected to.
:param bool pwm:
If ``True`` (the default), construct :class:`PWMOutputDevice`
instances for the motor controller pins, allowing both direction and
variable speed control. If ``False``, construct
:class:`DigitalOutputDevice` instances, allowing only direction
control.
:param Factory pin_factory:
See :doc:`api_pins` for more information (this is an advanced feature
which most users can ignore).
"""
def __init__(self, phase=None, enable=None, pwm=True, pin_factory=None):
if not all([phase, enable]):
raise GPIOPinMissing('phase and enable pins must be provided')
PinClass = PWMOutputDevice if pwm else DigitalOutputDevice
super(PhaseEnableMotor, self).__init__(
phase_device=OutputDevice(phase, pin_factory=pin_factory),
enable_device=PinClass(enable, pin_factory=pin_factory),
_order=('phase_device', 'enable_device'),
pin_factory=pin_factory
)

@property
def value(self):
"""
Represents the speed of the motor as a floating point value between -1
(full speed backward) and 1 (full speed forward).
"""
return -self.enable_device.value if self.phase_device.is_active else self.enable_device.value

@value.setter
def value(self, value):
if not -1 <= value <= 1:
raise OutputDeviceBadValue("Motor value must be between -1 and 1")
if value > 0:
self.forward(value)
elif value < 0:
self.backward(-value)
else:
self.stop()

@property
def is_active(self):
"""
Returns ``True`` if the motor is currently running and ``False``
otherwise.
"""
return self.value != 0

def forward(self, speed=1):
"""
Drive the motor forwards.
:param float speed:
The speed at which the motor should turn. Can be any value between
0 (stopped) and the default 1 (maximum speed).
"""
if isinstance(self.enable_device, DigitalOutputDevice):
if speed not in (0, 1):
raise ValueError('forward speed must be 0 or 1 with non-PWM Motors')
self.enable_device.off()
self.phase_device.off()
self.enable_device.value = speed

def backward(self, speed=1):
"""
Drive the motor backwards.
:param float speed:
The speed at which the motor should turn. Can be any value between
0 (stopped) and the default 1 (maximum speed).
"""
if isinstance(self.enable_device, DigitalOutputDevice):
if speed not in (0, 1):
raise ValueError('backward speed must be 0 or 1 with non-PWM Motors')
self.enable_device.off()
self.phase_device.on()
self.enable_device.value = speed

def reverse(self):
"""
Reverse the current direction of the motor. If the motor is currently
idle this does nothing. Otherwise, the motor's direction will be
reversed at the current speed.
"""
self.value = -self.value

def stop(self):
"""
Stop the motor.
"""
self.enable_device.off()


class Servo(SourceMixin, CompositeDevice):
"""
Extends :class:`CompositeDevice` and represents a PWM-controlled servo
Expand Down

0 comments on commit 8d93860

Please sign in to comment.