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}