micropython_trasmit_recieve/servo.py

75 lines
2.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# servo.py — MG995 (and other analog hobby servos), 50 Hz PWM
from machine import Pin, PWM
# MG995: treat as standard 1.02.0 ms for ~0180°; many units reach full travel closer to 0.52.5 ms.
_DEFAULT_MIN_US = 1000
_DEFAULT_MAX_US = 2000
_WIDE_MIN_US = 500
_WIDE_MAX_US = 2500
class MG995:
def __init__(
self,
pin,
freq=50,
min_us=_DEFAULT_MIN_US,
max_us=_DEFAULT_MAX_US,
angle_max=180,
wide_range=False,
):
"""
Drive one MG995 on `pin` (GPIO number or Pin).
`wide_range=True` uses ~5002500 µs pulse limits (often needed for full
mechanical sweep); default 10002000 µs is gentler on the gearbox.
`angle_max` is the upper limit passed to write_angle (default 180).
"""
if wide_range:
min_us, max_us = _WIDE_MIN_US, _WIDE_MAX_US
self._pin_id = pin
self._freq = int(freq)
self._min_us = int(min_us)
self._max_us = int(max_us)
self._angle_max = float(angle_max)
self._pwm = PWM(Pin(pin), freq=self._freq, duty_u16=0)
self._use_ns = hasattr(self._pwm, 'duty_ns')
self._period_us = 1_000_000 // self._freq if self._freq > 0 else 20_000
def write_microseconds(self, us):
"""Set pulse width in microseconds (clamped to configured min/max)."""
us = max(self._min_us, min(self._max_us, int(us)))
if self._use_ns:
self._pwm.duty_ns(us * 1000)
else:
# Fraction of period → duty_u16 (ESP32-style full-scale mapping)
self._pwm.duty_u16(max(0, min(65535, int(us * 65535 / self._period_us)))))
def write_angle(self, degrees):
"""Map `degrees` (0 … angle_max) to pulse between min_us and max_us."""
a = max(0.0, min(self._angle_max, float(degrees)))
span = self._max_us - self._min_us
us = self._min_us + int(round(span * (a / self._angle_max)))
self.write_microseconds(us)
def center(self):
"""Mid pulse (~center position for default symmetric limits)."""
self.write_microseconds((self._min_us + self._max_us) // 2)
def off(self):
"""Stop PWM output (no holding torque from the signal)."""
try:
self._pwm.duty_u16(0)
except Exception:
pass
try:
if self._use_ns:
self._pwm.duty_ns(0)
except Exception:
pass
def deinit(self):
self.off()
self._pwm.deinit()