You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							148 lines
						
					
					
						
							4.9 KiB
						
					
					
				
			
		
		
	
	
							148 lines
						
					
					
						
							4.9 KiB
						
					
					
				| from gpiozero import PWMOutputDevice, SourceMixin, CompositeDevice
 | |
| 
 | |
| 
 | |
| class BetterServo(SourceMixin, CompositeDevice):
 | |
|     """
 | |
|     Copy of GPIOZero servo, but with control over pulse width and active_high
 | |
|     """
 | |
|     def __init__(
 | |
|             self, pin=None, initial_value=0.0,
 | |
|             min_pulse_width=1/1000, max_pulse_width=2/1000,
 | |
|             frame_width=20/1000, pin_factory=None, active_high=True):
 | |
|         if min_pulse_width >= max_pulse_width:
 | |
|             raise ValueError('min_pulse_width must be less than max_pulse_width')
 | |
|         if max_pulse_width >= frame_width:
 | |
|             raise ValueError('max_pulse_width must be less than frame_width')
 | |
|         self._frame_width = frame_width
 | |
|         self._min_dc = min_pulse_width / frame_width
 | |
|         self._dc_range = (max_pulse_width - min_pulse_width) / frame_width
 | |
|         self._min_value = -1
 | |
|         self._value_range = 2
 | |
|         super(BetterServo, self).__init__(
 | |
|             pwm_device=PWMOutputDevice(
 | |
|                 pin, frequency=int(1 / frame_width), pin_factory=pin_factory,
 | |
|                 active_high=False
 | |
|             ),
 | |
|             pin_factory=pin_factory
 | |
|         )
 | |
|         self.pwm_device.active_high=active_high
 | |
|         try:
 | |
|             self.value = initial_value
 | |
|         except:
 | |
|             self.close()
 | |
|             raise
 | |
| 
 | |
|     @property
 | |
|     def frame_width(self):
 | |
|         """
 | |
|         The time between control pulses, measured in seconds.
 | |
|         """
 | |
|         return self._frame_width
 | |
| 
 | |
|     @property
 | |
|     def min_pulse_width(self):
 | |
|         """
 | |
|         The control pulse width corresponding to the servo's minimum position,
 | |
|         measured in seconds.
 | |
|         """
 | |
|         return self._min_dc * self.frame_width
 | |
| 
 | |
|     @property
 | |
|     def max_pulse_width(self):
 | |
|         """
 | |
|         The control pulse width corresponding to the servo's maximum position,
 | |
|         measured in seconds.
 | |
|         """
 | |
|         return (self._dc_range * self.frame_width) + self.min_pulse_width
 | |
| 
 | |
|     @property
 | |
|     def pulse_width(self):
 | |
|         """
 | |
|         Returns the current pulse width controlling the servo.
 | |
|         """
 | |
|         if self.pwm_device.frequency is None:
 | |
|             return None
 | |
|         else:
 | |
|             return self.pwm_device.state * self.frame_width
 | |
| 
 | |
|     @pulse_width.setter
 | |
|     def pulse_width(self, value):
 | |
|         if value is None:
 | |
|             self.pwm_device.frequency = None
 | |
|         elif self.min_pulse_width <= value <= self.max_pulse_width:
 | |
|             self.pwm_device.frequency = int(1 / self.frame_width)
 | |
|             self.pwm_device.value = (value / self.frame_width)
 | |
|         else:
 | |
|             raise OutputDeviceBadValue("Servo pulse_width must be between min and max supplied during construction, or None")
 | |
| 
 | |
|     def min(self):
 | |
|         """
 | |
|         Set the servo to its minimum position.
 | |
|         """
 | |
|         self.value = -1
 | |
| 
 | |
|     def mid(self):
 | |
|         """
 | |
|         Set the servo to its mid-point position.
 | |
|         """
 | |
|         self.value = 0
 | |
| 
 | |
|     def max(self):
 | |
|         """
 | |
|         Set the servo to its maximum position.
 | |
|         """
 | |
|         self.value = 1
 | |
| 
 | |
|     def detach(self):
 | |
|         """
 | |
|         Temporarily disable control of the servo. This is equivalent to
 | |
|         setting :attr:`value` to ``None``.
 | |
|         """
 | |
|         self.value = None
 | |
| 
 | |
|     def _get_value(self):
 | |
|         if self.pwm_device.frequency is None:
 | |
|             return None
 | |
|         else:
 | |
|             return (
 | |
|                 ((self.pwm_device.state - self._min_dc) / self._dc_range) *
 | |
|                 self._value_range + self._min_value)
 | |
| 
 | |
|     @property
 | |
|     def value(self):
 | |
|         """
 | |
|         Represents the position of the servo as a value between -1 (the minimum
 | |
|         position) and +1 (the maximum position). This can also be the special
 | |
|         value ``None`` indicating that the servo is currently "uncontrolled",
 | |
|         i.e. that no control signal is being sent. Typically this means the
 | |
|         servo's position remains unchanged, but that it can be moved by hand.
 | |
|         """
 | |
|         result = self._get_value()
 | |
|         if result is None:
 | |
|             return result
 | |
|         else:
 | |
|             # NOTE: This round() only exists to ensure we don't confuse people
 | |
|             # by returning 2.220446049250313e-16 as the default initial value
 | |
|             # instead of 0. The reason _get_value and _set_value are split
 | |
|             # out is for descendents that require the un-rounded values for
 | |
|             # accuracy
 | |
|             return round(result, 14)
 | |
| 
 | |
|     @value.setter
 | |
|     def value(self, value):
 | |
|         if value is None:
 | |
|             self.pwm_device.frequency = None
 | |
|         elif -1 <= value <= 1:
 | |
|             self.pwm_device.frequency = int(1 / self.frame_width)
 | |
|             self.pwm_device.value = (
 | |
|                 self._min_dc + self._dc_range *
 | |
|                 ((value - self._min_value) / self._value_range)
 | |
|                 )
 | |
|         else:
 | |
|             raise OutputDeviceBadValue(
 | |
|                 "Servo value must be between -1 and 1, or None")
 | |
| 
 | |
|     @property
 | |
|     def is_active(self):
 | |
|         return self.value is not None
 |