capsules_extra/
dfrobot_rainfall_sensor.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! DFRobot Gravity Rainfall sensor using the I2C bus.
6//!
7//! <https://wiki.dfrobot.com/SKU_SEN0575_Gravity_Rainfall_Sensor>
8//! <https://github.com/DFRobot/DFRobot_RainfallSensor/tree/master>
9//!
10
11use core::cell::Cell;
12use kernel::hil::i2c::{self, I2CClient, I2CDevice};
13use kernel::hil::sensors::{RainFallClient, RainFallDriver};
14use kernel::hil::time::{Alarm, AlarmClient, ConvertTicks};
15use kernel::utilities::cells::{OptionalCell, TakeCell};
16use kernel::ErrorCode;
17
18pub const BUFFER_SIZE: usize = 4;
19
20const PID_REGISTER: u8 = 0x00;
21const TIME_RAINFALL_REGISTER: u8 = 0x0C;
22const RAIN_HOUR_REGISTER: u8 = 0x26;
23
24#[derive(Clone, Copy, PartialEq)]
25enum DeviceState {
26    Identify,
27    Normal,
28    StartRainFall(usize),
29    ContinueRainFall,
30    FinalRainFall,
31    Broken,
32}
33
34#[derive(Clone, Copy, PartialEq)]
35enum Operation {
36    None,
37    RainFall,
38}
39
40pub struct DFRobotRainFall<'a, A: Alarm<'a>, I: I2CDevice> {
41    buffer: TakeCell<'static, [u8]>,
42    i2c: &'a I,
43    rainfall_client: OptionalCell<&'a dyn RainFallClient>,
44    state: Cell<DeviceState>,
45    op: Cell<Operation>,
46    alarm: &'a A,
47}
48
49impl<'a, A: Alarm<'a>, I: I2CDevice> DFRobotRainFall<'a, A, I> {
50    pub fn new(i2c: &'a I, buffer: &'static mut [u8], alarm: &'a A) -> Self {
51        DFRobotRainFall {
52            buffer: TakeCell::new(buffer),
53            i2c,
54            rainfall_client: OptionalCell::empty(),
55            state: Cell::new(DeviceState::Identify),
56            op: Cell::new(Operation::None),
57            alarm,
58        }
59    }
60
61    pub fn startup(&self) {
62        self.buffer.take().map(|buffer| {
63            if self.state.get() == DeviceState::Identify {
64                // Read the version register
65                buffer[0] = PID_REGISTER;
66                if let Err((_e, buf)) = self.i2c.write_read(buffer, 1, 4) {
67                    self.buffer.replace(buf);
68                }
69            } else {
70                self.buffer.replace(buffer);
71            }
72        });
73    }
74}
75
76impl<'a, A: Alarm<'a>, I: I2CDevice> RainFallDriver<'a> for DFRobotRainFall<'a, A, I> {
77    fn set_client(&self, client: &'a dyn RainFallClient) {
78        self.rainfall_client.set(client);
79    }
80
81    fn read_rainfall(&self, hours: usize) -> Result<(), ErrorCode> {
82        if self.state.get() == DeviceState::Broken {
83            return Err(ErrorCode::NOSUPPORT);
84        }
85
86        if self.state.get() != DeviceState::Normal {
87            return Err(ErrorCode::BUSY);
88        }
89
90        if self.op.get() != Operation::None {
91            return Err(ErrorCode::BUSY);
92        }
93
94        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
95            buffer[0] = RAIN_HOUR_REGISTER;
96            buffer[1] = hours as u8;
97
98            self.op.set(Operation::RainFall);
99            self.state.set(DeviceState::StartRainFall(hours));
100            if let Err((e, buf)) = self.i2c.write(buffer, 2) {
101                self.buffer.replace(buf);
102                return Err(e.into());
103            }
104
105            Ok(())
106        })
107    }
108}
109
110impl<'a, A: Alarm<'a>, I: I2CDevice> I2CClient for DFRobotRainFall<'a, A, I> {
111    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
112        if let Err(i2c_err) = status {
113            self.buffer.replace(buffer);
114
115            match self.op.get() {
116                Operation::None => (),
117                Operation::RainFall => {
118                    self.op.set(Operation::None);
119
120                    self.rainfall_client
121                        .map(|client| client.callback(Err(i2c_err.into())));
122                }
123            }
124
125            return;
126        }
127
128        match self.state.get() {
129            DeviceState::Identify => {
130                let pid =
131                    buffer[0] as u32 | (buffer[1] as u32) << 8 | ((buffer[3] as u32) & 0xC0) << 10;
132                let vid = buffer[2] as u16 | ((buffer[3] as u16) & 0x3F) << 8;
133
134                if vid != 0x3343 || pid != 0x100C0 {
135                    self.buffer.replace(buffer);
136                    self.state.set(DeviceState::Broken);
137                    self.op.set(Operation::None);
138                    return;
139                }
140
141                self.buffer.replace(buffer);
142                self.state.set(DeviceState::Normal);
143                self.op.set(Operation::None);
144            }
145            DeviceState::StartRainFall(_hours) => match self.op.get() {
146                Operation::None => (),
147                Operation::RainFall => {
148                    self.state.set(DeviceState::ContinueRainFall);
149                    buffer[0] = TIME_RAINFALL_REGISTER;
150                    if let Err((e, buf)) = self.i2c.write(buffer, 1) {
151                        self.buffer.replace(buf);
152                        self.op.set(Operation::None);
153
154                        self.rainfall_client
155                            .map(|client| client.callback(Err(e.into())));
156                    }
157                }
158            },
159            DeviceState::ContinueRainFall => match self.op.get() {
160                Operation::None => (),
161                Operation::RainFall => {
162                    self.buffer.replace(buffer);
163                    let delay = self.alarm.ticks_from_us(6400);
164                    self.alarm.set_alarm(self.alarm.now(), delay);
165                }
166            },
167            DeviceState::FinalRainFall => match self.op.get() {
168                Operation::None => (),
169                Operation::RainFall => {
170                    let rainfall = (buffer[0] as u32
171                        | (buffer[1] as u32) << 8
172                        | (buffer[2] as u32) << 16
173                        | (buffer[3] as u32) << 24)
174                        / 10;
175
176                    self.state.set(DeviceState::Normal);
177                    self.buffer.replace(buffer);
178                    self.op.set(Operation::None);
179
180                    self.rainfall_client
181                        .map(|client| client.callback(Ok(rainfall as usize)));
182                }
183            },
184            DeviceState::Normal | DeviceState::Broken => {}
185        }
186    }
187}
188
189impl<'a, A: Alarm<'a>, I: I2CDevice> AlarmClient for DFRobotRainFall<'a, A, I> {
190    fn alarm(&self) {
191        match self.op.get() {
192            Operation::None => (),
193            Operation::RainFall => {
194                self.state.set(DeviceState::FinalRainFall);
195                self.buffer.take().map(|buffer| {
196                    if let Err((e, buf)) = self.i2c.read(buffer, 4) {
197                        self.buffer.replace(buf);
198                        self.op.set(Operation::None);
199
200                        self.rainfall_client
201                            .map(|client| client.callback(Err(e.into())));
202                    }
203                });
204            }
205        }
206    }
207}