RoadTest: Sensirion Environmental Sensor Shield - Industrial Sensing
Author: xukangmin
Creation date:
Evaluation Type: Development Boards & Tools
Did you receive all parts the manufacturer stated would be included in the package?: True
What other parts do you consider comparable to this product?: SHT35, MiCS5524, CCS811
What were the biggest problems encountered?: Support on SBC is not good, have to dig around to get proper library.
Detailed Review:
This development board looks pretty simple to work with, and it is indeed. It designed with Arduino Uno footprint to work directly stack on Arduino like a shield. But if you have other boards such as Raspberry Pi, Tinker board, Onion. It also provides 4 pin connector for easy 3~5V I2C connection.
It has two environmental sensors, SHTC1 and SGP30. SHTC1 measures temperature and humidity, while SGP30 measures TVOCs and CO2 levels. Temperature and humidity are very common in the maker community. SGP30 is something new to me. It is metal oxide (MOX) gas sensor which measures TVOC and CO2.
I drew a circuit diagram based on the board. Basically this shield equipped with two level shifter for I2C bus, it can covert the digital signal from 5V/3.3V to 1.8V and vice versa. It can work directly with Arduino Uno or boards with similar footprint. It also has 3 LEDS that can be controlled by Arduino PIN 9, 10, 11. See diagram below. I also attach the schematic at the end. After I drew this, official schematic is available here , but I still want to share my (reverse-engineered) drawing.
Here is an overview of what this board offers:
Key Parameters | Typical Value |
VDD | 1.8V |
Measurement Current | 48.2mA |
Sleep current | 2µA |
Communication | I2C |
I2C Address | 0x58 |
TVOC Range | 0 ppb to 60000 ppb |
CO2 | 400 ppm to 60000 ppm |
Key Parameters | Typical Value |
VDD | 1.8V |
Measurement Current | 385µA |
Sleep current | 4.8µA |
Communication | I2C |
I2C Address | 0x70 |
Temperature Range | -30 to +100°C |
Temperature Accuracy | ±0.3°C |
Humidity Range | 0 to 100 %RH |
Humidity Accuracy | ±3.0 %RH |
TVOC stands for Total Volatile Organic Compound. TVOC is a grouping of wide range of organic chemical compounds including benzene, fossil fuels, methylene chloride, etc. It is a good indicator of indoor air quality. There are several sources of TVOC such as indoor furnishings, new construction materials, cleaning products. Excessive amount of TVOC may cause health issue for human bodies, such as allergies, asthma, dizziness, nausea and fatigue.
Here is a chart to indicate the effect of different TVOC levels.
Details can be found here. (https://www.wikiwand.com/en/Volatile_organic_compound)
TVOC Level Reference
TVOC [ppb] | Level | Recommendation | Exposure Limit |
2200 – 5500 | 5 Unhealthy | Use only if unavoidable / Intense ventilation necessary | hours |
660 – 2200 | 4 Poor | Intensified ventilation / airing necessary Search for sources | < 1 month |
220 – 660 | 3 Moderate | Intensified ventilation / airing necessary Search for sources | < 12 months |
65 – 220 | 2 Good | Ventilation / airing recommended | no limit |
0 – 65 | 1 Excellent | Target value | no limit |
Source: TVOC guidelines issued by the German Federal Environmental Agency
Carbon dioxide is a non-toxic and non-flammable gas, but excessive amount of CO2 still may do harm to people’s health, here is a chart for reference.
CO2 Level Reference
CO2 [ppm] | Level | Effects |
>20% | 8 | Rapid unconsciousness, death |
>10% | 7 | Nausea, vomiting, unconsciousness |
3-8% | 6 | Increased respiration rate, headache |
6,000 – 30,000 ppm | 5 | Concern, short exposure only |
5,000 ppm | 4 | Average exposure limit over 8-hour period |
1,000 ppm | 3 | Tolerable indoor air quality |
600 – 800 ppm | 2 | Acceptable indoor air quality |
350 – 450 ppm | 1 | Typical atmospheric concentration |
Setup is pretty easy, basically plug and play.
Here is my setup.
Currently there are two libraries we can directly use.
1. Official library https://github.com/Sensirion/arduino-ess
Test Results: (Temperature (C), Humidity (%), TVOC (ppb), CO2 (ppm) )
Note: TVOC and CO2 values are constant 0 and 400 in the first 15 seconds, that's SGP30's internal initialization state for measurement.
SGP30 detected, running feature set version 32
26.13 42.86 0.00 400.00
26.16 42.83 0.00 400.00
26.16 42.84 0.00 400.00
26.18 42.83 0.00 400.00
26.16 42.82 0.00 400.00
26.17 42.86 0.00 400.00
26.16 42.88 0.00 400.00
26.16 42.90 0.00 400.00
26.18 42.86 0.00 400.00
26.13 42.82 0.00 400.00
26.16 42.81 0.00 400.00
26.16 42.81 0.00 400.00
26.15 42.84 0.00 400.00
26.19 42.91 0.00 400.00
26.18 42.94 0.00 400.00
26.16 42.99 0.00 400.00
26.19 42.99 2.00 400.00
26.18 43.04 5.00 400.00
26.19 43.08 1.00 403.00
26.18 43.08 0.00 406.00
26.19 43.09 0.00 400.00
2. Adafruit Library for SGP30 https://github.com/adafruit/Adafruit_SGP30
Test Results: (Temperature (C), Humidity (%), TVOC (ppb), CO2 (ppm) )
SGP30 test
Found SGP30 serial #07AE4F3
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 400 ppm
TVOC 3 ppb eCO2 404 ppm
TVOC 6 ppb eCO2 407 ppm
TVOC 3 ppb eCO2 404 ppm
TVOC 3 ppb eCO2 400 ppm
TVOC 5 ppb eCO2 400 ppm
TVOC 0 ppb eCO2 405 ppm
Setup:
Directly connect to the 3.3V, GND, SCL, SDA PINS on Raspberry Pi
Use i2cdetect to check connection of the board, if you see 0x58, 0x70, the board is connected.
Follow this guide to install circuit python on Raspberry Pi, https://learn.adafruit.com/circuitpython-on-raspberrypi-linux/installing-circuitpython-on-raspberry-pi
By using the Adafruit Circuit Python SGP30 library, you can get this output, Notice first 15 seconds' data are constant 0 and 400 because of chip initialization.
There is no available python library for SHTC1, I wrote a simple one for testing. You can expand to a full library easily using circuit python.
import board import busio import time from adafruit_bus_device.i2c_device import I2CDevice i2c = busio.I2C(board.SCL, board.SDA) device = I2CDevice(i2c,0x70) def _get_data(): with device: device.write(bytes([0x7C,0xA2]), stop=False) time.sleep(0.015) result = bytearray(6) device.readinto(result) temp_arr = [result[0],result[1]] val = int.from_bytes(temp_arr, byteorder='big', signed=False) temp = -45 + 175 * (val / 65535.0); hum_arr = [result[3], result[4]] val = int.from_bytes(hum_arr, byteorder='big', signed=False) hum = 100 * (val / 65535.0) return [temp, hum] while True: time.sleep(1) [t,h] = _get_data() print('temp={0:.3f}, humidity={1:.3f}'.format(t,h))
Here is the ouput:
Setup:
Sparkfun ESP8266 Thing board + 2000mAh Li-Ion battery + Sensirion ESS + Adafruit IO
I use the box come with the sensor to create a mobile testing box, and cut two holes for two sensors to expose to the atmosphere. (ignore my bad cutting skill XD)
Here is the code for remote monitoring / testing, it's based on Adafruit.io library publish example
#include "config.h" #include <sensirion_ess.h> #include <Wire.h> SensirionESS ess; AdafruitIO_Feed *temp = io.feed("sensirion-ess-test.temp"); AdafruitIO_Feed *hum = io.feed("sensirion-ess-test.hum"); AdafruitIO_Feed *co2 = io.feed("sensirion-ess-test.co2"); AdafruitIO_Feed *tvoc = io.feed("sensirion-ess-test.tvoc"); int count = 0; void setup() { // start the serial connection Serial.begin(115200); delay(10000); // let console settle // wait for serial monitor to open while(! Serial); Serial.print("Connecting to Adafruit IO"); // connect to io.adafruit.com io.connect(); // wait for a connection while(io.status() < AIO_CONNECTED) { Serial.print("."); delay(500); } // we are connected Serial.println(); Serial.println(io.statusText()); if (ess.initSensors() != 0) { Serial.print("Error while initializing sensors: "); Serial.print(ess.getError()); Serial.print("\n"); while (1) { // loop forever delay(1000); Serial.println("error"); } } int type = ess.getProductType(); int fsVersion = ess.getFeatureSetVersion(); Serial.print((type == SensirionESS::PRODUCT_TYPE_SGP30) ? "SGP30" : "SGPC3"); Serial.print(" detected, running feature set version "); Serial.println(fsVersion); } void loop() { // io.run(); is required for all sketches. // it should always be present at the top of your loop // function. it keeps the client connected to // io.adafruit.com, and processes any incoming data. io.run(); float temp_val, rh_val, tvoc_val, eco2_val = -1; // we'll start by triggering a measurement of the VOC/CO2 sensor; // it's important to do this first to make sure sleep timing is // correct. If the command succeeds, the local variables will // be set to the values we just read; if it fails, they'll be -1 if (ess.measureIAQ() != 0) { Serial.print("Error while measuring IAQ: "); Serial.print(ess.getError()); Serial.print("\n"); } else { tvoc_val = ess.getTVOC(); eco2_val = ess.getECO2(); // SGP30 only } // next, we'll trigger the humidity and temperature measurement if (ess.measureRHT() != 0) { Serial.print("Error while measuring RHT: "); Serial.print(ess.getError()); Serial.print("\n"); } else { temp_val = ess.getTemperature(); rh_val = ess.getHumidity(); } // finally, let's print those to the serial console Serial.print(temp_val); Serial.print(" "); Serial.print(rh_val); Serial.print(" "); Serial.print(tvoc_val); Serial.print(" "); if (ess.getProductType() == SensirionESS::PRODUCT_TYPE_SGP30) { Serial.print(eco2_val); } Serial.println(); // and then, we'll use remainingWaitTimeMS() to ensure the correct // Measurement rate delay(ess.remainingWaitTimeMS()); count++; if (count >= 60) { count = 0; temp->save(temp_val); hum->save(rh_val); co2->save(eco2_val); tvoc->save(tvoc_val); } }
I collect one full day of data for each environment. It's kind of surprise to see how bad our indoor air quality is.
Bedroom
Generally the sensor is sensitive enough to capture any changes in a enclosed environment. Whenever an event happens that can increase air circulation, the sensor is able to capture that, such as Opening window, close windows, AC running. So increasing indoor air circulation is the key to maintain a healthy indoor environment.
Baby room
The trend seems similar to the bedroom.
Outdoor
Outdoor data seems pretty smooth as expected, TVOC is less than 20 ppb most of the time and CO2 is around 400 ppm. A weried thing is that spike in the middle of the day. I guess it's a fly or bug stick on the sensor since it's not a single value that is way over range, it's a series of out-of-range values.
The out-of-range data sets are like below. It seems like an actual bug than sensor firmware bugs.
11:18 AM 53 426 11:19 AM 42 406 11:20 AM 81 477 11:21 AM 80 456 11:22 AM 55 412 11:23 AM 79 457 11:25 AM 174 934 11:26 AM 617 5201 11:27 AM 3137 11169 11:28 AM 1772 3811 11:29 AM 1013 2031 11:30 AM 680 1502 11:31 AM 482 1011 11:32 AM 371 856 11:33 AM 386 1061 11:35 AM 333 739 11:36 AM 233 533 11:37 AM 284 726 11:38 AM 219 506
Basement
Our basement is not finished, it is a more or less enclosed environment. The data seems pretty smooth. It is more like a human indicator. When we are at home, the CO2 and TVOC level begin to climb slowly, when we left home, they began to drop.
I use a OLED from Amazon and this shield to build a simple environmental testing box, it can display current envriomental data readings and push the data to the cloud. (adafruit.io)
Components:
Diagram:
Code:
#include "config.h" #include <sensirion_ess.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define OLED_RESET LED_BUILTIN //4 Adafruit_SSD1306 display(OLED_RESET); #define NUMFLAKES 10 #define XPOS 0 #define YPOS 1 #define DELTAY 2 #define LOGO16_GLCD_HEIGHT 16 #define LOGO16_GLCD_WIDTH 16 static const unsigned char PROGMEM logo16_glcd_bmp[] = { B00000000, B11000000, B00000001, B11000000, B00000001, B11000000, B00000011, B11100000, B11110011, B11100000, B11111110, B11111000, B01111110, B11111111, B00110011, B10011111, B00011111, B11111100, B00001101, B01110000, B00011011, B10100000, B00111111, B11100000, B00111111, B11110000, B01111100, B11110000, B01110000, B01110000, B00000000, B00110000 }; #if (SSD1306_LCDHEIGHT != 64) #error("Height incorrect, please fix Adafruit_SSD1306.h!"); #endif // Create an instance of SensirionESS SensirionESS ess; /************************ Example Starts Here *******************************/ // this int will hold the current count for our sketch // set up the 'counter' feed AdafruitIO_Feed *temp = io.feed("sensirion-ess-test.temp"); AdafruitIO_Feed *hum = io.feed("sensirion-ess-test.hum"); AdafruitIO_Feed *co2 = io.feed("sensirion-ess-test.co2"); AdafruitIO_Feed *tvoc = io.feed("sensirion-ess-test.tvoc"); int count = 0; void setup() { // start the serial connection Serial.begin(115200); delay(10000); // let console settle // wait for serial monitor to open while(! Serial); Serial.print("Connecting to Adafruit IO"); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64) // connect to io.adafruit.com io.connect(); // wait for a connection while(io.status() < AIO_CONNECTED) { Serial.print("."); delay(500); } // we are connected Serial.println(); Serial.println(io.statusText()); if (ess.initSensors() != 0) { Serial.print("Error while initializing sensors: "); Serial.print(ess.getError()); Serial.print("\n"); while (1) { // loop forever delay(1000); Serial.println("error"); } } int type = ess.getProductType(); int fsVersion = ess.getFeatureSetVersion(); Serial.print((type == SensirionESS::PRODUCT_TYPE_SGP30) ? "SGP30" : "SGPC3"); Serial.print(" detected, running feature set version "); Serial.println(fsVersion); } void display_on_oled(float t, float rh, float tvoc, float co2) { // Clear the buffer. display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(0,0); display.print("Temp:"); display.println(t,2); display.print("Hum:"); display.println(rh,2); display.setTextSize(2); display.setTextColor(WHITE); display.print("TVOC:"); display.println(tvoc,2); display.print("CO2:"); display.println(co2,2); display.display(); } void loop() { // io.run(); is required for all sketches. // it should always be present at the top of your loop // function. it keeps the client connected to // io.adafruit.com, and processes any incoming data. io.run(); float temp_val, rh_val, tvoc_val, eco2_val = -1; // we'll start by triggering a measurement of the VOC/CO2 sensor; // it's important to do this first to make sure sleep timing is // correct. If the command succeeds, the local variables will // be set to the values we just read; if it fails, they'll be -1 if (ess.measureIAQ() != 0) { Serial.print("Error while measuring IAQ: "); Serial.print(ess.getError()); Serial.print("\n"); } else { tvoc_val = ess.getTVOC(); eco2_val = ess.getECO2(); // SGP30 only } // next, we'll trigger the humidity and temperature measurement if (ess.measureRHT() != 0) { Serial.print("Error while measuring RHT: "); Serial.print(ess.getError()); Serial.print("\n"); } else { temp_val = ess.getTemperature(); rh_val = ess.getHumidity(); } // finally, let's print those to the serial console Serial.print(temp_val); Serial.print(" "); Serial.print(rh_val); Serial.print(" "); Serial.print(tvoc_val); Serial.print(" "); if (ess.getProductType() == SensirionESS::PRODUCT_TYPE_SGP30) { Serial.print(eco2_val); } display_on_oled(temp_val, rh_val, tvoc_val, eco2_val); Serial.println(); // and then, we'll use remainingWaitTimeMS() to ensure the correct // Measurement rate delay(ess.remainingWaitTimeMS()); count++; if (count >= 60) { count = 0; temp->save(temp_val); hum->save(rh_val); co2->save(eco2_val); tvoc->save(tvoc_val); } }
Build & Results:
Summary
I have tested Sensirion Environmental Sensor Shield on both MCUs (Arduino Uno and ESP8266) and SBC (Raspberry Pi). It is basically plug-and-play in Arduino Eco System, the library is capable of providing accurate results. When using ESP8266, make sure you remove LED controlling codes in the official library because they are not compatible as of now.
While working on Linux, I need a bit of tinkering to get the board working correctly with the help of CircuitPython, but not too much pain. Thank you winkj for the Arduino ESS library and Adafruit IO for providing CircuitPython and SGP30 python library.
I like the hardware design of the board because it provides on-board level shifter so everyone from the 3.3V / 5V world can talk to 1.8V sensors easily.
For the sensors data testing, since I don't have other sensors for comparison, I test multiple locations around my house to see if the results make sense. It turns out it met my expectation. While there is a human activity, the TVOC and CO2 level will increase slightly. Air circulation will greatly help improving indoor air quality. I may perform a lab test for SHTC1 if I have a chance later. I will keep the results posted.
Thanks for your time.
Let me know if you have any questions or suggestions.
Top Comments
Previously there is a small bug in the official library, now it has been resolved.
I didn't contact Sensirion about this because I'm not sure it's a firmware bug.
I really don't know what happened…
Thank you!
I'm also planning to do a RH calibration on this sensor (SHTC1, +/- 3%) and comparing to SHT35 (their RH sensor with highest accuracy +/- 1.5%).
I will post the result here.