air_navigator_rs/lib.rs
1#![deny(unsafe_code)]
2#![doc(html_logo_url = "https://upload.wikimedia.org/wikipedia/commons/1/12/Bluerobotics-logo.svg")]
3#![doc = include_str!("../README.md")]
4
5use ads1x1x::{
6 ic::{Ads1115, Resolution16Bit},
7 interface::I2cInterface,
8 Ads1x1x, DynamicOneShot, SlaveAddr as adc_Address,
9};
10use bmp280::{Bmp280, Bmp280Builder};
11use embedded_hal::{digital::v2::InputPin, prelude::_embedded_hal_blocking_delay_DelayMs};
12use icm20689::{self, AccelRange, Builder as imu_Builder, GyroRange, SpiInterface, ICM20689};
13use linux_embedded_hal::spidev::{self, SpidevOptions};
14use linux_embedded_hal::sysfs_gpio::Direction;
15use linux_embedded_hal::I2cdev;
16use linux_embedded_hal::{Delay, Pin, Spidev};
17use log::{info, warn};
18use nb::block;
19use pwm_pca9685::{Address as pwm_Address, Pca9685};
20use sk6812_rpi::led::Led as StripLed;
21use sk6812_rpi::strip::{Bus, Strip};
22use std::{
23 fmt,
24 ops::{Deref, DerefMut},
25};
26
27/// Navigator's default crystal clock for PWM, with a value of 24.5760 MHz
28const NAVIGATOR_PWM_XTAL_CLOCK_FREQ: f32 = 24_576_000.0;
29
30impl From<AdcChannel> for ads1x1x::ChannelSelection {
31 fn from(channel: AdcChannel) -> Self {
32 match channel {
33 AdcChannel::Ch0 => ads1x1x::ChannelSelection::SingleA0,
34 AdcChannel::Ch1 => ads1x1x::ChannelSelection::SingleA1,
35 AdcChannel::Ch2 => ads1x1x::ChannelSelection::SingleA2,
36 AdcChannel::Ch3 => ads1x1x::ChannelSelection::SingleA3,
37 }
38 }
39}
40
41impl From<PwmChannel> for pwm_pca9685::Channel {
42 fn from(channel: PwmChannel) -> Self {
43 match channel {
44 PwmChannel::Ch1 => pwm_pca9685::Channel::C0,
45 PwmChannel::Ch2 => pwm_pca9685::Channel::C1,
46 PwmChannel::Ch3 => pwm_pca9685::Channel::C2,
47 PwmChannel::Ch4 => pwm_pca9685::Channel::C3,
48 PwmChannel::Ch5 => pwm_pca9685::Channel::C4,
49 PwmChannel::Ch6 => pwm_pca9685::Channel::C5,
50 PwmChannel::Ch7 => pwm_pca9685::Channel::C6,
51 PwmChannel::Ch8 => pwm_pca9685::Channel::C7,
52 PwmChannel::Ch9 => pwm_pca9685::Channel::C8,
53 PwmChannel::Ch10 => pwm_pca9685::Channel::C9,
54 PwmChannel::Ch11 => pwm_pca9685::Channel::C10,
55 PwmChannel::Ch12 => pwm_pca9685::Channel::C11,
56 PwmChannel::Ch13 => pwm_pca9685::Channel::C12,
57 PwmChannel::Ch14 => pwm_pca9685::Channel::C13,
58 PwmChannel::Ch15 => pwm_pca9685::Channel::C14,
59 PwmChannel::Ch16 => pwm_pca9685::Channel::C15,
60 PwmChannel::All => pwm_pca9685::Channel::All,
61 }
62 }
63}
64
65/// Set of available options to select ADC's channel.
66#[derive(Debug, Clone, Copy)]
67pub enum AdcChannel {
68 Ch0,
69 Ch1,
70 Ch2,
71 Ch3,
72}
73
74/// Set of options to control navigator's LEDs.
75#[derive(Debug, Clone, Copy)]
76pub enum UserLed {
77 /// Attached to green LED through GPIO 24, labelled LED_1.
78 Led1,
79 /// Attached to blue LED through GPIO 25, labelled LED_2.
80 Led2,
81 /// Attached to red LED through GPIO 11, labelled LED_3.
82 Led3,
83}
84
85impl fmt::Display for UserLed {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 match self {
88 UserLed::Led1 => write!(f, "LED_1"),
89 UserLed::Led2 => write!(f, "LED_2"),
90 UserLed::Led3 => write!(f, "LED_3"),
91 }
92 }
93}
94
95/// Set of available options to select PWM's channel.
96#[derive(Debug, Clone, Copy, PartialEq)]
97pub enum PwmChannel {
98 Ch1,
99 Ch2,
100 Ch3,
101 Ch4,
102 Ch5,
103 Ch6,
104 Ch7,
105 Ch8,
106 Ch9,
107 Ch10,
108 Ch11,
109 Ch12,
110 Ch13,
111 Ch14,
112 Ch15,
113 Ch16,
114 All,
115}
116
117/// The `AxisData` struct encapsulate values for the x, y, and z axes.
118#[derive(Debug, Default, Clone, Copy)]
119pub struct AxisData {
120 pub x: f32,
121 pub y: f32,
122 pub z: f32,
123}
124
125/// Encapsulates the value of ADC's four channels.
126#[derive(Debug, Default, Clone, Copy)]
127pub struct ADCData {
128 pub channel: [f32; 4],
129}
130
131/// Encapsulates the value of all sensors on the board.
132#[derive(Debug, Default, Clone, Copy)]
133pub struct SensorData {
134 pub adc: ADCData,
135 pub temperature: f32,
136 pub pressure: f32,
137 pub accelerometer: AxisData,
138 pub gyro: AxisData,
139 pub leak: bool,
140}
141
142/// The `Led` struct represents the 3 LEDs on navigator board.
143///
144/// All this components were abstracted to be used directly from navigator module.
145pub struct Led {
146 first: Pin,
147 second: Pin,
148 third: Pin,
149}
150
151/// The `Navigator` struct contains various components used for navigator. It includes PWM control,
152/// pressure and temperature sensing, analog-to-digital conversion, inertial measurement unit,
153/// magnetometer, and LEDs control.
154///
155/// All this components were integrated and abstracted to be used directly from navigator module.
156///
157/// Please check [`Implementations`](struct.Navigator.html#implementations) and it's examples, then start to coding your applications!
158pub struct Navigator {
159 pwm: Pwm,
160 bmp: Bmp280,
161 adc: Ads1x1x<I2cInterface<I2cdev>, Ads1115, Resolution16Bit, ads1x1x::mode::OneShot>,
162 imu: ICM20689<SpiInterface<Spidev, Pin>>,
163 led: Led,
164 neopixel: Strip,
165 leak: Pin,
166}
167
168impl Deref for Pwm {
169 type Target = Pca9685<I2cdev>;
170
171 fn deref(&self) -> &Self::Target {
172 &self.pca
173 }
174}
175
176impl DerefMut for Pwm {
177 fn deref_mut(&mut self) -> &mut Self::Target {
178 &mut self.pca
179 }
180}
181
182/// The `Pwm` struct represents a PWM (Pulse Width Modulation) controller with a PCA9685 chip and it's
183/// output enable pin.
184///
185pub struct Pwm {
186 pca: Pca9685<I2cdev>,
187 /// * `oe_pin`: The `oe_pin` component is a pin that is used to enable or disable the output of the PWM
188 /// signal. It is connected to the Output Enable (OE) pin of the PCA9685 PWM controller.
189 /// The default initialization of the navigator sets oe_pin to a digital high state, which disables the PCA9685's PWM.
190 oe_pin: Pin,
191}
192
193/// Build pattern structure
194pub struct NavigatorBuilder {
195 rgb_led_strip_size: usize,
196}
197
198impl Default for Led {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204impl Led {
205 pub fn new() -> Led {
206 let mut led = Led {
207 first: Pin::new(24),
208 second: Pin::new(25),
209 third: Pin::new(11),
210 };
211
212 for pin in led.as_mut_array().iter_mut() {
213 pin.export().expect("Error: Error during led pins export");
214 Delay {}.delay_ms(30_u16);
215 pin.set_direction(Direction::High)
216 .expect("Error: Setting led pins as output");
217 }
218 led
219 }
220
221 pub fn as_mut_array(&mut self) -> [&mut Pin; 3] {
222 [&mut self.first, &mut self.second, &mut self.third]
223 }
224
225 pub fn select(&mut self, select: UserLed) -> &mut Pin {
226 match select {
227 UserLed::Led1 => &mut self.first,
228 UserLed::Led2 => &mut self.second,
229 UserLed::Led3 => &mut self.third,
230 }
231 }
232
233 pub fn get_led(&mut self, select: UserLed) -> bool {
234 let pin_struct = self.select(select);
235
236 pin_struct
237 .get_value()
238 .unwrap_or_else(|_| panic!("Error: Get {} LED value", select))
239 == 0
240 }
241
242 pub fn set_led(&mut self, select: UserLed, state: bool) {
243 let pin_struct = self.select(select);
244
245 pin_struct
246 .set_value((!state).into())
247 .unwrap_or_else(|_| panic!("Error: Set {} LED value to {}", select, state));
248 }
249
250 pub fn set_led_all(&mut self, state: bool) {
251 for pin in self.as_mut_array().iter_mut() {
252 pin.set_value((!state).into())
253 .unwrap_or_else(|_| panic!("Error: Set LED value to {state}"));
254 }
255 }
256
257 pub fn set_led_toggle(&mut self, select: UserLed) {
258 let state = self.get_led(select);
259
260 self.set_led(select, !state)
261 }
262}
263
264impl Default for Navigator {
265 fn default() -> Self {
266 Self::new()
267 }
268}
269
270impl NavigatorBuilder {
271 pub fn with_rgb_led_strip_size(mut self, size: usize) -> Self {
272 self.rgb_led_strip_size = size;
273 self
274 }
275
276 pub fn build(self) -> Navigator {
277 let dev = I2cdev::new("/dev/i2c-4").unwrap();
278 let address = pwm_Address::default();
279 let pwm = Pca9685::new(dev, address).unwrap();
280
281 let dev = I2cdev::new("/dev/i2c-1").unwrap();
282 let address = adc_Address::default();
283 let adc = Ads1x1x::new_ads1115(dev, address);
284
285 let mut bmp = Bmp280Builder::new()
286 .path("/dev/i2c-1")
287 .address(0x76)
288 .build()
289 .expect("Error: Failed to build BMP280 device");
290 bmp.zero().unwrap();
291
292 let mut neopixel = Strip::new(Bus::Spi0, self.rgb_led_strip_size).unwrap();
293 // Clear RGB led strip before starting using it
294 neopixel.clear();
295
296 let mut spi = Spidev::open("/dev/spidev1.0").expect("Error: Failed during setting up SPI");
297 let options = SpidevOptions::new()
298 .bits_per_word(8)
299 .max_speed_hz(10_000_000)
300 .mode(spidev::SpiModeFlags::SPI_MODE_0)
301 .build();
302 spi.configure(&options)
303 .expect("Error: Failed to configure SPI");
304
305 //Define CS2 pin ICM-20602
306 let cs_2 = Pin::new(16);
307 cs_2.export().expect("Error: Error during CS2 export");
308 Delay {}.delay_ms(30_u16);
309 cs_2.set_direction(Direction::High)
310 .expect("Error: Setting CS2 pin as output");
311
312 //not using yet, define CS1 pin for MMC5983
313 let cs_1 = Pin::new(17);
314 cs_1.export().expect("Error: Error during CS1 export");
315 Delay {}.delay_ms(30_u16);
316 cs_1.set_direction(Direction::High)
317 .expect("Error: Setting CS2 pin as output");
318
319 //Define pwm OE_Pin - PWM initialize disabled
320 let oe_pin = Pin::new(26);
321 oe_pin.export().expect("Error: Error during oe_pin export");
322 Delay {}.delay_ms(30_u16);
323 oe_pin
324 .set_direction(Direction::High)
325 .expect("Error: Setting oe_pin pin as output");
326
327 let imu = imu_Builder::new_spi(spi, cs_2);
328
329 let led = Led::new();
330
331 let leak = Pin::new(27);
332
333 Navigator {
334 adc: (adc),
335 bmp: (bmp),
336 pwm: Pwm { pca: pwm, oe_pin },
337 imu: (imu),
338 led: (led),
339 neopixel: (neopixel),
340 leak: (leak),
341 }
342 }
343}
344
345impl Navigator {
346 pub fn new() -> Navigator {
347 Self::create().build()
348 }
349
350 pub fn create() -> NavigatorBuilder {
351 NavigatorBuilder {
352 rgb_led_strip_size: 1, // There is only a single LED on the board
353 }
354 }
355
356 pub fn init(&mut self) {
357 self.self_test();
358 //Initialize devices on navigator's default settings,
359 //read more on ./navigator-api.pdf
360 self.imu
361 .setup(&mut Delay {})
362 .expect("Error: Failed on IMU setup");
363 self.imu.set_accel_range(AccelRange::Range_2g).unwrap();
364 self.imu.set_gyro_range(GyroRange::Range_250dps).unwrap();
365
366 self.adc.reset_internal_driver_state();
367 self.adc
368 .set_full_scale_range(ads1x1x::FullScaleRange::Within4_096V)
369 .unwrap();
370 self.adc
371 .set_data_rate(ads1x1x::DataRate16Bit::Sps860)
372 .unwrap();
373
374 self.pwm.reset_internal_driver_state();
375 self.pwm.use_external_clock().unwrap();
376 self.pwm.set_prescale(100).unwrap();
377 self.pwm.enable().unwrap();
378
379 self.bmp.zero().unwrap();
380
381 self.led.set_led_all(false);
382
383 self.leak
384 .export()
385 .expect("Error: Failed to export leak pin");
386 Delay {}.delay_ms(30_u16);
387 self.leak
388 .set_direction(Direction::In)
389 .expect("Error: Failed to set leak pin as input");
390 }
391
392 pub fn self_test(&mut self) -> bool {
393 //Check if the sensors are attached by it's IDs,
394 //run self-test if they have.
395 self.imu.check_identity(&mut Delay {}).unwrap()
396 }
397
398 /// Sets the PWM IC to be enabled through OE_pin.
399 ///
400 /// # Arguments
401 ///
402 /// * `state` - The state of PWM output, it's enabled with a true logic value.
403 ///
404 /// # Examples
405 ///
406 /// Please check [`set_pwm_channel_value`](struct.Navigator.html#method.set_pwm_channel_value).
407 pub fn set_pwm_enable(&mut self, state: bool) {
408 if state {
409 self.pwm.oe_pin.set_direction(Direction::Low).unwrap();
410 } else {
411 self.pwm.oe_pin.set_direction(Direction::High).unwrap();
412 }
413 }
414
415 /// Get the PWM IC enabling value through OE_pin.
416 ///
417 /// # Examples
418 ///
419 /// ```no_run
420 /// use air_navigator_rs::{Navigator};
421 /// use std::thread::sleep;
422 /// use std::time::Duration;
423 ///
424 /// let mut nav = Navigator::new();
425 ///
426 /// nav.init();
427 /// loop {
428 /// let previous = nav.get_pwm_enable();
429 /// println!("Enable pin logic value is {previous}.");
430 ///
431 /// nav.set_pwm_enable(!previous);
432 ///
433 /// println!("Enable pin logic value is {}.", nav.get_pwm_enable());
434 ///
435 /// sleep(Duration::from_millis(1000));
436 /// }
437 /// ```
438 pub fn get_pwm_enable(&mut self) -> bool {
439 self.pwm.oe_pin.get_value().expect("Error: Get PWM value") == 1
440 }
441
442 /// Sets the Duty Cycle (high value time) of selected channel.
443 ///
444 /// On PCA9685, this function sets the `OFF` counter and uses ON value as 0.
445 ///
446 /// # Further info
447 /// Check **[7.3.3 LED output and PWM control](https://www.nxp.com/docs/en/data-sheet/PCA9685.pdf#page=16)**
448 ///
449 /// # Examples
450 ///
451 /// ```no_run
452 /// use air_navigator_rs::{Navigator, PwmChannel};
453 ///
454 /// let mut nav = Navigator::new();
455 ///
456 /// nav.init();
457 /// nav.set_pwm_enable(true);
458 ///
459 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
460 /// nav.set_pwm_channel_value(PwmChannel::Ch1, 2048); // sets the duty cycle to 50%
461 /// ```
462 pub fn set_pwm_channel_value(&mut self, channel: PwmChannel, mut value: u16) {
463 let max_value = 4095;
464 if value > max_value {
465 warn!("Invalid value. Value must be less than or equal {max_value}.");
466 value = max_value;
467 }
468 self.pwm
469 .set_channel_on_off(channel.into(), 0, value)
470 .unwrap();
471 }
472
473 /// Calculate and set the necessary values for the desired Duty Cycle (high value time) of selected channel.
474 ///
475 /// This method also allows the relay mode using a value of 1.0.
476 ///
477 /// # Examples
478 ///
479 /// ```no_run
480 /// use air_navigator_rs::{Navigator, PwmChannel};
481 ///
482 /// let mut nav = Navigator::new();
483 ///
484 /// nav.init();
485 /// nav.set_pwm_enable(true);
486 ///
487 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
488 /// nav.set_pwm_channel_duty_cycle(PwmChannel::Ch1, 0.50); // sets the duty cycle to 50%
489 /// ```
490 pub fn set_pwm_channel_duty_cycle(&mut self, channel: PwmChannel, duty_cycle: f32) {
491 let duty_cycle = duty_cycle.clamp(0.0, 1.0);
492 let max_value = 4095;
493
494 if approx::relative_eq!(duty_cycle, 1.0) {
495 self.pwm.set_channel_full_on(channel.into(), 0).unwrap();
496 return;
497 }
498
499 let value = (duty_cycle * max_value as f32) as u16;
500
501 self.set_pwm_channel_value(channel, value);
502 }
503
504 /// Like [`set_pwm_channel_value`](struct.Navigator.html#method.set_pwm_channel_value). This function
505 /// sets the Duty Cycle for a list of multiple channels.
506 ///
507 /// # Examples
508 ///
509 /// ```no_run
510 /// use air_navigator_rs::{Navigator, PwmChannel};
511 ///
512 /// let mut nav = Navigator::new();
513 ///
514 /// nav.init();
515 /// nav.set_pwm_enable(true);
516 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
517 ///
518 /// let channels: [PwmChannel; 3] = [PwmChannel::Ch1, PwmChannel::Ch1, PwmChannel::Ch2];
519 /// let values: [u16; 3] = [200, 1000, 300];
520 ///
521 /// nav.set_pwm_channels_value(&channels, 2048); // sets the duty cycle according to the list.
522 /// ```
523 pub fn set_pwm_channels_value<const N: usize>(
524 &mut self,
525 channels: &[PwmChannel; N],
526 value: u16,
527 ) {
528 for &channel in channels.iter().take(N) {
529 self.set_pwm_channel_value(channel, value)
530 }
531 }
532
533 /// Like [`set_pwm_channel_duty_cycle`](struct.Navigator.html#method.set_pwm_channel_duty_cycle). This function
534 /// sets the Duty Cycle for a list of multiple channels.
535 ///
536 /// # Examples
537 ///
538 /// ```no_run
539 /// use air_navigator_rs::{Navigator, PwmChannel};
540 ///
541 /// let mut nav = Navigator::new();
542 ///
543 /// nav.init();
544 /// nav.set_pwm_enable(true);
545 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
546 ///
547 /// let channels: [PwmChannel; 3] = [PwmChannel::Ch1, PwmChannel::Ch1, PwmChannel::Ch2];
548 ///
549 /// nav.set_pwm_channels_duty_cycle(&channels, 0.5); // sets the duty cycle according to the list.
550 /// ```
551 pub fn set_pwm_channels_duty_cycle<const N: usize>(
552 &mut self,
553 channels: &[PwmChannel; N],
554 duty_cycle: f32,
555 ) {
556 for &channel in channels {
557 self.set_pwm_channel_duty_cycle(channel, duty_cycle)
558 }
559 }
560
561 /// Like [`set_pwm_channel_value`](struct.Navigator.html#method.set_pwm_channel_value). This function
562 /// sets the Duty Cycle for a list of multiple channels with multiple values.
563 ///
564 /// # Examples
565 ///
566 /// ```no_run
567 /// use air_navigator_rs::{Navigator, PwmChannel};
568 ///
569 /// let mut nav = Navigator::new();
570 ///
571 /// nav.init();
572 /// nav.set_pwm_enable(true);
573 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
574 ///
575 /// let channels: [PwmChannel; 3] = [PwmChannel::Ch1, PwmChannel::Ch1, PwmChannel::Ch2];
576 /// let values: [u16; 3] = [200, 1000, 300];
577 ///
578 /// nav.set_pwm_channels_values(&channels, &values); // sets the duty cycle according to the lists.
579 /// ```
580 pub fn set_pwm_channels_values<const N: usize>(
581 &mut self,
582 channels: &[PwmChannel; N],
583 values: &[u16; N],
584 ) {
585 for i in 0..N {
586 self.set_pwm_channel_value(channels[i], values[i])
587 }
588 }
589
590 /// Like [`set_pwm_channel_duty_cycle`](struct.Navigator.html#method.set_pwm_channel_duty_cycle). This function
591 /// sets the Duty Cycle for a list of multiple channels with multiple values.
592 ///
593 /// # Examples
594 ///
595 /// ```no_run
596 /// use air_navigator_rs::{Navigator, PwmChannel};
597 ///
598 /// let mut nav = Navigator::new();
599 ///
600 /// nav.init();
601 /// nav.set_pwm_enable(true);
602 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
603 ///
604 /// let channels: [PwmChannel; 3] = [PwmChannel::Ch1, PwmChannel::Ch1, PwmChannel::Ch2];
605 /// let values: [f32; 3] = [0.1, 0.2, 0.3];
606 ///
607 /// nav.set_pwm_channels_duty_cycle_values(&channels, &values); // sets the duty cycle according to the lists.
608 /// ```
609 pub fn set_pwm_channels_duty_cycle_values<const N: usize>(
610 &mut self,
611 channels: &[PwmChannel; N],
612 duty_cycle: &[f32; N],
613 ) {
614 for i in 0..N {
615 self.set_pwm_channel_duty_cycle(channels[i], duty_cycle[i])
616 }
617 }
618
619 /// Sets the PWM frequency of [`Navigator`].
620 ///
621 /// It changes the PRE_SCALE value on PCA9685.
622 ///
623 /// The prescaler value can be calculated for an update rate using the formula:
624 ///
625 /// `prescale_value = round(clock_freq / (4096 * desired_freq)) - 1`.
626 ///
627 /// The minimum prescaler value is 3, which corresponds to 1526 Hz.
628 /// The maximum prescaler value is 255, which corresponds to 24 Hz.
629 ///
630 /// If you want to control a servo, set a prescaler value of 100. This will
631 /// correspond to a frequency of about 60 Hz, which is the frequency at
632 /// which servos work.
633 ///
634 /// Internally, this function stops the oscillator and restarts it after
635 /// setting the prescaler value if it was running.
636 ///
637 /// Re-run the set_pwm_channel_value() is required.
638 ///
639 /// # Further info
640 /// Check **[7.3.5 - PWM frequency PRE_SCALE](https://www.nxp.com/docs/en/data-sheet/PCA9685.pdf#page=25)**
641 ///
642 /// # Examples
643 ///
644 /// ```no_run
645 /// use air_navigator_rs::{Navigator, PwmChannel};
646 ///
647 /// let mut nav = Navigator::new();
648 ///
649 /// nav.init();
650 /// nav.set_pwm_enable(true);
651 ///
652 /// nav.set_pwm_freq_prescale(99); // sets the pwm frequency to 60 Hz
653 ///
654 /// nav.set_pwm_channel_value(PwmChannel::Ch1, 2048); // sets the duty cycle to 50%
655 /// ```
656 pub fn set_pwm_freq_prescale(&mut self, mut value: u8) {
657 let min_prescale = 3;
658 if value < min_prescale {
659 warn!("Invalid value. Value must be greater than {min_prescale}.");
660 value = min_prescale;
661 }
662 self.pwm.set_prescale(value).unwrap();
663
664 let clamped_freq = NAVIGATOR_PWM_XTAL_CLOCK_FREQ / (4_096.0 * (value as f32 + 1.0));
665 info!("PWM frequency set to {clamped_freq:.2} Hz. Prescaler value: {value}");
666 }
667
668 /// Sets the pwm frequency in Hertz of [`Navigator`].
669 ///
670 /// The navigator module uses a crystal with a 24.5760 MHz clock. You can set a value for a frequency between 24 and 1526 Hz.
671 ///
672 /// # Examples
673 ///
674 /// ```no_run
675 /// use air_navigator_rs::{Navigator, PwmChannel};
676 /// use std::thread::sleep;
677 /// use std::time::Duration;
678 ///
679 /// let mut nav = Navigator::new();
680 ///
681 /// nav.init();
682 /// nav.set_pwm_enable(true);
683 ///
684 /// let mut i: f32 = 10.0;
685 ///
686 /// loop {
687 /// nav.set_pwm_freq_hz(i);
688 /// nav.set_pwm_channel_value(PwmChannel::Ch1, 2048); // sets the duty cycle to 50%
689 /// i = i + 10.0;
690 /// sleep(Duration::from_millis(1000));
691 /// }
692 /// ```
693 pub fn set_pwm_freq_hz(&mut self, mut freq: f32) {
694 let min_freq = 24.0;
695 if freq < min_freq {
696 warn!("Invalid value. Value must be greater than or equal to {min_freq}.");
697 freq = min_freq;
698 }
699
700 let max_freq = 1526.0;
701 if freq > max_freq {
702 warn!("Invalid value. Value must be less than or equal to {max_freq}.");
703 freq = max_freq;
704 }
705
706 let prescale_clamped_value =
707 (NAVIGATOR_PWM_XTAL_CLOCK_FREQ / (4_096.0 * freq)).round() as u8 - 1;
708
709 self.set_pwm_freq_prescale(prescale_clamped_value);
710 }
711 /// Gets the selected navigator LED output state. The true state means the LED is on.
712 ///
713 /// # Arguments
714 ///
715 /// * `select` - A pin selected from [`UserLed`](enum.UserLed.html).
716 ///
717 /// # Examples
718 ///
719 /// ```no_run
720 /// use air_navigator_rs::{UserLed, Navigator};
721 /// use std::thread::sleep;
722 /// use std::time::Duration;
723 ///
724 /// let mut nav = Navigator::new();
725 ///
726 /// nav.init();
727 /// loop {
728 /// let logic_value: bool = nav.get_led(UserLed::Led2);
729 /// println!("Blue LED logic value is {logic_value}.");
730 /// nav.set_led_toggle(UserLed::Led2);
731 /// sleep(Duration::from_millis(1000));
732 /// }
733 /// ```
734 pub fn get_led(&mut self, select: UserLed) -> bool {
735 self.led.get_led(select)
736 }
737
738 /// Sets the selected navigator LED output.
739 ///
740 /// # Arguments
741 ///
742 /// * `select` - A pin selected from [`UserLed`](enum.UserLed.html).
743 /// * `state` - The value of output, LED is on with a true logic value.
744 ///
745 /// # Examples
746 ///
747 /// ```no_run
748 /// use air_navigator_rs::{UserLed, Navigator};
749 /// use std::thread::sleep;
750 /// use std::time::Duration;
751 ///
752 /// let mut nav = Navigator::new();
753 ///
754 /// nav.init();
755 /// loop {
756 /// nav.set_led(UserLed::Led1, true);
757 /// sleep(Duration::from_millis(1000));
758 /// nav.set_led(UserLed::Led1, false);
759 /// sleep(Duration::from_millis(1000));
760 /// }
761 /// ```
762 pub fn set_led(&mut self, select: UserLed, state: bool) {
763 self.led.set_led(select, state)
764 }
765
766 /// Toggle the output of selected LED.
767 ///
768 /// # Arguments
769 ///
770 /// * `select` - A pin selected from [`UserLed`](enum.UserLed.html).
771 ///
772 /// # Examples
773 ///
774 /// ```no_run
775 /// use air_navigator_rs::{UserLed, Navigator};
776 /// use std::thread::sleep;
777 /// use std::time::Duration;
778 ///
779 /// let mut nav = Navigator::new();
780 ///
781 /// nav.init();
782 /// loop {
783 /// nav.set_led_toggle(UserLed::Led1);
784 /// sleep(Duration::from_millis(1000));
785 /// }
786 /// ```
787 pub fn set_led_toggle(&mut self, select: UserLed) {
788 self.led.set_led_toggle(select)
789 }
790
791 /// Set all LEDs on desired state ( Blue, Green and Red ).
792 ///
793 /// # Arguments
794 ///
795 /// * `state` - The value of output, LED is on with a true logic value.
796 ///
797 /// # Examples
798 ///
799 /// ```no_run
800 /// use air_navigator_rs::{Navigator};
801 /// use std::thread::sleep;
802 /// use std::time::Duration;
803 ///
804 /// let mut nav = Navigator::new();
805 ///
806 /// nav.init();
807 /// loop {
808 /// nav.set_led_all(true);
809 /// sleep(Duration::from_millis(1000));
810 /// nav.set_led_all(false);
811 /// sleep(Duration::from_millis(1000));
812 /// }
813 /// ```
814 pub fn set_led_all(&mut self, state: bool) {
815 self.led.set_led_all(state)
816 }
817
818 /// Set the values of the neopixel LED array.
819 ///
820 /// # Arguments
821 ///
822 /// * `array` - A 2D array containing RGB values for each LED.
823 /// Each inner array is a [u8; 3] representing the Red, Green and Blue from a LED.
824 ///
825 /// # Example
826 ///
827 /// ```no_run
828 /// use air_navigator_rs::{Navigator};
829 ///
830 /// let mut nav = Navigator::new();
831 ///
832 /// nav.init();
833 /// let mut leds = [[0, 0, 255], [0, 255, 0], [255, 0, 0]];
834 /// nav.set_neopixel(&mut leds);
835 /// ```
836 ///
837 /// This will set the first LED to blue, second to green, and third to red.
838 pub fn set_neopixel(&mut self, array: &[[u8; 3]]) {
839 for (index, value) in array.iter().enumerate() {
840 self.neopixel.leds[index] = StripLed::from_rgb(value[0], value[1], value[2]);
841 }
842 self.neopixel.update().unwrap();
843 }
844
845 /// Set the values of the neopixel RGBW LED array.
846 ///
847 /// # Arguments
848 ///
849 /// * `array` - A 2D array containing RGBW values for each LED.
850 /// Each inner array is a [u8; 4] representing the Red, Green, Blue and White from a LED.
851 ///
852 /// # Example
853 ///
854 /// ```no_run
855 /// use air_navigator_rs::{Navigator};
856 ///
857 /// let mut nav = Navigator::new();
858 ///
859 /// nav.init();
860 /// let mut leds = [[0, 0, 255, 255], [0, 255, 0, 0], [255, 0, 0, 128]];
861 /// nav.set_neopixel_rgbw(&mut leds);
862 /// ```
863 ///
864 /// This will set the first LED to blue, second to green, third to red and fourth to white.
865 pub fn set_neopixel_rgbw(&mut self, array: &[[u8; 4]]) {
866 for (index, value) in array.iter().enumerate() {
867 self.neopixel.leds[index] = StripLed::from_rgbw(value[0], value[1], value[2], value[3]);
868 }
869 self.neopixel.update().unwrap();
870 }
871
872 pub fn read_temperature(&mut self) -> f32 {
873 self.bmp.temperature_celsius().unwrap()
874 }
875
876 pub fn read_altitude(&mut self) -> f32 {
877 self.bmp.altitude_m().unwrap()
878 }
879
880 pub fn read_pressure(&mut self) -> f32 {
881 self.bmp.pressure_kpa().unwrap()
882 }
883
884 pub fn read_adc_all(&mut self) -> ADCData {
885 ADCData {
886 channel: [
887 self.read_adc(AdcChannel::Ch0),
888 self.read_adc(AdcChannel::Ch1),
889 self.read_adc(AdcChannel::Ch2),
890 self.read_adc(AdcChannel::Ch3),
891 ],
892 }
893 }
894
895 pub fn read_adc(&mut self, channel: AdcChannel) -> f32 {
896 let conversion_volts: f32 = 0.000_125; // According to data-sheet, LSB = 125 μV for ±4.096 scale register, navigator's default
897 block!(self.adc.read(channel.into())).unwrap() as f32 * conversion_volts
898 }
899
900 pub fn read_accel(&mut self) -> AxisData {
901 let reading: [f32; 3] = self.imu.get_scaled_accel().unwrap();
902 // Change the axes to navigator's standard. Right-handed, Z-axis down (aeronautical frame, NED).
903 // Obs.: ICM20602 sensor is measuring with inverted directions, not following data-sheet.
904 // Thus, with IC's placement only Z needs to be inverted.
905 AxisData {
906 x: reading[0],
907 y: reading[1],
908 z: reading[2] * -1.0,
909 }
910 }
911
912 /// Reads angular velocity based on ICM20689 of [`Navigator`].
913 ///
914 /// Measurements in \[rad/s\]
915 ///
916 /// # Examples
917 ///
918 /// ```no_run
919 /// use air_navigator_rs::{Navigator};
920 /// use std::thread::sleep;
921 /// use std::time::Duration;
922 ///
923 /// let mut nav = Navigator::new();
924 /// nav.init();
925 ///
926 /// loop {
927 /// let gyro = nav.read_gyro();
928 /// println!("accel values: X={}, Y={}, Z={} [rad/s]", gyro.x, gyro.y, gyro.z);
929 /// sleep(Duration::from_millis(1000));
930 /// }
931 /// ```
932 pub fn read_gyro(&mut self) -> AxisData {
933 let reading: [f32; 3] = self.imu.get_scaled_gyro().unwrap();
934 // Change the axes to navigator's standard. Right-handed, Z-axis down (aeronautical frame, NED).
935 AxisData {
936 x: reading[0] * -1.0,
937 y: reading[1] * -1.0,
938 z: reading[2],
939 }
940 }
941
942 /// Reads the state of leak detector pin from [`Navigator`].
943 ///
944 /// The value is true when a leak is detected.
945 ///
946 /// # Examples
947 ///
948 /// ```no_run
949 /// use air_navigator_rs::{Navigator};
950 /// use std::thread::sleep;
951 /// use std::time::Duration;
952 ///
953 /// let mut nav = Navigator::new();
954 /// nav.init();
955 ///
956 /// loop {
957 /// println!("Leak: {}", nav.read_leak());
958 /// sleep(Duration::from_millis(1000));
959 /// }
960 /// ```
961 pub fn read_leak(&self) -> bool {
962 self.leak
963 .is_high()
964 .expect("Failed to read state of leak pin")
965 }
966
967 /// Reads all sensors and stores on a single structure.
968 ///
969 /// # Examples
970 ///
971 /// ```no_run
972 /// use air_navigator_rs::{Navigator, SensorData};
973 /// use std::thread::sleep;
974 /// use std::time::Duration;
975 ///
976 /// let mut nav = Navigator::new();
977 /// nav.init();
978 ///
979 /// loop {
980 /// println!("---------------reading---------------");
981 /// let sensors_data = nav.read_all();
982 /// println!("{sensors_data:#?}");
983 /// sleep(Duration::from_millis(1000));
984 /// }
985 /// ```
986 pub fn read_all(&mut self) -> SensorData {
987 SensorData {
988 adc: self.read_adc_all(),
989 temperature: self.read_temperature(),
990 pressure: self.read_pressure(),
991 accelerometer: self.read_accel(),
992 gyro: self.read_gyro(),
993 leak: self.read_leak(),
994 }
995 }
996
997 pub fn fmt_debug(&mut self) -> impl fmt::Debug {
998 #[allow(dead_code)]
999 #[derive(Debug)]
1000 struct Bmp {
1001 temperature: f32,
1002 altitude: f32,
1003 pressure: f32,
1004 }
1005
1006 #[allow(dead_code)]
1007 #[derive(Debug)]
1008 struct Imu {
1009 accelerometer: AxisData,
1010 gyroscope: AxisData,
1011 }
1012
1013 #[allow(dead_code)]
1014 #[derive(Debug)]
1015 struct Navigator {
1016 adc: ADCData,
1017 bmp: Bmp,
1018 imu: Imu,
1019 leak: bool,
1020 }
1021
1022 Navigator {
1023 adc: self.read_adc_all(),
1024 bmp: Bmp {
1025 temperature: self.read_temperature(),
1026 altitude: self.read_altitude(),
1027 pressure: self.read_pressure(),
1028 },
1029 imu: Imu {
1030 accelerometer: self.read_accel(),
1031 gyroscope: self.read_gyro(),
1032 },
1033 leak: self.read_leak(),
1034 }
1035 }
1036}