diff --git a/BME280.cpp b/BME280.cpp index ffcc581..ed8f513 100644 --- a/BME280.cpp +++ b/BME280.cpp @@ -10,143 +10,104 @@ ** ** *******************************************************************************************************************/ #include "BME280.h" // Include the header definition // - // // + //----------------------------------// +BME280_Class::BME280_Class() {} // Empty & unused class constructor // +BME280_Class::~BME280_Class() {} // Empty & unused class destructor // /******************************************************************************************************************* -** Class Constructor instantiates the class ** +** Method begin starts communications with the device. It is overloaded to allow for 3 different connection types ** +** to be used - I2C, Hardware SPI and Software SPI. When called with no parameters the I2C mode is enabled and ** +** the I2C bus is scanned for the first BME280 (typically at 0x76 or 0x77 unless an I2C expander is used to remap ** +** the address. ** *******************************************************************************************************************/ -BME280_Class::BME280_Class() {} // of class constructor // // -/******************************************************************************************************************* -** Class Destructor currently does nothing and is included for compatibility purposes ** -*******************************************************************************************************************/ -BME280_Class::~BME280_Class() {} // of class destructor // // -/******************************************************************************************************************* -** Method begin starts I2C communications with the device, using a default address if one is not specified and ** -** return true if the device has been detected and false if it was not ** -*******************************************************************************************************************/ -bool BME280_Class::begin(const uint8_t I2CAddress ) { // Start I2C communications // +bool BME280_Class::begin() { // Find I2C device // Wire.begin(); // Start I2C as master device // - _I2CAddress = I2CAddress; // Store actual I2C address to use // - Wire.beginTransmission(_I2CAddress); // Address the BME280 // - delay(BME280_I2C_DELAY); // Give the BME280 time to process // - uint8_t errorCode = Wire.endTransmission(); // See if there's a device present // - if (errorCode == 0) { // If we have a device at address, // - if (readByte(BME280_CHIPID_REG)==BME280_CHIPID) { // and it returns correct chip id, // - _cal_dig_T1 = readWordLE(BME280_T1_REG); // all of the calibration values // - _cal_dig_T2 = readWordLE(BME280_T2_REG); // so that computations can be done // - _cal_dig_T3 = readWordLE(BME280_T3_REG); // on readings to calibrate them // - _cal_dig_P1 = readWordLE(BME280_P1_REG); // // - _cal_dig_P2 = readWordLE(BME280_P2_REG); // // - _cal_dig_P3 = readWordLE(BME280_P3_REG); // // - _cal_dig_P4 = readWordLE(BME280_P4_REG); // // - _cal_dig_P5 = readWordLE(BME280_P5_REG); // // - _cal_dig_P6 = readWordLE(BME280_P6_REG); // // - _cal_dig_P7 = readWordLE(BME280_P7_REG); // // - _cal_dig_P8 = readWordLE(BME280_P8_REG); // // - _cal_dig_P9 = readWordLE(BME280_P9_REG); // // - _cal_dig_H1 = readByte(BME280_H1_REG); // // - _cal_dig_H2 = readWordLE(BME280_H2_REG); // // - _cal_dig_H3 = readByte(BME280_H3_REG); // // - _cal_dig_H4 = (readByte(BME280_H4_REG)<<4)| // // - (readByte(BME280_H4_REG+1)&0xF); // // - _cal_dig_H5 = (readByte(BME280_H5_REG+1)<<4)| // // - (readByte(BME280_H5_REG)>>4); // // - _cal_dig_H6 = readByte(BME280_H6_REG); // // - return true; // return success // - } // of if-then device is really a BME280 // // - } // of if-then device detected // // + for(_I2CAddress=0;_I2CAddress<127;_I2CAddress++) { // loop all possible addresses // + Wire.beginTransmission(_I2CAddress); // Check current address for BME280 // + if (Wire.endTransmission()==0) { // If no error we have a device // + if (readByte(BME280_CHIPID_REG)==BME280_CHIPID) { // check for correct chip id // + getCalibration(); // get the calibration values // + return true; // return success // + } // of if-then device is really a BME280 // // + } // of if-then we have found a device // // + } // of for-next each I2C address loop // // + _I2CAddress = 0; // Set to 0 to denote no I2C found // + return false; // return failure if we get here // +} // of method begin() // // +bool BME280_Class::begin(const uint8_t chipSelect) { // Use hardware SPI for comms // + _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 // + if (readByte(BME280_CHIPID_REG)==BME280_CHIPID) { // check for correct chip id // + getCalibration(); // get the calibration values // + return true; // return success // + } // of if-then device is really a BME280 // // + return false; // return failure if we get here // +} // of method begin() // // +bool BME280_Class::begin(const uint8_t chipSelect, const uint8_t mosi, // Start using software SPI // + const uint8_t miso, const uint8_t sck) { // // + _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 // + if (readByte(BME280_CHIPID_REG)==BME280_CHIPID) { // check for correct chip id // + getCalibration(); // get the calibration values // + return true; // return success // + } // of if-then device is really a BME280 // // return false; // return failure if we get here // } // of method begin() // // /******************************************************************************************************************* -** Method readByte reads 1 byte from the specified address ** -*******************************************************************************************************************/ -uint8_t BME280_Class::readByte(const uint8_t addr) { // // - Wire.beginTransmission(_I2CAddress); // Address the I2C device // - Wire.write(addr); // Send the register address to read// - _TransmissionStatus = Wire.endTransmission(); // Close transmission // - delayMicroseconds(BME280_I2C_DELAY); // delay required for sync // - Wire.requestFrom(_I2CAddress, (uint8_t)1); // Request 1 byte of data // - while(!Wire.available()); // Wait until the byte is ready // - return Wire.read(); // read it and return it // -} // of method readByte() // // -/******************************************************************************************************************* -** Method writeByte write 1 byte to the specified address ** -*******************************************************************************************************************/ -void BME280_Class::writeByte(const uint8_t addr, const uint8_t data) { // // - while(readByte(BME280_STATUS_REG)&B00001001!=0); // wait for completed measurement // - Wire.beginTransmission(_I2CAddress); // Address the I2C device // - Wire.write(addr); // Send the register address to read// - Wire.write(data); // Send the register address to read// - _TransmissionStatus = Wire.endTransmission(); // Close transmission // -} // of method writeByte() // // -/******************************************************************************************************************* -** Method readWord reads 1 word (2 bytes) from the specified address ** -*******************************************************************************************************************/ -uint16_t BME280_Class::readWord(const uint8_t addr) { // // - Wire.beginTransmission(_I2CAddress); // Address the I2C device // - Wire.write(addr); // Send the register address to read// - _TransmissionStatus = Wire.endTransmission(); // Close transmission // - delayMicroseconds(BME280_I2C_DELAY); // delay required for sync // - Wire.requestFrom(_I2CAddress, (uint8_t)2); // Request 2 bytes of data // - while(!Wire.available()); // Wait until the byte is ready // - uint16_t returnByte = Wire.read()<<8 | Wire.read(); // read it and return it // - return(returnByte); // return the value // -} // of method readWord() // // -/******************************************************************************************************************* -** Method readWordLE reads 1 word (2 bytes) from the specified address in little-endian format ** -*******************************************************************************************************************/ -uint16_t BME280_Class::readWordLE(const uint8_t addr) { // // - Wire.beginTransmission(_I2CAddress); // Address the I2C device // - Wire.write(addr); // Send the register address to read// - _TransmissionStatus = Wire.endTransmission(); // Close transmission // - delayMicroseconds(BME280_I2C_DELAY); // delay required for sync // - Wire.requestFrom(_I2CAddress, (uint8_t)2); // Request 2 bytes of data // - while(!Wire.available()); // Wait until the byte is ready // - uint16_t returnByte = Wire.read() | Wire.read()<<8; // read it and return it // - return(returnByte); // return the value // -} // of method readWord() // // -/******************************************************************************************************************* -** Method writeWord writes 2 bytes to the specified address ** -*******************************************************************************************************************/ -void BME280_Class::writeWord(const uint8_t addr, const uint16_t data) { // // - while(readByte(BME280_STATUS_REG)&B00001001!=0); // wait for completed measurement // - Wire.beginTransmission(_I2CAddress); // Address the I2C device // - Wire.write(addr); // Send the register address to read// - Wire.write((uint8_t)data>>8); // Send the register address to read// - Wire.write((uint8_t)data); // Send the register address to read// - _TransmissionStatus = Wire.endTransmission(); // Close transmission // -} // of method writeWord() // // -/******************************************************************************************************************* -** Method readI2C reads n-bytes from the specified address ** +** Method getCalibration reads the calibration register data into local variables for use in converting readings ** *******************************************************************************************************************/ -uint8_t BME280_Class::readI2C(const uint8_t addr, uint8_t *pdata, // // - const uint8_t bytesToRead) { // // - Wire.beginTransmission(_I2CAddress); // Address the I2C device // - Wire.write(addr); // Send the register address to read// - _TransmissionStatus = Wire.endTransmission(); // Close transmission // - delayMicroseconds(BME280_I2C_DELAY); // delay required for sync // - Wire.requestFrom(_I2CAddress, (uint8_t)bytesToRead); // Request 1 byte of data // - while(!Wire.available()); // Wait until the first byte ready // - uint8_t i = bytesToRead; // loop index // - while (i--) { // post-decrement the counter // - pdata[0] = Wire.read(); // Read byte to pointer address // - pdata++; // increment pointer to next address// - } // while we still have bytes to write // // - _TransmissionStatus = Wire.endTransmission(); // Close transmission // -} // of method readI2C() // // +void BME280_Class::getCalibration() { // Read and store registers // + getData(BME280_T1_REG,_cal_dig_T1); // Retrieve calibration values // + getData(BME280_T2_REG,_cal_dig_T2); // // + getData(BME280_T3_REG,_cal_dig_T3); // // + getData(BME280_P1_REG,_cal_dig_P1); // // + getData(BME280_P2_REG,_cal_dig_P2); // // + getData(BME280_P3_REG,_cal_dig_P3); // // + getData(BME280_P4_REG,_cal_dig_P4); // // + getData(BME280_P5_REG,_cal_dig_P5); // // + getData(BME280_P6_REG,_cal_dig_P6); // // + getData(BME280_P7_REG,_cal_dig_P7); // // + getData(BME280_P8_REG,_cal_dig_P8); // // + getData(BME280_P9_REG,_cal_dig_P9); // // + getData(BME280_H1_REG,_cal_dig_H1); // // + getData(BME280_H2_REG,_cal_dig_H2); // // + getData(BME280_H3_REG,_cal_dig_P3); // // + uint8_t tempVar; // Single-Byte temporary variable // + getData(BME280_H4_REG,tempVar); // Retrieve byte // + _cal_dig_H4 = tempVar<<4; // // + getData(BME280_H4_REG+1,tempVar); // Retrieve byte // + _cal_dig_H4 |= tempVar&0xF; // // + getData(BME280_H5_REG+2,tempVar); // Retrieve byte // + _cal_dig_H5 = tempVar<<4; // // + getData(BME280_H5_REG,tempVar); // Retrieve byte // + _cal_dig_H5 |= tempVar>>4; // // + getData(BME280_H6_REG,_cal_dig_H6); // // + _cal_dig_H5 = (readByte(BME280_H5_REG+1)<<4)| // // + (readByte(BME280_H5_REG)>>4); // // + _cal_dig_H6 = readByte(BME280_H6_REG); // // +} // of method getCalibration() // // /******************************************************************************************************************* -** Method mode() returns the current mode when called with no parameters, otherwise it sets the mode and returns ** -** the mode that was set. ** +** Method readByte is an interlude function to the getData() function. Reads 1 byte from the given address ** *******************************************************************************************************************/ -uint8_t BME280_Class::mode() { // Return the mode // - uint8_t returnMode = readByte(BME280_CONTROL_REG) & B00000011; // Read the 2 mode bits // - return(returnMode); // Return the value read // -} // of method mode() // // +uint8_t BME280_Class::readByte(const uint8_t addr) { // // + uint8_t returnValue; // Storage for returned value // + getData(addr,returnValue); // Read just one byte // + return (returnValue); // Return byte just read // +} // of method readByte() // // /******************************************************************************************************************* -** Overloaded method mode() sets the current mode bits ** +** Method mode() sets the current mode bits or returns the current value if the parameter isn't used ** *******************************************************************************************************************/ uint8_t BME280_Class::mode(const uint8_t operatingMode) { // Set device mode // + uint8_t controlRegister = readByte(BME280_CONTROL_REG); // Get the control register // + if (operatingMode==UINT8_MAX) return(controlRegister&B00000011); // Return setting if no parameter // _mode = operatingMode&B00000011; // Mask 2 bits in input parameter // - writeByte(BME280_CONTROL_REG,(readByte(BME280_CONTROL_REG)&B11111100)|_mode);// Write value back to register // + controlRegister = (controlRegister&B11111100) | _mode; // set the new value // + putData(BME280_CONTROL_REG,controlRegister); // Write value back to register // return(_mode); // and return that value // } // of method mode() // // /******************************************************************************************************************* @@ -157,20 +118,16 @@ uint8_t BME280_Class::mode(const uint8_t operatingMode) { // bool BME280_Class::setOversampling(const uint8_t sensor, // Set enum sensorType to Oversample// const uint8_t sampling) { // // if(sensor>=UnknownSensor || sampling>=UnknownOversample) return(false); // return error if out of range // - uint8_t tempControl = readByte(BME280_CONTROL_REG); // Read the control register // - delay(60); // Give the BME280 a bit of time // - writeByte(BME280_CONTROL_REG,0); // Put BME280 into sleep mode // + uint8_t originalControl = readByte(BME280_CONTROL_REG); // Read the control register // + while(readByte(BME280_CONTROL_REG)!=0) putData(BME280_CONTROL_REG,0); // Put BME280 into sleep mode // if(sensor==HumiditySensor) { // If we have a humidity setting, // - writeByte(BME280_CONTROLHUMID_REG,sampling); // Update humidity register // - delay(60); // Give the BME280 a bit of time // - writeByte(BME280_CONTROL_REG,tempControl); // Restore register values // - delay(60); // Give the BME280 a bit of time // + putData(BME280_CONTROLHUMID_REG,sampling); // Update humidity register // } else if (sensor==TemperatureSensor) { // otherwise if we have temperature // - tempControl = (tempControl&B00011111)|(sampling<<5); // Update the register bits // + originalControl = (originalControl&B00011111)|(sampling<<5); // Update the register bits // } else { // // - tempControl = (tempControl&B11100011)|(sampling<<2); // Update the register bits // + originalControl = (originalControl&B11100011)|(sampling<<2); // Update the register bits // } // of if-then-else temperature reading // // - writeByte(BME280_CONTROL_REG,tempControl); // Write value to the register // + putData(BME280_CONTROL_REG,originalControl); // Write value to the register // return(true); // return success // } // of method setOversampling() // // /******************************************************************************************************************* @@ -188,8 +145,8 @@ uint8_t BME280_Class::getOversampling(const uint8_t sensor, // else // // returnValue = (readByte(BME280_CONTROL_REG)>>2)&B00000111; // // if (actual) { // If the actual flag has been set, // - if (returnValue==3) returnValue = 4; // then return the oversampling // - else if (returnValue==4) returnValue = 8; // multiplier instead of the table // + if (returnValue==3) returnValue = 4; // then return the oversampling // + else if (returnValue==4) returnValue = 8; // multiplier instead of the table // else if (returnValue>4) returnValue = 16; // index value // } // of if-then we return the actual count // // return(returnValue); // return oversampling bits // @@ -206,9 +163,10 @@ void BME280_Class::readSensors() { // int64_t i, j, p; // Work variables // if((_mode==ForcedMode||_mode==ForcedMode2)&&mode()==SleepMode) mode(_mode); // Force a reading if necessary // while(readByte(BME280_STATUS_REG)&B00001001!=0); // wait for measurement to complete // - readI2C(BME280_PRESSUREDATA_REG,registerBuffer,8); // read all 8 bytes in one go // - /* First compute the temperature so that we can get the "_tfine" variable, which is used to compensate both the ** - ** humidity and pressure readings */ + getData(BME280_PRESSUREDATA_REG,registerBuffer); // read all 8 bytes in one go // + //*******************************// // // + // First compute the temperature // // // + //*******************************// // // _Temperature = (int32_t)registerBuffer[3]<<12|(int32_t)registerBuffer[4]<<4|// // (int32_t)registerBuffer[5]>>4; // // i = ((((_Temperature>>3)-((int32_t)_cal_dig_T1 <<1)))* // // @@ -218,7 +176,9 @@ void BME280_Class::readSensors() { // *((int32_t)_cal_dig_T3))>>14; // // _tfine = i + j; // // _Temperature = (_tfine * 5 + 128) >> 8; // In centi-degrees Celsius // - /* Now compute the pressure value */ + //*******************************// // // + // Now compute the pressure // // // + //*******************************// // // _Pressure = (int32_t)registerBuffer[0]<<12|(int32_t)registerBuffer[1]<<4|// // (int32_t)registerBuffer[2]>>4; // // i = ((int64_t)_tfine) - 128000; // // @@ -236,7 +196,9 @@ void BME280_Class::readSensors() { // p = ((p + i + j) >> 8) + (((int64_t)_cal_dig_P7)<<4); // // _Pressure = p>>8; // in pascals // } // of if pressure would cause error // // - /* Now compute the Humidity value */ + //**********************************// // // + // And finally compute the humidity // // // + //**********************************// // // _Humidity = (int32_t)registerBuffer[6]<<8|(int32_t)registerBuffer[7]; // // i = (_tfine - ((int32_t)76800)); // // i = (((((_Humidity<<14)-(((int32_t)_cal_dig_H4)<<20)-(((int32_t)_cal_dig_H5)// // @@ -260,24 +222,21 @@ uint8_t BME280_Class::iirFilter() { // uint8_t BME280_Class::iirFilter(const uint8_t iirFilterSetting ) { // set the IIR Filter value // uint8_t returnValue = readByte(BME280_CONFIG_REG)&B11110001; // Get control reg, mask IIR bits // returnValue |= (iirFilterSetting&B00000111)<<2; // use 3 bits of iirFilterSetting // - writeByte(BME280_CONFIG_REG,returnValue); // Write new control register value // + putData(BME280_CONFIG_REG,returnValue); // Write new control register value // returnValue = (returnValue>>2)&B00000111; // Extract IIR filter setting // return(returnValue); // Return IIR Filter setting // } // of method iirFilter() // // /******************************************************************************************************************* -** Overloaded method inactiveTime() when called with no parameters returns the current inactive time setting, ** -** otherwise when called with one parameter will set the inactive time and return the new setting ** +** Method inactiveTime() when called with no parameters returns the current inactive time setting, otherwise uses ** +** the parameter to set the inactive time. ** *******************************************************************************************************************/ -uint8_t BME280_Class::inactiveTime() { // return the IIR Filter setting // - uint8_t returnValue = (readByte(BME280_CONFIG_REG)>>5)&B00000111; // Get 3 bits for the inactive time // - return(returnValue); // Return IIR Filter setting // -} // of method inactiveTime() // // uint8_t BME280_Class::inactiveTime(const uint8_t inactiveTimeSetting) { // return the IIR Filter setting // - uint8_t returnValue = readByte(BME280_CONFIG_REG)&B10001111; // Get control reg, mask inactive // - returnValue |= (inactiveTimeSetting&B00000111)<<5; // use 3 bits of inactiveTimeSetting// - writeByte(BME280_CONFIG_REG,returnValue); // Write new control register value // - returnValue = (returnValue>>5)&B00000111; // Extract inactive setting // - return(returnValue); // Return inactive time setting // + uint8_t returnValue = readByte(BME280_CONFIG_REG); // Get control register // + if (inactiveTimeSetting!=UINT8_MAX) { // If we have a specified value // + returnValue = (returnValue&B00011111)|(inactiveTimeSetting<<5); // use 3 bits of inactiveTimeSetting// + putData(BME280_CONFIG_REG,returnValue); // Write new control register value // + } // of if-then we have specified a new setting // // + return(returnValue>>5); // Return inactive time setting // } // of method inactiveTime() // // /******************************************************************************************************************* ** Method measurementTime() returns the time in microseconds for a measurement cycle with the current settings. ** @@ -326,6 +285,8 @@ void BME280_Class::getSensorData(int32_t &temp, int32_t &hum, int32_t &press){// ** Method reset() performs a device reset, as if it were powered down and back up again ** *******************************************************************************************************************/ void BME280_Class::reset() { // reset device // - writeByte(BME280_SOFTRESET_REG,BME280_SOFTWARE_CODE); // writing code here resets device // - begin(_I2CAddress); // Start device again // + putData(BME280_SOFTRESET_REG,BME280_SOFTWARE_CODE); // writing code here resets device // + if (_I2CAddress) begin(); // Start device again if I2C // + else if(_sck) begin(_cs,_mosi,_miso,_sck); // Use software serial again // + else begin(_cs); // otherwise it must be hardware SPI// } // of method reset() // // \ No newline at end of file diff --git a/BME280.h b/BME280.h index f23fda7..91c6152 100644 --- a/BME280.h +++ b/BME280.h @@ -3,7 +3,8 @@ ** at https://www.bosch-sensortec.com/bst/products/all_products/bme280 and the datasheet is available from Bosch ** ** at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf ** ** ** -** The BME280 can use either SPI or I2C for communications. The initial library version uses I2C exclusively. ** +** The BME280 can use either SPI or I2C for communications. The initial library version 1.0.0 defines only I2C ** +** while subsequent versions also allow SPI communications ** ** ** ** The most recent version of the library is available at https://github.com/SV-Zanshin/BME280 and extensive ** ** documentation of the library as well as example programs are described in the project's wiki pages located at ** @@ -27,19 +28,35 @@ ** ** ** Vers. Date Developer Comments ** ** ====== ========== =================== ======================================================================== ** +** 1.0.1 2017-08-03 Arnd@SV-Zanshin.Com All read/writes now use getData() and putData() templates in this header ** +** changed begin() method for I2C to search for first instance of BME280 ** +** Added hardware and software SPI functionality and tested it ** +** 1.0.0 2017-08-03 Arnd@SV-Zanshin.Com Initial version with just I2C connectivity ** ** 1.0.0b 2017-07-31 Arnd@SV-Zanshin.Com Continued development ** ** 1.0.0a 2017-07-30 Arnd@SV-Zanshin.Com Started coding ** ** ** *******************************************************************************************************************/ #include "Arduino.h" // Arduino data type definitions // #include // Standard I2C "Wire" library // +#include // Standard SPI library // #ifndef BME280_h // Guard code definition // #define BME280_h // Define the name inside guard code// /***************************************************************************************************************** ** Declare constants used in the class ** *****************************************************************************************************************/ - const uint8_t BME280_ADDRESS = 0x77; // Device address power-up default // - const uint8_t BME280_I2C_DELAY = 0; // I2C and write time delay // + const uint8_t I2C_READ_ATTEMPTS = 1000; // Attempts to read before timeout // + const uint32_t SPI_HERTZ = 500000; // SPI speed in Hz // + const uint8_t BME280_CHIPID_REG = 0xD0; // Chip-Id register // + const uint8_t BME280_CHIPID = 0x60; // Hard-coded value 0x60 for BME280 // + const uint8_t BME280_SOFTRESET_REG = 0xE0; // Reset when 0xB6 is written here // + const uint8_t BME280_CONTROLHUMID_REG = 0xF2; // Humidity control register // + const uint8_t BME280_STATUS_REG = 0xF3; // Device status register // + const uint8_t BME280_CONTROL_REG = 0xF4; // Device control register // + const uint8_t BME280_CONFIG_REG = 0xF5; // Device configuration register // + const uint8_t BME280_PRESSUREDATA_REG = 0xF7; // Pressure readings register // + const uint8_t BME280_TEMPDATA_REG = 0xFA; // Temperature readings register // + const uint8_t BME280_HUMIDDATA_REG = 0xFD; // Humidity readings register // + const uint8_t BME280_SOFTWARE_CODE = 0xB6; // Reset on this written to resetreg// //----------------------------------// const uint8_t BME280_T1_REG = 0x88; // Declare BME280 registers for the // const uint8_t BME280_T2_REG = 0x8A; // calibration data used to convert // @@ -59,17 +76,6 @@ const uint8_t BME280_H4_REG = 0xE4; // // const uint8_t BME280_H5_REG = 0xE5; // // const uint8_t BME280_H6_REG = 0xE7; //----------------------------------// - const uint8_t BME280_CHIPID_REG = 0xD0; // Chip-Id register // - const uint8_t BME280_CHIPID = 0x60; // Hard-coded value 0x60 for BME280 // - const uint8_t BME280_SOFTRESET_REG = 0xE0; // Reset when 0xB6 is written here // - const uint8_t BME280_SOFTWARE_CODE = 0xB6; // Reset when written to reset reg // - const uint8_t BME280_CONTROLHUMID_REG = 0xF2; // Humidity control register // - const uint8_t BME280_STATUS_REG = 0xF3; // Device status register // - const uint8_t BME280_CONTROL_REG = 0xF4; // Device control register // - const uint8_t BME280_CONFIG_REG = 0xF5; // Device configuration register // - const uint8_t BME280_PRESSUREDATA_REG = 0xF7; // Pressure readings register // - const uint8_t BME280_TEMPDATA_REG = 0xFA; // Temperature readings register // - const uint8_t BME280_HUMIDDATA_REG = 0xFD; // Humidity readings register // /***************************************************************************************************************** ** Declare enumerated types used in the class ** *****************************************************************************************************************/ @@ -91,30 +97,27 @@ public: // Publicly visible methods // BME280_Class(); // Class constructor // ~BME280_Class(); // Class destructor // - bool begin(const uint8_t I2CAddress = BME280_ADDRESS ); // I2C Communications at address // - uint8_t mode(); // return device mode // - uint8_t mode(const uint8_t operatingMode); // Set device mode // + bool begin(); // Start using I2C Communications // + 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); // // + uint8_t mode(const uint8_t operatingMode=UINT8_MAX); // Get or Set device mode // bool setOversampling(const uint8_t sensor, const uint8_t sampling); // Set enum sensorType Oversampling // uint8_t getOversampling(const uint8_t sensor, // Get enum sensorType oversampling // const bool actual = false); // if "actual" set then return # // uint8_t iirFilter(); // Return the IIR Filter setting // uint8_t iirFilter(const uint8_t iirFilterSetting ); // Set IIR Filter and return value // - uint8_t inactiveTime(); // Return the inactive time setting // - uint8_t inactiveTime(const uint8_t inactiveTimeSetting ); // Set inactive time & return value // + uint8_t inactiveTime(const uint8_t inactiveTimeSetting=UINT8_MAX); // Set inactive time & return value // uint32_t measurementTime(const uint8_t measureTimeSetting=1); // Return measurement cycle time // void getSensorData(int32_t &temp, int32_t &hum, int32_t &press); // get most recent readings // void reset(); // Reset the BME280 // - private: // Private methods // + private: // -------- Private methods ------- // + uint8_t readByte(const uint8_t addr); // Read byte from register address // void readSensors(); // read the registers in one burst // - uint8_t readI2C(const uint8_t addr, uint8_t *pdata, // Read n-Bytes from I2C // - const uint8_t bytesToRead); // // - uint8_t readByte(const uint8_t addr); // Read 1 byte from address on I2C // - void writeByte(const uint8_t addr, const uint8_t data); // Write 1 byte at address to I2C // - uint16_t readWord(const uint8_t addr); // Read 2 bytes from address on I2C // - uint16_t readWordLE(const uint8_t addr); // Read 2 bytes Little-Endian on I2C// - void writeWord(const uint8_t addr, const uint16_t data); // Write 2 bytes at address to I2C // + void getCalibration(); // Load calibration from registers // bool _TransmissionStatus = false; // I2C communications status // - uint8_t _I2CAddress = BME280_ADDRESS; // Actual I2C address used w/default// + uint8_t _I2CAddress = 0; // Default is no I2C address known // + uint8_t _cs,_sck,_mosi,_miso; // Hardware and software SPI pins // uint8_t _cal_dig_H1,_cal_dig_H3; // Declare all of the calibration // int8_t _cal_dig_H6 = 0; // variables // uint16_t _cal_dig_T1,_cal_dig_P1; // // @@ -122,7 +125,113 @@ _cal_dig_P5,_cal_dig_P6,_cal_dig_P7,_cal_dig_P8,_cal_dig_P9, // // _cal_dig_H2,_cal_dig_H4,_cal_dig_H5; // // uint8_t _mode = UINT8_MAX; // Last mode set // - int32_t _tfine; // Global calibration value // - int32_t _Temperature,_Pressure,_Humidity; // Store the last readings // + int32_t _tfine,_Temperature,_Pressure,_Humidity; // Sensor global variables // + /************************************************************************************************************* + ** 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. ** + *************************************************************************************************************/ + template< typename T > uint8_t &getData(const uint8_t addr,T &value) { // method to write a structure // + uint8_t* bytePtr = (uint8_t*)&value; // Pointer to structure beginning // + uint8_t structSize = sizeof(T); // Number of bytes in structure // + uint8_t timeoutI2C = I2C_READ_ATTEMPTS; // set tries before timeout // + if (_I2CAddress) { // Using I2C if address is non-zero // + Wire.beginTransmission(_I2CAddress); // Address the I2C device // + Wire.write(addr); // Send register address to read // + _TransmissionStatus = Wire.endTransmission(); // Close transmission // + Wire.requestFrom(_I2CAddress, sizeof(T)); // Request 1 byte of data // + while(!Wire.available()&&timeoutI2C--!=0); // Wait until byte ready or timeout // + 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 BME280 to stop listening // + + + + + } // of if-then-else we are using hardware SPI // // + } // of if-then-else we are using I2C // // + return(structSize); // return the number of bytes read // + } // of method getData() //----------------------------------// + templateuint8_t &putData(const uint8_t addr,const T &value){// method to write a structure // + const uint8_t* bytePtr = (const uint8_t*)&value; // Pointer to structure beginning // + 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<. ** +** ** +** Vers. Date Developer Comments ** +** ====== ========== =================== ======================================================================== ** +** 1.0.0 2017-08-04 Arnd@SV-Zanshin.Com Cloned from I2CDemo.ino program ** +** ** +*******************************************************************************************************************/ +#include // Include the BME280 Sensor library // +/******************************************************************************************************************* +** Declare all program constants ** +*******************************************************************************************************************/ +const uint32_t SERIAL_SPEED = 115200; // Set the baud rate for Serial I/O // + // The pin used for slave-select can// + // be freely chosen from the digital// + // pins available. This is default // + // pin used on an Arduino MEGA2560 // +const uint8_t SPI_CS_PIN = 53; // Pin for slave-select of BME280 // +/******************************************************************************************************************* +** Declare global variables and instantiate classes ** +*******************************************************************************************************************/ +BME280_Class BME280; // Create an instance of the BME280 // +/******************************************************************************************************************* +** Method altitude(). This converts a pressure measurement into a height in meters. The corrected sea-level ** +** pressure can be passed into the function if it is know, otherwise the standard atmosphereic pressure of ** +** 1013.25hPa is used (see https://en.wikipedia.org/wiki/Atmospheric_pressure ** +*******************************************************************************************************************/ +float altitude(const float seaLevel=1013.25) { // Return computed altitude // + int32_t temp,hum,press; // Temp variables to store a reading// + BME280.getSensorData(temp,hum,press); // Get the most recent values // + float Altitude = 44330.0*(1.0-pow(((float)press/100.0)/seaLevel,0.1903)); // Convert into altitude in meters // + return(Altitude); // return computation result // +} // of method altitude() // // +/******************************************************************************************************************* +** Method Setup(). This is an Arduino IDE method which is called upon boot or restart. It is only called one time ** +** and then control goes to the main loop, which loop indefinately. ** +*******************************************************************************************************************/ +void setup() { // Arduino standard setup method // + Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate // + #ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then// + delay(3000); // wait 3 seconds for the serial // + #endif // interface to initialize // + Serial.println(F("Starting Hardware SPIDemo example program for BME280")); // // + Serial.print(F("- Initializing BME280 sensor\n")); // // + //==================================// + while (!BME280.begin(SPI_CS_PIN)) { // Start using hardware SPI protocol// + //==================================// + Serial.println(F("- Unable to find BME280. Waiting 3 seconds.")); // Show error text // + delay(3000); // wait three seconds // + } // of loop until device is located // // + BME280.mode(SleepMode); // // + Serial.print(F("- Sensor detected in operating mode \"")); // // + Serial.print(BME280.mode()); // // + Serial.println(F("\".")); // // + if(BME280.mode()==0) { // // + Serial.print(F("- Turning sensor to normal mode, mode is now \"")); // // + Serial.print(BME280.mode(NormalMode)); // Use enumerated type values // + Serial.println("\""); // // + } // of if-then we have sleep mode // // + Serial.println(F("- Setting 16x oversampling for all sensors")); // // + BME280.setOversampling(TemperatureSensor,Oversample16); // Use enumerated type values // + BME280.setOversampling(HumiditySensor, Oversample16); // // + BME280.setOversampling(PressureSensor, Oversample16); // // + Serial.println(F("- Setting IIR filter to maximum value of 16 samples")); // // + BME280.iirFilter(IIR16); // Use enumerated type values // + Serial.println(F("- Setting time between measurements to 1 second")); // // + BME280.inactiveTime(inactive1000ms); // Use enumerated type values // + Serial.print(F("- Each measurement cycle will take ")); // // + Serial.print(BME280.measurementTime(MaximumMeasure)/1000); // returns microseconds // + Serial.println(F("ms.\n\n")); // // +} // of method setup() // // +/******************************************************************************************************************* +** This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. ** +*******************************************************************************************************************/ +void loop() { // // + static uint8_t loopCounter = 0; // iteration counter // + static int32_t temperature, humidity, pressure; // Store readings // + BME280.getSensorData(temperature,humidity,pressure); // Get most recent readings // + Serial.print(F("Temperature: ")); // // + Serial.print(temperature/100.0); // Temperature in deci-degrees // + Serial.print(F("C ")); // // + if (BME280.getOversampling(HumiditySensor)!=0) { // // + Serial.print(F("Humidity: ")); // // + Serial.print(humidity/100.0); // Humidity in deci-percent // + Serial.print(F("% ")); // // + } // of if-then humidity sensing turned off // // + Serial.print(F("Pressure: ")); // // + Serial.print(pressure/100.0); // Pressure in Pascals // + Serial.print(F("hPa Altitude: ")); // // + Serial.print(altitude()); // // + Serial.println(F("m")); // // + delay(1000); // Wait a bit before repeating // + if (++loopCounter%10==0) { // Every 10th reading // + Serial.print(F("\n- Turning ")); // // + if (BME280.getOversampling(HumiditySensor)==0) { // // + BME280.setOversampling(HumiditySensor,Oversample16); // Turn humidity sensing on // + Serial.print(F("ON")); // // + } else { // // + BME280.setOversampling(HumiditySensor,SensorOff); // No longer interested in humidity // + Serial.print(F("OFF")); // // + } // of if-then-else humidity sensing turned off // // + Serial.println(F(" humidity sensing")); // // + Serial.print(F("- Each measurement cycle will now take ")); // // + Serial.print(BME280.measurementTime(MaximumMeasure)/1000.0); // returns microseconds // + Serial.println(F("ms.\n")); // // + } // of if-then first loop iteration // // +} // of method loop() //----------------------------------// diff --git a/Examples/SoftSPIDemo/SoftSPIDemo.ino b/Examples/SoftSPIDemo/SoftSPIDemo.ino new file mode 100644 index 0000000..6dd4b80 --- /dev/null +++ b/Examples/SoftSPIDemo/SoftSPIDemo.ino @@ -0,0 +1,146 @@ +/******************************************************************************************************************* +** Example program for using the Bosch BME280 sensor. The sensor measures temperature, pressure and humidity and ** +** is described at https://www.bosch-sensortec.com/bst/products/all_products/bme280. The datasheet is available ** +** from Bosch at https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf ** +** ** +** The most recent version of the BME280 library is available at https://github.com/SV-Zanshin/BME280 and the ** +** documentation of the library as well as example programs are described in the project's wiki pages located at ** +** https://github.com/SV-Zanshin/BME280/wiki. ** +** ** +** The BME280 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://learn.adafruit.com/adafruit-bme280-humidity-barometric-pressure-temperature-sensor-breakout. ** +** ** +** This example program initializes the BME280 to use software SPI for communications. The library does not use ** +** floating point mathematics to save on computation space and time, the values for Temperature, Pressure and ** +** Humidity are returned in deci-units, e.g. a Temperature reading of "2731" means "27.31" degrees Celsius. The ** +** display in the example program uses floating point for demonstration purposes only. Note that the temperature ** +** reading is generally higher than the ambient temperature due to die and PCB temperature and self-heating of ** +** the element. ** +** ** +** The pressure reading needs to be adjusted for altitude to get the adjusted pressure reading. There are numerous** +** sources on the internet for formula converting from standard sea-level pressure to altitude, see the data sheet** +** for the BME180 on page 16 of http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf. Rather than put a ** +** floating-point function in the library which may not be used but which would use space, an example altitude ** +** computation function has been added to this example program to show how it might be done. ** +** ** +** 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 . ** +** ** +** Vers. Date Developer Comments ** +** ====== ========== =================== ======================================================================== ** +** 1.0.0 2017-08-04 Arnd@SV-Zanshin.Com Cloned from I2CDemo.ino program ** +** ** +*******************************************************************************************************************/ +#include // Include the BME280 Sensor library // +/******************************************************************************************************************* +** Declare all program constants ** +*******************************************************************************************************************/ +const uint32_t SERIAL_SPEED = 115200; // Set the baud rate for Serial I/O // + + /************************************************************************************ + ** Software SPI requires 4 digital pins to be defined in order to work: ** + ** CS - Chip Select or sometimes called SS for Slave-select. Used to address chip** + ** MISO - Master In, Slave Out. Wire used to send data from the BME280 to program ** + ** MOSI - Mast Out, Slave In. Wire used to send data from the program to BME280 ** + ** SCK - System Clock. This is used for timing data ** + ** ** + ************************************************************************************/ +const uint8_t SPI_CS_PIN = 10; // Pin for slave-select of BME280 // +const uint8_t SPI_SCK_PIN = 13; // Pin for clock signal // +const uint8_t SPI_MOSI_PIN = 11; // Master-out, Slave-in Pin // +const uint8_t SPI_MISO_PIN = 12; // Master-in, Slave-out Pin // + +/******************************************************************************************************************* +** Declare global variables and instantiate classes ** +*******************************************************************************************************************/ +BME280_Class BME280; // Create an instance of the BME280 // +/******************************************************************************************************************* +** Method altitude(). This converts a pressure measurement into a height in meters. The corrected sea-level ** +** pressure can be passed into the function if it is know, otherwise the standard atmosphereic pressure of ** +** 1013.25hPa is used (see https://en.wikipedia.org/wiki/Atmospheric_pressure ** +*******************************************************************************************************************/ +float altitude(const float seaLevel=1013.25) { // Return computed altitude // + int32_t temp,hum,press; // Temp variables to store a reading// + BME280.getSensorData(temp,hum,press); // Get the most recent values // + float Altitude = 44330.0*(1.0-pow(((float)press/100.0)/seaLevel,0.1903)); // Convert into altitude in meters // + return(Altitude); // return computation result // +} // of method altitude() // // +/******************************************************************************************************************* +** Method Setup(). This is an Arduino IDE method which is called upon boot or restart. It is only called one time ** +** and then control goes to the main loop, which loop indefinately. ** +*******************************************************************************************************************/ +void setup() { // Arduino standard setup method // + Serial.begin(SERIAL_SPEED); // Start serial port at Baud rate // + #ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, then// + delay(3000); // wait 3 seconds for the serial // + #endif // interface to initialize // + Serial.println(F("Starting Software SPIDemo example program for BME280")); // // + Serial.print(F("- Initializing BME280 sensor\n")); // // + //==================================// + while (!BME280.begin(SPI_CS_PIN,SPI_MOSI_PIN,SPI_MISO_PIN,SPI_SCK_PIN)) { // Start using software SPI protocol// + //==================================// + Serial.println(F("- Unable to find BME280. Waiting 3 seconds.")); // Show error text // + delay(3000); // wait three seconds // + } // of loop until device is located // // + BME280.mode(SleepMode); // // + Serial.print(F("- Sensor detected in operating mode \"")); // // + Serial.print(BME280.mode()); // // + Serial.println(F("\".")); // // + if(BME280.mode()==0) { // // + Serial.print(F("- Turning sensor to normal mode, mode is now \"")); // // + Serial.print(BME280.mode(NormalMode)); // Use enumerated type values // + Serial.println("\""); // // + } // of if-then we have sleep mode // // + Serial.println(F("- Setting 16x oversampling for all sensors")); // // + BME280.setOversampling(TemperatureSensor,Oversample16); // Use enumerated type values // + BME280.setOversampling(HumiditySensor, Oversample16); // // + BME280.setOversampling(PressureSensor, Oversample16); // // + Serial.println(F("- Setting IIR filter to maximum value of 16 samples")); // // + BME280.iirFilter(IIR16); // Use enumerated type values // + Serial.println(F("- Setting time between measurements to 1 second")); // // + BME280.inactiveTime(inactive1000ms); // Use enumerated type values // + Serial.print(F("- Each measurement cycle will take ")); // // + Serial.print(BME280.measurementTime(MaximumMeasure)/1000); // returns microseconds // + Serial.println(F("ms.\n\n")); // // +} // of method setup() // // +/******************************************************************************************************************* +** This is the main program for the Arduino IDE, it is an infinite loop and keeps on repeating. ** +*******************************************************************************************************************/ +void loop() { // // + static uint8_t loopCounter = 0; // iteration counter // + static int32_t temperature, humidity, pressure; // Store readings // + BME280.getSensorData(temperature,humidity,pressure); // Get most recent readings // + Serial.print(F("Temperature: ")); // // + Serial.print(temperature/100.0); // Temperature in deci-degrees // + Serial.print(F("C ")); // // + if (BME280.getOversampling(HumiditySensor)!=0) { // // + Serial.print(F("Humidity: ")); // // + Serial.print(humidity/100.0); // Humidity in deci-percent // + Serial.print(F("% ")); // // + } // of if-then humidity sensing turned off // // + Serial.print(F("Pressure: ")); // // + Serial.print(pressure/100.0); // Pressure in Pascals // + Serial.print(F("hPa Altitude: ")); // // + Serial.print(altitude()); // // + Serial.println(F("m")); // // + delay(5000); // Wait a bit before repeating // + if (++loopCounter%10==0) { // Every 10th reading // + Serial.print(F("\n- Turning ")); // // + if (BME280.getOversampling(HumiditySensor)==0) { // // + BME280.setOversampling(HumiditySensor,Oversample16); // Turn humidity sensing on // + Serial.print(F("ON")); // // + } else { // // + BME280.setOversampling(HumiditySensor,SensorOff); // No longer interested in humidity // + Serial.print(F("OFF")); // // + } // of if-then-else humidity sensing turned off // // + Serial.println(F(" humidity sensing")); // // + Serial.print(F("- Each measurement cycle will now take ")); // // + Serial.print(BME280.measurementTime(MaximumMeasure)/1000.0); // returns microseconds // + Serial.println(F("ms.\n")); // // + } // of if-then first loop iteration // // +} // of method loop() //----------------------------------//