-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBME68x.cpp
More file actions
582 lines (575 loc) · 32.2 KB
/
Copy pathBME68x.cpp
File metadata and controls
582 lines (575 loc) · 32.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
/*! @file BME68x.cpp
@section BME680 Description
Arduino Library for the Bosch BME680 sensor\n\n
See the main library header file for all details
*/
#include <arduino.h>
#include "BME68x.h" // Include the library header definition
/*******************************************************************************
** Declare class-internal and not publically visible constants **
*******************************************************************************/
const uint8_t BME680_STATUS_REGISTER{0x1D}; // < Device status register
const uint8_t BME680_GAS_HEATER_REGISTER0{0x5A}; // < Heater Register 0 address
const uint8_t BME680_GAS_DURATION_REGISTER0{0x64};// < Heater Register 0 address
const uint8_t BME680_CONTROL_GAS_REGISTER1{0x70}; // < Gas control register on/off
const uint8_t BME680_CONTROL_GAS_REGISTER2{0x71}; // < Gas control register settings
const uint8_t BME680_CONTROL_HUMIDITY_REGISTER{0x72};// < Humidity control register
const uint8_t BME680_SPI_REGISTER{0x73}; // < Status register for SPI memory
const uint8_t BME680_CONTROL_MEASURE_REGISTER{0x74}; // < Temp, Pressure control register
const uint8_t BME680_CONFIG_REGISTER{0x75}; // < Configuration register
const uint8_t BME680_CHIPID_REGISTER{0xD0}; // < Chip-Id register
const uint8_t BME680_SOFTRESET_REGISTER{0xE0};// < Reset when 0xB6 is written here
const uint8_t BME680_CHIPID{0x61}; // < Hard-coded value 0x61 for BME680
const uint8_t BME680_RESET_CODE{0xB6}; // < Reset when this put in reset reg
const uint8_t BME680_MEASURING_BIT_POSITION{5}; // < Bit position for measuring flag
const uint8_t BME680_I2C_MIN_ADDRESS{0x76};// < Minimum possible address for BME680
const uint8_t BME680_I2C_MAX_ADDRESS{0x77};// < Minimum possible address for BME680
const uint8_t BME680_SPI_MEM_PAGE_POSITION{4};// < Bit position for memory page value
const uint8_t BME680_HUMIDITY_MASK{0xF8}; // < Mask is binary B11111000
const uint8_t BME680_PRESSURE_MASK{0xE3}; // < Mask is binary B11100011
const uint8_t BME680_TEMPERATURE_MASK{0x1F};// < Mask is binary B00011111
/*******************************************************************************
** Declare the constants used for calibration **
*******************************************************************************/
const uint8_t BME680_COEFF_SIZE1{25}; // < First array with coefficients
const uint8_t BME680_COEFF_SIZE2{16}; // < Second array with coefficients
const uint8_t BME680_COEFF_START_ADDRESS1{0x89};// < start address for array 1
const uint8_t BME680_COEFF_START_ADDRESS2{0xE1};// < start address for array 2
const uint8_t BME680_HUM_REG_SHIFT_VAL{4}; // < Ambient humidity shift value
const uint8_t BME680_BIT_H1_DATA_MSK{0x0F}; // < Mask for humidity
const uint8_t BME680_T2_LSB_REG{1}; // < Register for temperature calibration
const uint8_t BME680_T2_MSB_REG{2}; // < Register for temperature calibration
const uint8_t BME680_T3_REG{3}; // < Register for temperature calibration
const uint8_t BME680_P1_LSB_REG{5}; // < Register for pressure calibration
const uint8_t BME680_P1_MSB_REG{6}; // < Register for pressure calibration
const uint8_t BME680_P2_LSB_REG{7}; // < Register for pressure calibration
const uint8_t BME680_P2_MSB_REG{8}; // < Register for pressure calibration
const uint8_t BME680_P3_REG{9}; // < Register for pressure calibration
const uint8_t BME680_P4_LSB_REG{11}; // < Register for pressure calibration
const uint8_t BME680_P4_MSB_REG{12}; // < Register for pressure calibration
const uint8_t BME680_P5_LSB_REG{13}; // < Register for pressure calibration
const uint8_t BME680_P5_MSB_REG{14}; // < Register for pressure calibration
const uint8_t BME680_P7_REG{15}; // < Register for pressure calibration
const uint8_t BME680_P6_REG{16}; // < Register for pressure calibration
const uint8_t BME680_P8_LSB_REG{19}; // < Register for pressure calibration
const uint8_t BME680_P8_MSB_REG{20}; // < Register for pressure calibration
const uint8_t BME680_P9_LSB_REG{21}; // < Register for pressure calibration
const uint8_t BME680_P9_MSB_REG{22}; // < Register for pressure calibration
const uint8_t BME680_P10_REG{23}; // < Register for pressure calibration
const uint8_t BME680_H2_MSB_REG{0}; // < Register for humidity calibration
const uint8_t BME680_H2_LSB_REG{1}; // < Register for humidity calibration
const uint8_t BME680_H1_LSB_REG{1}; // < Register for humidity calibration
const uint8_t BME680_H1_MSB_REG{2}; // < Register for humidity calibration
const uint8_t BME680_H3_REG{3}; // < Register for humidity calibration
const uint8_t BME680_H4_REG{4}; // < Register for humidity calibration
const uint8_t BME680_H5_REG{5}; // < Register for humidity calibration
const uint8_t BME680_H6_REG{6}; // < Register for humidity calibration
const uint8_t BME680_H7_REG{7}; // < Register for humidity calibration
const uint8_t BME680_T1_LSB_REG{8}; // < Register for gas calibration
const uint8_t BME680_T1_MSB_REG{9}; // < Register for gas calibration
const uint8_t BME680_GH2_LSB_REG{10}; // < Register for gas calibration
const uint8_t BME680_GH2_MSB_REG{11}; // < Register for gas calibration
const uint8_t BME680_GH1_REG{12}; // < Register for gas calibration
const uint8_t BME680_GH3_REG{13}; //< Register for gas calibration
const uint8_t BME680_ADDR_RES_HEAT_RANGE_ADDR{0x02};//< Register for gas calibration
const uint8_t BME680_RHRANGE_MSK{0x30};// < Register for gas calibration
const uint8_t BME680_ADDR_RES_HEAT_VAL_ADDR{0x00}; // < Register for gas calibration
const uint8_t BME680_ADDR_RANGE_SW_ERR_ADDR{0x04};// < Register for gas calibration
const uint8_t BME680_RSERROR_MSK{0xF0};// < Register for gas calibration
BME680_Class::BME680_Class() {
/*!
* @brief Class constructor
* @details Currently empty and unused
*/
} // of class constructor
BME680_Class::~BME680_Class() {
/*!
* @brief Class destructor
* @details Currently empty and unused
*/
} // of class destructor
/*******************************************************************************
** The "begin()" function is overloaded
to allow for variations in I2C and SPI initialization **
*******************************************************************************/
bool BME680_Class::begin() {
/*!
* @brief starts communications with device (overloaded)
* @details When called with no parameters the I2C protocol with I2C_STANDARD_MODE speed is
selected. The I2C network is searched for the first BME680 device.
* return "true" if successful otherwise false
*/
return begin(I2C_STANDARD_MODE, 0); // Initialize I2C with standard speed, allow any i2c address
} // of method begin()
bool BME680_Class::begin(const uint32_t i2cSpeed) {
/*!
@brief starts communications with device (overloaded)
@details When called with a 32-bit parameter it is assumed that the I2C protocol is to be used
and the speed setting is determined by the parameter.
@param[in] i2cSpeed I2C Speed value
return "true" if successful otherwise false
*/
return begin(i2cSpeed, 0); // Initialize I2C with given speed, allow any i2c address
} // of method begin()
bool BME680_Class::begin(const uint32_t i2cSpeed, const uint8_t i2cAddress) {
/*!
@brief starts communications with device (overloaded)
@details When called with a 32-bit parameter it is assumed that the I2C
protocol is to be used
and the speed setting is determined by the parameter. If the
i2cAddress is 0 then check
both, otherwise check only only the specified address
@param[in] i2cSpeed I2C Speed value
@param[in] i2cAddress I2C Address, use 0 to self-determine
return "true" if successful otherwise false
*/
Wire.begin(); // Start I2C as master
Wire.setClock(i2cSpeed); // and set bus speed
_I2CSpeed = i2cSpeed;
for (_I2CAddress = BME680_I2C_MIN_ADDRESS; _I2CAddress <= BME680_I2C_MAX_ADDRESS;
_I2CAddress++) { // Loop through all I2C addresses
if (i2cAddress == 0 || _I2CAddress == i2cAddress) { // Only check if relevant
Wire.beginTransmission(_I2CAddress); // Check for BME680 here
if (Wire.endTransmission() == 0) { // We have found a device that could be
return commonInitialization(); // a BME680, so perform common init
} // of if-then we have found a device
} // of if-then check all or a specific address
} // of for-next each I2C address loop
_I2CAddress = 0; // Set to denote no device found
_I2CSpeed = 0; // Set to denote no device found
return false; // return failure if we get here
} // of method begin()
bool BME680_Class::begin(const uint8_t chipSelect) {
/*!
@brief starts communications with device (overloaded)
@details When called with a single 8-bit parameter is assumed that hardware SPI is to be used
if the value is not 0x76 or 0x77, in which case it is assumed that it is the I2C addr
@param[in] chipSelect Arduino Pin number for the Slave-Select pin or the I2C address
return "true" if successful otherwise false
*/
if (chipSelect == BME680_I2C_MIN_ADDRESS || chipSelect == BME680_I2C_MAX_ADDRESS) {
// If 0x76 or 0x77 then we have I2C
return begin(I2C_STANDARD_MODE, chipSelect); // Std speed with explicit address
} // if-then we have an I2C call
_cs = chipSelect; // Store value for future use
digitalWrite(_cs, HIGH); // High means ignore master
pinMode(_cs, OUTPUT); // Make the chip select pin output
SPI.begin(); // Start hardware SPI
return commonInitialization(); // Perform common initialization
} // of method begin()
bool BME680_Class::begin(const uint8_t chipSelect, const uint8_t mosi, const uint8_t miso,
const uint8_t sck) {
/*!
@brief starts communications with device (overloaded)
@details When called with 4 parameters then software SPI is assumed
@param[in] chipSelect Arduino Pin number for the Slave-Select pin
@param[in] mosi Arduino Pin number for the Master-out-Slave-in pin
@param[in] miso Arduino Pin number for the Master-in-Slave-out pin
@param[in] sck Arduino Pin number for the System clock pin
return "true" if successful otherwise false
*/
_cs = chipSelect;
_mosi = mosi;
_miso = miso;
_sck = sck; // Store SPI pins
digitalWrite(_cs, HIGH); // High means ignore master
pinMode(_cs, OUTPUT); // Make the chip select pin output
pinMode(_sck, OUTPUT); // Make system clock pin output
pinMode(_mosi, OUTPUT); // Make master-out slave-in output
pinMode(_miso, INPUT); // Make master-in slave-out input
return commonInitialization(); // Perform common initialization
} // of method begin()
bool BME680_Class::commonInitialization() {
/*!
@brief Common BME680 initialization function
@details Called from all of the overloaded "begin()" functions once the communications protocol
has been selected return "true" if successful otherwise false
*/
uint8_t SPI_Register = readByte(BME680_SPI_REGISTER); // Read SPI reg. byte
if (_I2CAddress == 0 &&
bitRead(SPI_Register, BME680_SPI_MEM_PAGE_POSITION)) {// Wrong mode for ID
bitWrite(SPI_Register, BME680_SPI_MEM_PAGE_POSITION, 0);// Turn off Page bit to go to Page 0
putData(BME680_SPI_REGISTER, SPI_Register); // Write value to register
} // of if-then we are in SPI mode
if (readByte(BME680_CHIPID_REGISTER) == BME680_CHIPID) {// check for correct chip id
getCalibration(); // get the calibration values
if (_I2CAddress == 0) { // If using SPI, switch to correct
bitWrite(SPI_Register, BME680_SPI_MEM_PAGE_POSITION, 1); // Page "1" again
putData(BME680_SPI_REGISTER, SPI_Register); // Update register value
} // of if-then SPI mode
uint8_t workRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the control measure
putData(BME680_CONTROL_MEASURE_REGISTER,
(uint8_t)(workRegister | 1)); // Trigger 1st measurement
return true;
} // of if-then device is really a BME680
else
return false;
} // of method commonInitialization
uint8_t BME680_Class::readByte(const uint8_t addr) const {
/*!
@brief Read a single byte from the given address
@details interlude function to the "getData()" function. This is called so that the return value
is the byte of data that has just been read rather than the number of bytes read
returned by "getData()"
param[in] addr Address of device
return single byte of read from addr
*/
uint8_t returnValue; // Storage for returned value
getData(addr, returnValue); // Read just one byte
return (returnValue); // Return byte just read
} // of method readByte()
void BME680_Class::reset() {
/*!
* @brief Performs a device reset
*/
putData(BME680_SOFTRESET_REGISTER, BME680_RESET_CODE); // write reset code to device
delay(2); // Datasheet states 2ms Start-up time
if (_I2CAddress) { // Branch depending on if using I2C or SPI
begin(_I2CSpeed, _I2CAddress); // If I2C start device with same settings
} else {
if (_sck) { // If _sck is set the use software SPI
begin(_cs, _mosi, _miso, _sck); // Use software SPI again
} else {
begin(_cs); // otherwise it must be hardware SPI
} // if-then-else using sw or hw SPI
} // if-then-else using I2C
} // of method reset()
void BME680_Class::getCalibration() {
/*!
@brief reads the calibration register data into local variables for use in converting readings
@details The calibration registers are read into a temporary array and then parsed into the
appropriate calibration variables, this was taken from the example BOSCH software and
minimizes register reads, but makes it rather difficult to read. This will be redone
for legibility at some point in the future
param[in] addr Address of device
return single byte read
*/
/*************************************
** Temperature related coefficients **
*************************************/
uint8_t coeff_arr1[BME680_COEFF_SIZE1] = {0};// Define temp array 1 and initialize with 0x
uint8_t coeff_arr2[BME680_COEFF_SIZE2] = {0};// Define temp array 2 and initialize with 0s
getData(BME680_COEFF_START_ADDRESS1, coeff_arr1);// Split reading registers into 2
getData(BME680_COEFF_START_ADDRESS2, coeff_arr2);// one 25 bytes and the other 16
_T1 = (uint16_t)(CONCAT_BYTES(coeff_arr2[BME680_T1_MSB_REG], coeff_arr2[BME680_T1_LSB_REG]));
_T2 = (int16_t)(CONCAT_BYTES(coeff_arr1[BME680_T2_MSB_REG], coeff_arr1[BME680_T2_LSB_REG]));
_T3 = (int8_t)(coeff_arr1[BME680_T3_REG]);
/*************************************
** Pressure related coefficients **
*************************************/
_P1 = (uint16_t)(CONCAT_BYTES(coeff_arr1[BME680_P1_MSB_REG], coeff_arr1[BME680_P1_LSB_REG]));
_P2 = (int16_t)(CONCAT_BYTES(coeff_arr1[BME680_P2_MSB_REG], coeff_arr1[BME680_P2_LSB_REG]));
_P3 = (int8_t)coeff_arr1[BME680_P3_REG];
_P4 = (int16_t)(CONCAT_BYTES(coeff_arr1[BME680_P4_MSB_REG], coeff_arr1[BME680_P4_LSB_REG]));
_P5 = (int16_t)(CONCAT_BYTES(coeff_arr1[BME680_P5_MSB_REG], coeff_arr1[BME680_P5_LSB_REG]));
_P6 = (int8_t)(coeff_arr1[BME680_P6_REG]);
_P7 = (int8_t)(coeff_arr1[BME680_P7_REG]);
_P8 = (int16_t)(CONCAT_BYTES(coeff_arr1[BME680_P8_MSB_REG], coeff_arr1[BME680_P8_LSB_REG]));
_P9 = (int16_t)(CONCAT_BYTES(coeff_arr1[BME680_P9_MSB_REG], coeff_arr1[BME680_P9_LSB_REG]));
_P10 = (uint8_t)(coeff_arr1[BME680_P10_REG]);
/**********************************
** Humidity related coefficients **
**********************************/
_H1 = (uint16_t)(
((uint16_t)coeff_arr2[BME680_H1_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) |
((coeff_arr2[BME680_H1_LSB_REG] >> BME680_HUM_REG_SHIFT_VAL) & BME680_BIT_H1_DATA_MSK));
_H2 = (uint16_t)(
((uint16_t)coeff_arr2[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) |
((coeff_arr2[BME680_H2_LSB_REG] >> BME680_HUM_REG_SHIFT_VAL) & BME680_BIT_H1_DATA_MSK));
_H3 = (int8_t)coeff_arr2[BME680_H3_REG];
_H4 = (int8_t)coeff_arr2[BME680_H4_REG];
_H5 = (int8_t)coeff_arr2[BME680_H5_REG];
_H6 = (uint8_t)coeff_arr2[BME680_H6_REG];
_H7 = (int8_t)coeff_arr2[BME680_H7_REG];
/************************************
** Gas heater related coefficients **
************************************/
_G1 = (int8_t)coeff_arr2[BME680_GH1_REG];
_G2 = (int16_t)(CONCAT_BYTES(coeff_arr2[BME680_GH2_MSB_REG], coeff_arr2[BME680_GH2_LSB_REG]));
_G3 = (int8_t)coeff_arr2[BME680_GH3_REG];
uint8_t temp_var = 0;
getData(BME680_ADDR_RES_HEAT_RANGE_ADDR, temp_var);
_res_heat_range = ((temp_var & BME680_RHRANGE_MSK) / 16);
getData(BME680_ADDR_RES_HEAT_VAL_ADDR, temp_var);
_res_heat = (int8_t)temp_var;
getData(BME680_ADDR_RANGE_SW_ERR_ADDR, temp_var);
_rng_sw_err = ((int8_t)temp_var & (int8_t)BME680_RSERROR_MSK) / 16;
} // of method getCalibration()
uint8_t BME680_Class::setOversampling(const uint8_t sensor, const uint8_t sampling) const {
/*!
@brief sets the oversampling mode for the sensor
@details See enumerated "sensorTypes" for a list of values. Set to a valid oversampling rate as
defined in the enumerated type oversamplingTypes. If either value is out of range or
another error occurs then the return value is false.
param[in] sensor Enumerated sensor type
param[in] sampling Sampling rate from enumerated type
return "true" if successful otherwise false
*/
if (sensor >= UnknownSensor ||
(sampling != UINT8_MAX &&
sampling >= UnknownOversample)) { // return an error if sensor or oversample is out of range
return (UINT8_MAX);
}
uint8_t tempRegister; // Temporary register variable
uint8_t returnValue = sampling; // Return sampling value
waitForReadings(); // Ensure any active reading is finished
switch (sensor) { // Depending upon which sensor is chosen
case HumiditySensor: // Set the humidity oversampling
{
tempRegister = readByte(BME680_CONTROL_HUMIDITY_REGISTER); // Read the register contents
if (sampling == UINT8_MAX) { // If we just want to read values
returnValue = tempRegister & ~BME680_HUMIDITY_MASK; // Set return value
} else {
tempRegister &= BME680_HUMIDITY_MASK; // Mask bits to 0
tempRegister |= sampling; // Add in the sampling bits
putData(BME680_CONTROL_HUMIDITY_REGISTER,
(uint8_t)tempRegister); // Update humidity bits 0:2
} // if-then return current value or set new value
break;
} // of HumiditySensor
case PressureSensor: // Set the pressure oversampling
{
tempRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the register contents
if (sampling == UINT8_MAX) { // If we just want to read the values
returnValue = (tempRegister & ~BME680_PRESSURE_MASK) >> 2; // Set return value
} else {
tempRegister &= BME680_PRESSURE_MASK; // Mask bits to 0
tempRegister |= (sampling << 2); // Add in sampling bits at offset
putData(BME680_CONTROL_MEASURE_REGISTER, (uint8_t)tempRegister); // Update register
} // if-then return current value or set new value
break;
} // of PressureSensor
case TemperatureSensor: // Set the temperature oversampling
{
tempRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the register contents
if (sampling == UINT8_MAX) { // If we just want to read the values
returnValue = (tempRegister & ~BME680_TEMPERATURE_MASK) >> 5; // Set return value
} else {
tempRegister &= BME680_TEMPERATURE_MASK; // Mask bits to 0
tempRegister |= (sampling << 5); // Add in the sampling bits at offset
putData(BME680_CONTROL_MEASURE_REGISTER,
(uint8_t)tempRegister); // Update humidity bits 5:7
} // if-then return current value or set new value
break;
} // of TemperatureSensor
default:
return (UINT8_MAX); // Return an error if no match
} // of switch the sensor type
return (returnValue); // Otherwise return current value
} // of method setOversampling()
uint8_t BME680_Class::setIIRFilter(const uint8_t iirFilterSetting) const {
/*!
@brief Set or return the current IIR filter setting
@details When called with no parameters returns the current IIR Filter setting, otherwise
when called with one parameter will set the IIR filter value and return the new setting
param[in] iirFilterSetting New setting
return IIR Filter setting
*/
waitForReadings(); // Ensure any active reading is finished
uint8_t returnValue = readByte(BME680_CONFIG_REGISTER); // Get control register byte contents
if (iirFilterSetting != UINT8_MAX){ // If the value is to be changed
returnValue = returnValue & 0B11100011; // mask IIR bits
returnValue |= (iirFilterSetting & 0B00000111) << 2; // use 3 bits of iirFilterSetting
putData(BME680_CONFIG_REGISTER, returnValue); // Write new control register value
} // if the value is to be changed //
returnValue = (returnValue >> 2) & 0B00000111; // Extract IIR filter setting from register
return (returnValue); // Return IIR Filter setting
} // of method setIIRFilter()
uint8_t BME680_Class::getSensorData(int32_t& temp, int32_t& hum, int32_t& press, int32_t& gas,
const bool waitSwitch) {
/*!
@brief Returns the most recent temperature, humidity and pressure readings
param[out] temp Temperature reading
param[out] hum Humidity reading
param[out] press Pressure reading
param[out] gas Gas reading
param[in] waitSwitch (Optional) When set will not return until reading is finished
*/
uint8_t status = readSensors(waitSwitch); // Get compensated data from BME680
temp = _Temperature; // Copy global variables to parameters
hum = _Humidity; // Copy global variables to parameters
press = _Pressure; // Copy global variables to parameters
gas = _Gas; // Copy global variables to parameters
return (status); // Nonzero if gas is invalid or unstable
} // of method getSensorData()
uint8_t BME680_Class::getI2CAddress() const {
/*!
@brief Returns the I2C address of the BME680
return I2C Address from private variables
*/
return (_I2CAddress);
} // of method getI2CAddress()
uint8_t BME680_Class::readSensors(const bool waitSwitch) {
/*!
@brief reads all 4 sensor values from the registers in one operation and then proceeds to
convert the raw temperature, pressure & humidity readings into standard metric units
@details The formula is written in the BME680's documentation but the math used below was
taken from Adafruit's Adafruit_BME680_Library at
https://github.com/adafruit/Adafruit_BME680. I think it can be refactored into more
efficient code at some point in the future, but it does work correctly.
param[in] waitSwitch (Optional) When set will not return until reading is finished
*/
/*! Lookup table for the possible gas range values */
const uint32_t lookupTable1[16] = {
UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647),
UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2130303777),
UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2143188679), UINT32_C(2136746228),
UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2147483647)};
const uint32_t lookupTable2[16] = {
UINT32_C(4096000000), UINT32_C(2048000000), UINT32_C(1024000000), UINT32_C(512000000),
UINT32_C(255744255), UINT32_C(127110228), UINT32_C(64000000), UINT32_C(32258064),
UINT32_C(16016016), UINT32_C(8000000), UINT32_C(4000000), UINT32_C(2000000),
UINT32_C(1000000), UINT32_C(500000), UINT32_C(250000), UINT32_C(125000)};
uint8_t buff[15], gas_range = 0; // declare array for registers
int64_t var1, var2, var3, var4, var5, var6, temp_scaled; // Work variables
uint32_t adc_temp, adc_pres; // Raw ADC temperature and pressure
uint16_t adc_hum, adc_gas_res; // Raw ADC humidity and gas
if (waitSwitch) waitForReadings(); // Doesn't return until the readings are finished
getData(BME680_STATUS_REGISTER, buff); // read all 15 bytes in one go
adc_pres = (uint32_t)(((uint32_t)buff[2] << 12) | ((uint32_t)buff[3] << 4) |
((uint32_t)buff[4] >> 4)); // put the 3 bytes of Pressure
adc_temp = (uint32_t)(((uint32_t)buff[5] << 12) | ((uint32_t)buff[6] << 4) |
((uint32_t)buff[7] >> 4)); // put the 3 bytes of Temperature
adc_hum =
(uint16_t)(((uint32_t)buff[8] << 8) | (uint32_t)buff[9]); // put the 2 bytes of Humidity
adc_gas_res =
(uint16_t)((uint32_t)buff[13] << 2 | (((uint32_t)buff[14]) >> 6)); // put the 2 bytes of Gas
gas_range = buff[14] & 0X0F; // Retrieve the range
//*******************************//
// First compute the temperature //
//*******************************//
var1 = ((int32_t)adc_temp >> 3) - ((int32_t)_T1 << 1); // Perform calibration/adjustment
var2 = (var1 * (int32_t)_T2) >> 11; // of Temperature values according
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12; // to formula defined by Bosch
var3 = ((var3) * ((int32_t)_T3 << 4)) >> 14;
_tfine = (int32_t)(var2 + var3);
_Temperature = (int16_t)(((_tfine * 5) + 128) >> 8);
//*******************************//
// Now compute the pressure //
//*******************************//
var1 = (((int32_t)_tfine) >> 1) - 64000;
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * (int32_t)_P6) >> 2;
var2 = var2 + ((var1 * (int32_t)_P5) << 1);
var2 = (var2 >> 2) + ((int32_t)_P4 << 16);
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) * ((int32_t)_P3 << 5)) >> 3) +
(((int32_t)_P2 * var1) >> 1);
var1 = var1 >> 18;
var1 = ((32768 + var1) * (int32_t)_P1) >> 15;
_Pressure = 1048576 - adc_pres;
_Pressure = (int32_t)((_Pressure - (var2 >> 12)) * ((uint32_t)3125));
if (_Pressure >= INT32_C(0x40000000)) // Issue #26
_Pressure = ((_Pressure / (uint32_t)var1) << 1);
else
_Pressure = ((_Pressure << 1) / (uint32_t)var1);
var1 = ((int32_t)_P9 * (int32_t)(((_Pressure >> 3) * (_Pressure >> 3)) >> 13)) >> 12;
var2 = ((int32_t)(_Pressure >> 2) * (int32_t)_P8) >> 13;
var3 = ((int32_t)(_Pressure >> 8) * (int32_t)(_Pressure >> 8) * (int32_t)(_Pressure >> 8) *
(int32_t)_P10) >>
17;
_Pressure = (int32_t)(_Pressure) + ((var1 + var2 + var3 + ((int32_t)_P7 << 7)) >> 4);
//**********************//
// Compute the humidity //
//**********************//
temp_scaled = (((int32_t)_tfine * 5) + 128) >> 8;
var1 = (int32_t)(adc_hum - ((int32_t)((int32_t)_H1 << 4))) -
(((temp_scaled * (int32_t)_H3) / ((int32_t)100)) >> 1);
var2 =
((int32_t)_H2 *
(((temp_scaled * (int32_t)_H4) / ((int32_t)100)) +
(((temp_scaled * ((temp_scaled * (int32_t)_H5) / ((int32_t)100))) >> 6) / ((int32_t)100)) +
(int32_t)(1 << 14))) >>
10;
var3 = var1 * var2;
var4 = (int32_t)_H6 << 7;
var4 = ((var4) + ((temp_scaled * (int32_t)_H7) / ((int32_t)100))) >> 4;
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
var6 = (var4 * var5) >> 1;
_Humidity = (((var3 + var6) >> 10) * ((int32_t)1000)) >> 12;
if (_Humidity > 100000) /* Cap at 100%rH */
_Humidity = 100000;
else if (_Humidity < 0)
_Humidity = 0;
//**********************//
// Compute the Gas //
//**********************//
uint64_t uvar2;
var1 = (int64_t)((1340 + (5 * (int64_t)_rng_sw_err)) * ((int64_t)lookupTable1[gas_range])) >> 16;
uvar2 = (((int64_t)((int64_t)adc_gas_res << 15) - (int64_t)(16777216)) + var1);
var3 = (((int64_t)lookupTable2[gas_range] * (int64_t)var1) >> 9);
_Gas = (uint32_t)((var3 + ((int64_t)uvar2 >> 1)) / (int64_t)uvar2);
triggerMeasurement(); // trigger the next measurement
return (buff[14] & 0X30); // Return nonzero if gas or heat stabilization is invalid
} // of method readSensors()
void BME680_Class::waitForReadings() const {
/*!
@brief Only returns once a measurement on the BME680 has completed
*/
while (measuring()) {
} // loop until any active measurment is complete
} // of method waitForReadings
bool BME680_Class::setGas(uint16_t GasTemp, uint16_t GasMillis) const {
/*!
* @brief sets the gas measurement target temperature and heating time
* param[in] GasTemp Target temperature in Celsius
* param[in] GasMillis Milliseconds to turn on heater
* return Always returns "true"
*/
waitForReadings(); // Ensure any active reading is finished
uint8_t gasRegister = readByte(BME680_CONTROL_GAS_REGISTER2); // Read current register values
if (GasTemp == 0 || GasMillis == 0) {
// If either input variable is zero //
putData(BME680_CONTROL_GAS_REGISTER1, (uint8_t)0B00001000); // Turn off gas heater
putData(BME680_CONTROL_GAS_REGISTER2,
(uint8_t)(gasRegister & 0B11101111)); // Turn off gas measurements
} else {
putData(BME680_CONTROL_GAS_REGISTER1, (uint8_t)0); // Turn off heater bit to turn on
uint8_t heatr_res;
int32_t var1, var2, var3, var4, var5, heatr_res_x100;
if (GasTemp < 200)
GasTemp = 200;
else if (GasTemp > 400)
GasTemp = 400; // Clamp temperature to min/max
var1 = (((int32_t)(_Temperature / 100) * _H3) / 1000) << 8;
var2 = (_G1 + 784) * (((((_G2 + 154009) * GasTemp * 5) / 100) + 3276800) / 10); // Issue #26
var3 = var1 + (var2 / 2);
var4 = (var3 / (_res_heat_range + 4));
var5 = (131 * _res_heat) + 65536;
heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34);
heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100);
putData(BME680_GAS_HEATER_REGISTER0, heatr_res);
uint8_t factor = 0;
uint8_t durval;
if (GasMillis >= 0xfc0)
durval = 0xff; // Max duration
else {
while (GasMillis > 0x3F) {
GasMillis = GasMillis >> 2;
factor += 1;
} // of while loop
durval = (uint8_t)(GasMillis + (factor * 64));
} // of if-then-else duration exceeds max
putData(BME680_CONTROL_GAS_REGISTER1, (uint8_t)0);// then turn off gas heater
putData(BME680_GAS_DURATION_REGISTER0, durval);
putData(BME680_CONTROL_GAS_REGISTER2, (uint8_t)(gasRegister | 0B00010000));
} // of if-then-else turn gas measurements on or off
return true;
} // of method setGas()
bool BME680_Class::measuring() const {
/*!
* @brief Returns whether the BME680 is currently measuring
* return "true" if a measurement is active, otherwise "false"
*/
bool result(false);
if ((readByte(BME680_STATUS_REGISTER) & _BV(BME680_MEASURING_BIT_POSITION)) != 0) {
result = true;
} // if-then device is currently measuring
return result;
} // of method "measuring()"
void BME680_Class::triggerMeasurement() const {
/*!
* @brief Trigger a new measurement on the BME680
*/
uint8_t workRegister = readByte(BME680_CONTROL_MEASURE_REGISTER); // Read the control measure
putData(BME680_CONTROL_MEASURE_REGISTER,
(uint8_t)(workRegister | 1)); // Trigger start of next measurement
} // of method "triggerMeasurement()"