CCS811 Digital Air Quality Sensor

CCS811 is an I2C digital gas sensor for monitoring indoor air quality, which is handled with the CCS811 module.

Wiring Up

The simplest way to use this sensor is connect power, SDA, SCL, and tie nWake pin to low.

CCS811 pin Connection Remark
#1 ADDR - Sets the I2C address. low: 0x5A (default), high: 0x5B
#2 nRESET - Active low, pulled up internally by the sensor, can be used to perform a hard-reset
#3 nINT optional GPIO It's pulled low by the CCS811 to indicate end of measurement or a set threshold value has been triggered
#4 - #5 - Pin 4 and 5 is probably already connected on the breakout board
#6 VDD 1.8 - 3.3V
#7 nWAKE optional GPIO, or tie to low Not relevant if running with 1 sec sample rate, can be used to reduce power usage of the CCS811
#8 NC - No connect
#9 SDA Espruino SDA -
#10 SCL Espruino SCL -

How to use

var gas = require("CCS811").connectI2C(I2C1);
// wait for the sensor to be ready
setTimeout(function() {
}, 3000);
// prints { "eCO2": 491, "TVOC": 13, "new": true }

Or specify an interrupt pin to have data 'pushed' as soon as it is available:

var gas = require("CCS811").connectI2C(I2C1, {int : B8});
gas.on('data', print);
// prints { "eCO2": 491, "TVOC": 13, "new": true }

Modes of Operation

The CCS811 has 5 modes of operation as follows:

  • Mode 0: Idle, low current mode
  • Mode 1: Constant power mode, IAQ measurement every second
  • Mode 2: Pulse heating mode IAQ measurement every 10 seconds
  • Mode 3: Low power pulse heating mode IAQ measurement every 60 seconds
  • Mode 4: Constant power mode, sensor measurement every 250ms. This mode is meant for factory testing. The ALG_RESULT_DATA is not updated, only RAW_DATA; the processing must be done on the host system.

Mode 1 is the default. Mode 4 is for factory tests. Pass a mode parameter in the options object:

var gas = require("CCS811").connectI2C(I2C1, {int: B0, mode: 2}); // use interrupts and take measurements every 10 seconds

Change operation mode

You can change the sampling rate with setMode. The modes are the same as you would pass in through the constructor. Calling gas.setMode(0) is the same as gas.stop().

Quoting the datasheet: "When a sensor operating mode is changed to a new mode with a lower sample rate (e.g. from Mode 1 to Mode 3), it should be placed in Mode 0 (Idle) for at least 10 minutes before enabling the new mode. When a sensor operating mode is changed to a new mode with a higher sample rate (e.g. from Mode 3 to Mode 1), there is no requirement to wait before enabling the new mode."

Using nWake

You can save power by using lower sample rates, and utilising the nWake pin. If you use 1 second sample rate, or don't care about power consumption, you can just tie the CCS811's nWake pin to GND and you are good to go.

Without nWake the sensor's power consumption between measurements is around 4.5mA @3.3V. Using the nWake pin it's standby current drops down to the uA range.

Using B7 pin as nWake:

var gas = require("CCS811").connectI2C(I2C1, {int: B0, mode: 2, nWake: B7}); // use interrupts and take measurements every 10 seconds, using B7 as nWake

Temperature & humidity compensation

If an external sensor is available this information can be written to CCS811 so that they will be used to compensate gas readings due to temperature and humidity changes. When ENV_DATA has been written, the next eCO2 and eTVOC readings (in ALG_RESULT_DATA) may not yet use the latest ENV_DATA. All subsequent reading will use the ALG_RESULT_DATA.

The default value is 25°C and 50% relative humidity.

// Assuming that `gas` is an instance of the CCS811 module
// Humidity is relative humidity in percent (0-100%), and temperature is in °C
gas.setEnvData(humidity, temperature);

Sensor firmware version

There is a firmware inside the sensor itself. Newer sensors ship with v 2-0-0, but older ones were shipped with 1-0-0. You can find description of versions and the update process at ams's website

Known versions at the time of writing:

  • 1-1-0 Initial Version
  • 2-0-0 Several improvements, this version manages the burn-in period, and should be flashed to new sensors
  • 2-0-1 This version should be used

This module is compatible with 1-1-0 and 2-0-x of the firmware. But the isNew property is always false if you have 1-1-0!

To quickly check the firmware version in the CCS811, just run the example program, and type gas.r(0x24,2) at the left hand side of the Espruino IDE. 0x24 is the firmware version register. If the response is

  • =new Uint8Array([17, 0]), you have version 1-1-0 (17 = binary 0001 0001, the upper half of the first bit is the major, the lower half is the minor version).
  • =new Uint8Array([32, 0]), you have 2-0-0, if you have already ran the sensor for a couple of days, you can upgrade to 2-0-1
  • =new Uint8Array([32, 1]), you have 2-0-1, nothing to do

Firmware updater

There is a separate module, that can be used to do firmware updates. Since the chip contains a separate "bootloader" and "application" code, you can safely update the "application" code, the bootloader stays there, and I think there is no real risk of bricking the sensor. Just run the updater again.

Usage is pretty simple, upload the following code:

var CCS811_FW = require("CCS811_FW");

function onInit(){
  gas = CCS811_FW.connectI2C(I2C1, { nWake: A3 });

After upload, it will output the current version of the firmware in the sensor.

 ____                 _
|  __|___ ___ ___ _ _|_|___ ___
|  __|_ -| . |  _| | | |   | . |
|____|___|  _|_| |___|_|_|_|___|
 2v02 (c) 2018 G.Williams
Running onInit()...
CCS811 firmware updater
The version of firmware to use depends on the usage status of the device:
    - New fresh sensors use firmware 2-0-0  -> call gas.flash200()
    - Sensors run for a number of days use firmware 2-0-1  -> call gas.flash201()
| CCS811 FW - status register:
|  7: false Boot mode
|  6: false No erase completed
|  5: false No verify completed
|  4: true Valid firmware
|  3: false No data ready
|  1: false No error

Current fw version: 2.0.1, fwBootVersion: 16

To update, just type gas.flash200() or gas.flash201().

Example output:

>gas.flash201(true)   // note, I'm using the force update parameter, because already have updated to 2-0-1 version

CCS811 FW - Starting app update from v.2.0.1 -> v.2.0.1 fw length: 5112
CCS811 FW - Erase ok!
CCS811 FW - write @0
CCS811 FW - write @256
CCS811 FW - write @4864
CCS811 FW - update done, firmware valid: true
| CCS811 FW - status register:
|  7: false Boot mode
|  6: false No erase completed
|  5: true Verify completed
|  4: true Valid firmware
|  3: false No data ready
|  1: false No error


// Sets up watch, if needed, and not already set up.
CCS811.prototype._setupWatch = function () { ... }

// Shut down the CCS811
CCS811.prototype.stop = function () { ... }

// Returns true if data is available
CCS811.prototype.available = function () { ... }

/* Sets the mode (0 -> idle / 1 -> 1s / 2 -> 10s / 3 -> 60s)
 * @param {number} mode The new drive mode (0..4)
CCS811.prototype.setMode = function (mode) { ... }

// Reads the BASELINE register. See AMS AN000370 for details about baseline save and restore.
CCS811.prototype.readBaseline = function () { ... }

// Writes to the BASELINE register. See AMS AN000370 for details about baseline save and restore.
CCS811.prototype.writeBaseline = function (baseline) { ... }

/* Set humidity and temperature for compensation
 * @param {number} humidity The relative humidity in %
 * @param {number} temperature The temperature in °C
CCS811.prototype.setEnvData = function (humidity, temperature) { ... }

/* read the current environment settings, assuming available()==true.
 eCO2 : int, // equivalent CO2, in ppm (400..29206)
 TVOC : int, // Total Volatile Organic Compounds, in ppb (0..32768)
 new : bool  // true if this is a new reading
ec02 and TVOC values are clipped to the given ranges - so for instance you'll never see a CO2 value below 400.
CCS811.prototype.get = function () { ... }

/* Initialise the CCS811 module with the given I2C interface
options = {
  int : pin // optional - DRDY interrupt pin. If specified, 'data' event with data from 'get' will be emitted when data is ready
  addr: 0x5A | 0x5B // optional - I2C address of the CCS811, defaults to 0x5A. When ADDR pin of the CCS811 is low, the I2C address 0x5A. When ADDR is high the I2C address is 0x5B.
  mode: 0..4 // optional - Operation mode, defaults to 1: Constant power mode, IAQ measurement every second
  nWake: pin // optional - Connect a pin to the CCS811's nWake pin to the lower it's power consumption in Mode 2 or Mode 3
exports.connectI2C = function (i2c, options) { ... }

Firmware updater reference

CCS811_FW.prototype.printStatusReg = function (status) { ... }

CCS811_FW.prototype.readAppInfo = function () { ... }

// internals of firmware update
CCS811_FW.prototype._writeFirmware = function (fw, vMajor, vMinor, vTrivial, forceUpdate) { ... }

// writes firmware version 2.0.0
CCS811_FW.prototype.flash200 = function (forceUpdate) { ... }

// writes firmware version 2.0.1
CCS811_FW.prototype.flash201 = function (forceUpdate) { ... }

exports.connectI2C = function (i2c, options) { ... }


CCS811 can be bought on breakout boards from several different sellers:

This page is auto-generated from GitHub. If you see any mistakes or have suggestions, please let us know.