TVOC on the AirGradient DIY Sensor


We do no longer recommend to use the SGP30 on the AirGradient board as the removal of the pull up reisitors is difficult and some people still experienced issues. If you want a stable and working version, please get the AirGradient SGP41 TVOC/NOx sensor from our shop.

There are small boards with the Sensirion SGP30 TVOC sensor that is pin compatible to the SHT30 temperature and humidity sensor that we already use on the board. So it is easy to use this TVOC sensor as a drop in replacement.

If you buy one, please make sure it is pin compatible. There are different versions available.

Here is an example of a pin compatible one available on Ali Express.

To adjust the code, please proceed the following:

In the Arduino library manager install the SGP30 library by Rob Tillaart. I used version 0.1.4 that works well.

This library comes with a sample example code that should work out of the box. You can then add that code to the existing code that you already use for the AirGradient DIY sensor relatively easily.

By the way, since both SHT30 (Temperature/Humidity Sensor) and the SGP30 use the i2c bus, you can theoretically also use both in parallel by connecting one of them to the available SCL, SDA breakout pins on the AirGradient DIY board.

However since all I2C modules already have pull-up resistors on the I2C bus, the system might become unstable.

In this case, removing the pull-up resistors on any of the modules will help. It is easiest to remove it on the SHT temperature / humidity module with a soldering iron. The following picture indicates which resistors need to be removed:

Pull-Up resistors to be removed on SHT3x module

Here is the example code, if you send data to the AirGradient dashboard.

This is the code for the AirGradient DIY Air Quality Sensor with an ESP8266 Microcontroller.

It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi.

For build instructions please visit

Compatible with the following sensors:
Plantower PMS5003 (Fine Particle Sensor)
SenseAir S8 (CO2 Sensor)

Please install ESP8266 board manager (tested with version 3.0.0)

The codes needs the following libraries installed:
"WifiManager by tzapu, tablatronix" tested with Version 2.0.3-alpha
"ESP8266 and ESP32 OLED driver for SSD1306 displays by ThingPulse, Fabrice Weinberg" tested with Version 4.1.0
"SGP30" by Rob Tillaart tested with Version 0.1.4 

Please set in the code below which sensor you are using and if you want to connect it to WiFi.

If you are a school or university contact us for a free trial on the AirGradient platform.

MIT License

#include "SGP30.h"

#include <AirGradient.h>
#include <WiFiManager.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

#include <Wire.h>
#include "SSD1306Wire.h"

AirGradient ag = AirGradient();

SSD1306Wire display(0x3c, SDA, SCL);

// set sensors that you do not use to false
boolean hasPM=true;
boolean hasCO2=true;
boolean hasSHT=false;
boolean hasTVOC=true;

// set to true if you want to connect to wifi. The display will show values only when the sensor has wifi connection
boolean connectWIFI=true;

// change if you want to send the data to another server
String APIROOT = "";

void setup(){

  showTextRectangle("Init", String(ESP.getChipId(),HEX),true);

  if (hasTVOC) SGP.begin();
  if (hasPM) ag.PMS_Init();
  if (hasCO2) ag.CO2_Init();
  if (hasSHT) ag.TMP_RH_Init(0x44);

  if (connectWIFI) connectToWifi();

void loop(){

  if (hasTVOC) SGP.measure(false);   

    // create payload

  String payload = "{\"wifi\":" + String(WiFi.RSSI()) + ",";

  if (hasPM) {
    int PM2 = ag.getPM2_Raw();
    payload=payload+"\"pm02\":" + String(PM2);

    if (hasTVOC) {
    if (hasPM) payload=payload+",";
    int TVOC = SGP.getTVOC();
    payload=payload+"\"tvoc\":" + String(TVOC);

  if (hasCO2) {
    if (hasTVOC) payload=payload+",";
    int CO2 = ag.getCO2_Raw();
    payload=payload+"\"rco2\":" + String(CO2);

  if (hasSHT) {
    if (hasCO2 || hasPM) payload=payload+",";
    TMP_RH result = ag.periodicFetchData();
    payload=payload+"\"atmp\":" + String(result.t) +   ",\"rhum\":" + String(result.rh);


  // send payload
  if (connectWIFI){
  String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(),HEX) + "/measures";
  WiFiClient client;
  HTTPClient http;
  http.begin(client, POSTURL);
  http.addHeader("content-type", "application/json");
  int httpCode = http.POST(payload);
  String response = http.getString();

void showTextRectangle(String ln1, String ln2, boolean small) {
  if (small) {
  } else {
  display.drawString(32, 16, ln1);
  display.drawString(32, 36, ln2);

// Wifi Manager
void connectToWifi(){
  WiFiManager wifiManager;
  //WiFi.disconnect(); //to delete previous saved hotspot
  String HOTSPOT = "AIRGRADIENT-"+String(ESP.getChipId(),HEX);
  if(!wifiManager.autoConnect((const char*)HOTSPOT.c_str())) {
      Serial.println("failed to connect and hit timeout");


We will also add this example code into our library in one of the next updates.