-
-
Save just4give/14f9acdd5dab5774cd537289feb1a19d to your computer and use it in GitHub Desktop.
Particle Boron Air Quality Monitor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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