1use embedded_hal::delay::DelayNs;
4use embedded_hal::digital::OutputPin;
5
6use crate::config::units::{DegreesPerSec, DegreesPerSecSquared, Microsteps};
7use crate::config::{MechanicalConstraints, MotorConfig, SystemConfig};
8use crate::error::{ConfigError, Error, Result};
9
10use super::driver::StepperMotor;
11use super::state::Idle;
12
13pub struct StepperMotorBuilder<STEP, DIR, DELAY>
15where
16 STEP: OutputPin,
17 DIR: OutputPin,
18 DELAY: DelayNs,
19{
20 step_pin: Option<STEP>,
21 dir_pin: Option<DIR>,
22 delay: Option<DELAY>,
23 name: Option<heapless::String<32>>,
24 steps_per_revolution: Option<u16>,
25 microsteps: Option<Microsteps>,
26 gear_ratio: f32,
27 max_velocity: Option<DegreesPerSec>,
28 max_acceleration: Option<DegreesPerSecSquared>,
29 invert_direction: bool,
30 constraints: Option<MechanicalConstraints>,
31 backlash_steps: i64,
32}
33
34impl<STEP, DIR, DELAY> Default for StepperMotorBuilder<STEP, DIR, DELAY>
35where
36 STEP: OutputPin,
37 DIR: OutputPin,
38 DELAY: DelayNs,
39{
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl<STEP, DIR, DELAY> StepperMotorBuilder<STEP, DIR, DELAY>
46where
47 STEP: OutputPin,
48 DIR: OutputPin,
49 DELAY: DelayNs,
50{
51 pub fn new() -> Self {
53 Self {
54 step_pin: None,
55 dir_pin: None,
56 delay: None,
57 name: None,
58 steps_per_revolution: None,
59 microsteps: None,
60 gear_ratio: 1.0,
61 max_velocity: None,
62 max_acceleration: None,
63 invert_direction: false,
64 constraints: None,
65 backlash_steps: 0,
66 }
67 }
68
69 pub fn step_pin(mut self, pin: STEP) -> Self {
71 self.step_pin = Some(pin);
72 self
73 }
74
75 pub fn dir_pin(mut self, pin: DIR) -> Self {
77 self.dir_pin = Some(pin);
78 self
79 }
80
81 pub fn delay(mut self, delay: DELAY) -> Self {
83 self.delay = Some(delay);
84 self
85 }
86
87 pub fn name(mut self, name: &str) -> Self {
89 self.name = heapless::String::try_from(name).ok();
90 self
91 }
92
93 pub fn steps_per_revolution(mut self, steps: u16) -> Self {
95 self.steps_per_revolution = Some(steps);
96 self
97 }
98
99 pub fn microsteps(mut self, microsteps: Microsteps) -> Self {
101 self.microsteps = Some(microsteps);
102 self
103 }
104
105 pub fn gear_ratio(mut self, ratio: f32) -> Self {
107 self.gear_ratio = ratio;
108 self
109 }
110
111 pub fn max_velocity(mut self, velocity: DegreesPerSec) -> Self {
113 self.max_velocity = Some(velocity);
114 self
115 }
116
117 pub fn max_acceleration(mut self, acceleration: DegreesPerSecSquared) -> Self {
119 self.max_acceleration = Some(acceleration);
120 self
121 }
122
123 pub fn invert_direction(mut self, invert: bool) -> Self {
125 self.invert_direction = invert;
126 self
127 }
128
129 pub fn backlash_steps(mut self, steps: i64) -> Self {
133 self.backlash_steps = steps;
134 self
135 }
136
137 pub fn from_motor_config(mut self, config: &MotorConfig) -> Self {
139 self.name = Some(config.name.clone());
140 self.steps_per_revolution = Some(config.steps_per_revolution);
141 self.microsteps = Some(config.microsteps);
142 self.gear_ratio = config.gear_ratio;
143 self.max_velocity = Some(config.max_velocity);
144 self.max_acceleration = Some(config.max_acceleration);
145 self.invert_direction = config.invert_direction;
146 self.constraints = Some(MechanicalConstraints::from_config(config));
147 if let Some(backlash_deg) = config.backlash_compensation {
149 let steps_per_degree = config.steps_per_degree();
150 self.backlash_steps = (backlash_deg.0 * steps_per_degree) as i64;
151 }
152 self
153 }
154
155 pub fn from_config(self, config: &SystemConfig, motor_name: &str) -> Result<Self> {
157 let motor_config = config
158 .motor(motor_name)
159 .ok_or_else(|| Error::Config(ConfigError::MotorNotFound(
160 heapless::String::try_from(motor_name).unwrap_or_default(),
161 )))?;
162
163 Ok(self.from_motor_config(motor_config))
164 }
165
166 pub fn build(self) -> Result<StepperMotor<STEP, DIR, DELAY, Idle>> {
172 let step_pin = self.step_pin.ok_or_else(|| {
173 Error::Config(ConfigError::ParseError(
174 heapless::String::try_from("step_pin is required").unwrap(),
175 ))
176 })?;
177
178 let dir_pin = self.dir_pin.ok_or_else(|| {
179 Error::Config(ConfigError::ParseError(
180 heapless::String::try_from("dir_pin is required").unwrap(),
181 ))
182 })?;
183
184 let delay = self.delay.ok_or_else(|| {
185 Error::Config(ConfigError::ParseError(
186 heapless::String::try_from("delay is required").unwrap(),
187 ))
188 })?;
189
190 let name = self.name.unwrap_or_else(|| {
191 heapless::String::try_from("motor").unwrap()
192 });
193
194 let constraints = if let Some(c) = self.constraints {
195 c
196 } else {
197 let steps = self.steps_per_revolution.ok_or_else(|| {
199 Error::Config(ConfigError::ParseError(
200 heapless::String::try_from("steps_per_revolution is required").unwrap(),
201 ))
202 })?;
203
204 let microsteps = self.microsteps.unwrap_or(Microsteps::FULL);
205 let max_velocity = self.max_velocity.ok_or_else(|| {
206 Error::Config(ConfigError::ParseError(
207 heapless::String::try_from("max_velocity is required").unwrap(),
208 ))
209 })?;
210
211 let max_acceleration = self.max_acceleration.ok_or_else(|| {
212 Error::Config(ConfigError::ParseError(
213 heapless::String::try_from("max_acceleration is required").unwrap(),
214 ))
215 })?;
216
217 let config = MotorConfig {
219 name: name.clone(),
220 steps_per_revolution: steps,
221 microsteps,
222 gear_ratio: self.gear_ratio,
223 max_velocity,
224 max_acceleration,
225 invert_direction: self.invert_direction,
226 limits: None,
227 backlash_compensation: None,
228 };
229
230 MechanicalConstraints::from_config(&config)
231 };
232
233 Ok(StepperMotor::new(
234 step_pin,
235 dir_pin,
236 delay,
237 constraints,
238 name,
239 self.invert_direction,
240 self.backlash_steps,
241 ))
242 }
243}