AirGradient Forum

New AirGradient DIY Kit Version 3 Feedback and Discussion

You can try a few things:
a) See if you can detect the SGP30 with an i2c scanner (can be installed from Arduino library). It should then output you the i2c addresses for OLED, SHT and SGP. If not it could indicate a connection issue or the sensor being dead.
b) Install a dedicated SGP30 library and some sample code and see if you can get it running
c) Make sure you wait a few hours. The SGP30 needs a burn in time and will not deliver values for a few hours.

I also used an I2C scanner, and it was only finding the following:

  • 0x3C (OLED?)
  • 0x44 (SH3x?)

It doesn’t find the SGP30 as I would expect (at 0x58), and modifying the AQ code shows that indeed the call to SGP.begin() fails. I’ve tried the SGP30 in both the I2C 3v3 location, and the I2C 5V location with no difference. As I ordered two of these kits, I’ve also tried an alternate SGP30 from the other kit, but with the same result.

My scanning code:

/*
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 https://www.airgradient.com/diy/

Instructions on using the TVOC sensor (SGP30) instead of the Temperature / Humidity sensor (SHT3x).

https://www.airgradient.com/resources/tvoc-on-airgradient-diy-sensor/

The codes needs the following libraries installed:
“WifiManager by tzapu, tablatronix” tested with version 2.0.11-beta
“U8g2” by oliver tested with version 2.32.15
“SGP30” by Rob Tilaart tested with Version 0.1.5

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

If you have any questions please visit our forum at https://forum.airgradient.com/

If you are a school or university contact us for a free trial on the AirGradient platform.
https://www.airgradient.com/schools/

MIT License

*/


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

#include "SGP30.h"
#include <U8g2lib.h>

AirGradient ag = AirGradient();
SGP30 SGP;
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

unsigned long currentMillis = 0;

const int oledInterval = 5000;
unsigned long previousOled = 0;

const int sendToServerInterval = 10000;
unsigned long previoussendToServer = 0;

const int tvocInterval = 1000;
unsigned long previousTVOC = 0;
int TVOC = 0;

const int co2Interval = 5000;
unsigned long previousCo2 = 0;
int Co2 = 0;

const int pm25Interval = 5000;
unsigned long previousPm25 = 0;
int pm25 = 0;

const int tempHumInterval = 2500;
unsigned long previousTempHum = 0;
float temp = 0;
int hum = 0;

String APIROOT = "http://hw.airgradient.com/";

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


void setup()
{
  Serial.begin(115200);

  u8g2.begin();
  updateOLED();

    if (connectWIFI) {
    connectToWifi();
  }

  updateOLED2("Warming up the", "sensors.", "");

  if (SGP.begin())
  {
    Serial.println("SGP Connected");
  } else
  {
    Serial.println("ERROR: SGP not found");
  }

  SGP.GenericReset();
  
  ag.CO2_Init();
  ag.PMS_Init();
  ag.TMP_RH_Init(0x44);
}


void loop()
{
  currentMillis = millis();
  updateTVOC();
  updateOLED();
  updateCo2();
  updatePm25();
  updateTempHum();
//  sendToServer();
}

void updateTVOC()
{
    if (currentMillis - previousTVOC >= tvocInterval) {
      previousTVOC += tvocInterval;
      SGP.measure(true);
      TVOC = SGP.getTVOC();
      Serial.println("TVOC: " + String(TVOC));
    }
}

void updateCo2()
{
    if (currentMillis - previousCo2 >= co2Interval) {
      previousCo2 += co2Interval;
      Co2 = ag.getCO2_Raw();
      Serial.println("CO2: " + String(Co2));
    }
}

void updatePm25()
{
    if (currentMillis - previousPm25 >= pm25Interval) {
      previousPm25 += pm25Interval;
      pm25 = ag.getPM2_Raw();
      Serial.println("PM25: " + String(pm25));
    }
}

void updateTempHum()
{
    if (currentMillis - previousTempHum >= tempHumInterval) {
      previousTempHum += tempHumInterval;
      TMP_RH result = ag.periodicFetchData();
      temp = result.t;
      hum = result.rh;
      Serial.println("Temp: " + String(temp));
      Serial.println("Hum: " + String(hum));
    }
}

void updateOLED() {
   if (currentMillis - previousOled >= oledInterval) {
     previousOled += oledInterval;
     String ln1;
     String ln2;
      if (inUSaqi) {
        ln1 = "CO2:" + String(Co2) + " AQI:" + String(PM_TO_AQI_US(pm25));
        } else {
        ln1 = "CO2:" + String(Co2) + " PM:" + String(pm25);
       }

      if (inF) {
        ln2 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%";
        } else {
        ln2 = "C:" + String(temp) + " H:" + String(hum)+"%";
       }

     String ln3 = "TVOC:" + String(TVOC) ;
     updateOLED2(ln1, ln2, ln3);
   }
}

void updateOLED2(String ln1, String ln2, String ln3) {
      char buf[9];
        u8g2.firstPage();
          u8g2.firstPage();
          do {
          u8g2.setFont(u8g2_font_t0_16_tf);
          u8g2.drawStr(1, 10, String(ln1).c_str());
          u8g2.drawStr(1, 30, String(ln2).c_str());
          u8g2.drawStr(1, 50, String(ln3).c_str());
            } while ( u8g2.nextPage() );
}

void sendToServer() {
   if (currentMillis - previoussendToServer >= sendToServerInterval) {
     previoussendToServer += sendToServerInterval;
      String payload = "{\"wifi\":" + String(WiFi.RSSI())
      + ", \"rco2\":" + String(Co2)
      + ", \"pm02\":" + String(pm25)
      + ", \"tvoc\":" + String(TVOC)
      + ", \"atmp\":" + String(temp)
      + ", \"rhum\":" + String(hum)
      + "}";

      if(WiFi.status()== WL_CONNECTED){
        Serial.println(payload);
        String POSTURL = APIROOT + "sensors/airgradient:" + String(ESP.getChipId(), HEX) + "/measures";
        Serial.println(POSTURL);
        WiFiClient client;
        HTTPClient http;
        http.begin(client, POSTURL);
        http.addHeader("content-type", "application/json");
        int httpCode = http.POST(payload);
        String response = http.getString();
        Serial.println(httpCode);
        Serial.println(response);
        http.end();
      }
      else {
        Serial.println("WiFi Disconnected");
      }
   }
}

// Wifi Manager
 void connectToWifi() {
   WiFiManager wifiManager;
   //WiFi.disconnect(); //to delete previous saved hotspot
   String HOTSPOT = "AG-" + String(ESP.getChipId(), HEX);
   updateOLED2("To setup connect", "to Wifi Hotspot", HOTSPOT);
   wifiManager.setTimeout(120);
   if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) {
     Serial.println("failed to connect and hit timeout");
     delay(3000);
     ESP.restart();
     delay(5000);
   }
}

// 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;
};```

It seems the SGPs are broken. Sorry for that. Please contact our support to sort this out at https://www.airgradient.com/support/

Yes I am using that code. I think my SGP30 is broken as well. (Ignore PM being 0, that is working fine. Other AirGradient v2 kit was reporting 1 ug/mÂł)

EDIT: I just noticed the display isn’t updating. It’s stuck with the same values from startup. So the SGP30 is hanging up the I2C bus.

Hmm, yeah, I ran an i2c scanner as well and all I see is 0x3C and 0x44.

I’m wondering if this is an issue with the SGP30, or crosstalk on the board. I’ve definitely had issues that look like noise (values switching to nonsense temporarily, screen updates in a non-clean fashion with artifacts)

I have had the same PCB here running without any issue for quite some time (incl. TVOC). However, it is important that the soldering joints are very clean.
Is there a nearby radio device (e.g. WiFi router) that could cause the issues you describe?

@Fenaer @sabertooth @awh @johnsom since I do not have a potentially broken TVOC sensor, would it be possible that you do the following test to exclude a potential problem from the PCB.

Could you connect the TVOC module directly to the D1 mini with jumper cables and see if you can detect the i2c address? Maybe also try it on 3.3 and 5v.

This could be quite helpful for trouble shooting.

I’ve just connected the SGP30 to the D1 mini with jumper cables with the following layout. I used the existing headers on the components, so the existing solder joints were included (see picture).

SGP -> D1 mini
---------------
VIN -> 3v3
GND -> G
SCL -> D1
SDA -> D2

I re-ran the I2C scanner, and it indeed found the module at address 0x58, which would indicate to me that this is a problem with the board.

Thank you. This is very helpful. Could you test on the AirGradient PCB to put the Temp sensor (SHT30) into the slot on the top where you had the TVOC module to see if you then get Temp/Hum values or not?

I was using ESPHome earlier which does find the SGP30 at address 0x58, but the I2C bus didn’t work.

Now I made a new firmware using ESPHome only for the SGP30, and it seems to be working…

[11:33:34][C][i2c.arduino:038]: I2C Bus:
[11:33:34][C][i2c.arduino:039]:   SDA Pin: GPIO4
[11:33:34][C][i2c.arduino:040]:   SCL Pin: GPIO5
[11:33:34][C][i2c.arduino:041]:   Frequency: 50000 Hz
[11:33:34][C][i2c.arduino:044]:   Recovery: bus successfully recovered
[11:33:34][I][i2c.arduino:054]: Results from i2c bus scan:
[11:33:34][I][i2c.arduino:060]: Found i2c device at address 0x58
[11:35:28][D][sgp30:282]: Got eCO2=415.0ppm TVOC=6.0ppb
[11:35:30][D][sensor:124]: 'eCO2': Sending state 415.00000 ppm with 1 decimals of accuracy
[11:35:30][D][sensor:124]: 'TVOC': Sending state 6.00000 ppb with 1 decimals of accuracy
[11:35:30][D][sgp30:156]: Baseline reading not available for: 43165s
esphome:
  name: "test-sgp30"

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Test Fallback Hotspot"
    password: "3Ts6suoef35J"

captive_portal:

web_server:
  port: 80

i2c:
  sda: D2
  scl: D1
  
sensor:
  - platform: sgp30
    eco2:
      id: eco2
      name: eCO2
      accuracy_decimals: 1
    tvoc:
      id: tvoc
      name: TVOC
      accuracy_decimals: 1
    store_baseline: yes
    address: 0x58
    update_interval: 1s

This seems to be the same discovery I made earlier, using the SGP30 connected via jumpers is fine, but on the board it doesn’t work

I loaded the Arduino Sketch back onto the D1 Mini. I put the temp sensor in the I2C 3.3v slot and it worked. I also used the I2C 5v slot on top and it also worked.

If I place the SGP30 on any of the I2C slots on the board, the I2C bus (OLED + TEMP) does not work (sometimes the OLED does display something initially, but it never changes). As soon as I remove the SGP30 from the board, the OLED starts working and values change.

This is very odd issue. If it was a board issue, then the SHT temp sensor would not work on all 3 I2C slots. Why is SGP30 causing issues here?

I have checked with magnifying glass and my solder joints look ok. I used a multi-meter to check for any shorts/continuity and didn’t find any.

1 Like

I must have had a bad connection because I swapped the temp/TVOC and all three showed up on the I2C scanner. I swapped them back and same thing, all three are present now.

Right, but are they sending data? I am at the same situation. All three show up on the I2C bus, but no values come from the sensors.

I get values for temperature and humidity, but I’m not sure on the TVOC, it’s showing as zero. Which might be correct.
I added code to query the eCO2 from the SGP30 and it appears to be returning reasonable data after some startup time.

@sabertooth how many amps is your power adapter? Did you try changing it? In the past I sometimes had issues caused by unstable or too weak power plugs.

Yes, I am using many different power adapters. I tried 5V 2A and USB-PD chargers which can do 5V 3A. Voltage was checked with multi-meter and they are steady.

Another interesting thing I noticed, if I remove the temp sensor, the I2C bus starts working and TVOC values are reported from SGP30.

It seems like I cannot have both the TVOC sensor and the temp sensor on at the same time.

Further testing on my unit says that both the SHT and SGP work, just not together. There seems to be an incompatibility between the two boards. In other words if you have both plugged in, the unit freezes, with either one plugged it the unit works and it doesn’t matter which I2C port is used. I have locally sources another SGP and it should arrive later today and I will let everyone know those results.

2 Likes

@awh I am facing the same issue it seems. The unit itself does not freeze, the CO2 and PM sensor continue working as they use UART. Anything using I2C (OLED, Temp, TVOC) freezes.

Keep us posted with the replacement SGP30.