Skip to content

Instantly share code, notes, and snippets.

@just4give
Created May 22, 2024 02:57
Show Gist options
  • Select an option

  • Save just4give/14f9acdd5dab5774cd537289feb1a19d to your computer and use it in GitHub Desktop.

Select an option

Save just4give/14f9acdd5dab5774cd537289feb1a19d to your computer and use it in GitHub Desktop.
Particle Boron Air Quality Monitor
// Include Particle Device OS APIs
#include "Particle.h"
#include "JsonParserGeneratorRK.h"
#include "Air_Quality_Sensor.h"
#include <math.h>
#include "sensirion_common.h"
#include "sgp30.h"
#define AQS_PIN A2
#define DUST_SENSOR_PIN D4
#define SENSOR_READING_INTERVAL 1000 * 60 * 30
// Let Device OS manage the connection to the Particle Cloud
SYSTEM_MODE(AUTOMATIC);
// Run the application and system concurrently in separate threads
SYSTEM_THREAD(ENABLED);
SystemSleepConfiguration config;
// Show system, cloud connectivity, and application logs over USB
// View logs with CLI using 'particle serial monitor --follow'
SerialLogHandler logHandler(LOG_LEVEL_INFO);
AirQualitySensor aqSensor(AQS_PIN);
// Adafruit_SGP30 adafruit_SGP30;
unsigned long lastInterval;
unsigned long lowpulseoccupancy = 0;
unsigned long last_lpo = 0;
unsigned long duration;
float ratio = 0;
float concentration = 0;
void getDustSensorReadings();
String getAirQuality();
void createEventPayload(int airQuality, uint16_t voc, uint16_t eco2, uint16_t charge);
FuelGauge fuel;
// setup() runs once, when the device is first turned on
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!");
}
s16 err;
u32 ah = 0;
u16 scaled_ethanol_signal, scaled_h2_signal;
while (sgp_probe() != STATUS_OK)
{
Serial.println("SGP failed");
while (1)
;
}
/*Read H2 and Ethanol signal in the way of blocking*/
err = sgp_measure_signals_blocking_read(&scaled_ethanol_signal,
&scaled_h2_signal);
if (err == STATUS_OK)
{
Serial.println("get ram signal!");
}
else
{
Serial.println("error reading signals");
}
// Set absolute humidity to 13.000 g/m^3
// It's just a test value
sgp_set_absolute_humidity(13000);
err = sgp_iaq_init();
lastInterval = millis();
}
// loop() runs over and over again, as quickly as it can execute.
void loop()
{
// float voltage = analogRead(BATT) * 0.0011224;
FuelGauge fuel;
uint16_t charge = fuel.getNormalizedSoC();
// Log.info("SoC=%.2f", fuel.getSoC());
duration = pulseIn(DUST_SENSOR_PIN, LOW);
lowpulseoccupancy = lowpulseoccupancy + duration;
uint16_t voc = 0;
uint16_t eco2 = 0;
if ((millis() - lastInterval) > SENSOR_READING_INTERVAL)
{
// String quality = getAirQuality();
int quality = aqSensor.slope();
// Serial.printlnf("Air Quality: %s", quality.c_str());
Serial.printlnf("Air Quality: %d", quality);
s16 err = 0;
u16 tvoc_ppb, co2_eq_ppm;
err = sgp_measure_iaq_blocking_read(&tvoc_ppb, &co2_eq_ppm);
if (err == STATUS_OK)
{
Serial.print("tVOC Concentration:");
Serial.print(tvoc_ppb);
Serial.println("ppb");
Serial.print("CO2eq Concentration:");
Serial.print(co2_eq_ppm);
Serial.println("ppm");
}
else
{
Serial.println("error reading IAQ values\n");
}
getDustSensorReadings();
createEventPayload(quality, tvoc_ppb, co2_eq_ppm, charge);
lowpulseoccupancy = 0;
lastInterval = millis();
}
}
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;
}
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 airQuality, uint16_t tvoc, uint16_t eco2, uint16_t charge)
{
JsonWriterStatic<256> jw;
{
JsonWriterAutoObject obj(&jw);
jw.insertKeyValue("aq", airQuality);
jw.insertKeyValue("tvoc", tvoc);
jw.insertKeyValue("eco2", eco2);
jw.insertKeyValue("chg", charge);
if (lowpulseoccupancy > 0)
{
jw.insertKeyValue("dustlpo", lowpulseoccupancy);
jw.insertKeyValue("dustratio", ratio);
jw.insertKeyValue("dustcon", concentration);
}
}
Particle.publish("aqas-env-raw", jw.getBuffer(), PRIVATE);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment