1use crate::motor::*;
52use crate::{Controller, ControllerCapabilities, ControllerError, ControllerHealth, ControllerState};
53use async_trait::async_trait;
54use mecha10_core::actuator::MotorStatus;
55use mecha10_drivers_motor::l298n::{L298NConfig, L298NDriver, L298NMotorPins as DriverMotorPins};
56use mecha10_drivers_motor::{ControlMode, MotorCommand, MotorDriver};
57use serde::{Deserialize, Serialize};
58use std::collections::HashMap;
59use std::time::{SystemTime, UNIX_EPOCH};
60use tracing::{debug, info, warn};
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct L298nControllerConfig {
65 pub gpio_chip: String,
67
68 pub motor_a: L298nMotorPins,
70
71 pub motor_b: L298nMotorPins,
73
74 #[serde(default = "default_pwm_frequency")]
76 pub pwm_frequency_hz: u32,
77
78 #[serde(default = "default_wheel_base")]
80 pub wheel_base: f32,
81
82 #[serde(default = "default_max_velocity")]
84 pub max_velocity: f32,
85
86 #[serde(default = "default_max_duty_cycle")]
88 pub max_duty_cycle: f32,
89}
90
91fn default_pwm_frequency() -> u32 {
92 1000
93}
94
95fn default_wheel_base() -> f32 {
96 0.3
97}
98
99fn default_max_velocity() -> f32 {
100 1.0
101}
102
103fn default_max_duty_cycle() -> f32 {
104 1.0
105}
106
107impl Default for L298nControllerConfig {
108 fn default() -> Self {
109 Self {
110 gpio_chip: "gpiochip0".to_string(),
111 motor_a: L298nMotorPins::default_motor_a(),
112 motor_b: L298nMotorPins::default_motor_b(),
113 pwm_frequency_hz: default_pwm_frequency(),
114 wheel_base: default_wheel_base(),
115 max_velocity: default_max_velocity(),
116 max_duty_cycle: default_max_duty_cycle(),
117 }
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct L298nMotorPins {
124 pub in1: u32,
126
127 pub in2: u32,
129
130 pub enable: u32,
132
133 #[serde(default)]
135 pub invert: bool,
136}
137
138impl L298nMotorPins {
139 pub fn default_motor_a() -> Self {
141 Self {
142 in1: 17,
143 in2: 27,
144 enable: 18,
145 invert: false,
146 }
147 }
148
149 pub fn default_motor_b() -> Self {
151 Self {
152 in1: 22,
153 in2: 23,
154 enable: 12,
155 invert: false,
156 }
157 }
158}
159
160impl From<L298nMotorPins> for DriverMotorPins {
161 fn from(pins: L298nMotorPins) -> Self {
162 DriverMotorPins {
163 in1: pins.in1,
164 in2: pins.in2,
165 enable: pins.enable,
166 invert: pins.invert,
167 }
168 }
169}
170
171pub struct L298nMotorController {
175 config: L298nControllerConfig,
176 driver: L298NDriver,
177 state: ControllerState,
178 left_velocity: f32,
179 right_velocity: f32,
180 left_encoder: i32,
182 right_encoder: i32,
183 last_update_us: u64,
184}
185
186#[async_trait]
187impl Controller for L298nMotorController {
188 type Config = L298nControllerConfig;
189 type Error = ControllerError;
190
191 async fn init(config: Self::Config) -> Result<Self, Self::Error> {
192 info!("Initializing L298N motor controller");
193 info!("Wheel base: {} m", config.wheel_base);
194 info!("Max velocity: {}", config.max_velocity);
195 info!("Max duty cycle: {}%", config.max_duty_cycle * 100.0);
196
197 let driver_config = L298NConfig {
199 gpio_chip: config.gpio_chip.clone(),
200 motor_a: config.motor_a.clone().into(),
201 motor_b: config.motor_b.clone().into(),
202 pwm_frequency_hz: config.pwm_frequency_hz,
203 max_duty_cycle: config.max_duty_cycle,
204 update_rate_hz: 50.0,
205 };
206
207 let mut driver = L298NDriver::new(driver_config);
208 driver
209 .init()
210 .await
211 .map_err(|e| ControllerError::InitializationFailed(e.to_string()))?;
212
213 Ok(Self {
214 config,
215 driver,
216 state: ControllerState::Initialized,
217 left_velocity: 0.0,
218 right_velocity: 0.0,
219 left_encoder: 0,
220 right_encoder: 0,
221 last_update_us: now_us(),
222 })
223 }
224
225 async fn start(&mut self) -> Result<(), Self::Error> {
226 info!("Starting L298N motor controller");
227
228 self.driver
230 .enable(0)
231 .await
232 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
233 self.driver
234 .enable(1)
235 .await
236 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
237
238 self.state = ControllerState::Running;
239 self.last_update_us = now_us();
240 info!("L298N motor controller started");
241
242 Ok(())
243 }
244
245 async fn stop(&mut self) -> Result<(), Self::Error> {
246 info!("Stopping L298N motor controller");
247
248 self.set_velocity(0.0, 0.0).await?;
250
251 self.driver
253 .disable(0)
254 .await
255 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
256 self.driver
257 .disable(1)
258 .await
259 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
260
261 self.state = ControllerState::Stopped;
262 info!("L298N motor controller stopped");
263
264 Ok(())
265 }
266
267 async fn health_check(&self) -> ControllerHealth {
268 match self.state {
269 ControllerState::Running => ControllerHealth::Healthy,
270 ControllerState::Initialized => ControllerHealth::Degraded {
271 reason: "Controller initialized but not started".to_string(),
272 },
273 ControllerState::Stopped => ControllerHealth::Degraded {
274 reason: "Controller stopped".to_string(),
275 },
276 ControllerState::Error => ControllerHealth::Unhealthy {
277 reason: "Controller in error state".to_string(),
278 },
279 ControllerState::Uninitialized => ControllerHealth::Unknown,
280 }
281 }
282
283 fn capabilities(&self) -> ControllerCapabilities {
284 ControllerCapabilities::new("motor", "l298n")
285 .with_vendor("STMicroelectronics")
286 .with_model("L298N Dual H-Bridge")
287 .with_feature("velocity_control", true)
288 .with_feature("encoders", false) .with_feature("position_control", false)
290 .with_feature("torque_control", false)
291 .with_feature("current_sensing", false)
292 .with_feature("temperature_sensing", false)
293 .with_metadata("wheel_base", format!("{} m", self.config.wheel_base))
294 .with_metadata("max_duty_cycle", format!("{}%", self.config.max_duty_cycle * 100.0))
295 }
296
297 async fn diagnostics(&self) -> HashMap<String, String> {
298 let mut diag = HashMap::new();
299 diag.insert("state".to_string(), format!("{:?}", self.state));
300 diag.insert("left_velocity".to_string(), format!("{:.2}", self.left_velocity));
301 diag.insert("right_velocity".to_string(), format!("{:.2}", self.right_velocity));
302 diag.insert("left_encoder_est".to_string(), self.left_encoder.to_string());
303 diag.insert("right_encoder_est".to_string(), self.right_encoder.to_string());
304 diag.insert("gpio_chip".to_string(), self.config.gpio_chip.clone());
305 diag
306 }
307}
308
309#[async_trait]
310impl MotorController for L298nMotorController {
311 async fn set_velocity(&mut self, left: f32, right: f32) -> Result<(), Self::Error> {
312 if self.state != ControllerState::Running {
313 return Err(ControllerError::InvalidState("Controller not running".to_string()));
314 }
315
316 let max_vel = self.config.max_velocity;
318 let left_clamped = left.clamp(-max_vel, max_vel);
319 let right_clamped = right.clamp(-max_vel, max_vel);
320
321 let left_norm = left_clamped / max_vel;
323 let right_norm = right_clamped / max_vel;
324
325 self.update_encoder_estimates(left_norm, right_norm);
327
328 self.left_velocity = left_clamped;
330 self.right_velocity = right_clamped;
331
332 let left_cmd = MotorCommand {
335 motor_id: 0,
336 mode: ControlMode::PWM,
337 target: left_norm as f64,
338 velocity_limit: None,
339 torque_limit: None,
340 profile_velocity: None,
341 profile_acceleration: None,
342 };
343
344 let right_cmd = MotorCommand {
345 motor_id: 1,
346 mode: ControlMode::PWM,
347 target: right_norm as f64,
348 velocity_limit: None,
349 torque_limit: None,
350 profile_velocity: None,
351 profile_acceleration: None,
352 };
353
354 self.driver
355 .send_command(&left_cmd)
356 .await
357 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
358
359 self.driver
360 .send_command(&right_cmd)
361 .await
362 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
363
364 debug!(
365 "Set velocity: left={:.2}, right={:.2} (norm: {:.2}, {:.2})",
366 left_clamped, right_clamped, left_norm, right_norm
367 );
368
369 Ok(())
370 }
371
372 async fn get_encoders(&mut self) -> Result<(i32, i32), Self::Error> {
373 warn!("L298N has no encoders - returning estimated values");
375 Ok((self.left_encoder, self.right_encoder))
376 }
377
378 async fn get_status(&mut self) -> Result<MotorStatus, Self::Error> {
379 let wheel_radius = 0.05; let vel_to_rpm = 60.0 / (2.0 * std::f32::consts::PI * wheel_radius);
382
383 Ok(MotorStatus {
384 timestamp: now_us(),
385 left_motor_rpm: self.left_velocity * vel_to_rpm,
386 right_motor_rpm: self.right_velocity * vel_to_rpm,
387 battery_voltage: 0.0, temperature_c: 0.0, })
390 }
391
392 async fn emergency_stop(&mut self) -> Result<(), Self::Error> {
393 info!("Emergency stop triggered!");
394
395 self.left_velocity = 0.0;
397 self.right_velocity = 0.0;
398
399 let stop_cmd = MotorCommand {
401 motor_id: 0,
402 mode: ControlMode::PWM,
403 target: 0.0,
404 velocity_limit: None,
405 torque_limit: None,
406 profile_velocity: None,
407 profile_acceleration: None,
408 };
409
410 let _ = self.driver.send_command(&stop_cmd).await;
412 let mut stop_cmd_b = stop_cmd;
413 stop_cmd_b.motor_id = 1;
414 let _ = self.driver.send_command(&stop_cmd_b).await;
415
416 let _ = self.driver.disable(0).await;
418 let _ = self.driver.disable(1).await;
419
420 self.state = ControllerState::Stopped;
421 Ok(())
422 }
423
424 async fn enable(&mut self) -> Result<(), Self::Error> {
425 self.driver
426 .enable(0)
427 .await
428 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
429 self.driver
430 .enable(1)
431 .await
432 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
433 Ok(())
434 }
435
436 async fn disable(&mut self) -> Result<(), Self::Error> {
437 self.set_velocity(0.0, 0.0).await?;
439
440 self.driver
441 .disable(0)
442 .await
443 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
444 self.driver
445 .disable(1)
446 .await
447 .map_err(|e| ControllerError::CommunicationError(e.to_string()))?;
448 Ok(())
449 }
450
451 async fn reset_encoders(&mut self) -> Result<(), Self::Error> {
452 self.left_encoder = 0;
453 self.right_encoder = 0;
454 info!("Reset estimated encoder counts");
455 Ok(())
456 }
457}
458
459impl L298nMotorController {
460 fn update_encoder_estimates(&mut self, left_norm: f32, right_norm: f32) {
462 let now = now_us();
463 let dt_us = now.saturating_sub(self.last_update_us);
464 let dt_s = dt_us as f32 / 1_000_000.0;
465
466 let counts_per_second = 1000.0;
468
469 self.left_encoder += (left_norm * counts_per_second * dt_s) as i32;
470 self.right_encoder += (right_norm * counts_per_second * dt_s) as i32;
471
472 self.last_update_us = now;
473 }
474
475 pub fn state(&self) -> ControllerState {
477 self.state
478 }
479
480 pub fn config(&self) -> &L298nControllerConfig {
482 &self.config
483 }
484}
485
486fn now_us() -> u64 {
488 SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_micros() as u64
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 #[tokio::test]
496 async fn test_controller_init() {
497 let config = L298nControllerConfig::default();
498 let controller = L298nMotorController::init(config).await;
499 assert!(controller.is_ok());
500 }
501
502 #[tokio::test]
503 async fn test_controller_start_stop() {
504 let config = L298nControllerConfig::default();
505 let mut controller = L298nMotorController::init(config).await.unwrap();
506
507 controller.start().await.unwrap();
508 assert_eq!(controller.state(), ControllerState::Running);
509
510 controller.stop().await.unwrap();
511 assert_eq!(controller.state(), ControllerState::Stopped);
512 }
513
514 #[tokio::test]
515 async fn test_set_velocity() {
516 let config = L298nControllerConfig::default();
517 let mut controller = L298nMotorController::init(config).await.unwrap();
518 controller.start().await.unwrap();
519
520 let result = controller.set_velocity(0.5, 0.5).await;
521 assert!(result.is_ok());
522 }
523
524 #[tokio::test]
525 async fn test_emergency_stop() {
526 let config = L298nControllerConfig::default();
527 let mut controller = L298nMotorController::init(config).await.unwrap();
528 controller.start().await.unwrap();
529
530 controller.set_velocity(1.0, 1.0).await.unwrap();
531 controller.emergency_stop().await.unwrap();
532
533 assert_eq!(controller.state(), ControllerState::Stopped);
534 }
535
536 #[tokio::test]
537 async fn test_capabilities() {
538 let config = L298nControllerConfig::default();
539 let controller = L298nMotorController::init(config).await.unwrap();
540
541 let caps = controller.capabilities();
542 assert_eq!(caps.controller_type, "motor");
543 assert_eq!(caps.implementation, "l298n");
544 assert!(!caps.supports("encoders"));
545 assert!(caps.supports("velocity_control"));
546 }
547}