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}