SGP30 doesn't work on Gen3 devices

I have had no problems running the SGP30 on an Argon and Xenon using the Adafruit example.

Initially it worked fine using code modified from the example provided with the library for the sensor, but that only read and stored the CO2 and VOC readings. Now I would like to be warned of rapidly increasing levels and it won’t work. The code below is meant to take samples of 5 readings 1 sec apart and average them. If the averages increase for 3 consecutive samples, then I want an alarm published to the cloud. I have mostly copied the SGP code from the example and placed it into a boiler plate that builds and rotates the samples. This code keeps reading 400 CO2 and 0 VOC, which means 0. This is still the case even after running it over night. I assume that I am mishandling the baselines somehow.

I have tried to organize and comment my amateurish code. Any help is appreciated.

// This #include statement was automatically added by the Particle IDE.
#include <Adafruit_SGP30.h>
Adafruit_SGP30 sgp;

int sampleSize = 5; //# of readings included in a sample
int sample[5];

const char room = 'B200';    //Location of sensor

void setup() {
    Serial.begin();
    RGB.control(true);  
    RGB.brightness(0);  //Turn off the RGB LED
    
    if (! sgp.begin()){
        Serial.println("Sensor not found :(");
        while (1);
    }
    
   Serial.print("Found SGP30 serial #");    //Retrieve the serial number of the sensor 
   Serial.print(sgp.serialnumber[0], HEX);
   Serial.print(sgp.serialnumber[1], HEX);
   Serial.println(sgp.serialnumber[2], HEX);

}

void loop() {
    Time.zone(-5);
    int defcon = 0; //The number of consecutive increases before triggering alert.
    int newAvg = 0;
    int oldAvg = 0;
    int sampling = 1;
    int sum = 0;
    
    if (! sgp.IAQmeasure()) {
        Serial.println("Measurement failed");
        return;
    }
    
    Particle.publish("Sampling Beginning");
    while (sampling < 7) {  

        for (int reading = 0;reading < sampleSize ;reading++) { //Build sample of 5 readings, 1 second apart.
            sample[reading] = sgp.TVOC;
            sum = sum + sample[reading];
            delay(1000);
        }
        newAvg = sum / sampleSize;
        Mesh.publish("sgp reading", Time.format("%H:%M:%S") + "," + String(newAvg));
        sum = 0;

        if (newAvg > oldAvg) {  //Raise alert level if new avg is greater than previous
            defcon++;
            Serial.print("Defcon: " + String(defcon));
        }

        else {
            defcon = 0; // Reset alert level if new avg not greater than previous
        }
        
        oldAvg = newAvg;    
        
        if (defcon > 3){    // Publish alert after enough consecutive increases
            Mesh.publish("sgp reading", "Alert: " + String(room) + "," + String(newAvg));
            Particle.publish("alert", room + "," + String(newAvg));
            defcon = 0;    
        }
        
        if (sampling == 6) {    //Reset the baseline every 6 samples (30 readings). I think my issue may lie here!
            uint16_t TVOC_base, eCO2_base;
            if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
                Serial.println("Failed to get baseline readings");
                return;
            }
            
            sampling = 1; //Resart the loop
        }

    } //end while
}

I guess you should call sgp.IAQmeasure() each time you want to get a new reading :wink:

That worked. Thanks, I thought that step was just a preliminary check.

How did you go with this? I am having the exact same issue.

edit: to elaborate further. I am using Particle Boron and Argon and getting either ‘sensor not found’ or flat TVOC and eCO2 values of 0 & 400 respectively.

Have tried with/without any other sensors on the I2C and also have confirmed the I2C works as a number of other sensors on the same line are working fine (incl a BME280).

Killing me.

Just a note that mine stopped working on the Argon, not sure when exactly, it was long ago. It detects the sensor but I get 0-1 and 400-401 forever. I was actually thinking it was broken and was going to get another until I saw your reply that reminded me of this issue.

Sounds like it must be the library… I’ll see if adafruit are aware of the issue perhaps?

I’ve been assuming that since it works on the Photon it’s up to us to figure out what changes are needed for the Argon. I doubt they’ll assist us with that.

With my multimeter on my Photon, I can see the SGP30 starts drawing about 45ma right after the sgp.begin() until the next power cycle, presumably this is for the onboard heater. If you hit the reset button or flash new firmware the heater stays powered up. On the Argon the heater never powers up and the SGP30 only uses 0.01ma the entire time it’s on. I tried using the latest Adafruit code from git but it didn’t make a difference, works on Photon, not on Argon. I tried adding delays to no effect. I also tried running that .begin() command multiple times but it didn’t help. Finally I enabled i2c debug and compared the input/output of each but there were no differences. I’m out of ideas :slightly_frowning_face:

1 Like

Have you tried this library?

Yes, couldnt get it to work either. I’m at my witts end.

Didn’t work for my Argon either, it’s fine on the Photon though.

I don’t have the Adafruit breakout on hand right now to test, but it worked for me last year with both an argon and a boron running DeviceOS 1.2.1 and Adafruit_SGP30. Does it work if you downgrade the firmware?

That’s a tricky downgrade, wouldn’t take 1.2.1 directly, SOS10, I was able to go to 1.1.1 but only briefly before an OTA update kicks in and ends up back with an SOS10 after a multistep update process. Safe mode is either broken or what’s doing the SOS. 1.3.1 was the oldest I was able to go w/o SOS and it had the same SGP issue as the newer firmware versions.

The issues I encountered and steps I took can be seen here: How to downgrade firmware

To prevent an auto-upgrade you need to flash an application that is not targeted at a higher version than you are aiming for, otherwise this will always trigger the consolidation upgrade.

Typically we suggest particle flash --usb tinker -v for that as that version of Tinker is always targeted at the lowest stable device OS version possible.

Yea, I tired that, numerous times. Probably did about 10 attempts total including probably 30 usb flashes. I’m pretty sure I tried every possible suggestion in that downgrade thread in every conceivable order. That’s why I tried, successfully, other versions like 1.3.1 to confirm I wasn’t missing something fundamental.

Update all (zero thanks to Particle support btw). Got this working with some tweaking of the arduino library. Will get it tidied up and send link soon. Seems to be posting fine now via particle.publish so presume the rest is working.

3 Likes

@irwige What changes to code were needed?

Sorry for delay, no idea how to post a lib to github… ill get there.

The first thing (after cloning the adafruit library) was to move the .h and .cpp into src. then also created a properties file. Then, not 100% sure if needed, but added the Wire.h as that was in the example.


note: using udp as a workaround for posting my data to InfluxDB whilst Particle tech sort out my webhook issue. Not holding my breath on that though. So, you dont need to call UDP or TCPClient unless posting to web.

Then added the library to properties

Now into SGP30.h and changed it to Particle.h


I also had some issues with the default address on my sensor. I think this is the default, but some other makers use slightly different addresses such as 0x5A, 0x45, etc… look yours up, but the image worked for my adafruit branded one.

As for code, this is what is in my SGP30.ino

/*
 * Project SGP30
 * Description:
 * Author:
 * Date:
 */
#include "Particle.h"
#include "Wire.h"
#include "Adafruit_SGP30.h"

Adafruit_SGP30 sgp;


/* return absolute humidity [mg/m^3] with approximation formula
* @param temperature [°C]
* @param humidity [%RH]
*/
uint32_t getAbsoluteHumidity(float temperature, float humidity)
{
  // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15
  const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3]
  const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity);                                                                // [mg/m^3]
  return absoluteHumidityScaled;
}

void setup()
{
  Serial.begin(9600);
  Particle.publish("Argon_002", "SGP30 test", PRIVATE); // Argon_002 can be renamed to whatever you like.

  if (!sgp.begin())
  {
    Particle.publish("Argon_002", "SGP NOT FOUND", PRIVATE);
  }

  String serialString = "SGP30 Serial:";
  serialString += String(sgp.serialnumber[0], HEX);
  serialString += String(sgp.serialnumber[1], HEX);
  serialString += String(sgp.serialnumber[2], HEX);
  Particle.publish("Argon_002", serialString, PRIVATE);
  // If you have a baseline measurement from before you can assign it to start, to 'self-calibrate'
  //sgp.setIAQBaseline(0x8E68, 0x8F41);  // Will vary for each sensor!
}

int counter = 0;
void loop()
{

  /////// CO2 Sensor ///////
  // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals
  float temperature = 22.1; // [°C]    <-- SET THESE FROM TEMP/HUMI SENSOR WHEN WORKING
  float humidity = 45.2;    // [%RH]   <-- SET THESE FROM TEMP/HUMI SENSOR WHEN WORKING
  sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));

  if (!sgp.IAQmeasure())
  {
    Particle.publish("Argon_002", "Measurement Failed", PRIVATE);
    return;
  }

  if (!sgp.IAQmeasureRaw())
  {
    //Serial.println("Raw Measurement failed");
    Particle.publish("Argon_002", "Raw Measurement failed", PRIVATE);
    return;
  }
  Particle.publish("Argon_002",String(sgp.eCO2),PRIVATE); // <-- can also call tvoc here. but I'm only interested in co2
  delay(9000);

  counter++;
  if (counter == 30)
  {
    counter = 0;

    uint16_t TVOC_base, eCO2_base;
    if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base))
    {
      //Serial.println("Failed to get baseline readings");

      Particle.publish("Argon_002", "Failed to get baseline readings", PRIVATE);
      return;
    }
    String baselineString = "****Baseline values: eCO2: 0x";
    baselineString += String(eCO2_base, HEX);
    baselineString += " & TVOC: 0x";
    baselineString += String(TVOC_base, HEX);

    Particle.publish("Argon_002", baselineString, PRIVATE);
  }
  float voltage = analogRead(BATT) * 0.0011224;
}
2 Likes

Thanks for taking the time to post that.

1 Like

Thanks for posting about your success, I had all but given up but you gave me the motivation to try once more :wink:

I finally figured out my problem, a bad power wire. I have a Photon on one breadboard and an Argon on another and I was moving the SGP30 between them to test. Finally I decided to move the Argon to the Photon’s breadboard, adjusting the connections as appropriate, and then it started to work. Replacing the power wires on my Argon breadboard resolved my issue. It was a bit confusing because an SHT31 sensor plugged into the same power wires was working but I guess the SGP requires significantly more power and my faulty wire couldn’t supply that.

I then retried the half dozen SGP tests I had tried in the past and every single one of them worked. Including those that use the Adafruit and Sensirion Particle libraries. So if anyone else is still having issues, try swapping the power/ground wires.

2 Likes