/*! @file Zanshin_BME680.h @mainpage Arduino Library to control a Bosch BME Sensor @section Zanshin_BME680_section Description Class definition header for the Bosch BME680 temperature / humidity / pressure sensor. The sensor is described at https://www.bosch-sensortec.com/bst/products/all_products/BME680 and the datasheet is available from Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf \n\n The BME680 can use either SPI or I2C for communications. This library allow I2C at various bus speeds as well as both standard Arduino hardware SPI and Software SPI.\n\n The most recent version of the library is available at https://github.com/SV-Zanshin/BME680 and extensive documentation of the library as well as example programs are described in the project's wiki pages located at https://github.com/SV-Zanshin/BME680/wiki. \n\n The BME680 is a very small package so it is unlikely for an Arduino hobbyist to play around with directly, the hardware used to develop this library is a breakout board from AdaFruit which is well-documented at https://www.adafruit.com/product/3660. I purchased a https://www.bluedot.space/sensor-boards/bme680/ as I couldn't get a local adafruit board. @section license License This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . @section author Author Written by Arnd\@SV-Zanshin @section versions Changelog Version | Date | Developer | Comments ------- | ---------- | ----------------------------- | -------- 1.0.2 | 2019-01-26 | https://github.com/SV-Zanshin | Issue #3 - Converted documentation to doxygen style 1.0.1 | 2018-07-22 | https://github.com/SV-Zanshin | Corrected I2C datatypes 1.0.1 | 2018-07-03 | https://github.com/SV-Zanshin | Issue #1. Added waitForReading and parameter to getSensorData() 1.0.0 | 2018-07-02 | https://github.com/SV-Zanshin | Added guard code against multiple I2C constants definitions 1.0.0 | 2018-07-01 | https://github.com/SV-Zanshin | Added and tested I2C, SPI and software SPI connections 1.0.0a | 2018-06-30 | https://github.com/SV-Zanshin | Cloned from BME280 library and started recoding */ #include "Arduino.h" // Arduino data type definitions #include // Standard I2C "Wire" library #include // Standard SPI library #ifndef BME680_h /** @brief Guard code definition for the library header*/ #define BME680_h #define CONCAT_BYTES(msb, lsb) (((uint16_t)msb << 8) | (uint16_t)lsb) ///< Inline to combine msb and lsb /***************************************************************************************************************** ** Declare constants used in the class ** *****************************************************************************************************************/ #ifndef I2C_MODES /** @brief Guard code definition for the I2C modes */ #define I2C_MODES const uint32_t I2C_STANDARD_MODE = 100000; ///< Default normal I2C 100KHz speed const uint32_t I2C_FAST_MODE = 400000; ///< Fast mode const uint32_t I2C_FAST_MODE_PLUS = 1000000; ///< Really fast mode const uint32_t I2C_HIGH_SPEED_MODE = 3400000; ///< Turbo mode #endif const uint32_t SPI_HERTZ = 500000; ///< SPI speed in Hz 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 /***************************************************************************************************************** ** Declare enumerated types used in the class ** *****************************************************************************************************************/ /*! @brief Enumerate the sensor type */ enum sensorTypes {TemperatureSensor,HumiditySensor,PressureSensor,GasSensor,UnknownSensor}; /*! @brief Enumerate the Oversampling types */ enum oversamplingTypes {SensorOff,Oversample1,Oversample2,Oversample4,Oversample8,Oversample16,UnknownOversample }; /*! @brief Enumerate the iir filter types */ enum iirFilterTypes {IIROff,IIR2,IIR4,IIR8,IIR16,IIR32,IIR64,IIR128,UnknownIIR }; /*! * @class BME680_Class * @brief Main BME680 class for the temperature / humidity / pressure sensor */ class BME680_Class { public: BME680_Class(); ~BME680_Class(); bool begin(); // Start using I2C Communications // bool begin(const uint32_t i2cSpeed); // I2C with a non-default speed // bool begin(const uint8_t chipSelect); // Start using hardware SPI // bool begin(const uint8_t chipSelect, const uint8_t mosi, // Start using software SPI // const uint8_t miso, const uint8_t sck); // // bool setOversampling(const uint8_t sensor, const uint8_t sampling); // Set enum sensorType Oversampling // bool setGas(uint16_t GasTemp, uint16_t GasMillis); // Gas heating temperature and time // uint8_t setIIRFilter(const uint8_t iirFilterSetting=UINT8_MAX); // Set IIR Filter and return value // void getSensorData(int32_t &temp, int32_t &temp1, int32_t &hum, // get most recent readings // int32_t &press, int32_t &gas, // // const bool waitSwitch = true); // // void reset(); // Reset the BME680 // private: bool commonInitialization(); ///< Common initialization code uint8_t readByte(const uint8_t addr); ///< Read byte from register address void readSensors(const bool waitSwitch); ///< read the registers in one burst void waitForReadings(); ///< Wait for readings to finish void getCalibration(); ///< Load calibration from registers uint8_t _I2CAddress = 0; ///< Default is no I2C address known uint8_t _cs,_sck,_mosi,_miso; ///< Hardware and software SPI pins uint8_t _H6,_P10,_res_heat_range; ///< unsigned configuration variables int8_t _H3,_H4,_H5,_H7,_G1,_G3,_T3,_P3,_P6,_P7,_res_heat_val,_range_sw_error; ///< signed configuration variables uint16_t _H1,_H2,_T1,_P1; ///< unsigned 16bit configuration variables int16_t _G2,_T2,_P2,_P4,_P5,_P8,_P9; ///< signed 16bit configuration variables int32_t _tfine,_Temperature,_Pressure,_Humidity,_Gas; ///< signed 32bit configuratio variables uint32_t adc_temp, adc_pres; uint16_t adc_hum, adc_gas_res; /************************************************************************************************************* ** Declare the getData and putData methods as template functions. All device I/O is done through these two ** ** functions regardless of whether I2C, hardware SPI or software SPI is being used. The two functions are ** ** designed so that only the address and a variable are passed in and the functions determine the size of ** ** the parameter variable and reads or writes that many bytes. So if a read is called using a character ** ** array[10] then 10 bytes are read, if called with a int8 then only one byte is read. The return value, if ** ** used, is the number of bytes read or written ** ** This is done by using template function definitions which need to be defined in this header file rather ** ** than in the c++ program library file. ** *************************************************************************************************************/ /*! @brief Template for reading from I2C or SPI using any data type @details As a template it can support compile-time data type definitions @param[in] addr Memory address @param[in] value Data Type "T" to read @return Size of data read in bytes */ template< typename T > uint8_t &getData(const uint8_t addr,T &value) { uint8_t* bytePtr = (uint8_t*)&value; // Pointer to structure beginning static uint8_t structSize = sizeof(T); // Number of bytes in structure if (_I2CAddress) { // Using I2C if address is non-zero Wire.beginTransmission(_I2CAddress); // Address the I2C device Wire.write(addr); // Send register address to read Wire.endTransmission(); // Close transmission Wire.requestFrom(_I2CAddress, sizeof(T)); // Request 1 byte of data structSize = Wire.available(); // Use the actual number of bytes for (uint8_t i=0;i=0; j--) { // First send the address byte digitalWrite(_sck, LOW); // set the clock signal digitalWrite(_mosi, ((addr)|0x80)&(1<=0; j--) { // Now read the data at that byte reply <<= 1; // shift buffer one bit left digitalWrite(_sck, LOW); // set and reset the clock signal digitalWrite(_sck, HIGH); // pin to get the next MISO bit if (digitalRead(_miso)) reply |= 1; // read the MISO bit, add to reply } // of for-next each bit *bytePtr++ = reply; // Add byte just read to return data } // of for-next each byte to be read digitalWrite(_cs, HIGH); // Tell BME680 to stop listening } // of if-then-else we are using hardware SPI } // of if-then-else we are using I2C return(structSize); } // of method getData() /*! @brief Template for writing to I2C or SPI using any data type @details As a template it can support compile-time data type definitions @param[in] addr Memory address @param[in] value Data Type "T" to write @return Size of data written in bytes */ templateuint8_t &putData(const uint8_t addr,const T &value) { const uint8_t* bytePtr = (const uint8_t*)&value; // Pointer to structure beginning static uint8_t structSize = sizeof(T); // Number of bytes in structure if (_I2CAddress) { // Using I2C if address is non-zero Wire.beginTransmission(_I2CAddress); // Address the I2C device Wire.write(addr); // Send register address to write for (uint8_t i=0;i=0; j--) { // First send the address byte digitalWrite(_sck, LOW); // set the clock signal digitalWrite(_mosi, (addr&~0x80)&(1<=0; j--) { // Now read the data at that byte reply <<= 1; // shift buffer one bit left digitalWrite(_sck, LOW); // set the clock signal digitalWrite(_mosi, *bytePtr&(1<