capsules_extra/
bme280.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//! SyscallDriver for the Bosch BME280 Combined humidity and pressure
6//! sensor using the I2C bus.
7//!
8//! <https://cdn.sparkfun.com/assets/learn_tutorials/4/1/9/BST-BME280_DS001-10.pdf>
9//!
10
11use core::cell::Cell;
12use kernel::hil::i2c::{self, I2CClient, I2CDevice};
13use kernel::hil::sensors::{HumidityClient, HumidityDriver, TemperatureClient, TemperatureDriver};
14use kernel::utilities::cells::{OptionalCell, TakeCell};
15use kernel::ErrorCode;
16
17const HUM_MSB: u8 = 0xFD;
18const TEMP_MSB: u8 = 0xFA;
19#[allow(dead_code)]
20const PRESS_MSB: u8 = 0xF7;
21#[allow(dead_code)]
22const CONFIG: u8 = 0xF5;
23const CTRL_MEAS: u8 = 0xF4;
24#[allow(dead_code)]
25const STATUS: u8 = 0xF3;
26const CTRL_HUM: u8 = 0xF2;
27#[allow(dead_code)]
28const CALIB41: u8 = 0xF0;
29const CALIB26: u8 = 0xE1;
30#[allow(dead_code)]
31const RESET: u8 = 0xE0;
32const ID: u8 = 0xD0;
33#[allow(dead_code)]
34const CALIB25: u8 = 0xA1;
35const CALIB00: u8 = 0x88;
36
37#[derive(Clone, Copy, PartialEq)]
38enum DeviceState {
39    Identify,
40    CalibrationLow,
41    CalibrationHigh,
42    Start,
43    Normal,
44}
45
46#[allow(dead_code)]
47#[derive(Clone, Copy, PartialEq)]
48enum Operation {
49    None,
50    Temp,
51    Pressure,
52    Humidity,
53}
54
55#[derive(Clone, Copy, PartialEq, Default)]
56struct CalibrationData {
57    temp1: u16,
58    temp2: u16,
59    temp3: u16,
60
61    press1: u16,
62    press2: u16,
63    press3: u16,
64    press4: u16,
65    press5: u16,
66    press6: u16,
67    press7: u16,
68    press8: u16,
69    press9: u16,
70
71    hum1: u16,
72    hum2: u16,
73    hum3: u16,
74    hum4: u16,
75    hum5: u16,
76    hum6: u16,
77}
78
79pub struct Bme280<'a, I: I2CDevice> {
80    buffer: TakeCell<'static, [u8]>,
81    i2c: &'a I,
82    calibration: Cell<CalibrationData>,
83    temperature_client: OptionalCell<&'a dyn TemperatureClient>,
84    humidity_client: OptionalCell<&'a dyn HumidityClient>,
85    state: Cell<DeviceState>,
86    op: Cell<Operation>,
87    t_fine: Cell<i32>,
88}
89
90impl<'a, I: I2CDevice> Bme280<'a, I> {
91    pub fn new(i2c: &'a I, buffer: &'static mut [u8]) -> Self {
92        Bme280 {
93            buffer: TakeCell::new(buffer),
94            i2c,
95            calibration: Cell::new(CalibrationData::default()),
96            temperature_client: OptionalCell::empty(),
97            humidity_client: OptionalCell::empty(),
98            state: Cell::new(DeviceState::Identify),
99            op: Cell::new(Operation::None),
100            t_fine: Cell::new(0),
101        }
102    }
103
104    pub fn startup(&self) {
105        self.buffer.take().map(|buffer| {
106            if self.state.get() == DeviceState::Identify {
107                // Read the ID buffer
108                buffer[0] = ID;
109                self.i2c.write_read(buffer, 1, 1).unwrap();
110            }
111        });
112    }
113}
114
115impl<'a, I: I2CDevice> TemperatureDriver<'a> for Bme280<'a, I> {
116    fn set_client(&self, client: &'a dyn TemperatureClient) {
117        self.temperature_client.set(client);
118    }
119
120    fn read_temperature(&self) -> Result<(), ErrorCode> {
121        if self.state.get() != DeviceState::Normal {
122            return Err(ErrorCode::BUSY);
123        }
124
125        if self.op.get() != Operation::None {
126            return Err(ErrorCode::BUSY);
127        }
128
129        self.buffer.take().map(|buffer| {
130            buffer[0] = TEMP_MSB;
131
132            self.op.set(Operation::Temp);
133            self.i2c.write_read(buffer, 1, 3).unwrap();
134        });
135
136        Ok(())
137    }
138}
139
140impl<'a, I: I2CDevice> HumidityDriver<'a> for Bme280<'a, I> {
141    fn set_client(&self, client: &'a dyn HumidityClient) {
142        self.humidity_client.set(client);
143    }
144
145    fn read_humidity(&self) -> Result<(), ErrorCode> {
146        if self.state.get() != DeviceState::Normal {
147            return Err(ErrorCode::BUSY);
148        }
149
150        if self.op.get() != Operation::None {
151            return Err(ErrorCode::BUSY);
152        }
153
154        self.buffer.take().map(|buffer| {
155            buffer[0] = HUM_MSB;
156
157            self.op.set(Operation::Humidity);
158            self.i2c.write_read(buffer, 1, 3).unwrap();
159        });
160
161        Ok(())
162    }
163}
164
165impl<I: I2CDevice> I2CClient for Bme280<'_, I> {
166    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
167        if let Err(i2c_err) = status {
168            // We have no way to report an error, so just return a bogus value
169            match self.op.get() {
170                Operation::None => (),
171                Operation::Temp => {
172                    self.temperature_client
173                        .map(|client| client.callback(Err(i2c_err.into())));
174                }
175                Operation::Pressure => {
176                    unimplemented!();
177                }
178                Operation::Humidity => {
179                    self.humidity_client.map(|client| client.callback(0));
180                }
181            }
182            self.buffer.replace(buffer);
183            self.op.set(Operation::None);
184            return;
185        }
186
187        match self.state.get() {
188            DeviceState::Identify => {
189                if buffer[0] != 0x60 {
190                    // We don't have the correct ID, this isn't the correct device
191                    // Just stop here
192                    self.buffer.replace(buffer);
193                    return;
194                }
195
196                buffer[0] = CALIB00;
197                self.i2c.write_read(buffer, 1, 26).unwrap();
198                self.state.set(DeviceState::CalibrationLow);
199            }
200            DeviceState::CalibrationLow => {
201                let mut calib = self.calibration.take();
202                calib.temp1 = buffer[0] as u16 | (buffer[1] as u16) << 8;
203                calib.temp2 = buffer[2] as u16 | (buffer[3] as u16) << 8;
204                calib.temp3 = buffer[4] as u16 | (buffer[5] as u16) << 8;
205                calib.press1 = buffer[6] as u16 | (buffer[7] as u16) << 8;
206                calib.press2 = buffer[8] as u16 | (buffer[9] as u16) << 8;
207                calib.press3 = buffer[10] as u16 | (buffer[11] as u16) << 8;
208                calib.press4 = buffer[12] as u16 | (buffer[13] as u16) << 8;
209                calib.press5 = buffer[14] as u16 | (buffer[15] as u16) << 8;
210                calib.press6 = buffer[16] as u16 | (buffer[17] as u16) << 8;
211                calib.press7 = buffer[18] as u16 | (buffer[19] as u16) << 8;
212                calib.press8 = buffer[20] as u16 | (buffer[21] as u16) << 8;
213                calib.press9 = buffer[22] as u16 | (buffer[23] as u16) << 8;
214                calib.hum1 = buffer[25] as u16;
215                self.calibration.set(calib);
216
217                if calib.temp1 == 0 || calib.temp2 == 0 || calib.temp3 == 0 {
218                    // We received stale calibration data, let's try again
219
220                    buffer[0] = CALIB00;
221                    self.i2c.write_read(buffer, 1, 26).unwrap();
222                    self.state.set(DeviceState::CalibrationLow);
223                    return;
224                }
225
226                buffer[0] = CALIB26;
227                self.i2c.write_read(buffer, 1, 8).unwrap();
228
229                self.state.set(DeviceState::CalibrationHigh);
230            }
231            DeviceState::CalibrationHigh => {
232                let mut calib = self.calibration.take();
233                calib.hum2 = buffer[0] as u16 | (buffer[1] as u16) << 8;
234                calib.hum3 = buffer[3] as u16;
235                calib.hum4 = buffer[4] as u16 | (buffer[5] as u16) << 4;
236                calib.hum5 = (buffer[6] as u16 >> 4) | (buffer[7] as u16) << 4;
237                calib.hum6 = buffer[8] as u16;
238                self.calibration.set(calib);
239
240                // Set humidity oversampling to 1
241                buffer[0] = CTRL_HUM;
242                buffer[1] = 1;
243                self.i2c.write(buffer, 2).unwrap();
244                self.state.set(DeviceState::Start);
245            }
246            DeviceState::Start => {
247                // Set the mode to normal and set oversampling to 1
248                buffer[0] = CTRL_MEAS;
249                buffer[1] = 0x11 | 1 << 5 | 1 << 2;
250                self.i2c.write(buffer, 2).unwrap();
251
252                self.state.set(DeviceState::Normal);
253            }
254            DeviceState::Normal => {
255                match self.op.get() {
256                    Operation::None => (),
257                    Operation::Temp => {
258                        let calib = self.calibration.get();
259
260                        let adc_temperature: i32 = ((buffer[0] as usize) << 12
261                            | (buffer[1] as usize) << 4
262                            | (((buffer[2] as usize) >> 4) & 0x0F))
263                            as i32;
264
265                        if adc_temperature == 0 {
266                            // We got a misread, try again
267                            self.buffer.replace(buffer);
268                            self.op.set(Operation::None);
269                            let _ = self.read_temperature();
270                            return;
271                        }
272
273                        let var1 = (((adc_temperature >> 3) - ((calib.temp1 as i32) << 1))
274                            * (calib.temp2 as i32))
275                            >> 11;
276                        let var2 = (((((adc_temperature >> 4) - (calib.temp1 as i32))
277                            * ((adc_temperature >> 4) - (calib.temp1 as i32)))
278                            >> 12)
279                            * (calib.temp3 as i32))
280                            >> 14;
281
282                        self.t_fine.set(var1 + var2);
283
284                        let temperature = (self.t_fine.get() * 5 + 128) >> 8;
285
286                        self.temperature_client
287                            .map(|client| client.callback(Ok(temperature)));
288                    }
289                    Operation::Pressure => {
290                        unimplemented!();
291                    }
292                    Operation::Humidity => {
293                        let calib = self.calibration.get();
294                        let adc_hum = (((buffer[0] as u32) << 8) | (buffer[1] as u32)) as i32;
295
296                        if adc_hum == 0 {
297                            // We got a misread, try again
298                            self.buffer.replace(buffer);
299                            self.op.set(Operation::None);
300                            let _ = self.read_humidity();
301                            return;
302                        }
303
304                        let t_fine_offset = self.t_fine.get() - 76800;
305
306                        // This is straight from the datasheet
307                        let var1 = ((((adc_hum << 14)
308                            - ((calib.hum4 as i32) << 20)
309                            - ((calib.hum5 as i32) * t_fine_offset))
310                            + 16384)
311                            >> 15)
312                            * (((((((t_fine_offset * (calib.hum6 as i32)) >> 10)
313                                * (((t_fine_offset * (calib.hum3 as i32)) >> 11) + 32768))
314                                >> 10)
315                                + 2097152)
316                                * (calib.hum2 as i32)
317                                + 8192)
318                                >> 14);
319                        let var2 = var1
320                            - (((((var1 >> 15) * (var1 >> 15)) >> 7) * (calib.hum1 as i32)) >> 4);
321
322                        let var3 = if var2 < 0 { 0 } else { var2 };
323                        let var6 = if var3 > 419430400 { 419430400 } else { var3 };
324
325                        let hum = (((var6 >> 12) * 100) / 1024) as usize;
326
327                        self.humidity_client.map(|client| client.callback(hum));
328                    }
329                }
330                self.buffer.replace(buffer);
331                self.op.set(Operation::None);
332            }
333        }
334    }
335}