diff --git a/.gitmodules b/.gitmodules index b7655a4..5ada61b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "libraries/BME280"] path = libraries/BME280 url = https://github.com/finitespace/BME280.git +[submodule "libraries/Adafruit_SGP30"] + path = libraries/Adafruit_SGP30 + url = https://github.com/adafruit/Adafruit_SGP30 diff --git a/esp32-env-monitor.ino b/esp32-env-monitor.ino index 996e9cc..d7e0fad 100644 --- a/esp32-env-monitor.ino +++ b/esp32-env-monitor.ino @@ -1,4 +1,5 @@ #include +#include #include #include "wifi_login.h" @@ -10,13 +11,16 @@ #endif #include -#include + +#include U8X8_SSD1327_EA_W128128_4W_HW_SPI u8x8(/* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); BME280I2C bme; // Default : forced mode, standby time = 1000 ms // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off, +Adafruit_SGP30 sgp; + // setup the terminal (U8X8LOG) and connect to u8g2 for automatic refresh of the display // The size (width * height) depends on the display #define U8LOG_WIDTH 16 @@ -24,6 +28,17 @@ BME280I2C bme; // Default : forced mode, standby time = 1000 ms uint8_t u8log_buffer[U8LOG_WIDTH*U8LOG_HEIGHT]; U8X8LOG u8x8log; +/* 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(1000.0f * absoluteHumidity); // [mg/m^3] + return absoluteHumidityScaled; +} + void setup() { //Init Display @@ -75,19 +90,47 @@ void setup() { u8x8log.print("Found UNKNOWN sensor! Error!\n"); } + + //Init SGP30 sensor + while(!sgp.begin()) + { + u8x8log.print("Could not find SGP30 sensor!\n"); + delay(1000); + } + + u8x8log.print("Found SGP30 serial #"); + u8x8log.print(sgp.serialnumber[0], HEX); + u8x8log.print(sgp.serialnumber[1], HEX); + u8x8log.print(sgp.serialnumber[2], HEX); + u8x8log.print("\n"); + + // 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() { //Clear screen u8x8log.print("\f"); + //Read Temperature, humidity and pressure float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); BME280::PresUnit presUnit(BME280::PresUnit_Pa); bme.read(pres, temp, hum, tempUnit, presUnit); + //Read Air Quality + sgp.setHumidity(getAbsoluteHumidity(temp, hum)); + + if (! sgp.IAQmeasure()) { + u8x8log.print("Air Quality Measurement failed\n"); + return; + } + u8x8log.print("Temp: "); u8x8log.print(temp); u8x8log.print("°"+ String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F')); @@ -98,6 +141,30 @@ void loop() { u8x8log.print(pres); u8x8log.print("Pa\n"); - delay(10000); + u8x8log.print("TVOC "); + u8x8log.print(sgp.TVOC); + u8x8log.print(" ppb\n"); + u8x8log.print("eCO2 "); + u8x8log.print(sgp.eCO2); + u8x8log.print(" ppm\n"); + + //Get Baseline readings every 30s + counter++; + if (counter == 30) { + counter = 0; + + uint16_t TVOC_base, eCO2_base; + if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { + u8x8log.print("Failed to get baseline readings\n"); + return; + } + u8x8log.print("Baseline values: eCO2: 0x"); + u8x8log.print(eCO2_base, HEX); + u8x8log.print(" & TVOC: 0x"); + u8x8log.print(TVOC_base, HEX); + u8x8log.print("\n"); + } + + delay(1000); } diff --git a/libraries/Adafruit_SGP30 b/libraries/Adafruit_SGP30 new file mode 160000 index 0000000..f0f80ad --- /dev/null +++ b/libraries/Adafruit_SGP30 @@ -0,0 +1 @@ +Subproject commit f0f80ad69a62fd6577fb4a9c0cd0b86bccea4279