Source code for openbricks.interfaces

# SPDX-License-Identifier: MIT
"""
Abstract interfaces for openbricks components.

MicroPython doesn't ship full ``typing.Protocol`` support, so these are plain
base classes. Drivers should subclass the appropriate interface and fill in
every method. The higher-level modules (``robotics``, ``config``) only depend
on these interfaces, never on concrete drivers — that's what makes the system
plug-and-play.

If you add a new category of component (e.g. a distance sensor), add its
interface here.
"""


[docs] class Motor: """A bidirectional motor. Implementations range from an open-loop H-bridge driver (L298N) to a closed-loop geared motor with quadrature encoder (JGB37-520). Units ----- * ``power`` is -100..100 (percent duty cycle, sign = direction). * ``speed`` is degrees per second at the output shaft (closed-loop only). * ``angle`` is degrees at the output shaft (closed-loop only). """
[docs] def run(self, power): """Run at the given power (-100..100). Non-blocking.""" raise NotImplementedError
[docs] def brake(self): """Stop with active braking (both terminals shorted).""" raise NotImplementedError
[docs] def coast(self): """Stop by cutting drive power (motor free-wheels).""" raise NotImplementedError
[docs] def hold(self): """Stop and actively hold the current shaft angle via closed-loop control. Only motors with position-mode hardware (e.g. ST-3215) or a software position loop implement this; open-loop drivers raise ``NotImplementedError`` — pick ``brake`` or ``coast`` instead.""" raise NotImplementedError
# --- Optional closed-loop methods --- # Open-loop drivers may raise NotImplementedError or simply not override.
[docs] def angle(self): """Return the current shaft angle in degrees.""" raise NotImplementedError
[docs] def reset_angle(self, angle=0): """Set the current angle to ``angle`` degrees.""" raise NotImplementedError
[docs] def run_speed(self, deg_per_s): """Hold a target speed (closed loop).""" raise NotImplementedError
[docs] def run_angle(self, deg_per_s, target_angle, wait=True): """Rotate by ``target_angle`` degrees at ``deg_per_s``. Blocks if ``wait``; otherwise returns immediately and the caller polls ``done()`` to advance the move and detect completion.""" raise NotImplementedError
[docs] def done(self): """Return ``True`` if no non-blocking move is in flight or the active ``run_angle(wait=False)`` move has reached its target. Drivers that don't support non-blocking moves always return ``True`` (a wait=True call is finished before returning to the caller, by definition).""" return True
[docs] class Servo: """A position-controlled servo (angle-addressable)."""
[docs] def move_to(self, angle_deg, speed=None, wait=True): """Move to absolute angle in degrees.""" raise NotImplementedError
[docs] def angle(self): """Read back the current angle.""" raise NotImplementedError
[docs] class IMU: """A 3-axis inertial measurement unit. The expected unit convention is: * heading/yaw/pitch/roll in degrees * angular_velocity in degrees / second * acceleration in m / s^2 """
[docs] def heading(self): """Return heading (yaw) in degrees, wrapped to [-180, 180).""" raise NotImplementedError
[docs] def angular_velocity(self): """Return (wx, wy, wz) in deg/s.""" raise NotImplementedError
[docs] def acceleration(self): """Return (ax, ay, az) in m/s^2.""" raise NotImplementedError
[docs] class ColorSensor: """An RGB-ish color sensor."""
[docs] def rgb(self): """Return ``(r, g, b)`` each in 0..255.""" raise NotImplementedError
[docs] def ambient(self): """Return ambient / clear-channel intensity in 0..100.""" raise NotImplementedError
# NOTE: ``DistanceSensor`` lives in ``openbricks.distance`` rather # than here, even though it's an interface. ``openbricks/__init__.py`` # eagerly loads this module to re-export Motor/Servo/IMU/ColorSensor, # so every byte of bytecode here is paid by *every* import of # anything in the openbricks package — including the observer test # which runs against a tight MicroPython heap budget. Distance-sensor # users explicitly ``from openbricks.distance import DistanceSensor`` # (or just import their concrete driver, which does). # Hub-layer interfaces (StatusLED, Button, Display, Hub) live in # ``openbricks.hub`` alongside their concrete implementations so that # tests which don't touch the hub don't pay the class-loading cost on # MicroPython's tight unix heap.