Skyward boardcore
Loading...
Searching...
No Matches
BME280.cpp
Go to the documentation of this file.
1/* Copyright (c) 2021 Skyward Experimental Rocketry
2 * Author: Alberto Nidasio
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "BME280.h"
24
26#include <math.h>
27
28using namespace std;
29
30namespace Boardcore
31{
32
33const BME280::BME280Config BME280::BME280_DEFAULT_CONFIG = {
34 SKIPPED, 0, 0, SLEEP_MODE, SKIPPED, SKIPPED, 0, FILTER_OFF, STB_TIME_0_5};
35
36const BME280::BME280Config BME280::BME280_CONFIG_ALL_ENABLED = {OVERSAMPLING_1,
37 0,
38 0,
39 NORMAL_MODE,
40 OVERSAMPLING_16,
41 OVERSAMPLING_2,
42 0,
43 FILTER_COEFF_16,
44 STB_TIME_0_5};
45
46const BME280::BME280Config BME280::BME280_CONFIG_TEMP_SINGLE = {
47 SKIPPED, 0, 0, FORCED_MODE, SKIPPED,
48 OVERSAMPLING_1, 0, FILTER_OFF, STB_TIME_0_5};
49
51 : spiSlave(spiSlave), config(config)
52{
53}
54
56{
57 if (!checkWhoAmI())
58 {
59 LOG_ERR(logger, "Invalid WHO AM I");
60
62 return false;
63 }
64
65 reset();
66 miosix::Thread::sleep(3);
67
68 loadCompensationParameters();
69
70 // Read once the temperature to compute fineTemperature
71 setConfiguration(BME280_CONFIG_TEMP_SINGLE);
72 miosix::Thread::sleep(
75
76 // Set the target configuration
77 setConfiguration();
78
79 BME280Config readBackConfig = readConfiguration();
80
81 // Check if the configuration on the device matches ours
82 if (config.bytes.ctrlHumidity != readBackConfig.bytes.ctrlHumidity ||
83 config.bytes.ctrlPressureAndTemperature !=
84 readBackConfig.bytes.ctrlPressureAndTemperature ||
85 config.bytes.config != readBackConfig.bytes.config)
86 {
87 LOG_ERR(logger, "Device configuration incorrect, setup failed.");
88
90 return false;
91 }
92
93 return true;
94}
95
97{
98 config.bits.mode = mode;
99
100 setConfiguration();
101}
102
104{
105 config.bits.oversamplingHumidity = oversampling;
106
107 setConfiguration();
108}
109
111{
112 config.bits.oversamplingPressure = oversampling;
113
114 setConfiguration();
115}
116
118{
119 config.bits.oversamplingTemperature = oversampling;
120
121 setConfiguration();
122}
123
125{
126 config.bits.filter = filterCoeff;
127
128 setConfiguration();
129}
130
132{
133 config.bits.standbyTime = standbyTime;
134
135 setConfiguration();
136}
137
139{
140 uint8_t buffer[2];
141 {
142 SPITransaction transaction(spiSlave);
143
144 transaction.readRegisters(REG_HUM_MSB, buffer, 2);
145 }
146
147 int32_t adc_H = ((uint32_t)buffer[0] << 8);
148 adc_H |= buffer[1];
149
150 HumidityData data;
152 data.humidity = compensateHumidity(adc_H);
153 data.humidity /= 1024; // Convert to to %RH
154
155 return data;
156}
157
159{
160 uint8_t buffer[3];
161 {
162 SPITransaction transaction(spiSlave);
163
164 transaction.readRegisters(REG_PRESS_MSB, buffer, 3);
165 }
166
167 int32_t adc_P = ((uint32_t)buffer[0]) << 12;
168 adc_P |= ((uint32_t)buffer[1]) << 4;
169 adc_P |= (buffer[2] >> 4) & 0x0F;
170
171 PressureData data;
173 data.pressure = compensatePressure(adc_P);
174 data.pressure /= 256; // Convert to Pa
175
176 return data;
177}
178
180{
181 uint8_t buffer[3];
182 {
183 SPITransaction transaction(spiSlave);
184
185 transaction.readRegisters(REG_TEMP_MSB, buffer, 3);
186 }
187
188 int32_t adcTemperature = ((uint32_t)buffer[0]) << 12;
189 adcTemperature |= ((uint32_t)buffer[1]) << 4;
190 adcTemperature |= (buffer[2] >> 4) & 0x0F;
191
192 fineTemperature = computeFineTemperature(adcTemperature);
193
194 TemperatureData data;
196 data.temperature = compensateTemperature(fineTemperature);
197 data.temperature /= 100; // Convert to to DegC
198
199 return data;
200}
201
203{
204 return ceil(1.25 + (2.3 * config.bits.oversamplingTemperature) +
205 (2.3 * config.bits.oversamplingPressure + 0.575) +
206 (2.3 * config.bits.oversamplingHumidity + 0.575));
207}
208
210{
211 return calculateMaxMeasurementTime(config);
212}
213
214bool BME280::selfTest() { return checkWhoAmI(); }
215
217{
218 // TODO: implement selective read!
219
220 uint8_t buffer[8];
221 {
222 SPITransaction transaction(spiSlave);
223
224 transaction.readRegisters(REG_PRESS_MSB, buffer, 8);
225 }
226
227 BME280Data data;
228
229 int32_t adcTemperature = ((uint32_t)buffer[3]) << 12;
230 adcTemperature |= ((uint32_t)buffer[4]) << 4;
231 adcTemperature |= (buffer[5] >> 4) & 0x0F;
232
233 int32_t adc_P = ((uint32_t)buffer[0]) << 12;
234 adc_P |= ((uint32_t)buffer[1]) << 4;
235 adc_P |= (buffer[2] >> 4) & 0x0F;
236
237 int32_t adc_H = ((uint32_t)buffer[6] << 8);
238 adc_H |= buffer[7];
239
240 // Compensate temperature
241 fineTemperature = computeFineTemperature(adcTemperature);
243 data.temperature = compensateTemperature(fineTemperature);
244 data.temperature /= 100; // Convert to to DegC
245
246 // Compensate pressure
248 data.pressure = compensatePressure(adc_P);
249 data.pressure /= 256; // Convert to Pa
250
251 // Compensate humidity
253 data.humidity = compensateHumidity(adc_H);
254 data.humidity /= 1024; // Convert to to %RH
255
256 return data;
257}
258
259void BME280::reset()
260{
261 SPITransaction transaction(spiSlave);
262
263 transaction.writeRegister(REG_RESET, 0xB6);
264}
265
266bool BME280::checkWhoAmI()
267{
268 SPITransaction transaction(spiSlave);
269
270 uint8_t whoAmIValue = transaction.readRegister(REG_ID);
271
272 return whoAmIValue == REG_ID_VAL;
273}
274
275void BME280::setConfiguration() { setConfiguration(config); }
276
277void BME280::setConfiguration(BME280Config config)
278{
279 SPITransaction transaction(spiSlave);
280
281 transaction.writeRegister(REG_CONFIG, config.bytes.config);
282 transaction.writeRegister(REG_CTRL_HUM, config.bytes.ctrlHumidity);
283 transaction.writeRegister(REG_CTRL_MEAS,
284 config.bytes.ctrlPressureAndTemperature);
285}
286
287BME280::BME280Config BME280::readConfiguration()
288{
289 BME280Config tmp;
290 SPITransaction transaction(spiSlave);
291
292 transaction.readRegisters(REG_CTRL_HUM, (uint8_t*)&tmp, 4);
293
294 return tmp;
295}
296
297void BME280::loadCompensationParameters()
298{
299 // Read first batch of compensation parameters
300 {
301 SPITransaction transaction(spiSlave);
302
303 transaction.readRegisters(REG_CALIB_0, (uint8_t*)&compParams, 25);
304 }
305
306 // Read second batch of compensation parameters
307 {
308 SPITransaction transaction(spiSlave);
309
310 transaction.readRegisters(REG_CALIB_26,
311 (uint8_t*)&compParams.bits.dig_H2, 7);
312 }
313
314 // Adjust unaligned data
315 compParams.bytesArray[29] =
316 (compParams.bytesArray[29] << 4) | (compParams.bytesArray[29] >> 4);
317 compParams.bits.dig_H4 =
318 (compParams.bits.dig_H4 << 4) | (compParams.bits.dig_H4 >> 8);
319}
320
321int32_t BME280::computeFineTemperature(int32_t adcTemperature)
322{
323 int32_t var1, var2;
324 var1 = ((((adcTemperature >> 3) - ((int32_t)compParams.bits.dig_T1 << 1))) *
325 ((int32_t)compParams.bits.dig_T2)) >>
326 11;
327 var2 = (((((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1)) *
328 ((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1))) >>
329 12) *
330 ((int32_t)compParams.bits.dig_T3)) >>
331 14;
332 return var1 + var2;
333}
334
335int32_t BME280::compensateTemperature(int32_t fineTemperature)
336{
337 return (fineTemperature * 5 + 128) >> 8;
338}
339
340uint32_t BME280::compensatePressure(int32_t adc_P)
341{
342 int64_t var1, var2, p;
343 var1 = ((int64_t)fineTemperature) - 128000;
344 var2 = var1 * var1 * (int64_t)compParams.bits.dig_P6;
345 var2 = var2 + ((var1 * (int64_t)compParams.bits.dig_P5) << 17);
346 var2 = var2 + (((int64_t)compParams.bits.dig_P4) << 35);
347 var1 = ((var1 * var1 * (int64_t)compParams.bits.dig_P3) >> 8) +
348 ((var1 * ((int64_t)compParams.bits.dig_P2) << 12));
349 var1 =
350 ((((int64_t)1) << 47) + var1) * ((int64_t)compParams.bits.dig_P1) >> 33;
351 if (var1 == 0)
352 return 0; // avoid exception caused by division by zero
353 p = 1048576 - adc_P;
354 p = (((p << 31) - var2) * 3125) / var1;
355 var1 = (((int64_t)compParams.bits.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
356 var2 = (((int64_t)compParams.bits.dig_P8) * p) >> 19;
357 p = ((p + var1 + var2) >> 8) + (((int64_t)compParams.bits.dig_P7) << 4);
358 return (uint32_t)p;
359}
360
361uint32_t BME280::compensateHumidity(int32_t adc_H)
362{
363 int32_t v_x1_u32r;
364
365 v_x1_u32r = (fineTemperature - ((int32_t)768000));
366 v_x1_u32r = (((((adc_H << 14) - (((int32_t)compParams.bits.dig_H4) << 20) -
367 (((int32_t)compParams.bits.dig_H5) * v_x1_u32r)) +
368 ((int32_t)16384)) >>
369 15) *
370 (((((((v_x1_u32r * ((int32_t)compParams.bits.dig_H6)) >> 10) *
371 (((v_x1_u32r * ((int32_t)compParams.bits.dig_H3)) >> 11) +
372 ((int32_t)32768))) >>
373 10) +
374 ((int32_t)2097152)) *
375 ((int32_t)compParams.bits.dig_H2) +
376 8192) >>
377 14));
378 v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
379 ((int32_t)compParams.bits.dig_H1)) >>
380 4));
381 v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
382 v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
383 return (uint32_t)(v_x1_u32r >> 12);
384}
385
386} // namespace Boardcore
#define LOG_ERR(logger,...)
SensorErrors lastError
Definition Sensor.h:54
static const BME280Config BME280_CONFIG_TEMP_SINGLE
Definition BME280.h:158
void setHumidityOversampling(Oversampling oversampling)
Sets the oversampling for humidity readings, use SKIPPED to disable humidity sampling.
Definition BME280.cpp:103
static const BME280Config BME280_CONFIG_ALL_ENABLED
Temperature enabled in forced mode.
Definition BME280.h:155
void setTemperatureOversampling(Oversampling oversampling)
Sets the oversampling for temperature readings, use SKIPPED to disable temperature sampling.
Definition BME280.cpp:117
bool init() override
Initialize the device with the specified configuration.
Definition BME280.cpp:55
void setStandbyTime(StandbyTime standbyTime)
Sets the standby time between readings in normal mode.
Definition BME280.cpp:131
void setSensorMode(Mode mode)
Sets the sensor mode.
Definition BME280.cpp:96
BME280(SPISlave spiSlave, BME280Config config=BME280_CONFIG_ALL_ENABLED)
Definition BME280.cpp:50
static const BME280Config BME280_DEFAULT_CONFIG
Datasheet values for indoor navigation.
Definition BME280.h:152
unsigned int getMaxMeasurementTime()
Definition BME280.cpp:209
bool selfTest() override
Reads the WHO AM I register.
Definition BME280.cpp:214
static unsigned int calculateMaxMeasurementTime(BME280Config config)
Maximum measurement time formula from datasheet page 51.
Definition BME280.cpp:202
HumidityData readHumidity()
Reads only the humidity, does not set the configuration.
Definition BME280.cpp:138
PressureData readPressure()
Reads only the pressure, does not set the configuration.
Definition BME280.cpp:158
void setPressureOversampling(Oversampling oversampling)
Sets the oversampling for pressure readings, use SKIPPED to disable pressure sampling.
Definition BME280.cpp:110
void setFilterCoeff(FilterCoeff filterCoeff)
Sets the coefficient for the IIR filter (applied to temperature and pressure)
Definition BME280.cpp:124
TemperatureData readTemperature()
Reads only the temperature, does not set the configuration.
Definition BME280.cpp:179
BME280Data sampleImpl() override
Read a data sample from the sensor. In case of errors, the method should return the last available co...
Definition BME280.cpp:216
static constexpr uint8_t REG_ID_VAL
Who am I value.
Definition BME280.h:149
Provides high-level access to the SPI Bus for a single transaction.
void readRegisters(uint8_t reg, uint8_t *data, size_t size)
Reads multiple bytes starting from the specified register.
uint64_t getTimestamp()
Returns the current timer value in microseconds.
This file includes all the types the logdecoder script will decode.
@ INVALID_WHOAMI
Definition SensorData.h:40
Definition WIZ5500.h:318
Structure to handle humidity data.
Definition SensorData.h:105
Contains information about a single SPI slave device.
struct Boardcore::BME280::BME280Config::@2 bytes