Files
OpenMQTT-Gateway/lib/SparkFun_BME280/src/SparkFunBME280.cpp
T

401 lines
13 KiB
C++

/******************************************************************************
SparkFunBME280.cpp
BME280 Arduino and Teensy Driver
Marshall Taylor @ SparkFun Electronics
May 20, 2015
https://github.com/sparkfun/BME280_Breakout
Resources:
Uses Wire.h for i2c operation
Uses SPI.h for SPI operation
Development environment specifics:
Arduino IDE 1.6.4
Teensy loader 1.23
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
Please review the LICENSE.md file included with this example. If you have any questions
or concerns with licensing, please contact techsupport@sparkfun.com.
Distributed as-is; no warranty is given.
******************************************************************************/
//See SparkFunBME280.h for additional topology notes.
#include "SparkFunBME280.h"
#include "stdint.h"
#include <math.h>
#include <Arduino.h>
#include "Wire.h"
#include "SPI.h"
//****************************************************************************//
//
// Settings and configuration
//
//****************************************************************************//
//Constructor -- Specifies default configuration
BME280::BME280( void )
{
//Construct with these default settings if nothing is specified
//Select interface mode
settings.commInterface = I2C_MODE; //Can be I2C_MODE, SPI_MODE
//Select address for I2C. Does nothing for SPI
settings.I2CAddress = 0x77; //Ignored for SPI_MODE
//Select CS pin for SPI. Does nothing for I2C
settings.chipSelectPin = 10;
settings.runMode = 0;
settings.tempOverSample = 0;
settings.pressOverSample = 0;
settings.humidOverSample = 0;
}
//****************************************************************************//
//
// Configuration section
//
// This uses the stored SensorSettings to start the IMU
// Use statements such as "mySensor.settings.commInterface = SPI_MODE;" to
// configure before calling .begin();
//
//****************************************************************************//
uint8_t BME280::begin()
{
//Check the settings structure values to determine how to setup the device
uint8_t dataToWrite = 0; //Temporary variable
switch (settings.commInterface)
{
case I2C_MODE:
Wire.begin();
break;
case SPI_MODE:
// start the SPI library:
SPI.begin();
#ifdef ARDUINO_ARCH_ESP32
SPI.setFrequency(1000000);
// Data is read and written MSb first.
SPI.setBitOrder(SPI_MSBFIRST);
// Like the standard arduino/teensy comment below, mode0 seems wrong according to standards
// but conforms to the timing diagrams when used for the ESP32
SPI.setDataMode(SPI_MODE0);
#else
// Maximum SPI frequency is 10MHz, could divide by 2 here:
SPI.setClockDivider(SPI_CLOCK_DIV32);
// Data is read and written MSb first.
SPI.setBitOrder(MSBFIRST);
// Data is captured on rising edge of clock (CPHA = 0)
// Base value of the clock is HIGH (CPOL = 1)
// This was SPI_MODE3 for RedBoard, but I had to change to
// MODE0 for Teensy 3.1 operation
SPI.setDataMode(SPI_MODE3);
#endif
// initalize the data ready and chip select pins:
pinMode(settings.chipSelectPin, OUTPUT);
digitalWrite(settings.chipSelectPin, HIGH);
break;
default:
break;
}
//Reading all compensation data, range 0x88:A1, 0xE1:E7
calibration.dig_T1 = ((uint16_t)((readRegister(BME280_DIG_T1_MSB_REG) << 8) + readRegister(BME280_DIG_T1_LSB_REG)));
calibration.dig_T2 = ((int16_t)((readRegister(BME280_DIG_T2_MSB_REG) << 8) + readRegister(BME280_DIG_T2_LSB_REG)));
calibration.dig_T3 = ((int16_t)((readRegister(BME280_DIG_T3_MSB_REG) << 8) + readRegister(BME280_DIG_T3_LSB_REG)));
calibration.dig_P1 = ((uint16_t)((readRegister(BME280_DIG_P1_MSB_REG) << 8) + readRegister(BME280_DIG_P1_LSB_REG)));
calibration.dig_P2 = ((int16_t)((readRegister(BME280_DIG_P2_MSB_REG) << 8) + readRegister(BME280_DIG_P2_LSB_REG)));
calibration.dig_P3 = ((int16_t)((readRegister(BME280_DIG_P3_MSB_REG) << 8) + readRegister(BME280_DIG_P3_LSB_REG)));
calibration.dig_P4 = ((int16_t)((readRegister(BME280_DIG_P4_MSB_REG) << 8) + readRegister(BME280_DIG_P4_LSB_REG)));
calibration.dig_P5 = ((int16_t)((readRegister(BME280_DIG_P5_MSB_REG) << 8) + readRegister(BME280_DIG_P5_LSB_REG)));
calibration.dig_P6 = ((int16_t)((readRegister(BME280_DIG_P6_MSB_REG) << 8) + readRegister(BME280_DIG_P6_LSB_REG)));
calibration.dig_P7 = ((int16_t)((readRegister(BME280_DIG_P7_MSB_REG) << 8) + readRegister(BME280_DIG_P7_LSB_REG)));
calibration.dig_P8 = ((int16_t)((readRegister(BME280_DIG_P8_MSB_REG) << 8) + readRegister(BME280_DIG_P8_LSB_REG)));
calibration.dig_P9 = ((int16_t)((readRegister(BME280_DIG_P9_MSB_REG) << 8) + readRegister(BME280_DIG_P9_LSB_REG)));
calibration.dig_H1 = ((uint8_t)(readRegister(BME280_DIG_H1_REG)));
calibration.dig_H2 = ((int16_t)((readRegister(BME280_DIG_H2_MSB_REG) << 8) + readRegister(BME280_DIG_H2_LSB_REG)));
calibration.dig_H3 = ((uint8_t)(readRegister(BME280_DIG_H3_REG)));
calibration.dig_H4 = ((int16_t)((readRegister(BME280_DIG_H4_MSB_REG) << 4) + (readRegister(BME280_DIG_H4_LSB_REG) & 0x0F)));
calibration.dig_H5 = ((int16_t)((readRegister(BME280_DIG_H5_MSB_REG) << 4) + ((readRegister(BME280_DIG_H4_LSB_REG) >> 4) & 0x0F)));
calibration.dig_H6 = ((uint8_t)readRegister(BME280_DIG_H6_REG));
//Set the oversampling control words.
//config will only be writeable in sleep mode, so first insure that.
writeRegister(BME280_CTRL_MEAS_REG, 0x00);
//Set the config word
dataToWrite = (settings.tStandby << 0x5) & 0xE0;
dataToWrite |= (settings.filter << 0x02) & 0x1C;
writeRegister(BME280_CONFIG_REG, dataToWrite);
//Set ctrl_hum first, then ctrl_meas to activate ctrl_hum
dataToWrite = settings.humidOverSample & 0x07; //all other bits can be ignored
writeRegister(BME280_CTRL_HUMIDITY_REG, dataToWrite);
//set ctrl_meas
//First, set temp oversampling
dataToWrite = (settings.tempOverSample << 0x5) & 0xE0;
//Next, pressure oversampling
dataToWrite |= (settings.pressOverSample << 0x02) & 0x1C;
//Last, set mode
dataToWrite |= (settings.runMode) & 0x03;
//Load the byte
writeRegister(BME280_CTRL_MEAS_REG, dataToWrite);
return readRegister(0xD0);
}
//Strictly resets. Run .begin() afterwards
void BME280::reset( void )
{
writeRegister(BME280_RST_REG, 0xB6);
}
//****************************************************************************//
//
// Pressure Section
//
//****************************************************************************//
float BME280::readFloatPressure( void )
{
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
int32_t adc_P = ((uint32_t)readRegister(BME280_PRESSURE_MSB_REG) << 12) | ((uint32_t)readRegister(BME280_PRESSURE_LSB_REG) << 4) | ((readRegister(BME280_PRESSURE_XLSB_REG) >> 4) & 0x0F);
int64_t var1, var2, p_acc;
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)calibration.dig_P6;
var2 = var2 + ((var1 * (int64_t)calibration.dig_P5)<<17);
var2 = var2 + (((int64_t)calibration.dig_P4)<<35);
var1 = ((var1 * var1 * (int64_t)calibration.dig_P3)>>8) + ((var1 * (int64_t)calibration.dig_P2)<<12);
var1 = (((((int64_t)1)<<47)+var1))*((int64_t)calibration.dig_P1)>>33;
if (var1 == 0)
{
return 0; // avoid exception caused by division by zero
}
p_acc = 1048576 - adc_P;
p_acc = (((p_acc<<31) - var2)*3125)/var1;
var1 = (((int64_t)calibration.dig_P9) * (p_acc>>13) * (p_acc>>13)) >> 25;
var2 = (((int64_t)calibration.dig_P8) * p_acc) >> 19;
p_acc = ((p_acc + var1 + var2) >> 8) + (((int64_t)calibration.dig_P7)<<4);
return (float)p_acc / 256.0;
}
float BME280::readFloatAltitudeMeters( void )
{
float heightOutput = 0;
heightOutput = ((float)-45846.2)*(pow(((float)readFloatPressure()/(float)101325), 0.190263) - (float)1);
return heightOutput;
}
float BME280::readFloatAltitudeFeet( void )
{
float heightOutput = 0;
heightOutput = readFloatAltitudeMeters() * 3.28084;
return heightOutput;
}
//****************************************************************************//
//
// Humidity Section
//
//****************************************************************************//
float BME280::readFloatHumidity( void )
{
// Returns humidity in %RH as unsigned 32 bit integer in Q22. 10 format (22 integer and 10 fractional bits).
// Output value of “47445” represents 47445/1024 = 46. 333 %RH
int32_t adc_H = ((uint32_t)readRegister(BME280_HUMIDITY_MSB_REG) << 8) | ((uint32_t)readRegister(BME280_HUMIDITY_LSB_REG));
int32_t var1;
var1 = (t_fine - ((int32_t)76800));
var1 = (((((adc_H << 14) - (((int32_t)calibration.dig_H4) << 20) - (((int32_t)calibration.dig_H5) * var1)) +
((int32_t)16384)) >> 15) * (((((((var1 * ((int32_t)calibration.dig_H6)) >> 10) * (((var1 * ((int32_t)calibration.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
((int32_t)calibration.dig_H2) + 8192) >> 14));
var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)calibration.dig_H1)) >> 4));
var1 = (var1 < 0 ? 0 : var1);
var1 = (var1 > 419430400 ? 419430400 : var1);
return (float)(var1>>12) / 1024.0;
}
//****************************************************************************//
//
// Temperature Section
//
//****************************************************************************//
float BME280::readTempC( void )
{
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
//get the reading (adc_T);
int32_t adc_T = ((uint32_t)readRegister(BME280_TEMPERATURE_MSB_REG) << 12) | ((uint32_t)readRegister(BME280_TEMPERATURE_LSB_REG) << 4) | ((readRegister(BME280_TEMPERATURE_XLSB_REG) >> 4) & 0x0F);
//By datasheet, calibrate
int64_t var1, var2;
var1 = ((((adc_T>>3) - ((int32_t)calibration.dig_T1<<1))) * ((int32_t)calibration.dig_T2)) >> 11;
var2 = (((((adc_T>>4) - ((int32_t)calibration.dig_T1)) * ((adc_T>>4) - ((int32_t)calibration.dig_T1))) >> 12) *
((int32_t)calibration.dig_T3)) >> 14;
t_fine = var1 + var2;
float output = (t_fine * 5 + 128) >> 8;
output = output / 100;
return output;
}
float BME280::readTempF( void )
{
float output = readTempC();
output = (output * 9) / 5 + 32;
return output;
}
//****************************************************************************//
//
// Utility
//
//****************************************************************************//
void BME280::readRegisterRegion(uint8_t *outputPointer , uint8_t offset, uint8_t length)
{
//define pointer that will point to the external space
uint8_t i = 0;
char c = 0;
switch (settings.commInterface)
{
case I2C_MODE:
Wire.beginTransmission(settings.I2CAddress);
Wire.write(offset);
Wire.endTransmission();
// request bytes from slave device
Wire.requestFrom(settings.I2CAddress, length);
while ( (Wire.available()) && (i < length)) // slave may send less than requested
{
c = Wire.read(); // receive a byte as character
*outputPointer = c;
outputPointer++;
i++;
}
break;
case SPI_MODE:
// take the chip select low to select the device:
digitalWrite(settings.chipSelectPin, LOW);
// send the device the register you want to read:
SPI.transfer(offset | 0x80); //Ored with "read request" bit
while ( i < length ) // slave may send less than requested
{
c = SPI.transfer(0x00); // receive a byte as character
*outputPointer = c;
outputPointer++;
i++;
}
// take the chip select high to de-select:
digitalWrite(settings.chipSelectPin, HIGH);
break;
default:
break;
}
}
uint8_t BME280::readRegister(uint8_t offset)
{
//Return value
uint8_t result;
uint8_t numBytes = 1;
switch (settings.commInterface) {
case I2C_MODE:
Wire.beginTransmission(settings.I2CAddress);
Wire.write(offset);
Wire.endTransmission();
Wire.requestFrom(settings.I2CAddress, numBytes);
while ( Wire.available() ) // slave may send less than requested
{
result = Wire.read(); // receive a byte as a proper uint8_t
}
break;
case SPI_MODE:
// take the chip select low to select the device:
digitalWrite(settings.chipSelectPin, LOW);
// send the device the register you want to read:
SPI.transfer(offset | 0x80); //Ored with "read request" bit
// send a value of 0 to read the first byte returned:
result = SPI.transfer(0x00);
// take the chip select high to de-select:
digitalWrite(settings.chipSelectPin, HIGH);
break;
default:
break;
}
return result;
}
int16_t BME280::readRegisterInt16( uint8_t offset )
{
uint8_t myBuffer[2];
readRegisterRegion(myBuffer, offset, 2); //Does memory transfer
int16_t output = (int16_t)myBuffer[0] | int16_t(myBuffer[1] << 8);
return output;
}
void BME280::writeRegister(uint8_t offset, uint8_t dataToWrite)
{
switch (settings.commInterface)
{
case I2C_MODE:
//Write the byte
Wire.beginTransmission(settings.I2CAddress);
Wire.write(offset);
Wire.write(dataToWrite);
Wire.endTransmission();
break;
case SPI_MODE:
// take the chip select low to select the device:
digitalWrite(settings.chipSelectPin, LOW);
// send the device the register you want to read:
SPI.transfer(offset & 0x7F);
// send a value of 0 to read the first byte returned:
SPI.transfer(dataToWrite);
// decrement the number of bytes left to read:
// take the chip select high to de-select:
digitalWrite(settings.chipSelectPin, HIGH);
break;
default:
break;
}
}