1use std::fmt::{Display, Formatter};
2use std::sync::Arc;
3use std::time::SystemTime;
4
5use parking_lot::RwLock;
6
7use crate::animations::{Animation, Easing, Keyframe, Segment, Track};
8use crate::devices::{Device, Output};
9use crate::errors::HardwareError::IncompatiblePin;
10use crate::errors::{Error, StateError};
11use crate::hardware::Hardware;
12use crate::io::{IoProtocol, Pin, PinModeId};
13use crate::utils::{task, Range, Scalable, State};
14use crate::{pause, pause_sync};
15
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
18pub enum ServoType {
19 #[default]
20 Standard,
21 Continuous,
22}
23
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[derive(Clone, Debug)]
27pub struct Servo {
28 pin: u8,
32 #[cfg_attr(feature = "serde", serde(with = "crate::devices::arc_rwlock_serde"))]
34 state: Arc<RwLock<u16>>,
35 default: u16,
37
38 servo_type: ServoType,
42 range: Range<u16>,
44 pwm_range: Range<u16>,
46 degree_range: Range<u16>,
48 #[cfg_attr(
50 feature = "serde",
51 serde(skip_serializing_if = "crate::utils::is_default")
52 )]
53 #[cfg_attr(feature = "serde", serde(default))]
54 inverted: bool,
55 #[cfg_attr(
57 feature = "serde",
58 serde(skip_serializing_if = "crate::utils::is_default")
59 )]
60 #[cfg_attr(feature = "serde", serde(default))]
61 auto_detach: bool,
62 #[cfg_attr(
64 feature = "serde",
65 serde(skip_serializing_if = "crate::utils::is_default")
66 )]
67 #[cfg_attr(feature = "serde", serde(default))]
68 detach_delay: usize,
69
70 #[cfg_attr(feature = "serde", serde(skip))]
74 previous: u16,
75 #[cfg_attr(feature = "serde", serde(skip))]
76 protocol: Box<dyn IoProtocol>,
77 #[cfg_attr(feature = "serde", serde(skip))]
79 animation: Arc<Option<Animation>>,
80 #[cfg_attr(feature = "serde", serde(skip))]
81 last_move: Arc<RwLock<Option<SystemTime>>>,
82}
83
84impl Servo {
85 pub fn new(board: &dyn Hardware, pin: u8, default: u16) -> Result<Self, Error> {
91 Self::create(board, pin, default, false)
92 }
93
94 pub fn new_inverted(board: &dyn Hardware, pin: u8, default: u16) -> Result<Self, Error> {
100 Self::create(board, pin, default, true)
101 }
102
103 fn create(board: &dyn Hardware, pin: u8, default: u16, inverted: bool) -> Result<Self, Error> {
105 let pwm_range = Range::from([600, 2400]);
106
107 let mut servo = Self {
108 pin,
109 state: Arc::new(RwLock::new(default)),
110 default,
111 servo_type: ServoType::default(),
112 range: Range::from([0, 180]),
113 pwm_range,
114 degree_range: Range::from([0, 180]),
115 inverted,
116 auto_detach: false,
117 detach_delay: 20000,
118 previous: u16::MAX, protocol: board.get_protocol(),
120 animation: Arc::new(None),
121 last_move: Arc::new(RwLock::new(None)),
122 };
123
124 servo
128 .get_pin_info()?
129 .supports_mode(PinModeId::SERVO)
130 .ok_or(IncompatiblePin {
131 pin,
132 mode: PinModeId::SERVO,
133 context: "create a new Servo device",
134 })?;
135 servo.protocol.servo_config(pin, pwm_range)?;
136 servo.to(servo.default)?;
137 servo.protocol.set_pin_mode(pin, PinModeId::SERVO)?;
138 pause_sync!(100);
139 Ok(servo)
140 }
141
142 pub fn to(&mut self, to: u16) -> Result<&Self, Error> {
144 self.stop();
146
147 self.set_state(to.into())?;
148 Ok(self)
149 }
150
151 pub fn sweep(&mut self, ms: u64) -> &Self {
157 let mut animation = Animation::from(
158 Segment::from(
159 Track::new(self.clone())
160 .with_keyframe(Keyframe::new(self.range.end, 0, ms))
161 .with_keyframe(Keyframe::new(self.range.start, ms, ms * 2)),
162 )
163 .set_repeat(true),
164 );
165 animation.play();
166 self.animation = Arc::new(Some(animation));
167
168 self
169 }
170
171 pub fn get_pin(&self) -> u8 {
176 self.pin
177 }
178
179 pub fn get_pin_info(&self) -> Result<Pin, Error> {
181 let lock = self.protocol.get_io().read();
182 Ok(lock.get_pin(self.pin)?.clone())
183 }
184
185 pub fn get_type(&self) -> ServoType {
187 self.servo_type
188 }
189
190 pub fn set_type(mut self, servo_type: ServoType) -> Self {
192 self.servo_type = servo_type;
193 self
194 }
195
196 pub fn get_range(&self) -> Range<u16> {
203 self.range
204 }
205
206 pub fn set_range<R: Into<Range<u16>>>(mut self, range: R) -> Self {
212 let input = range.into();
213
214 let input = Range {
216 start: input.start.min(input.end),
217 end: input.end.max(input.start),
218 };
219
220 self.range = Range {
222 start: input
223 .start
224 .clamp(self.degree_range.start, self.degree_range.end),
225 end: input
226 .end
227 .clamp(self.degree_range.start, self.degree_range.end),
228 };
229 self.default = self.default.clamp(self.range.start, self.range.end);
231
232 self
233 }
234
235 pub fn get_degree_range(&self) -> Range<u16> {
237 self.degree_range
238 }
239
240 pub fn set_degree_range<R: Into<Range<u16>>>(mut self, degree_range: R) -> Self {
245 let input = degree_range.into();
246
247 let input = Range {
249 start: input.start.min(input.end),
250 end: input.end.max(input.start),
251 };
252
253 self.degree_range = input;
254
255 self.range = Range {
257 start: self
258 .range
259 .start
260 .clamp(self.degree_range.start, self.degree_range.end),
261 end: self
262 .range
263 .end
264 .clamp(self.degree_range.start, self.degree_range.end),
265 };
266 self.default = self.default.clamp(self.range.start, self.range.end);
268
269 self
270 }
271
272 pub fn get_pwn_range(&self) -> Range<u16> {
274 self.pwm_range
275 }
276
277 pub fn set_pwn_range<R: Into<Range<u16>>>(mut self, pwm_range: R) -> Result<Self, Error> {
282 let input = pwm_range.into();
283 self.pwm_range = input;
284 self.protocol.servo_config(self.pin, input)?;
285 Ok(self)
286 }
287
288 pub fn is_inverted(&self) -> bool {
290 self.inverted
291 }
292
293 pub fn set_inverted(mut self, inverted: bool) -> Self {
295 self.inverted = inverted;
296 self
297 }
298
299 pub fn is_auto_detach(&self) -> bool {
301 self.auto_detach
302 }
303
304 pub fn set_auto_detach(mut self, auto_detach: bool) -> Self {
306 self.auto_detach = match auto_detach {
307 false => {
308 self.protocol
309 .set_pin_mode(self.pin, PinModeId::SERVO)
310 .unwrap();
311 false
312 }
313 true => {
314 self.protocol
315 .set_pin_mode(self.pin, PinModeId::OUTPUT)
316 .unwrap();
317 true
318 }
319 };
320 self
321 }
322
323 pub fn get_detach_delay(&self) -> usize {
325 self.detach_delay
326 }
327
328 pub fn set_detach_delay(mut self, detach_delay: usize) -> Self {
330 self.detach_delay = detach_delay;
331 self
332 }
333}
334
335impl Display for Servo {
336 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
337 write!(
338 f,
339 "SERVO (pin={}) [state={}, default={}, range={}-{}]",
340 self.pin,
341 self.state.read(),
342 self.default,
343 self.range.start,
344 self.range.end
345 )
346 }
347}
348
349#[cfg_attr(feature = "serde", typetag::serde)]
350impl Device for Servo {}
351
352#[cfg_attr(feature = "serde", typetag::serde)]
353impl Output for Servo {
354 fn get_state(&self) -> State {
355 (*self.state.read()).into()
356 }
357 fn set_state(&mut self, state: State) -> Result<State, Error> {
359 let value = match state {
361 State::Integer(value) => Ok(value as u16),
362 State::Signed(value) => match value >= 0 {
363 true => Ok(value as u16),
364 false => Err(StateError),
365 },
366 State::Float(value) => match value >= 0.0 {
367 true => Ok(value as u16),
368 false => Err(StateError),
369 },
370 _ => Err(StateError),
371 }?;
372
373 let value: u16 = value.clamp(self.range.start, self.range.end);
375 let pwm: f64 = match self.inverted {
381 false => value.scale(
382 self.degree_range.start,
383 self.degree_range.end,
384 self.pwm_range.start,
385 self.pwm_range.end,
386 ),
387 true => value.scale(
388 self.degree_range.end,
389 self.degree_range.start,
390 self.pwm_range.start,
391 self.pwm_range.end,
392 ),
393 };
394
395 match self.auto_detach {
397 false => self.protocol.analog_write(self.pin, pwm as u16)?,
398 true => {
399 self.protocol.set_pin_mode(self.pin, PinModeId::SERVO)?;
400 self.protocol.analog_write(self.pin, pwm as u16)?;
401 *self.last_move.write() = Some(SystemTime::now());
402
403 let mut self_clone = self.clone();
404 task::run(async move {
405 pause!(self_clone.detach_delay);
406 if let Some(last_move) = self_clone.last_move.read().as_ref() {
407 if last_move.elapsed().unwrap().as_millis()
408 >= (self_clone.detach_delay as u128)
409 {
410 self_clone
411 .protocol
412 .set_pin_mode(self_clone.pin, PinModeId::UNSUPPORTED)
413 .unwrap();
414 }
415 }
416 })?;
417 }
418 }
419 let current = *self.state.read();
420 self.previous = current;
421 *self.state.write() = value;
422 Ok(value.into())
423 }
424 fn get_default(&self) -> State {
425 self.default.into()
426 }
427
428 fn animate<S: Into<State>>(&mut self, state: S, duration: u64, transition: Easing) {
429 let mut animation = Animation::from(
430 Track::new(self.clone())
431 .with_keyframe(Keyframe::new(state, 0, duration).set_transition(transition)),
432 );
433 animation.play();
434 self.animation = Arc::new(Some(animation));
435 }
436 fn is_busy(&self) -> bool {
437 self.animation.is_some()
438 }
439 fn stop(&mut self) {
440 if let Some(animation) = Arc::get_mut(&mut self.animation).and_then(Option::as_mut) {
441 animation.stop();
442 }
443 self.animation = Arc::new(None);
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use crate::animations::Easing;
450 use crate::devices::{Output, Servo};
451 use crate::hardware::Board;
452 use crate::io::PinModeId;
453 use crate::mocks::plugin_io::MockIoProtocol;
454 use crate::pause;
455 use crate::utils::{Range, State};
456 use hermes_five::devices::ServoType;
457
458 fn _setup_servo(pin: u8) -> Servo {
459 let board = Board::new(MockIoProtocol::default()); Servo::new(&board, pin, 90).unwrap()
461 }
462
463 #[test]
464 fn test_servo_creation() {
465 let board = Board::new(MockIoProtocol::default());
466
467 let servo = Servo::new(&board, 12, 90).unwrap();
468 assert_eq!(servo.get_pin(), 12);
469 assert_eq!(*servo.state.read(), 90);
470 assert!(!servo.is_inverted());
471
472 let inverted_servo = Servo::new_inverted(&board, 12, 90).unwrap();
473 assert!(inverted_servo.is_inverted());
474
475 let servo = Servo::new(&board, 12, 66).unwrap();
476 assert_eq!(servo.get_default(), State::Integer(66));
477 assert_eq!(servo.get_state(), State::Integer(66));
478 }
479
480 #[test]
481 fn test_servo_move() {
482 let mut servo = _setup_servo(12);
483 let result = servo.to(150); assert!(result.is_ok());
485 assert_eq!(*servo.state.read(), 150); }
487
488 #[test]
489 fn test_servo_range_setting() {
490 let mut servo = _setup_servo(12);
491 servo = servo.set_range([100, 200]); assert_eq!(servo.get_range(), Range::from([100, 180])); assert_eq!(servo.default, 100); }
495
496 #[test]
497 fn test_servo_degree_range_setting() {
498 let mut servo = _setup_servo(12);
499 servo = servo.set_degree_range([100, 200]); assert_eq!(servo.get_degree_range(), Range::from([100, 200]));
501 assert_eq!(servo.get_range(), Range::from([100, 180])); assert_eq!(servo.default, 100); }
504
505 #[test]
506 fn test_servo_pwm_range_setting() {
507 let servo = _setup_servo(12);
508 let result = servo.set_pwn_range([999, 9999]);
509 assert!(result.is_ok());
510 assert_eq!(result.unwrap().get_pwn_range(), Range::from([999, 9999]));
511 }
512
513 #[test]
514 fn test_servo_detach_delay() {
515 let mut servo = _setup_servo(12);
516 assert_eq!(servo.get_detach_delay(), 20000);
517 servo = servo.set_detach_delay(100);
518 assert_eq!(servo.get_detach_delay(), 100);
519 }
520
521 #[hermes_five_macros::test]
522 fn test_servo_auto_detach() {
523 let mut servo = _setup_servo(12).set_auto_detach(true).set_detach_delay(300);
524 assert!(servo.is_auto_detach());
525 assert_eq!(servo.get_pin_info().unwrap().mode.id, PinModeId::OUTPUT);
526
527 servo = servo.set_auto_detach(false);
529 assert!(!servo.is_auto_detach());
530 assert_eq!(servo.get_pin_info().unwrap().mode.id, PinModeId::SERVO);
531 let _ = servo.to(180);
532
533 servo = servo.set_auto_detach(true);
535 assert_eq!(servo.get_pin_info().unwrap().mode.id, PinModeId::OUTPUT);
536 assert!(servo.is_auto_detach());
537
538 servo.to(180).expect("");
540 assert_eq!(servo.get_pin_info().unwrap().mode.id, PinModeId::SERVO);
541 pause!(80);
542 servo.to(180).expect("");
544 pause!(80);
545 assert_eq!(servo.get_pin_info().unwrap().mode.id, PinModeId::SERVO);
546 pause!(3000);
548 assert!(servo.is_auto_detach());
549 assert_eq!(
550 servo.get_pin_info().unwrap().mode.id,
551 PinModeId::UNSUPPORTED
552 );
553 }
554
555 #[test]
556 fn test_servo_type() {
557 let mut servo = _setup_servo(12);
558 assert_eq!(servo.get_type(), ServoType::Standard);
559 servo = servo.set_type(ServoType::Continuous);
560 assert_eq!(servo.get_type(), ServoType::Continuous);
561 }
562
563 #[test]
564 fn test_servo_state() {
565 let mut servo = _setup_servo(12);
566
567 assert!(servo.set_state(State::Integer(66)).is_ok());
569 assert_eq!(servo.get_state(), State::Integer(66));
570
571 assert!(servo.set_state(State::Integer(666)).is_ok());
573 assert_eq!(servo.get_state(), State::Integer(180));
574
575 assert!(servo.set_state(State::Signed(-12)).is_err());
577 assert!(servo.set_state(State::Signed(12)).is_ok());
578 assert_eq!(servo.get_state(), State::Integer(12));
579
580 assert!(servo.set_state(State::Float(-12.5)).is_err());
582 assert!(servo.set_state(State::Float(12.5)).is_ok());
583 assert_eq!(servo.get_state(), State::Integer(12));
584
585 assert!(servo.set_state(State::Boolean(true)).is_err());
587
588 let mut servo = _setup_servo(12).set_inverted(true);
590 assert!(servo.set_state(State::Integer(66)).is_ok());
591 assert_eq!(servo.get_state(), State::Integer(66));
592 }
593
594 #[hermes_five_macros::test]
595 fn test_servo_sweep() {
596 let mut servo = _setup_servo(12);
597 assert!(!servo.is_busy());
598 servo.stop();
599 servo.sweep(200);
600 pause!(100);
601 assert!(servo.is_busy()); servo.stop();
603 assert!(!servo.is_busy());
604 }
605
606 #[hermes_five_macros::test]
607 fn test_animation() {
608 let mut servo = _setup_servo(12);
609 assert!(!servo.is_busy());
610 servo.stop();
612 servo.animate(66, 500, Easing::Linear);
614 pause!(100);
615 assert!(servo.is_busy()); servo.stop();
617 assert!(!servo.is_busy());
618 }
619
620 #[test]
621 fn test_servo_display() {
622 let servo = _setup_servo(12);
623 let display_output = format!("{}", servo);
624 let expected_output = "SERVO (pin=12) [state=90, default=90, range=0-180]";
625 assert_eq!(display_output, expected_output);
626 }
627}
628
629#[cfg(feature = "serde")]
630#[cfg(test)]
631mod serde_tests {
632 use crate::hardware::{Board, Hardware, PCA9685};
633 use crate::mocks::plugin_io::MockIoProtocol;
634 use hermes_five::devices::Servo;
635
636 #[test]
637 fn test_servo_serialize() {
638 let board = Board::new(MockIoProtocol::default());
639 let servo = Servo::new(&board, 12, 90).expect("servo");
640 let json = serde_json::to_string(&servo).unwrap();
641 assert_eq!(
642 json,
643 r#"{"pin":12,"state":90,"default":90,"servo_type":"Standard","range":[0,180],"pwm_range":[600,2400],"degree_range":[0,180],"detach_delay":20000}"#
644 );
645
646 let pca9685 = PCA9685::default(&board).expect("pca9685");
647 let servo = Servo::new(&pca9685, 12, 90).expect("servo");
648 let json = serde_json::to_string(&servo).unwrap();
649 assert_eq!(
650 json,
651 r#"{"pin":12,"state":90,"default":90,"servo_type":"Standard","range":[0,180],"pwm_range":[600,2400],"degree_range":[0,180],"detach_delay":20000}"#
652 );
653 }
654
655 #[test]
656 fn test_board_deserialize() {
657 let json =
658 r#"{"protocol":{"type":"RemoteIo","transport":{"type":"Serial","port":"mock"}}}"#;
659 let board: Board = serde_json::from_str(json).unwrap();
660 assert_eq!(board.get_protocol_name(), "RemoteIo");
661
662 let json = r#"{"protocol":{"type":"MockIoProtocol"}}"#;
663 let board: Board = serde_json::from_str(json).unwrap();
664 assert_eq!(board.get_protocol_name(), "MockIoProtocol");
665 }
666}