as3935_bbn/
lib.rs

1#[macro_use]
2extern crate log;
3
4use crate::device::registers::{
5    AfeGainBoost, CalibrateOscillators, DisplayTrcoOnIrqPin, DistanceEstimation, Interrupt,
6    MaskDisturber, MinimumNumberOfLightning, NoiseFloorLevel, PowerDown, PresetDefault,
7    WatchdogThreshold,
8};
9use crate::interface::i2c::{I2cAddress, I2cInterface};
10use crate::interface::{
11    Interface, Irq, CLOCK_GENERATION_DELAY, IRQ_TRIGGER_TO_READY_DELAY, LIGHTNING_CALCULATION_DELAY,
12};
13use rppal::gpio::{InputPin, Level, Trigger};
14use rppal::i2c::I2c;
15use rppal::spi::Spi;
16use std::error;
17use std::fmt;
18use std::result::Result::Err;
19use std::sync::mpsc::{channel, Receiver, Sender};
20use std::sync::{Arc, Mutex};
21use std::thread::sleep;
22use std::time::Duration;
23
24pub(crate) mod device;
25pub mod interface;
26
27pub type IrqPin = InputPin;
28
29#[derive(Debug)]
30pub enum Error {
31    Deadlock,
32    InterfaceError(interface::Error),
33    InvalidState,
34}
35
36pub type Result<T> = ::std::result::Result<T, Error>;
37
38impl error::Error for Error {}
39impl fmt::Display for Error {
40    fn fmt(&self, _f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
41        unimplemented!()
42    }
43}
44
45impl From<crate::interface::Error> for Error {
46    fn from(error: crate::interface::Error) -> Self {
47        Error::InterfaceError(error)
48    }
49}
50
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub enum SensorPlacing {
53    Indoor,
54    Outdoor,
55}
56
57#[derive(Clone, Copy, Debug, Eq, PartialEq)]
58pub enum MinimumLightningThreshold {
59    One,
60    Five,
61    Nine,
62    Sixteen,
63}
64
65/// Larger values correspond to more robust disturber rejection, with a decrease of the detection efficiency,
66/// Refer to Figure 20 in the datasheet for the relationship between this threshold and its impact.
67/// Defaults to 2.
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub struct SignalVerificationThreshold(pub(crate) u8);
70
71impl SignalVerificationThreshold {
72    pub fn new(value: u8) -> ::std::result::Result<Self, &'static str> {
73        if value > 10 {
74            return Err("Signal verification threshold must be in range 0-10");
75        }
76
77        Ok(Self(value))
78    }
79}
80
81#[derive(Clone, Copy, Debug, Eq, PartialEq)]
82pub struct NoiseFloorThreshold(pub(crate) u8);
83
84impl NoiseFloorThreshold {
85    pub fn new(value: u8) -> ::std::result::Result<Self, &'static str> {
86        if value > 11 {
87            return Err("Noise level threshold must be in range 0-11");
88        }
89
90        Ok(Self(value))
91    }
92}
93
94#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub enum IgnoreDisturbances {
96    Yes,
97    No,
98}
99
100/// Estimated distance to the head of storm, in kilometers.
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum HeadOfStormDistance {
103    /// the storm is within 5-40 km range
104    Kilometers(u8),
105    /// the storm is out of range (>40 km)
106    OutOfRange,
107    /// the storm is overhead (<5 km)
108    Overhead,
109}
110
111pub enum InterfaceSelection {
112    I2c(I2c, I2cAddress),
113    Spi(Spi, u8),
114}
115
116pub enum Event {
117    Disturbance,
118    Lightning(HeadOfStormDistance),
119    Noise,
120}
121
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123enum State {
124    Listening,
125    PoweredDown,
126    StandingBy,
127}
128
129#[derive(Default)]
130pub struct ListeningParameters {
131    pub(crate) sensor_placing: Option<SensorPlacing>,
132    pub(crate) minimum_lightning_threshold: Option<MinimumLightningThreshold>,
133    pub(crate) noise_floor_threshold: Option<NoiseFloorThreshold>,
134    pub(crate) signal_verification_threshold: Option<SignalVerificationThreshold>,
135    pub(crate) ignore_disturbances: Option<IgnoreDisturbances>,
136}
137
138impl ListeningParameters {
139    pub fn with_sensor_placing(mut self, sensor_placing: SensorPlacing) -> Self {
140        self.sensor_placing = Some(sensor_placing);
141        self
142    }
143
144    pub fn with_minimum_lightning_threshold(
145        mut self,
146        minimum_lightning_threshold: MinimumLightningThreshold,
147    ) -> Self {
148        self.minimum_lightning_threshold = Some(minimum_lightning_threshold);
149        self
150    }
151
152    pub fn with_noise_floor_threshold(
153        mut self,
154        noise_floor_threshold: NoiseFloorThreshold,
155    ) -> Self {
156        self.noise_floor_threshold = Some(noise_floor_threshold);
157        self
158    }
159
160    pub fn with_signal_verification_threshold(
161        mut self,
162        signal_verification_threshold: SignalVerificationThreshold,
163    ) -> Self {
164        self.signal_verification_threshold = Some(signal_verification_threshold);
165        self
166    }
167
168    pub fn with_ignore_disturbances(mut self, ignore_disturbances: IgnoreDisturbances) -> Self {
169        self.ignore_disturbances = Some(ignore_disturbances);
170        self
171    }
172}
173
174pub struct AS3935 {
175    interface: Arc<Mutex<Box<dyn Interface>>>,
176    irq_pin: IrqPin,
177    state: State,
178}
179
180impl AS3935 {
181    pub fn new(interface_selection: InterfaceSelection, irq_pin: IrqPin) -> Result<Self> {
182        Ok(match interface_selection {
183            InterfaceSelection::I2c(i2c, i2c_address) => Self {
184                interface: Arc::new(Mutex::new(Box::new(I2cInterface::new(i2c, i2c_address)?))),
185                irq_pin,
186                state: State::StandingBy,
187            },
188            InterfaceSelection::Spi(_, _) => unimplemented!(),
189        })
190    }
191
192    pub fn listen(&mut self, parameters: ListeningParameters) -> Result<Receiver<Event>> {
193        self.assert_state(&self.state, &[State::StandingBy, State::PoweredDown])?;
194
195        info!("starting listen sequence");
196
197        debug!("powering up");
198        self.power_up()?;
199
200        debug!("calibrating clock");
201        self.calibrate_clock()?;
202
203        debug!("resetting to defaults");
204        self.configure_defaults()?;
205
206        debug!("configuring listen parameters");
207        self.configure_listen_parameters(parameters)?;
208
209        let (sender, receiver) = channel::<Event>();
210        self.setup_irq(sender)?;
211
212        self.state = State::Listening;
213
214        Ok(receiver)
215    }
216
217    pub fn terminate(&mut self) -> Result<()> {
218        self.assert_state(&self.state, &[State::Listening])?;
219
220        self.irq_pin.clear_async_interrupt().unwrap();
221        self.power_down()?;
222
223        self.state = State::PoweredDown;
224
225        Ok(())
226    }
227
228    pub fn is_listening(&self) -> bool {
229        self.state == State::Listening
230    }
231
232    fn power_up(&mut self) -> Result<()> {
233        self.assert_state(&self.state, &[State::StandingBy, State::PoweredDown])?;
234
235        self.interface
236            .lock()
237            .unwrap()
238            .write(Box::new(PowerDown), 0b_0)?;
239        sleep(Duration::from_millis(2));
240
241        Ok(())
242    }
243
244    fn power_down(&mut self) -> Result<()> {
245        self.interface
246            .lock()
247            .unwrap()
248            .write(Box::new(PowerDown), 0b_1)?;
249
250        Ok(())
251    }
252
253    fn calibrate_clock(&mut self) -> Result<()> {
254        self.assert_state(&self.state, &[State::StandingBy, State::PoweredDown])?;
255
256        debug!("sending CALIB_RCO direct command");
257        self.interface
258            .lock()
259            .unwrap()
260            .write(Box::new(CalibrateOscillators), 0x96)?;
261        sleep(Duration::from_millis(2));
262
263        debug!("setting DISP_TRCO=1");
264        self.interface
265            .lock()
266            .unwrap()
267            .write(Box::new(DisplayTrcoOnIrqPin), 0b_1)?;
268
269        sleep(CLOCK_GENERATION_DELAY);
270
271        debug!("setting DISP_TRCO=0");
272        self.interface
273            .lock()
274            .unwrap()
275            .write(Box::new(DisplayTrcoOnIrqPin), 0)?;
276        sleep(Duration::from_millis(2));
277
278        Ok(())
279    }
280
281    fn configure_defaults(&mut self) -> Result<()> {
282        self.interface
283            .lock()
284            .unwrap()
285            .write(Box::new(PresetDefault), 0x96)?;
286
287        Ok(())
288    }
289
290    fn configure_listen_parameters(&mut self, parameters: ListeningParameters) -> Result<()> {
291        if let Some(sensor_placing) = parameters.sensor_placing {
292            debug!("configuring sensor placing");
293            self.configure_sensor_placing(&sensor_placing)?;
294        }
295
296        if let Some(minimum_lightning_threshold) = &parameters.minimum_lightning_threshold {
297            debug!("configuring minimum lightning threshold");
298            self.configure_minimum_lightning_threshold(&minimum_lightning_threshold)?;
299        }
300
301        if let Some(noise_floor_threshold) = &parameters.noise_floor_threshold {
302            debug!("configuring noise floor threshold");
303            self.configure_noise_floor_threshold(&noise_floor_threshold)?;
304        }
305
306        if let Some(signal_verification_threshold) = &parameters.signal_verification_threshold {
307            debug!("configuring signal verification threshold");
308            self.configure_signal_verification_threshold(&signal_verification_threshold)?;
309        }
310
311        if let Some(ignore_disturbances) = &parameters.ignore_disturbances {
312            debug!("configuring ignoring of disturbances");
313            self.configure_ignore_disturbances(&ignore_disturbances)?;
314        }
315
316        Ok(())
317    }
318
319    fn configure_sensor_placing(&mut self, placing: &SensorPlacing) -> Result<()> {
320        self.interface
321            .lock()
322            .unwrap()
323            .write(Box::new(AfeGainBoost), (*placing).into())?;
324
325        Ok(())
326    }
327
328    fn configure_minimum_lightning_threshold(
329        &mut self,
330        minimum_lightning_threshold: &MinimumLightningThreshold,
331    ) -> Result<()> {
332        self.interface.lock().unwrap().write(
333            Box::new(MinimumNumberOfLightning),
334            (*minimum_lightning_threshold).into(),
335        )?;
336
337        Ok(())
338    }
339
340    fn configure_noise_floor_threshold(
341        &mut self,
342        noise_floor_threshold: &NoiseFloorThreshold,
343    ) -> Result<()> {
344        self.interface
345            .lock()
346            .unwrap()
347            .write(Box::new(NoiseFloorLevel), (*noise_floor_threshold).into())?;
348
349        Ok(())
350    }
351
352    fn configure_signal_verification_threshold(
353        &mut self,
354        signal_verification_threshold: &SignalVerificationThreshold,
355    ) -> Result<()> {
356        self.interface.lock().unwrap().write(
357            Box::new(WatchdogThreshold),
358            (*signal_verification_threshold).into(),
359        )?;
360
361        Ok(())
362    }
363
364    fn configure_ignore_disturbances(
365        &mut self,
366        ignore_disturbances: &IgnoreDisturbances,
367    ) -> Result<()> {
368        self.interface
369            .lock()
370            .unwrap()
371            .write(Box::new(MaskDisturber), (*ignore_disturbances).into())?;
372
373        Ok(())
374    }
375
376    fn setup_irq(&mut self, sender: Sender<Event>) -> Result<()> {
377        let interface_mutex = self.interface.clone();
378
379        self.irq_pin
380            .set_async_interrupt(Trigger::RisingEdge, move |_level: Level| {
381                sleep(IRQ_TRIGGER_TO_READY_DELAY);
382
383                let mut interface = interface_mutex.lock().unwrap();
384
385                let irq = Irq::from(interface.read(Box::new(Interrupt)).unwrap());
386
387                let event = match irq {
388                    Irq::DistanceEstimationChanged => return,
389                    Irq::DisturberDetected => Event::Disturbance,
390                    Irq::Lightning => {
391                        sleep(LIGHTNING_CALCULATION_DELAY);
392                        Event::Lightning(HeadOfStormDistance::from(
393                            interface.read(Box::new(DistanceEstimation)).unwrap(),
394                        ))
395                    }
396                    Irq::NoiseLevelTooHigh => Event::Noise,
397                };
398
399                sender.send(event).unwrap();
400            })
401            .unwrap();
402
403        Ok(())
404    }
405
406    fn assert_state(&self, state: &State, valid_states: &[State]) -> Result<()> {
407        if !valid_states.contains(state) {
408            return Err(Error::InvalidState);
409        }
410
411        Ok(())
412    }
413}