Skip to main content

robot_control/
motor.rs

1//! Motor driver abstraction and motor definitions.
2//!
3//! This module defines the [`MotorDriver`] trait and the [`Motor`] enum.
4//! Concrete implementations live in separate modules (e.g., `pca9685` with the feature flag).
5
6use crate::error::MotorError;
7
8/// Identifies a DC motor connected to the motor driver board.
9///
10/// Most dual-motor robot kits use [`Motor1`](Motor::Motor1) (left) and
11/// [`Motor2`](Motor::Motor2) (right). Four-motor configurations use all four.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum Motor {
14    Motor1,
15    Motor2,
16    Motor3,
17    Motor4,
18}
19
20/// Abstraction over a motor driver board.
21///
22/// Implement this trait for your specific hardware (e.g., PCA9685) or use
23/// `MockMotorDriver` (available in test builds) for testing.
24pub trait MotorDriver: Send + Sync {
25    /// Set the speed and direction of a single motor.
26    ///
27    /// # Arguments
28    ///
29    /// * `motor` — Which motor to control.
30    /// * `speed` — PWM duty cycle (0–4095).
31    /// * `forward` — `true` for forward, `false` for reverse.
32    ///
33    /// # Errors
34    ///
35    /// Returns [`MotorError`] if the hardware communication fails.
36    fn set_motor_speed(&self, motor: Motor, speed: u16, forward: bool) -> Result<(), MotorError>;
37
38    /// Stop a single motor (set all channels to zero).
39    ///
40    /// # Errors
41    ///
42    /// Returns [`MotorError`] if the hardware communication fails.
43    fn stop_motor(&self, motor: Motor) -> Result<(), MotorError>;
44
45    /// Stop all motors. The default implementation attempts M1–M4 in order,
46    /// even if an earlier motor fails to stop.
47    ///
48    /// Custom overrides must provide their own exhaustive stopping behavior.
49    ///
50    /// # Errors
51    ///
52    /// Returns [`MotorError::Pwm`] if any stop fails, collecting every failed
53    /// motor and its original diagnostic. An error means outputs are uncertain.
54    fn stop_all(&self) -> Result<(), MotorError> {
55        let mut failures = Vec::new();
56        for motor in [Motor::Motor1, Motor::Motor2, Motor::Motor3, Motor::Motor4] {
57            if let Err(error) = self.stop_motor(motor) {
58                failures.push(format!("{motor:?} stop_motor: {error}"));
59            }
60        }
61        if failures.is_empty() {
62            Ok(())
63        } else {
64            Err(MotorError::Pwm(format!(
65                "stop_all failed: {}",
66                failures.join("; ")
67            )))
68        }
69    }
70}