1use crate::errors::{Error, HardwareError, UnknownError};
6use crate::hardware::{Board, Expander, Hardware};
7use crate::io::{IoData, IoProtocol, Pin, PinMode, PinModeId, IO};
8use crate::utils::{Range, Scalable};
9use parking_lot::RwLock;
10use std::collections::HashMap;
11use std::fmt::{Display, Formatter};
12use std::sync::Arc;
13
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[derive(Debug, Clone)]
16pub struct PCA9685 {
17 address: u8,
19 frequency: u16,
21
22 #[cfg_attr(feature = "serde", serde(skip))]
25 servo_configs: HashMap<u8, Range<u16>>,
26 #[cfg_attr(feature = "serde", serde(skip))]
27 data: Arc<RwLock<IoData>>,
28 #[cfg_attr(feature = "serde", serde(skip))]
29 protocol: Box<dyn IoProtocol>,
30}
31
32impl PCA9685 {
33 const MODE1: u8 = 0x0;
35 const PRESCALE: u8 = 0xFE;
37 const BASE: u8 = 0x06;
38 const SLEEP: u8 = 0x10;
39 const RESET: u8 = 0x00;
40 const RESTART: u8 = 0x80;
41 const AUTO_INCREMENT: u8 = 0x20;
42 const MIN_FREQUENCY: u16 = 24; const MAX_FREQUENCY: u16 = 1526; const OSC_CLOCK: f32 = 25_000_000.0; fn _build_pca9685_data() -> IoData {
48 let mut data = IoData {
49 pins: Default::default(),
50 i2c_data: vec![],
51 digital_reported_pins: vec![],
52 analog_reported_channels: vec![],
53 protocol_version: "PCA9685".to_string(),
54 firmware_name: "PCA9685".to_string(),
55 firmware_version: "n/a".to_string(),
56 connected: false,
57 };
58
59 for id in 0..16 {
60 data.pins.insert(
61 id,
62 Pin {
63 id,
64 name: format!("D{}", id),
65 mode: Default::default(),
66 supported_modes: vec![
67 PinMode {
68 id: PinModeId::OUTPUT,
69 resolution: 1,
70 },
71 PinMode {
72 id: PinModeId::PWM,
73 resolution: 8,
74 },
75 PinMode {
76 id: PinModeId::SERVO,
77 resolution: 8,
78 },
79 PinMode {
80 id: PinModeId::ANALOG,
81 resolution: 8,
82 },
83 PinMode {
84 id: PinModeId::UNSUPPORTED,
85 resolution: 0,
86 },
87 ],
88 channel: None,
89 value: 0,
90 },
91 );
92 }
93
94 data
95 }
96
97 pub fn default(board: &Board) -> Result<Self, Error> {
98 PCA9685::new(board, 0x40)
99 }
100
101 pub fn new(board: &dyn Hardware, address: u8) -> Result<Self, Error> {
102 let protocol = board.get_protocol();
103 let mut expander = Self {
104 address,
105 frequency: 50,
106 servo_configs: Default::default(),
107 data: Arc::new(RwLock::new(PCA9685::_build_pca9685_data())),
108 protocol,
109 };
110 IoProtocol::open(&mut expander)?;
111 Ok(expander)
112 }
113
114 pub fn get_address(&self) -> u8 {
115 self.address
116 }
117
118 pub fn get_frequency(&self) -> u16 {
119 self.frequency
120 }
121
122 pub fn set_frequency(&mut self, frequency: u16) -> Result<&Self, Error> {
124 if !(Self::MIN_FREQUENCY..=Self::MAX_FREQUENCY).contains(&frequency) {
126 return Err(UnknownError {
127 info: format!(
128 "Frequency must be between {} and {} Hz",
129 Self::MIN_FREQUENCY,
130 Self::MAX_FREQUENCY
131 ),
132 });
133 };
134
135 self.frequency = frequency;
136
137 self.write_to_reg(PCA9685::MODE1, PCA9685::RESET)?;
140 self.write_to_reg(PCA9685::MODE1, PCA9685::SLEEP)?;
141
142 let prescale = ((PCA9685::OSC_CLOCK / (4096.0 * self.frequency as f32)) + 0.5 - 1.0)
146 .clamp(3.0, 255.0) as u8;
147 self.write_to_reg(PCA9685::PRESCALE, prescale)?;
148
149 self.write_to_reg(PCA9685::MODE1, PCA9685::RESET)?;
151 self.write_to_reg(PCA9685::MODE1, PCA9685::RESTART | PCA9685::AUTO_INCREMENT)?;
153
154 Ok(self)
163 }
164
165 pub fn write_to_reg(&mut self, register: u8, value: u8) -> Result<(), Error> {
166 self.protocol
167 .i2c_write(self.address, &[register as u16, value as u16])
168 }
169
170 pub fn read_from_reg(&mut self, register: u8) -> Result<u8, Error> {
171 self.i2c_write(self.address, &[register as u16])?;
172 self.i2c_read(self.address, 1)?;
173 let register_value = {
174 let lock = self.protocol.get_io().read();
175 *lock.i2c_data.last().unwrap().data.last().unwrap()
176 };
177 Ok(register_value)
178 }
179}
180
181impl Expander for PCA9685 {}
182
183impl Hardware for PCA9685 {
184 fn get_protocol(&self) -> Box<dyn IoProtocol> {
185 Box::new(self.clone())
186 }
187
188 #[cfg(not(tarpaulin_include))]
189 fn set_protocol(&mut self, protocol: Box<dyn IoProtocol>) {
190 self.protocol = protocol;
191 }
192}
193
194#[cfg_attr(feature = "serde", typetag::serde)]
195impl IoProtocol for PCA9685 {
196 fn open(&mut self) -> Result<(), Error> {
197 self.i2c_config(0)?;
198 self.data.write().connected = true;
199 Ok(())
200 }
201
202 fn close(&mut self) -> Result<(), Error> {
203 self.write_to_reg(PCA9685::MODE1, PCA9685::RESTART)?;
204 self.data.write().connected = false;
205 Ok(())
206 }
207
208 #[cfg(not(tarpaulin_include))]
209 fn report_analog(&mut self, _: u8, _: bool) -> Result<(), Error> {
210 unimplemented!();
211 }
212
213 #[cfg(not(tarpaulin_include))]
214 fn report_digital(&mut self, _: u8, _: bool) -> Result<(), Error> {
215 unimplemented!()
216 }
217
218 #[cfg(not(tarpaulin_include))]
219 fn sampling_interval(&mut self, _: u16) -> Result<(), Error> {
220 unimplemented!()
221 }
222}
223
224impl IO for PCA9685 {
225 fn get_io(&self) -> &Arc<RwLock<IoData>> {
226 &self.data
227 }
228
229 fn is_connected(&self) -> bool {
230 self.data.read().connected
231 }
232
233 fn set_pin_mode(&mut self, pin: u8, mode: PinModeId) -> Result<(), Error> {
234 {
235 let mut lock = self.data.write();
236 let pin_instance = lock.get_pin_mut(pin)?;
237 let _mode = pin_instance
238 .supports_mode(mode)
239 .ok_or(HardwareError::IncompatiblePin {
240 pin,
241 mode,
242 context: "try to set pin mode",
243 })?;
244 pin_instance.mode = _mode;
245 }
246
247 if mode == PinModeId::UNSUPPORTED {
249 let payload = &[(PCA9685::BASE + 4 * pin) as u16, 0, 0, 4096, 4096 >> 8];
250 self.protocol.i2c_write(self.address, payload)?;
251 return Ok(());
252 }
253
254 let frequency: u16 = match mode {
256 PinModeId::OUTPUT => Ok(300), PinModeId::ANALOG => Ok(30), PinModeId::PWM => Ok(300), PinModeId::SERVO => Ok(50),
260 _ => Err(Error::from(HardwareError::IncompatiblePin {
261 mode,
262 pin,
263 context: "update digital output",
264 })),
265 }?;
266 self.set_frequency(frequency)?;
267
268 Ok(())
269 }
270
271 fn digital_write(&mut self, pin: u8, level: bool) -> Result<(), Error> {
272 let value = if level { 255 } else { 0 };
273 self.analog_write(pin, value)
274 }
275
276 fn analog_write(&mut self, pin: u8, level: u16) -> Result<(), Error> {
277 {
278 let mut lock = self.data.write();
279 let pin_instance = lock.get_pin_mut(pin)?;
281 pin_instance.value = level;
283 };
284
285 let servo_range = self.servo_configs.get(&pin);
288
289 let (on, off): (u16, u16) = match servo_range {
290 Some(_) => (0, (level as f32 / 4.88) as u16),
291 None => {
292 let level = level.clamp(0, 255);
293 match level {
294 0 => (0, 4096),
295 255 => (4096, 0),
296 level => (0, level.scale(0, 255, 0, 4095)),
297 }
298 }
299 };
300
301 let payload = &[(PCA9685::BASE + 4 * pin) as u16, on, on >> 8, off, off >> 8];
304
305 self.protocol.i2c_write(self.address, payload)
313 }
314
315 #[cfg(not(tarpaulin_include))]
316 fn digital_read(&mut self, _: u8) -> Result<bool, Error> {
317 unimplemented!()
318 }
319
320 #[cfg(not(tarpaulin_include))]
321 fn analog_read(&mut self, _: u8) -> Result<u16, Error> {
322 unimplemented!()
323 }
324
325 fn servo_config(&mut self, pin: u8, pwm_range: Range<u16>) -> Result<(), Error> {
326 self.servo_configs.insert(pin, pwm_range);
327 Ok(())
328 }
329
330 fn i2c_config(&mut self, delay: u16) -> Result<(), Error> {
331 self.protocol.i2c_config(delay)
332 }
333
334 fn i2c_read(&mut self, address: u8, size: u16) -> Result<(), Error> {
335 self.protocol.i2c_read(address, size)
336 }
337
338 fn i2c_write(&mut self, address: u8, data: &[u16]) -> Result<(), Error> {
339 self.protocol.i2c_write(address, data)
340 }
341}
342impl Display for PCA9685 {
343 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
344 let data = self.data.read();
345 write!(
346 f,
347 "{} [firmware={}, version={}, protocol={}, transport=I2C]",
348 self.get_name(),
349 data.firmware_name,
350 data.firmware_version,
351 data.protocol_version,
352 )
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::io::RemoteIo;
360 use crate::mocks::create_test_plugin_io_data;
361 use crate::mocks::plugin_io::MockIoProtocol;
362 use crate::mocks::transport_layer::MockTransportLayer;
363 use crate::utils::Range;
364
365 #[test]
366 fn test_helper() {
367 let data = PCA9685::_build_pca9685_data();
368 assert_eq!(data.firmware_name, "PCA9685");
369 assert_eq!(data.protocol_version, "PCA9685");
370 assert_eq!(data.pins.len(), 16);
371 }
372
373 #[test]
374 fn test_default_initialization() {
375 let board = Board::new(MockIoProtocol::default());
376 let pca9685 = PCA9685::default(&board).unwrap();
377
378 assert_eq!(pca9685.address, 0x40);
379 assert_eq!(pca9685.frequency, 50);
380 }
381
382 #[test]
383 fn test_custom_initialization() {
384 let board = Board::new(MockIoProtocol::default());
385 let pca9685 = PCA9685::new(&board, 0x41).unwrap();
386
387 assert_eq!(pca9685.address, 0x41);
388 assert_eq!(pca9685.frequency, 50);
389 }
390
391 #[test]
392 fn test_set_frequency_valid() {
393 let board = Board::new(MockIoProtocol::default());
394 let mut pca9685 = PCA9685::default(&board).unwrap();
395
396 assert!(pca9685.set_frequency(100).is_ok());
397 assert_eq!(pca9685.frequency, 100);
398 }
399
400 #[test]
401 fn test_set_frequency_outofbound() {
402 let board = Board::new(MockIoProtocol::default());
403 let mut pca9685 = PCA9685::default(&board).unwrap();
404
405 let result = pca9685.set_frequency(20);
406 assert!(result.is_err());
407 assert_eq!(
408 result.err().unwrap().to_string(),
409 "Unknown error: Frequency must be between 24 and 1526 Hz."
410 );
411
412 let result = pca9685.set_frequency(1600);
413 assert!(result.is_err());
414 assert_eq!(
415 result.err().unwrap().to_string(),
416 "Unknown error: Frequency must be between 24 and 1526 Hz."
417 );
418 }
419
420 #[test]
421 fn test_write_to_reg() {
422 let transport = MockTransportLayer::default();
423 let board = Board::new(RemoteIo::from(transport));
424 let mut pca9685 = PCA9685::default(&board).unwrap();
425
426 assert!(pca9685.write_to_reg(0x69, 0x42).is_ok());
427 }
428
429 #[test]
430 fn test_read_from_reg() {
431 let mut transport = MockTransportLayer::default();
432
433 let data = &[0xF0, 0x77, 0x40, 0x00, 0x69, 0x00, 0x42, 0x00, 0xF7];
435 transport.read_buf[..data.len()].copy_from_slice(data);
436 let protocol = RemoteIo::from(transport);
437 *protocol.get_io().write() = create_test_plugin_io_data();
438
439 let board = Board::new(protocol);
440 let mut pca9685 = PCA9685::new(&board, 0x40).unwrap();
441
442 let value = pca9685.read_from_reg(0x69).unwrap();
443 assert_eq!(value, 0x42);
444 }
445
446 #[test]
447 fn test_read_from_reg_failure() {
448 let mut transport = MockTransportLayer::default();
449
450 let data = &[0xF0, 0x77, 0x40, 0x00, 0xF7];
452 transport.read_buf[..data.len()].copy_from_slice(data);
453 let protocol = RemoteIo::from(transport);
454 *protocol.get_io().write() = create_test_plugin_io_data();
455
456 let board = Board::new(protocol);
457 let mut pca9685 = PCA9685::new(&board, 0x40).unwrap();
458
459 let result = pca9685.read_from_reg(PCA9685::MODE1);
460 assert!(result.is_err());
461 }
462
463 #[test]
464 fn test_set_pin_mode() {
465 let board = Board::new(MockIoProtocol::default());
466 let mut pca9685 = PCA9685::default(&board).unwrap();
467
468 assert!(pca9685.set_pin_mode(0, PinModeId::OUTPUT).is_ok());
470 assert_eq!(pca9685.get_frequency(), 300);
471
472 assert!(pca9685.set_pin_mode(1, PinModeId::SERVO).is_ok());
474 assert_eq!(pca9685.get_frequency(), 50);
475
476 assert!(pca9685.set_pin_mode(1, PinModeId::ANALOG).is_ok());
478 assert_eq!(pca9685.get_frequency(), 30);
479
480 assert!(pca9685.set_pin_mode(1, PinModeId::PWM).is_ok());
482 assert_eq!(pca9685.get_frequency(), 300);
483
484 let result = pca9685.set_pin_mode(2, PinModeId::DHT);
486 assert!(result.is_err());
487 assert_eq!(
488 result.unwrap_err().to_string(),
489 "Hardware error: Pin (2) not compatible with mode (DHT) - try to set pin mode."
490 )
491 }
492
493 #[test]
494 fn test_digital_write() {
495 let board = Board::new(MockIoProtocol::default());
496 let mut pca9685 = PCA9685::new(&board, 0x41).unwrap();
497
498 assert!(pca9685.digital_write(1, true).is_ok());
499 let value = pca9685.data.read().get_pin(1).unwrap().value;
500 assert_eq!(value, 255);
501
502 assert!(pca9685.digital_write(1, false).is_ok());
503 let value = pca9685.data.read().get_pin(1).unwrap().value;
504 assert_eq!(value, 0);
505 }
506
507 #[test]
508 fn test_analog_write() {
509 let board = Board::new(MockIoProtocol::default());
510 let mut pca9685 = PCA9685::default(&board).unwrap();
511
512 assert!(pca9685.analog_write(0, 128).is_ok());
513 let value = pca9685.data.read().get_pin(0).unwrap().value;
514 assert_eq!(value, 128);
515
516 assert!(pca9685.analog_write(0, 0).is_ok());
517 let value = pca9685.data.read().get_pin(0).unwrap().value;
518 assert_eq!(value, 0);
519
520 assert!(pca9685.analog_write(0, 255).is_ok());
521 let value = pca9685.data.read().get_pin(0).unwrap().value;
522 assert_eq!(value, 255);
523
524 pca9685.data.write().get_pin_mut(1).unwrap().mode.id = PinModeId::SERVO;
525 assert!(pca9685.servo_config(1, Range::from([300, 600])).is_ok());
526 assert!(pca9685.analog_write(1, 128).is_ok());
527 let value = pca9685.data.read().get_pin(1).unwrap().value;
528 assert_eq!(value, 128);
529 }
530
531 #[test]
532 fn test_servo_config() {
533 let board = Board::new(MockIoProtocol::default());
534 let mut pca9685 = PCA9685::default(&board).unwrap();
535
536 let pwm_range = Range::from([1000, 2000]);
538 assert!(pca9685.servo_config(0, pwm_range).is_ok());
539
540 assert!(pca9685.servo_configs.contains_key(&0));
542 assert_eq!(pca9685.servo_configs.get(&0).unwrap().start, 1000);
543 assert_eq!(pca9685.servo_configs.get(&0).unwrap().end, 2000);
544 }
545
546 #[test]
547 fn test_open() {
548 let board = Board::new(MockIoProtocol::default());
549 let mut pca9685 = PCA9685::default(&board).unwrap();
550 assert!(pca9685.open().is_ok());
551 assert!(pca9685.is_connected());
552 }
553
554 #[test]
555 fn test_close() {
556 let board = Board::new(MockIoProtocol::default());
557 let mut pca9685 = PCA9685::default(&board).unwrap();
558 pca9685.data.write().connected = true; assert!(pca9685.close().is_ok());
560 assert!(!pca9685.is_connected());
561 }
562
563 #[test]
564 fn test_display() {
565 let board = Board::new(MockIoProtocol::default());
566 let pca9685 = PCA9685::default(&board).unwrap();
567
568 assert_eq!(
569 format!("{}", pca9685),
570 "PCA9685 [firmware=PCA9685, version=n/a, protocol=PCA9685, transport=I2C]"
571 );
572 }
573
574 #[test]
575 fn test_hardware() {
576 let board = Board::new(MockIoProtocol::default());
577 let pca9685 = PCA9685::new(&board, 0x41).unwrap();
578 assert_eq!(
579 pca9685.get_protocol().to_string(),
580 "PCA9685 [firmware=PCA9685, version=n/a, protocol=PCA9685, transport=I2C]"
581 );
582 assert_eq!(pca9685.get_io().read().firmware_name, "PCA9685");
583 assert!(pca9685.is_connected());
584 }
585}