Skyward boardcore
Loading...
Searching...
No Matches
BME280I2C.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 "BME280I2C.h"
24
26#include <math.h>
27
28using namespace std;
29
30namespace Boardcore
31{
32
33const BME280I2C::BME280Config BME280I2C::BME280_DEFAULT_CONFIG = {
34 SKIPPED, 0, 0, SLEEP_MODE, SKIPPED, SKIPPED, 0, FILTER_OFF, STB_TIME_0_5};
35
36const BME280I2C::BME280Config BME280I2C::BME280_CONFIG_ALL_ENABLED = {
37 OVERSAMPLING_1,
38 0,
39 0,
40 NORMAL_MODE,
41 OVERSAMPLING_16,
42 OVERSAMPLING_2,
43 0,
44 FILTER_COEFF_16,
45 STB_TIME_0_5};
46
47const BME280I2C::BME280Config BME280I2C::BME280_CONFIG_TEMP_SINGLE = {
48 SKIPPED, 0, 0, FORCED_MODE, SKIPPED,
49 OVERSAMPLING_1, 0, FILTER_OFF, STB_TIME_0_5};
50
51BME280I2C::BME280I2C(I2C& bus, BME280Config config) : bus(bus), 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 if (!reset())
66 return false;
67 miosix::Thread::sleep(3);
68
69 loadCompensationParameters();
70
71 // Read once the temperature to compute fineTemperature
72 setConfiguration(BME280_CONFIG_TEMP_SINGLE);
73 miosix::Thread::sleep(
76
77 // Set the target configuration
78 setConfiguration();
79
80 BME280Config readBackConfig = readConfiguration();
81
82 // Check if the configuration on the device matches ours
83 if (config.bytes.ctrlHumidity != readBackConfig.bytes.ctrlHumidity ||
84 config.bytes.ctrlPressureAndTemperature !=
85 readBackConfig.bytes.ctrlPressureAndTemperature ||
86 config.bytes.config != readBackConfig.bytes.config)
87 {
88 LOG_ERR(logger, "Device configuration incorrect, setup failed.");
89
91 return false;
92 }
93
94 return true;
95}
96
98{
99 config.bits.mode = mode;
100
101 setConfiguration();
102}
103
105{
106 config.bits.oversamplingHumidity = oversampling;
107
108 setConfiguration();
109}
110
112{
113 config.bits.oversamplingPressure = oversampling;
114
115 setConfiguration();
116}
117
119{
120 config.bits.oversamplingTemperature = oversampling;
121
122 setConfiguration();
123}
124
126{
127 config.bits.filter = filterCoeff;
128
129 setConfiguration();
130}
131
133{
134 config.bits.standbyTime = standbyTime;
135
136 setConfiguration();
137}
138
140{
141 uint8_t buffer[2];
142 if (bus.readFromRegister(slaveConfig, REG_HUM_MSB, buffer, 2))
143 {
144 int32_t adc_H = ((uint32_t)buffer[0] << 8);
145 adc_H |= buffer[1];
146
147 HumidityData data;
149 data.humidity = compensateHumidity(adc_H);
150 data.humidity /= 1024; // Convert to to %RH
151
152 return data;
153 }
154 else
155 {
157 return lastSample;
158 }
159}
160
162{
163 uint8_t buffer[3];
164 if (bus.readFromRegister(slaveConfig, REG_PRESS_MSB, buffer, 3))
165 {
166 int32_t adc_P = ((uint32_t)buffer[0]) << 12;
167 adc_P |= ((uint32_t)buffer[1]) << 4;
168 adc_P |= (buffer[2] >> 4) & 0x0F;
169
170 PressureData data;
172 data.pressure = compensatePressure(adc_P);
173 data.pressure /= 256; // Convert to Pa
174
175 return data;
176 }
177 else
178 {
180 return lastSample;
181 }
182}
183
185{
186 uint8_t buffer[3];
187 if (bus.readFromRegister(slaveConfig, REG_TEMP_MSB, buffer, 3))
188 {
189 int32_t adcTemperature = ((uint32_t)buffer[0]) << 12;
190 adcTemperature |= ((uint32_t)buffer[1]) << 4;
191 adcTemperature |= (buffer[2] >> 4) & 0x0F;
192
193 fineTemperature = computeFineTemperature(adcTemperature);
194
195 TemperatureData data;
197 data.temperature = compensateTemperature(fineTemperature);
198 data.temperature /= 100; // Convert to to DegC
199
200 return data;
201 }
202 else
203 {
205 return lastSample;
206 }
207}
208
210{
211 return ceil(1.25 + (2.3 * config.bits.oversamplingTemperature) +
212 (2.3 * config.bits.oversamplingPressure + 0.575) +
213 (2.3 * config.bits.oversamplingHumidity + 0.575));
214}
215
217{
218 return calculateMaxMeasurementTime(config);
219}
220
221bool BME280I2C::selfTest() { return checkWhoAmI(); }
222
224{
225 // TODO: implement selective read!
226
227 uint8_t buffer[8];
228 if (bus.readFromRegister(slaveConfig, REG_PRESS_MSB, buffer, 8))
229 {
230 BME280Data data;
231
232 int32_t adcTemperature = ((uint32_t)buffer[3]) << 12;
233 adcTemperature |= ((uint32_t)buffer[4]) << 4;
234 adcTemperature |= (buffer[5] >> 4) & 0x0F;
235
236 int32_t adc_P = ((uint32_t)buffer[0]) << 12;
237 adc_P |= ((uint32_t)buffer[1]) << 4;
238 adc_P |= (buffer[2] >> 4) & 0x0F;
239
240 int32_t adc_H = ((uint32_t)buffer[6] << 8);
241 adc_H |= buffer[7];
242
243 // Compensate temperature
244 fineTemperature = computeFineTemperature(adcTemperature);
246 data.temperature = compensateTemperature(fineTemperature);
247 data.temperature /= 100; // Convert to to DegC
248
249 // Compensate pressure
251 data.pressure = compensatePressure(adc_P);
252 data.pressure /= 256; // Convert to Pa
253
254 // Compensate humidity
256 data.humidity = compensateHumidity(adc_H);
257 data.humidity /= 1024; // Convert to to %RH
258
259 return data;
260 }
261 else
262 {
264 return lastSample;
265 }
266}
267
268bool BME280I2C::reset()
269{
270 if (!bus.writeRegister(slaveConfig, REG_RESET, 0xB6))
271 {
273 return false;
274 }
275
276 return true;
277}
278
279bool BME280I2C::checkWhoAmI()
280{
281 uint8_t whoAmIValue;
282
283 if (bus.readRegister(slaveConfig, REG_ID, whoAmIValue))
284 {
285 return whoAmIValue == REG_ID_VAL;
286 }
287 else
288 {
290 return false;
291 }
292}
293
294void BME280I2C::setConfiguration() { setConfiguration(config); }
295
296void BME280I2C::setConfiguration(BME280Config config)
297{
298 if (!bus.writeRegister(slaveConfig, REG_CONFIG, config.bytes.config))
299 {
300 LOG_ERR(logger, "Error while writing to register REG_CONFIG");
301 return;
302 }
303
304 if (!bus.writeRegister(slaveConfig, REG_CTRL_HUM,
305 config.bytes.ctrlHumidity))
306 {
307 LOG_ERR(logger, "Error while writing to register REG_CTRL_HUM");
308 return;
309 }
310
311 if (!bus.writeRegister(slaveConfig, REG_CTRL_MEAS,
312 config.bytes.ctrlPressureAndTemperature))
313 {
314 LOG_ERR(logger, "Error while writing to register REG_CTRL_MEAS");
315 return;
316 }
317}
318
319BME280I2C::BME280Config BME280I2C::readConfiguration()
320{
321 BME280Config tmp;
322
323 if (bus.readFromRegister(slaveConfig, REG_CTRL_HUM, (uint8_t*)&tmp, 4))
324 {
325 return tmp;
326 }
327 else
328 {
331 }
332}
333
334void BME280I2C::loadCompensationParameters()
335{
336 // Read first batch of compensation parameters
337 if (!bus.readFromRegister(slaveConfig, REG_CALIB_0, (uint8_t*)&compParams,
338 25))
339 {
341 return;
342 }
343
344 // Read second batch of compensation parameters
345 if (!bus.readFromRegister(slaveConfig, REG_CALIB_26,
346 (uint8_t*)&compParams.bits.dig_H2, 7))
347 {
349 return;
350 }
351
352 // Adjust unaligned data
353 compParams.bytesArray[29] =
354 (compParams.bytesArray[29] << 4) | (compParams.bytesArray[29] >> 4);
355 compParams.bits.dig_H4 =
356 (compParams.bits.dig_H4 << 4) | (compParams.bits.dig_H4 >> 8);
357}
358
359int32_t BME280I2C::computeFineTemperature(int32_t adcTemperature)
360{
361 int32_t var1, var2;
362 var1 = ((((adcTemperature >> 3) - ((int32_t)compParams.bits.dig_T1 << 1))) *
363 ((int32_t)compParams.bits.dig_T2)) >>
364 11;
365 var2 = (((((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1)) *
366 ((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1))) >>
367 12) *
368 ((int32_t)compParams.bits.dig_T3)) >>
369 14;
370 return var1 + var2;
371}
372
373int32_t BME280I2C::compensateTemperature(int32_t fineTemperature)
374{
375 return (fineTemperature * 5 + 128) >> 8;
376}
377
378uint32_t BME280I2C::compensatePressure(int32_t adc_P)
379{
380 int64_t var1, var2, p;
381 var1 = ((int64_t)fineTemperature) - 128000;
382 var2 = var1 * var1 * (int64_t)compParams.bits.dig_P6;
383 var2 = var2 + ((var1 * (int64_t)compParams.bits.dig_P5) << 17);
384 var2 = var2 + (((int64_t)compParams.bits.dig_P4) << 35);
385 var1 = ((var1 * var1 * (int64_t)compParams.bits.dig_P3) >> 8) +
386 ((var1 * ((int64_t)compParams.bits.dig_P2) << 12));
387 var1 =
388 ((((int64_t)1) << 47) + var1) * ((int64_t)compParams.bits.dig_P1) >> 33;
389 if (var1 == 0)
390 return 0; // avoid exception caused by division by zero
391 p = 1048576 - adc_P;
392 p = (((p << 31) - var2) * 3125) / var1;
393 var1 = (((int64_t)compParams.bits.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
394 var2 = (((int64_t)compParams.bits.dig_P8) * p) >> 19;
395 p = ((p + var1 + var2) >> 8) + (((int64_t)compParams.bits.dig_P7) << 4);
396 return (uint32_t)p;
397}
398
399uint32_t BME280I2C::compensateHumidity(int32_t adc_H)
400{
401 int32_t v_x1_u32r;
402
403 v_x1_u32r = (fineTemperature - ((int32_t)768000));
404 v_x1_u32r = (((((adc_H << 14) - (((int32_t)compParams.bits.dig_H4) << 20) -
405 (((int32_t)compParams.bits.dig_H5) * v_x1_u32r)) +
406 ((int32_t)16384)) >>
407 15) *
408 (((((((v_x1_u32r * ((int32_t)compParams.bits.dig_H6)) >> 10) *
409 (((v_x1_u32r * ((int32_t)compParams.bits.dig_H3)) >> 11) +
410 ((int32_t)32768))) >>
411 10) +
412 ((int32_t)2097152)) *
413 ((int32_t)compParams.bits.dig_H2) +
414 8192) >>
415 14));
416 v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
417 ((int32_t)compParams.bits.dig_H1)) >>
418 4));
419 v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
420 v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
421 return (uint32_t)(v_x1_u32r >> 12);
422}
423
424} // namespace Boardcore
#define LOG_ERR(logger,...)
SensorErrors lastError
Definition Sensor.h:54
bool init() override
Initialize the device with the specified configuration.
Definition BME280I2C.cpp:55
static const BME280Config BME280_CONFIG_TEMP_SINGLE
Definition BME280I2C.h:158
void setTemperatureOversampling(Oversampling oversampling)
Sets the oversampling for temperature readings, use SKIPPED to disable temperature sampling.
static constexpr uint8_t REG_ID_VAL
Who am I value.
Definition BME280I2C.h:149
void setHumidityOversampling(Oversampling oversampling)
Sets the oversampling for humidity readings, use SKIPPED to disable humidity sampling.
void setSensorMode(Mode mode)
Sets the sensor mode.
Definition BME280I2C.cpp:97
static unsigned int calculateMaxMeasurementTime(BME280Config config)
Maximum measurement time formula from datasheet page 51.
BME280Data sampleImpl() override
Read a data sample from the sensor. In case of errors, the method should return the last available co...
PressureData readPressure()
Reads only the pressure, does not set the configuration.
static const BME280Config BME280_DEFAULT_CONFIG
Datasheet values for indoor navigation.
Definition BME280I2C.h:152
static const BME280Config BME280_CONFIG_ALL_ENABLED
Temperature enabled in forced mode.
Definition BME280I2C.h:155
HumidityData readHumidity()
Reads only the humidity, does not set the configuration.
void setPressureOversampling(Oversampling oversampling)
Sets the oversampling for pressure readings, use SKIPPED to disable pressure sampling.
BME280I2C(I2C &bus, BME280Config config=BME280_CONFIG_ALL_ENABLED)
Definition BME280I2C.cpp:51
bool selfTest() override
Reads the WHO AM I register.
void setStandbyTime(StandbyTime standbyTime)
Sets the standby time between readings in normal mode.
unsigned int getMaxMeasurementTime()
void setFilterCoeff(FilterCoeff filterCoeff)
Sets the coefficient for the IIR filter (applied to temperature and pressure)
TemperatureData readTemperature()
Reads only the temperature, does not set the configuration.
High level driver for the I2C peripherals.
Definition I2C.h:40
bool readFromRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, const uint8_t registerAddress, void *buffer, const size_t nBytes)
Non blocking operation to read n-bytes from register from a slave.
Definition I2C.cpp:189
bool writeRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, const uint8_t registerAddress, const uint8_t registerContent)
Non blocking operation to write an 8-bit register from a slave.
Definition I2C.cpp:111
bool readRegister(const I2CDriver::I2CSlaveConfig &slaveConfig, const uint8_t registerAddress, uint8_t &registerContent)
Non blocking operation to read an 8-bit register from a slave.
Definition I2C.cpp:48
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
struct Boardcore::BME280I2C::BME280Config::@3 bytes