CCS811 Air Quality Breakout Hookup Guide
Introduction
The CCS811 Air Quality Breakout is a digital gas sensor solution that senses a wide range of Total Volatile Organic Compounds (TVOCs), including equivalent carbon dioxide (eCO2) and metal oxide (MOX) levels. It is intended for indoor air quality monitoring in personal devices such as watches and phones, but we've put it on a breakout board so you can use it as a regular I2C device.
SparkFun Air Quality Breakout - CCS811
SEN-14193Required Materials
To follow along with this project tutorial, you will need the following materials:
Suggested Reading
If you aren't familiar with the following concepts, we recommend checking out these tutorials before continuing.
How to Solder: Through-Hole Soldering
Installing an Arduino Library
How to Use a Breadboard
I2C
Hardware Overview
The CCS811 is supported by only a few passives, and so the breakout board is relatively simple. This section discusses the various pins on the board.
Pins
Pin | Description | Direction |
---|---|---|
RST | Reset (active low) | In |
INT | Interrupt (active low) | Out |
WAK | Wake (active low) | In |
SCL | Clock | In |
SDA | Data | In |
3.3V | Power | In |
GND | Ground | In |
NTC (2 pins) | Negative thermal coefficient resistor | N/A |
Power and I2C Bus
The minimum required connections are power, ground SDA and SCL. Supply a regulated 3.3V between the board's 3.3V pin and ground terminals. The sensor consumes an average of 12mA of current.
The I2C bus has pull-up resistors enabled by default. If not desired, these can be removed by separating the "I2C PU" triple jumper on the bottom side with a hobby knife.
An I2C address can be either 0x5A or 0x5B. The "ADDR" jumper is connected with copper from the factory, corresponding to a default address of 0x5B. Close this jumper to use the address 0x5A.
Control lines
Additionally, the three control lines RST, INT and WAK can be used to further the degree of control.
- RST --- Pull this line low to reset the IC.
- INT --- After configuring the sensor to emit interrupt requests, read this line to determine the state of the interrupt.
- WAK --- Pull this line high to put the sensor to sleep. This can be used to save power but is not necessary if power is not an issue.
NTC Thermistor operation
A thermistor can be used to determine the temperature of the CCS811's surroundings, which can be used to help compensate the readings. You'll need your own 10K NTC thermistor, such as our 10K Thermistor, soldered between the "NTC" pins. A thermistor is a nonpolarized device, so it can go in either way.
Hardware Assembly
Attach Headers
To prepare the sensor for the examples, attach seven pins from a Break Away Header to the through holes. Even though we only need the four I2C pins, we'll populate all of them for this guide in case we want to try them out.
Attach NTC thermistor (Optional)
If you would like to use a thermistor to compensate for temperature, solder in a 10K Thermistor (Vishay part number NTCLE100E3103JB0).
Example Assemblies
You're ready to start communicating with the CCS811! Here's an example with the NTC Thermistor populated, and one using right-angle headers instead.
Arduino Library and Usage
Getting the CCS811 Arduino Library
To get the Arduino library, download from GitHub or use the Arduino Library Manager.
Download the GitHub repository
Visit the GitHub repository to download the most recent version of the library, or click the button below:
Use the Library Manager or install in the Arduino IDE
For help installing the library, check out our Installing an Arduino Library tutorial.
If you don't end up using the manager, you'll need to move the SparkFun_CCS811_Arduino_Library folder into a libraries folder within your Arduino sketchbook. If you downloaded the zip, you can remove "master" from the name, but it's not required.
Using the 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, pass the I2C address to the object during construction.
To get the library included, and to take care of all the gritty compiler stuff, place the following at the beginning of the sketch before void setup()
//#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 wire stuff is kept under the hood.
To make the sensor get ready during program boot, myCCS811.begin()
must be called. Here's an example of the minimal usage of begin.
void setup() { myCCS811.begin(); }
Then, in the main loop of the program, call sensor functions such as mySensor.readAlgorithmResults()
to operate the sensor. The following snippet shows a simple check for data, to call on the sensor to calculate and get values, and to access those values. It doesn't do anything with the data, though! Check out the examples for fully functional code.
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.
bool begin( TwoWire = Wire )
--- Checks the ID register, checks for valid app data, starts the app, and establishes a drive mode.CCS811Core::status beginWithStatus( TwoWire = Wire )
--- Checks the ID register, checks for valid app data, starts the app, and establishes a drive mode. Returns the status.const char *CCS811::statusString(CCS811_Status_e stat)
--- Returns a printable string of the status value.CCS811Core::status readAlgorithmResults( void )
--- Call to cause the sensor to read its hardware and calculate TVOC and eCO2 levels.bool checkForStatusError( void )
--- Returnstrue
if there is an error pending. This checks the status register.bool dataAvailable( void )
--- Returnstrue
if a new sample is ready and hasn't been read.bool appValid( void )
--- Returnstrue
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 (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
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).
Unsupported Functions
Temperature compensation from an attached NTC thermistor is no longer supported on the CCS811 but these functions are still included in the library in case you are using an older version of this breakout (boards purchased in 2017).
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.CCS811Core::status readNTC( void )
--- Cause the CCS811 to get and calculate a temperature from the thermistor input.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 the following 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 if they are needed, here are the values the data type status
can hold:
// Return values typedef enum { CCS811_Stat_SUCCESS, CCS811_Stat_ID_ERROR, CCS811_Stat_I2C_ERROR, CCS811_Stat_INTERNAL_ERROR //... } CCS811_Status_e;
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::CCS811_Status_e 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:
CCS811Core::CCS811_Status_e returnCode = mySensor.beginCore(); Serial.print("beginCore exited with: "); switch ( returnCode ) { case CCS811Core::CCS811_Stat_SUCCESS: Serial.print("SUCCESS"); break; case CCS811Core::CCS811_Stat_ID_ERROR: Serial.print("ID_ERROR"); break; case CCS811Core::CCS811_Stat_I2C_ERROR: Serial.print("I2C_ERROR"); break; case CCS811Core::CCS811_Stat_INTERNAL_ERROR: Serial.print("INTERNAL_ERROR"); break; case CCS811Core::CCS811_Stat_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.
//Register addresses
Example: Basic Reading
After you've got pins attached to your breakout board, the first example to use should be BasicReadings. Select it from examples or copy from below.
Connect the sensor as follows as a starting place for the examples.
For this example, only 3.3V, GND, SDA and SCL are needed. The jumpers on the board are left in the default positions.
/****************************************************************************** Read basic CO2 and TVOCs Marshall Taylor @ SparkFun Electronics Nathan Seidle @ SparkFun Electronics April 4, 2017 https://github.com/sparkfun/CCS811_Air_Quality_Breakout https://github.com/sparkfun/SparkFun_CCS811_Arduino_Library Read the TVOC and CO2 values from the SparkFun CSS811 breakout board A new sensor requires at 48-burn in. Once burned in a sensor requires 20 minutes of run in before readings are considered good. Hardware Connections (Breakoutboard to Arduino): 3.3V to 3.3V pin GND to GND pin SDA to A4 SCL to A5 ******************************************************************************/ //#define CCS811_ADDR 0x5A //Alternate I2C Address CCS811 mySensor(CCS811_ADDR); void setup() { Serial.begin(115200); Serial.println("CCS811 Basic Example"); Wire.begin(); //Inialize I2C Hardware if (mySensor.begin() == false) { Serial.print("CCS811 error. Please check wiring. Freezing..."); while (1) ; } } void loop() { //Check to see if data is ready with .dataAvailable() if (mySensor.dataAvailable()) { //If so, have the sensor read and calculate the results. //Get them later mySensor.readAlgorithmResults(); Serial.print("CO2["); //Returns calculated CO2 reading Serial.print(mySensor.getCO2()); Serial.print("] tVOC["); //Returns calculated TVOC reading Serial.print(mySensor.getTVOC()); Serial.print("] millis["); //Display the time since program start Serial.print(millis()); Serial.print("]"); Serial.println(); } delay(10); //Don't spam the I2C bus }
At the beginning, an object is created in the global space CCS811 mySensor(CCS811_ADDR);
and is constructed with the address as a parameter.
To get data from the sensor, mySensor.dataAvailable()
is checked until a new reading is ready, mySensor.readAlgorithmResults();
is called to have the sensor process the reading, then mySensor.getCO2()
and mySensor.getTVOC()
are used to retrieve the calculated values for gas levels.
If everything is connected correctly, the serial window will report gas levels every second. Remember the sensor takes 20 minutes to properly warm up, so values reported will rise up in the early stages of operation!
Summary:
To get data from the CCS811, these minimum requirements must be met:
- Create a
CCS811
object in the global space - Run
.begin()
of your object (Return type monitoring optional) - Check for the availability of new data with
.dataAvailable()
- Use
.readAlgorithmResults()
to perform a measurement.getCO2()
to get the last equivalent CO2 reading (no I2C bus operation).getTVOC()
to get the last TVOC reading (no I2C bus operation)
Example: Additional Control Lines
The CCS811 has a couple extra control lines that are not part of the I2C bus, which can be utilized to improve the system. There's a pin to flag that data is ready, and a pin to make the sensor go to sleep.
To connect the interrupt line, connect it directly to an input pin. This is a 3.3V output from the sensor, so it's OK to drive the input logic of a 5V device from it. The example has nInt connected to pin 6.
To connect the wake stat line, use a voltage divider to divide the 5V coming from the Arduino down to something below 3.3V for the sensor. The example has nWake connected to pin 5 through a voltage divider made from two 4.7K resistors for a 2.5V output.
The example for these additional lines is called WakeAndInterrupt and is listed here:
/****************************************************************************** WakeAndInterrupt.ino Marshall Taylor @ SparkFun Electronics April 4, 2017 https://github.com/sparkfun/CCS811_Air_Quality_Breakout https://github.com/sparkfun/SparkFun_CCS811_Arduino_Library This example configures the nWAKE and nINT pins. The interrupt pin is configured to pull low when the data is ready to be collected. The wake pin is configured to enable the sensor during I2C communications Hardware Connections (Breakoutboard to Arduino): 3.3V to 3.3V pin GND to GND pin SDA to A4 SCL to A5 NOT_INT to D6 NOT_WAKE to D5 (For 5V arduinos, use resistor divider) D5--- | R1 = 4.7K | --------NOT_WAKE | R2 = 4.7K | GND Resources: Uses Wire.h for i2c operation Development environment specifics: Arduino IDE 1.8.1 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. ******************************************************************************/ //#define CCS811_ADDR 0x5A //Alternate I2C Address CCS811 myCCS811(CCS811_ADDR); //Global sensor object //--------------------------------------------------------------- void setup() { //Start the serial Serial.begin(9600); Serial.println(); Serial.println("..."); CCS811Core::status returnCode; //This begins the CCS811 sensor and prints error status of .begin() returnCode = myCCS811.begin(); Serial.print("CCS811 begin exited with: "); printDriverError( returnCode ); Serial.println(); //This sets the mode to 60 second reads, and prints returned error status. returnCode = myCCS811.setDriveMode(2); Serial.print("Mode request exited with: "); printDriverError( returnCode ); Serial.println(); //Configure and enable the interrupt line, //then print error status pinMode(PIN_NOT_INT, INPUT_PULLUP); returnCode = myCCS811.enableInterrupts(); Serial.print("Interrupt configuation exited with: "); printDriverError( returnCode ); Serial.println(); //Configure the wake line pinMode(PIN_NOT_WAKE, OUTPUT); digitalWrite(PIN_NOT_WAKE, 1); //Start asleep } //--------------------------------------------------------------- void loop() { //Look for interrupt request from CCS811 if (digitalRead(PIN_NOT_INT) == 0) { //Wake up the CCS811 logic engine digitalWrite(PIN_NOT_WAKE, 0); //Need to wait at least 50 us delay(1); //Interrupt signal caught, so cause the CCS811 to run its algorithm myCCS811.readAlgorithmResults(); //Calling this function updates the global tVOC and CO2 variables Serial.print("CO2["); Serial.print(myCCS811.getCO2()); Serial.print("] tVOC["); Serial.print(myCCS811.getTVOC()); Serial.print("] millis["); Serial.print(millis()); Serial.print("]"); Serial.println(); //Now put the CCS811's logic engine to sleep digitalWrite(PIN_NOT_WAKE, 1); //Need to be asleep for at least 20 us delay(1); } delay(1); //cycle kinda fast } //printDriverError decodes the CCS811Core::status type and prints the //type of error to the serial terminal. // //Save the return value of any function of type CCS811Core::status, then pass //to this function to see what the output was. void printDriverError( CCS811Core::status errorCode ) { switch ( errorCode ) { 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."); } } //printSensorError gets, clears, then prints the errors //saved within the error register. void printSensorError() { uint8_t error = myCCS811.getErrorRegister(); if ( error == 0xFF ) //comm error { Serial.println("Failed to get ERROR_ID register."); } else { Serial.print("Error: "); if (error & 1 << 5) Serial.print("HeaterSupply"); if (error & 1 << 4) Serial.print("HeaterFault"); if (error & 1 << 3) Serial.print("MaxResistance"); if (error & 1 << 2) Serial.print("MeasModeInvalid"); if (error & 1 << 1) Serial.print("ReadRegInvalid"); if (error & 1 << 0) Serial.print("MsgInvalid"); Serial.println(); } }
Notice that this example doesn't poll dataAvailable()
to check if data is ready; instead it reads the state of a digital input. When the input is low, data is ready in the sensor and readAlgorithmResults()
, then .getCO2()
and getTVOC()
are used as normal.
The WAK pin can be used to control the logic engine on the CCS811 to save a bit of power. When the WAK pin is low (or disconnected), the I2C bus will respond to commands, but when the pin is high it will not. Tens of microseconds are required to wake or sleep, so in this example, commands are wrapped with a 1ms delay.
The terminal displays the calculation every 10 seconds. You can see that it take a few samples for the algorithm to spit out data, even at a slow rate of acquisition. Between the sampling, power is decreased as much as possible.
Summary:
To use WAK,
- Set up a digital output for the wake pin
- To communicate with a sleeping sensor,
- Set WAK low
- Wait 50us
- Do your communication
- Set WAK high
- Wait 20us
To use INT,
- Set up a digital input for the interrupt pin
- Enable interrupts with enableInterrupts()
- Look for a falling edge on the input to detect the availability of new data
Example: Compensating for Climate
To have the CCS811 compensate for pressure and temperature conditions, obtain those metrics and pass to the sensor object with setEnvironmentalData
.
The examples from the library show three different sources of data that can be used to calibrate the CCS811:
- Randomly generated temperature and humidity data
- Data from a supplemental BME280 sensor
- Data collected by reading the NTC pins (No longer supported)
This guide only shows the example that uses randomized data, as it can be used without additional components yet still illustrate the effects of different climates.
From Arduino Library and Usage,
status_t 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
Compensating with Random Data
A starting place for working with the compensation is the setEnvironmentalReadings example. After the same configuration from the basic example, this sketch applies a random temperature and humidity, then takes 10 reads and repeats.
/****************************************************************************** setEnvironmentalReadings.ino Marshall Taylor @ SparkFun Electronics April 4, 2017 https://github.com/sparkfun/CCS811_Air_Quality_Breakout https://github.com/sparkfun/SparkFun_CCS811_Arduino_Library Hardware Connections (Breakoutboard to Arduino): 3.3V to 3.3V pin GND to GND pin SDA to A4 SCL to A5 Generates random temperature and humidity data, and uses it to compensate the CCS811. This just demonstrates how the algorithm responds to various compensation points. Use NTCCompensated or BME280Compensated for real-world examples. Resources: Uses Wire.h for i2c operation Development environment specifics: Arduino IDE 1.8.1 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. ******************************************************************************/ float temperatureVariable = 25.0; //in degrees C float humidityVariable = 65.0; //in % relative //#define CCS811_ADDR 0x5A //Alternate I2C Address CCS811 myCCS811(CCS811_ADDR); void setup() { Serial.begin(9600); Serial.println("CCS811 EnvironmentalReadings Example"); //This begins the CCS811 sensor and prints error status of .begin() CCS811Core::status returnCode = myCCS811.begin(); Serial.print("begin exited with: "); printDriverError( returnCode ); Serial.println(); } void loop() { Serial.println(); //Randomize the Temperature and Humidity humidityVariable = (float)random(0, 10000)/100; //0 to 100% temperatureVariable = (float)random(500, 7000)/100; // 5C to 70C Serial.println("New humidity and temperature:"); Serial.print(" Humidity: "); Serial.print(humidityVariable, 2); Serial.println("% relative"); Serial.print(" Temperature: "); Serial.print(temperatureVariable, 2); Serial.println(" degrees C"); myCCS811.setEnvironmentalData(humidityVariable, temperatureVariable); Serial.println("Environmental data applied!"); myCCS811.readAlgorithmResults(); //Dump a reading and wait delay(1000); //Print data points for( int i = 0; i < 10; i++) { if (myCCS811.dataAvailable()) { //Calling readAlgorithmResults() function updates the global tVOC and CO2 variables myCCS811.readAlgorithmResults(); Serial.print("CO2["); Serial.print(myCCS811.getCO2()); Serial.print("] tVOC["); Serial.print(myCCS811.getTVOC()); Serial.print("] millis["); Serial.print(millis()); Serial.print("]"); Serial.println(); } else if (myCCS811.checkForStatusError()) { //If the CCS811 found an internal error, print it. printSensorError(); } delay(1000); //Wait for next reading } } //printDriverError decodes the CCS811Core::status type and prints the //type of error to the serial terminal. // //Save the return value of any function of type CCS811Core::status, then pass //to this function to see what the output was. void printDriverError( CCS811Core::status errorCode ) { switch( errorCode ) { 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."); } } //printSensorError gets, clears, then prints the errors //saved within the error register. void printSensorError() { uint8_t error = myCCS811.getErrorRegister(); if( error == 0xFF )//comm error { Serial.println("Failed to get ERROR_ID register."); } else { Serial.print("Error: "); if (error & 1 << 5) Serial.print("HeaterSupply"); if (error & 1 << 4) Serial.print("HeaterFault"); if (error & 1 << 3) Serial.print("MaxResistance"); if (error & 1 << 2) Serial.print("MeasModeInvalid"); if (error & 1 << 1) Serial.print("ReadRegInvalid"); if (error & 1 << 0) Serial.print("MsgInvalid"); Serial.println(); } }
Compensating with BME280 Data
If you have a BME280 sensor, they work great for getting the compensation parameters. Use the example BME280Compensated to see compensation using another sensor.
Connecting the two devices is as simple as putting them on the bus together.
View BME280Compensated.ino on github, or use the example from Arduino.
Compensating from NTC Thermistor Readings
Alternately, an NTC resistor can be placed in the provided PTH terminals, and the example PTHCompensated can be used to see how the internal ADC is used to calibrate for temperature only.
There is one caveat to this method: no humidity data! Partially compensated is better than uncompensated, so punch in an average humidity for your area, or leave the example's default at 50 percent.
View NTCCompensated.ino on github, or use the example from Arduino.
Resources and Going Further
Now that you've successfully got your CCS811 breakout up and running, and have scientifically proved which family member is the smelliest, here are some additional resources:
- CCS811 Datasheet-DS000459.pdf --- PDF
- ASHRAE Allowable CO2 Levels.pdf --- PDF
- CC-000774-AN-3-Assembly guidelines for CCS811.pdf --- PDF
- CC-000783-AN-1-Mechanical Considerations for CCS811_0.pdf --- PDF
- CCS811 Firmware Download AN000371.pdf --- PDF
- CCS811 Programming Guide-AN000369.pdf --- PDF
- CCS811 Thermistor Interface AN000372.pdf --- PDF
- Indoor Air Quality Investigations TVOCs EU.pdf --- PDF
- CCS811_Air_Quality_Breakout product repository --- Design files and docs
- SparkFun_CCS811_Arduino_Library Repository --- Source and example files for the Arduino library used in this tutorial.
Also, the following examples are included with the library but not discussed. They may help you on your way!
- BaselineOperator --- Save and restore baselines to the EEPROM
- Core --- Show how the underlying hardware object works
- TwentyMinuteTest --- Report data with timestamp
Need some inspiration for your next project? Check out some of these sensor-related tutorials:
SparkFun Inventor's Kit for micro:bit Experiment Guide
LilyPad Safety Scarf
Qwiic Scale Hookup Guide
SparkFun Qwiic Digital Temperature Sensor - TMP102 Hookup Guide
Or maybe check out our tests using the sensor at SparkFun!