CCS811/BME280 (Qwiic) Environmental Combo Breakout Hookup Guide

Pages
Contributors: Englandsaurus
Favorited Favorite 2

Library Overview

Note: This example assumes you are using the latest version of the Arduino IDE on your desktop. If this is your first time using Arduino, please review our tutorial on installing the Arduino IDE. If you have not previously installed an Arduino library, please check out our installation guide.

Use the Arduino Library Manager! SparkFun has written libraries to control both the CCS811 and the BME280. You can obtain these libraries through the Arduino Library Manager. Search for SparkFun CCS811 and SparkFun BME280 and you should be able to install the latest version. Never installed a library before? That's ok! Checkout our tutorial on installing Arduino Libraries. If you prefer downloading the libraries you can grab them here to manually install:

Before we get started on a sketch, lets take a look at the libraries used.

BME280 Library

Construction

In the global scope, construct your sensor object (such as mySensor or pressureSensorA) without arguments.

Example:

BME280 mySensor;

Object Parameters and setup()

Rather that passing a bunch of data to the constructor, configuration is accomplished by setting the values of the BME280 type in the setup() function. They are exposed by being public: so use the myName.aVariable = someValue; syntax.

Settable variables of the class BME280:

//Main Interface and mode settings
uint8_t commInterface;
uint8_t I2CAddress;
uint8_t chipSelectPin;

uint8_t runMode;
uint8_t tStandby;
uint8_t filter;
uint8_t tempOverSample;
uint8_t pressOverSample;
uint8_t humidOverSample;

An example configuration of the BME280 type in setup():

language:c
#include <stdint.h>
#include "SparkFunBME280.h"

#include "Wire.h"
#include "SPI.h"

//Global sensor object
BME280 mySensor;

void setup()
{
    //***Driver settings********************************//
    //commInterface can be I2C_MODE
    //specify I2C address.  Can be 0x77(default) or 0x76

    //For I2C, enable the following
    mySensor.settings.commInterface = I2C_MODE;
    mySensor.settings.I2CAddress = 0x77;

    //***Operation settings*****************************//

    //runMode can be:
    //  0, Sleep mode
    //  1 or 2, Forced mode
    //  3, Normal mode
    mySensor.settings.runMode = 3; //Forced mode

    //tStandby can be:
    //  0, 0.5ms
    //  1, 62.5ms
    //  2, 125ms
    //  3, 250ms
    //  4, 500ms
    //  5, 1000ms
    //  6, 10ms
    //  7, 20ms
    mySensor.settings.tStandby = 0;

    //filter can be off or number of FIR coefficients to use:
    //  0, filter off
    //  1, coefficients = 2
    //  2, coefficients = 4
    //  3, coefficients = 8
    //  4, coefficients = 16
    mySensor.settings.filter = 0;

    //tempOverSample can be:
    //  0, skipped
    //  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
    mySensor.settings.tempOverSample = 1;

    //pressOverSample can be:
    //  0, skipped
    //  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
    mySensor.settings.pressOverSample = 1;

    //humidOverSample can be:
    //  0, skipped
    //  1 through 5, oversampling *1, *2, *4, *8, *16 respectively
    mySensor.settings.humidOverSample = 1;
    delay(10);  //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.         Serial.begin(57600);

    Serial.print("Starting BME280... result of .begin(): 0x");
    //Calling .begin() causes the settings to be loaded
    Serial.println(mySensor.begin(), HEX);

}
  • uint8_t begin( void ) --- In the above example, begin is used to start the sensor. The basic routine it follows is like this:

    • Starts up the wiring library if necessary, though #include "Wire.h" may be needed in your sketch.
    • Concatenates the calibration words as specified by Bosch.
    • Applies user settings to the configuration registers in the BME280.
    • Returns the ID register (should read 0x60).    

    To use it, call mySensor.begin(); or assign the output to something like uint8_t myReturnedValue = mySensor.begin();

    .begin() Needs to be run once during the setup, or after any settings have been modified. In order to let the sensor's configuration take place, the BME280 requires a minimum time of about 2 ms in the sketch before you take data.
  • void reset( void ) --- Send the reset word to the BME280. Afterwards, you'll have to run begin() again.

  • float readTempC( void ) --- Use to get the temperature in Celsius, as a float.

  • float readTempF( void ) --- Use to get the temperature in Fahrenheit, as a float. Takes no arguments.

  • float readFloatPressure( void ) --- Use to get pressure in units of kiloPascals, as a float.

  • float readFloatAltitudeMeters( void ) --- Use to get altitude in units of meters, as a float.

  • float readFloatAltitudeFeet( void ) --- Use to get altitude in units of feet, as a float. This function calculates based off the measured pressure.

  • float readFloatHumidity( void ) --- Use to get humidity in % relative, as a float.

CCS811 Library

The library is fairly normal to use compared with our other sensors. You'll have to include the library, create a sensor object in the global space, and then use functions of that object to begin and control the sensor. With this one, you must pass the I2C address to the object during construction.

CCS811 Burn-in Time: Please be aware that the CCS811 datasheet recommends a burn-in of 48 hours and a run-in of 20 minutes (i.e. you must allow 20 minutes for the sensor to warm up and output valid data).

To include the library and to take care of all the gritty compiler stuff, place the following at the beginning of the sketch before void setup() function.

language:c
#include <SparkFunCCS811.h>

#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address

CCS811 myCCS811(CCS811_ADDR);

Now functions of the object named myCCS811 can be called to set up and get data, while all the I2C stuff is kept under the hood.

To get the sensor ready during program boot, myCCS811.begin() must be called. Here's an example of the minimal usage of begin.

language:c
void setup()
{
    myCCS811.begin();
}
Error Status: The .begin() function has a special feature: it returns the status of the function call! If there was a problem during begin, it will return a non-zero code indicating what happened. It's optional, and is described in the "Custom Types and Literals" section below.

Then in the main loop() of the program, calls to the sensor functions such as mySensor.readAlgorithmResults() are needed to read the sensor. The following snippet shows a simple check for data by calling the sensor to calculate values, output data, and save the data in variables. However, it doesn't do anything with the data! Check out the examples for fully functional code to make use of the sensor data.

language:c
void loop()
{
  if (myCCS811.dataAvailable())
  {
    myCCS811.readAlgorithmResults();
    int tempCO2 = myCCS811.getCO2();
    int tempVOC = myCCS811.gettVOC();
  }
  else if (myCCS811.checkForStatusError())
  {
    while(1);
  }

  delay(1000); //Wait for next reading
}

Function Reference

The following functions exist for the CCS811 object. Functions with scoped return type CCS811Core::status report an error state as defined in the literals section below. It is optional and can be used to determine success or failure of call.

  • CCS811Core::status begin( void ) --- This starts wire, checks the ID register, checks for valid app data, starts the app, and establishes a drive mode.

  • CCS811Core::status readAlgorithmResults( void ) --- Call to cause the sensor to read its hardware and calculate TVOC and eCO2 levels.

  • bool checkForStatusError( void ) --- Returns true if there is an error pending. This checks the status register.

  • bool dataAvailable( void ) --- Returns true if a new sample is ready and hasn't been read.

  • bool appValid( void ) --- Returns true if there is a valid application within the internal CCS811 memory.

  • uint8_t getErrorRegister( void ) --- Returns the state of the ERROR_ID register.

  • uint16_t getBaseline( void ) --- Returns the baseline value.

  • CCS811Core::status setBaseline( uint16_t ) --- Apply a saved baseline to the CCS811.

  • CCS811Core::status enableInterrupts( void ) --- Enables the interrupt pin for data ready.

  • CCS811Core::status disableInterrupts( void ) --- Disables the interrupt pin.

  • CCS811Core::status setDriveMode( uint8_t mode ) --- Sets the drive mode where mode can be 0 through 4:

    • 0: Measurement off
    • 1: Measurement every 1 second
    • 2: Measurement every 10 seconds
    • 3: Measurement every 60 seconds
    • 4: Measurement every 0.25 seconds --- for use with external algorithms
  • CCS811Core::status setEnvironmentalData( float relativeHumidity, float temperature ) --- Sets the environmental conditions for compensation.

    • relativeHumidity in units of %, 0.00 through 100.0
    • temperature in degrees C, -25.0 through 50.0
  • void setRefResistance( float ) --- If you've changed the thermistor pull-up, call this to give the sensor the new resistor value. Otherwise, it will be 10000.

  • uint16_t getTVOC( void ) --- Collect the last calculated TVOC value, in parts per billion (ppb).

  • uint16_t getCO2( void ) --- Collect the last calculated eC02 value, in parts per million (ppm).

  • float getResistance( void ) --- Collect the last calculated resistance value of the NTC terminals.

  • float getTemperature( void ) --- Collect the last calculated temperature.

Custom Types and Literals

The CCS811 library defines a special data type to deal with error states of functions. In most places, the library can be used without paying attention to the function return types, but here are the values the data type status can hold if they are needed:

language:c
// Return values 
typedef enum
{
    SENSOR_SUCCESS,
    SENSOR_ID_ERROR,
    SENSOR_I2C_ERROR,
    SENSOR_INTERNAL_ERROR
    //...
} status;

To avoid the possibility of multiple libraries using the same status name, the enum is actually inside the scope of the CCS811 object, buried in the CCS811Core, which is the base class. Phew, don't worry about that too much; just place CCSCore:: before the status name when you want to use it, and use it like a regular enum (e.g., CCS811Core::status myLocalReturnStatus;). This just tells the compiler that the variable name is in a specific place. You'll also have to add the scope operator to the enum names.

Here's an example that shows how the status enum can be used:

language:c
CCS811Core::status returnCode = mySensor.beginCore();
Serial.print("beginCore exited with: ");
switch ( returnCode )
{
case CCS811Core::SENSOR_SUCCESS:
  Serial.print("SUCCESS");
  break;
case CCS811Core::SENSOR_ID_ERROR:
  Serial.print("ID_ERROR");
  break;
case CCS811Core::SENSOR_I2C_ERROR:
  Serial.print("I2C_ERROR");
  break;
case CCS811Core::SENSOR_INTERNAL_ERROR:
  Serial.print("INTERNAL_ERROR");
  break;
case CCS811Core::SENSOR_GENERIC_ERROR:
  Serial.print("GENERIC_ERROR");
  break;
default:
  Serial.print("Unspecified error.");
}

The library also defines names for CCS811 registers, if you're using direct read and write functions. These are globally scoped and can be used anywhere.

language:c
//Register addresses
#define CSS811_STATUS 0x00
#define CSS811_MEAS_MODE 0x01
#define CSS811_ALG_RESULT_DATA 0x02
#define CSS811_RAW_DATA 0x03
#define CSS811_ENV_DATA 0x05
#define CSS811_NTC 0x06
#define CSS811_THRESHOLDS 0x10
#define CSS811_BASELINE 0x11
#define CSS811_HW_ID 0x20
#define CSS811_HW_VERSION 0x21
#define CSS811_FW_BOOT_VERSION 0x23
#define CSS811_FW_APP_VERSION 0x24
#define CSS811_ERROR_ID 0xE0
#define CSS811_APP_START 0xF4
#define CSS811_SW_RESET 0xFF