diff --git a/examples/DIY_PRO_WITH_SENSIRION_NOX/DIY_PRO_WITH_SENSIRION_NOX.ino b/examples/DIY_PRO_WITH_SENSIRION_NOX/DIY_PRO_WITH_SENSIRION_NOX.ino new file mode 100644 index 0000000..f82e12c --- /dev/null +++ b/examples/DIY_PRO_WITH_SENSIRION_NOX/DIY_PRO_WITH_SENSIRION_NOX.ino @@ -0,0 +1,311 @@ +/* +This is the code for the AirGradient DIY PRO Air Quality Sensor with an ESP8266 Microcontroller with the SGP40 TVOC module from AirGradient. + +It is a high quality sensor showing PM2.5, CO2, Temperature and Humidity on a small display and can send data over Wifi. + +Build Instructions: https://www.airgradient.com/open-airgradient/instructions/diy-pro/ + +Kits (including a pre-soldered version) are available: https://www.airgradient.com/open-airgradient/kits/ + +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 +“DFRobot_SGP40” by DFRobot tested with Version 1.0.3 +"Sensirion I2C SGP41" by Sensation Version 0.1.0 +"Sensirion Gas Index Algorithm" by Sensation Version 3.2.1 + +Configuration: +Please set in the code below the configuration parameters. + +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/ + +MIT License + +*/ + + +#include +#include +#include +#include +#include + +//#include "SGP30.h" +#include +#include +#include + + +#include + +AirGradient ag = AirGradient(); +SensirionI2CSgp41 sgp41; +VOCGasIndexAlgorithm voc_algorithm; +NOxGasIndexAlgorithm nox_algorithm; +// time in seconds needed for NOx conditioning +uint16_t conditioning_s = 10; + +// Display bottom right +U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); + +// Replace above if you have display on top left +//U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE); + + +// CONFIGURATION START + +//set to the endpoint you would like to use +String APIROOT = "http://hw.airgradient.com/"; + +// set to true to switch from Celcius to Fahrenheit +boolean inF = false; + +// set to true if you want to connect to wifi. You have 60 seconds to connect. Then it will go into an offline mode. +boolean connectWIFI=true; + +// CONFIGURATION END + + +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; +int NOX = 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; + +void setup() +{ + Serial.begin(115200); + + u8g2.begin(); + updateOLED(); + + if (connectWIFI) { + connectToWifi(); + } + + updateOLED2("Warming up the", "sensors.", ""); + sgp41.begin(Wire); + ag.CO2_Init(); + ag.PMS_Init(); + ag.TMP_RH_Init(0x44); +} + + +void loop() +{ + currentMillis = millis(); + updateTVOC(); + updateOLED(); + updateCo2(); + updatePm25(); + updateTempHum(); + sendToServer(); +} + +void updateTVOC() +{ + uint16_t error; + char errorMessage[256]; + uint16_t defaultRh = 0x8000; + uint16_t defaultT = 0x6666; + uint16_t srawVoc = 0; + uint16_t srawNox = 0; + uint16_t defaultCompenstaionRh = 0x8000; // in ticks as defined by SGP41 + uint16_t defaultCompenstaionT = 0x6666; // in ticks as defined by SGP41 + uint16_t compensationRh = 0; // in ticks as defined by SGP41 + uint16_t compensationT = 0; // in ticks as defined by SGP41 + + delay(1000); + + compensationT = static_cast((temp + 45) * 65535 / 175); + compensationRh = static_cast(hum * 65535 / 100); + + // 3. Measure SGP4x signals + + if (conditioning_s > 0) { + // During NOx conditioning (10s) SRAW NOx will remain 0 + error = sgp41.executeConditioning(compensationRh, compensationT, srawVoc); + conditioning_s--; + } else { + // Read Measurement + error = sgp41.measureRawSignals(compensationRh, compensationT, srawVoc, + srawNox); + } + + if (error) { + Serial.print("Error trying to execute measureRawSignals(): "); + errorToString(error, errorMessage, 256); + Serial.println(errorMessage); + } else { + Serial.print("SRAW_VOC:"); + Serial.print(srawVoc); + Serial.print("\t"); + Serial.print("SRAW_NOx:"); + Serial.println(srawNox); + } + + + if (currentMillis - previousTVOC >= tvocInterval) { + previousTVOC += tvocInterval; + TVOC = voc_algorithm.process(srawVoc); + NOX = nox_algorithm.process(srawNox); + // TVOC = sgp40.getVoclndex(); + Serial.println(String(TVOC)); + } +} + +void updateCo2() +{ + if (currentMillis - previousCo2 >= co2Interval) { + previousCo2 += co2Interval; + Co2 = ag.getCO2_Raw(); + Serial.println(String(Co2)); + } +} + +void updatePm25() +{ + if (currentMillis - previousPm25 >= pm25Interval) { + previousPm25 += pm25Interval; + pm25 = ag.getPM2_Raw(); + Serial.println(String(pm25)); + } +} + +void updateTempHum() +{ + if (currentMillis - previousTempHum >= tempHumInterval) { + previousTempHum += tempHumInterval; + TMP_RH result = ag.periodicFetchData(); + temp = result.t; + hum = result.rh; + Serial.println(String(temp)); + } +} + +void updateOLED() { + if (currentMillis - previousOled >= oledInterval) { + previousOled += oledInterval; + + String ln3; + String ln1 = "PM:" + String(pm25) + " CO2:" + String(Co2); + // String ln2 = "AQI:" + String(PM_TO_AQI_US(pm25)) + " TVOC:" + String(TVOC); + String ln2 = "TVOC:" + String(TVOC) + " NOX:" + String(NOX); + + if (inF) { + ln3 = "F:" + String((temp* 9 / 5) + 32) + " H:" + String(hum)+"%"; + } else { + ln3 = "C:" + String(temp) + " H:" + String(hum)+"%"; + } + 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()) + + (Co2 < 0 ? "" : ", \"rco2\":" + String(Co2)) + + (pm25 < 0 ? "" : ", \"pm02\":" + String(pm25)) + + (TVOC < 0 ? "" : ", \"tvoc_index\":" + String(TVOC)) + + (NOX < 0 ? "" : ", \"nox_index\":" + String(NOX)) + + ", \"atmp\":" + String(temp) + + (hum < 0 ? "" : ", \"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("60s to connect", "to Wifi Hotspot", HOTSPOT); + wifiManager.setTimeout(60); + + + WiFiManagerParameter custom_text("

This is just a text paragraph

"); + wifiManager.addParameter(&custom_text); + + WiFiManagerParameter parameter("parameterId", "Parameter Label", "default value", 40); + wifiManager.addParameter(¶meter); + + + Serial.println("Parameter 1:"); + Serial.println(parameter.getValue()); + + if (!wifiManager.autoConnect((const char * ) HOTSPOT.c_str())) { + updateOLED2("booting into", "offline mode", ""); + Serial.println("failed to connect and hit timeout"); + delay(6000); + } + + Serial.println("Parameter 2:"); + Serial.println(parameter.getValue()); + +} + +// 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; +};