Question about Air Quality Kit and API calls

Hi Particle community,

I've been reattempting to work with the Particle Air Quality Kit after purchasing it two years ago. As I work to achieve the goal of getting the data through an API call, I'm having some challenges. I'm able to see the variables through the API call, yet I'm receiving the results as "Int32" instead of the data. I thought the Particle.variable needs to be in the void loop() section, but that doesn't seem to be correct.

I would greatly appreciate it if someone could help me with this code process and let me know what I'm doing incorrectly. I reviewed the documentation and searched a number of posts, yet I'm not code proficient enough to understand what I'm doing wrong. Even if you can point me to a section in the docs to read on this to provide some guidance, this would be great.

Here's the original code I'm using with the addition of the code I added for the Particle.variable for temp, humidity, and pressure, listed as "//* Publish variables for API calls":

/*
 * Project env-sensor-kit
 * Description: Basic Tutorial project for the Particle Envrionmental Sensor Kit
 * Author: Brandon Satrom <brandon@particle.io>
 * Date: 09/16/2019
 */
#include "Particle.h"

SYSTEM_THREAD(ENABLED);

#include "Air_Quality_Sensor.h"
#include "Adafruit_BME280.h"
#include "SeeedOLED.h"
#include "JsonParserGeneratorRK.h"

#define AQS_PIN A2
#define DUST_SENSOR_PIN D4
#define SENSOR_READING_INTERVAL 30000

AirQualitySensor aqSensor(AQS_PIN);
Adafruit_BME280 bme;

unsigned long lastInterval;
unsigned long lowpulseoccupancy = 0;
unsigned long last_lpo = 0;
unsigned long duration;

float ratio = 0;
float concentration = 0;

int getBMEValues(int &temp, int &humidity, int &pressure);
void getDustSensorReadings();
String getAirQuality();
void createEventPayload(int temp, int humidity, int pressure, String airQuality);
void updateDisplay(int temp, int humidity, int pressure, String airQuality);

void setup()
{
  Serial.begin(9600);
  delay(50);

  // Configure the dust sensor pin as an input
  pinMode(DUST_SENSOR_PIN, INPUT);

  if (aqSensor.init())
  {
    Serial.println("Air Quality Sensor ready.");
  }
  else
  {
    Serial.println("Air Quality Sensor ERROR!");
  }

  Wire.begin();
  SeeedOled.init();

  SeeedOled.clearDisplay();
  SeeedOled.setNormalDisplay();
  SeeedOled.setPageMode();

  SeeedOled.setTextXY(2, 0);
  SeeedOled.putString("Particle");
  SeeedOled.setTextXY(3, 0);
  SeeedOled.putString("Air Quality");
  SeeedOled.setTextXY(4, 0);
  SeeedOled.putString("Monitor");

  if (bme.begin())
  {
    Serial.println("BME280 Sensor ready.");
  }
  else
  {
    Serial.println("BME280 Sensor ERROR!");
  }

  lastInterval = millis();
}

void loop()
{
  int temp, pressure, humidity;

  duration = pulseIn(DUST_SENSOR_PIN, LOW);
  lowpulseoccupancy = lowpulseoccupancy + duration;

  if ((millis() - lastInterval) > SENSOR_READING_INTERVAL)
  {
    String quality = getAirQuality();
    Serial.printlnf("Air Quality: %s", quality.c_str());

    getBMEValues(temp, pressure, humidity);
    Serial.printlnf("Temp: %d", temp);
    Serial.printlnf("Pressure: %d", pressure);
    Serial.printlnf("Humidity: %d", humidity);

    getDustSensorReadings();

    updateDisplay(temp, humidity, pressure, quality);

    createEventPayload(temp, humidity, pressure, quality);

    lowpulseoccupancy = 0;
    lastInterval = millis();
  }

  //* Publish variables for API calls
  Particle.variable("Humidity2", humidity);    //make humidity available via variable
  Particle.variable("Temperature2", temp);     //make temperature available via variable
  Particle.variable("Pressure2", pressure);    //make pressure available via variable
}

String getAirQuality()
{
  int quality = aqSensor.slope();
  String qual = "None";

  if (quality == AirQualitySensor::FORCE_SIGNAL)
  {
    qual = "Danger";
  }
  else if (quality == AirQualitySensor::HIGH_POLLUTION)
  {
    qual = "High Pollution";
  }
  else if (quality == AirQualitySensor::LOW_POLLUTION)
  {
    qual = "Low Pollution";
  }
  else if (quality == AirQualitySensor::FRESH_AIR)
  {
    qual = "Fresh Air";
  }

  return qual;
}

int getBMEValues(int &temp, int &pressure, int &humidity)
{
  temp = (int)bme.readTemperature();
  pressure = (int)(bme.readPressure() / 100.0F);
  humidity = (int)bme.readHumidity();

  return 1;
}

void getDustSensorReadings()
{
  // This particular dust sensor returns 0s often, so let's filter them out by making sure we only
  // capture and use non-zero LPO values for our calculations once we get a good reading.
  if (lowpulseoccupancy == 0)
  {
    lowpulseoccupancy = last_lpo;
  }
  else
  {
    last_lpo = lowpulseoccupancy;
  }

  ratio = lowpulseoccupancy / (SENSOR_READING_INTERVAL * 10.0);                   // Integer percentage 0=>100
  concentration = 1.1 * pow(ratio, 3) - 3.8 * pow(ratio, 2) + 520 * ratio + 0.62; // using spec sheet curve

  Serial.printlnf("LPO: %d", lowpulseoccupancy);
  Serial.printlnf("Ratio: %f%%", ratio);
  Serial.printlnf("Concentration: %f pcs/L", concentration);
}

void createEventPayload(int temp, int humidity, int pressure, String airQuality)
{
  JsonWriterStatic<256> jw;
  {
    JsonWriterAutoObject obj(&jw);

    jw.insertKeyValue("temp", temp);
    jw.insertKeyValue("humidity", humidity);
    jw.insertKeyValue("pressure", pressure);
    jw.insertKeyValue("air-quality", airQuality);

    if (lowpulseoccupancy > 0)
    {
      jw.insertKeyValue("dust-lpo", lowpulseoccupancy);
      jw.insertKeyValue("dust-ratio", ratio);
      jw.insertKeyValue("dust-concentration", concentration);
    }
  }

  Particle.publish("env-vals", jw.getBuffer(), PRIVATE);
}

void updateDisplay(int temp, int humidity, int pressure, String airQuality)
{
  SeeedOled.clearDisplay();

  SeeedOled.setTextXY(0, 3);
  SeeedOled.putString(airQuality);

  SeeedOled.setTextXY(2, 0);
  SeeedOled.putString("Temp: ");
  SeeedOled.putNumber(temp);
  SeeedOled.putString("C");

  SeeedOled.setTextXY(3, 0);
  SeeedOled.putString("Humidity: ");
  SeeedOled.putNumber(humidity);
  SeeedOled.putString("%");

  SeeedOled.setTextXY(4, 0);
  SeeedOled.putString("Press: ");
  SeeedOled.putNumber(pressure);
  SeeedOled.putString(" hPa");

  if (concentration > 1)
  {
    SeeedOled.setTextXY(5, 0);
    SeeedOled.putString("Dust: ");
    SeeedOled.putNumber(concentration); // Will cast our float to an int to make it more compact
    SeeedOled.putString(" pcs/L");
  }
}

Thank you in advance for any advice!

Particle.variables goes in setup, and takes a reference to a global variable. When the global variable is updated, the new value will be used when requested.

Move this to be a global variable instead of in loop:

int temp, pressure, humidity;

Move this from loop to setup:

  Particle.variable("Humidity2", humidity);    //make humidity available via variable
  Particle.variable("Temperature2", temp);     //make temperature available via variable
  Particle.variable("Pressure2", pressure);    //make pressure available via variable
}

Hi @rickkas7, Thank you so much for the quick reply! I will try this later today, and hopefully, it will yield successful results!

Can I ask a dumb question? (Hopefully yes...) What's the difference between creating a Particle.variable vs. Particle.publish? Excuse my ignorance on these topics, I know they are rudimentary. I'm still learning and trying to comprehend these topics as I move forward on project developments.

Thank you for the support!
Tim

Variable is pulled from the device on request. Each time someone on the internet requests a variable, it uses one data operation. The variable can change between requests multiple times without using data or data operations.

Publish is push from the device. Each time the value is published, it uses one data operation. Publish is often used to trigger a webhook to access an external service, or trigger Logic to perform other operations.

Hi there, on top of what Rick has mentioned above, if you wanted to read more and see code examples, please refer to:

and:

Of course, feel free to continue asking questions to move forward your project.
Best,

1 Like

Hi @rickkas7 , thanks again for the support in this process. I updated the code as you mentioned, yet when I call the API in Postman, I'm still getting the following:

        "variables": {
            "Humidity2": "int32",
            "Temperature2": "int32",
            "Pressure2": "int32"
        },

I'm not sure what I'm doing wrong, any other ideas as to why I'm getting the int32 and not the actual data?

Thanks so much!
Tim

Hi @gusgonnet, thanks for the info on the variable vs. publish process. I'm still reading through the documents and working on the examples to familiarize myself with these topics.

I need to become more educated on the wealth of knowledge provided, and I appreciate your feedback!

Thanks,
Tim