SGP30 on AirGradient board

Anyone add an SGP30 sensor to their board for monitoring TVOC? I ordered 2 of the linked modules from the article:
GY SGP30 Air Quality Gas Sensor Measurement Gas Sensor TVOC/eCO2 Air Quality Formaldehyde Carbon Dioxide Detector Tester|Sensors| - AliExpress
I hadn’t installed my temp/humidity sensor yet, so I soldered a 4 pin socket to the board, and 4 pin headers to the SGP30 and plugged it in but always got 0 TVOC from the AirGradient code and when trying to use ESPHome, I was getting nothing but errors.
I tried my second module in case I had a bad one and still had errors. I started trying out different libraries in Arduino IDE and they were all erroring out as well. Finally I found one reference for a different sensor (BMP180) that said to connect it to 3.3 v instead of 5v, so I decided to connect the SGP30 to 3.3v instead of 5v on my breadboard and it started working!
So while it may be pin layout compatible, it does not appear to be compatible with just putting it on the same location on the AirGradient board. I added my temp sensor to extra holes for i2c and power/ground so I’ll probably move the SGP30 to these wires and change it to 3.3 v and then move the temp sensor wires to the regular location for the extension to the new 3D printed case.

Suggestion for future board revisions, add extra standard header compatible hole locations so other sensors like this can be added directly to the board. The I2C OLED header area has enough space, but not the correct order to work with the SGP30, and especially not now that it looks like it wants 3.3 v. The Temp sensor location should maybe specify it is 5v instead of just VIN


Yes we consider adding the TVOC for one of the future iterations of the board.

To get it running with the AirGradient Library please check out the instructions I wrote a while ago

Hope that helps.

My main point is that your instructions make it sound like you can just remove the SHT30 and replace it with SGP30 but after hours of struggling with that, I discovered that at least in my case, I could not use it as a drop-in replacement since the SGP30 appears to need 3.3 v and does not function with 5v that the SHT30 is fine with.

So the instructions may need updated to call that out, or a different sensor link that would work with 5v correctly.

In my previous testing I was using the D1 mini on a breadboard with only the SGP30 when I discovered that (at least with ESPHome) it would not work when connected to 5v but worked as expected with 3.3v

Today I connected the SGP30 to my AirGradient board using 3.3v and I was still getting errors when I wasn’t on the breadboard. After further troubleshooting, I discovered that if I remove the OLED shield, then the SGP30 works as expected with 3.3v. If I plug in the OLED, even with all of the code to communicate to it removed, I get checksum errors on the SGP30. Very odd. For giggles I tried the SGP30 back on 5v on the AirGradient board without the OLED, and I still got errors, so it seems the combination of 3.3 v and no OLED is what makes it work with the AirGradient board, but for this particular sensor, I want the display.

I know the AirGradient official code is Arduino sketch, but for me, HomeAssistant Integration is more important and I have it configured to also send to the AirGradient API.

Unsure why the OLED would be conflicting with the SGP30 on a D1 Mini, but for now I guess I’ll just work without the SPG30. I have more AirGradient boards coming that I’ll use without a display and then I can use it.

1 Like

Hey @MallocArray - or anyone else - have you successfully gotten this working with the display? I just ordered a few DIY kits and if it’s possible to get TVOC readings somehow that’d be awesome… ideally with the temp/humidity still working hooked up in parallel. Any newly-discovered tips or gotchas?

I was also wondering if the BME680 from Bosch could be used instead, and if anyone had tried that.

I’ve stopped trying for right now. Since I can reproduce an issue as soon as that particular SPG30 model is plugged in with the OLED, I’m not sure if there is more to do, unless some resistors are needed. My next step is to add the SPG30 to a different D1 Mini that I have connected to an Ikea air quality sensor that does not have a display, so I can compare the CO2 readings between it and the one from the DIY kit. If they are comparable, then I may keep exploring with them, but may try a different model than the one AirGradient linked to, as it should work at 5v, but in my testing with 2 different ones, neither worked until I changed to 3.3 v

Just want to let you know that I ordered a few different SGP30 modules and will have a closer look once they arrive. Then will report back what I find.

1 Like

I was able to add the SPG30 to my kit. Based on what was stated here I plugged it to 3.3v power instead of the 5v. It appears to take a few minutes to warm up so you will only get zero values initially, the got some positive values after cooking close by.

I changed the code to call a Raspberry PI to save values every 15 seconds to a MariaDB. Then I used Grafana to build this dashboard and others with more detailed historical values.

1 Like

Do you also have the OLED shield installed on your AirGradient? The SGP30 I ordered said it should work with 5v, but didn’t, so if you have a different item, maybe it would work with 5v as we originally expected. I also suspect similar things with my conflicts with the OLED and SGP30 at 3.3v.

So I hope I didn’t come off as the only way for any device to ever work is at 3.3v, but with the particular hardware I had, my experience was 3.3 for the SGP30 and no OLED to get it working.

Yes, other than the SGP30, the rest is just the standard components of the AirGradient Kit with CO2 sensor. The spec of the SGP30 thatI have stated 2-5v, so I figured I just try with the 3.3v.

I am having the same problem with the SGP30 added to the base sensors and OLED. I have it wired up to the 3.3v breakout at the top. Calls to measureTest() come back False (failed), but isConnected() come back True. The readings are garbage, TVOC 0 and eCO2 is bogus numbers. Also, the getFeatureSet() call returns varying values instead of the expected 0x0022.

Try removing the OLED if you have it on headers and it can be easily done. I tried reaching out to the seller of my SGP30 about it not working at 5v but they said that since it works at 3.3v then I have a working item…

I did some testing today.

On the AirGradient DIY board, the i2c bus is on 5v. This seems to work fine for the temperature sensor SHT30 but only for some TVOC SGP30 modules.

It seems that the small black SGP module

Only works with 3,3v even though the specs also say it works for 5v.

whereas the triangular module

Seems to work also on 5v.

Probably the modules use different voltage regulators.

So I would suggest you get the triangular module or you can also connect the SGP30 sensor to 3.3v with the breakout pins on the board and using a jumper cable.

I hope that helps. Please also feedback your own experiences.

Could you post any links to the triangle module? Will it fit on the existing headers on the board, or does it take up too much room?

No, the triangular module does not fit in. You need to use cables.

You can find them on AliExpress from various vendors. Here is an example


I hope that this might help someone out on here. I was having the same trouble with the SGP30 - though actually running in parallel with the SHT sensor.

I wasn’t getting any readings from the SGP30, though all other sensors were working fine.

I specifically had this version of the sensor, in case that matters:

the sensor is attached 3v rail, and the headers across the left_hand side (The pin-out from this version was perfect)

Anyhow, I tested the sensor, using the SGP30_demo sketch from the library’s creator, and in this case the sensor worked, and all of TVOC, eCO2, H2 and Ethanol readings output via Serial and appeared correct and updated when a VOC source was brought near.

From there, I basically added the demo Setup and Loop instructions into the AirGradient sketch, and bingo, all of a sudden, the sensors were working.

I pared it down a bit, and the important bits appear to be:
void setup(): Change Serial.begin(9600); to Serial.begin(115200);
void loop(): Added SGP.measure(true);

I am not familiar enough with Arduino to understand what the issue is, but as long as I have the above code from the demo, the sensor works perfectly, and I can display the results to the screen.

Maybe someone a bit more knowledgable can explain better, but for now, the issue (with my sensor at least) has been solved.

1 Like

I did some more testing with the triangular SGP30 module

  • it works both on 9600 and 115200 rate
  • it works on both SGP.measure true and false

the small black module seems to work only with 3.3 v

Below is the code that works for me for the triangular TVOC sensor mentioned above.

#include <AirGradient.h>

#include "SGP30.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 = true;
boolean hasTVOC=true;

// set to true to switch PM2.5 from ug/m3 to US AQI
boolean inUSaqi = false;

// set to true to switch from Celcius to Fahrenheit
boolean inF = false;

// 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 (inUSaqi) {
      showTextRectangle("AQI", String(PM_TO_AQI_US(PM2)), false);
    } else {
      showTextRectangle("PM2", String(PM2), false);



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

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

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

    if (inF) {
      showTextRectangle(String((result.t * 9 / 5) + 32), String(result.rh) + "%", false);
    } else {
      showTextRectangle(String(result.t), String(result.rh) + "%", false);


  payload = payload + "}";

  // 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");


// Calculate PM2.5 US AQI
int PM_TO_AQI_US(int pm02) {
  if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0);
  else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50);
  else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100);
  else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150);
  else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200);
  else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300);
  else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400);
  else return 500;

I’m also having nothing but problems using this GY-SGP30 sensor.

Using Rob’s SGP30 library, it always throws CRC errors. Using the Adafruit SGP30 library, the Arduino reboots with an exception.

I’ve wired it to the 3v/G/SDA/SCL headers at the top of the PCB (had to reverse two of the pins) so I could use it in parallel with the SHT. If I remove the OLED, the SGP is not addressable at all. Switching to 5V has no effect. Switching from 9600 to 115200 baud has no effect. I2C scanner confirms it’s on the bus at address 0x58.

I’m at a loss as to what would cause CRC errors other than another chip interfering, or possibly a bad solder joint, but that seems unlikely with all of the reports here (and my, ahem, flawless soldering skills /sarcasm)

Did anyone make progress on this?

I just got this sensor today, testing it on an ESP32. seems to function just fine at 3.3 or 5v. Will test next on the airgradient board…

I have been messing around with this sensor with Jeff Gerrlings sketch.

I don’t see any weirdness like noise or anything like that, but I am seeing no data from the sensor yet.

On a separate ESP32 I get all of the expected data, no issues…

I also ran my test sketch (Adafruit Example) on the airgradient board and it works just fine. Pulls the expected data from the SPG30.