diff --git a/README.org b/README.org index 6eb7a33..b44809a 100644 --- a/README.org +++ b/README.org @@ -24,8 +24,16 @@ make flash In the event of TVOC issues, inspections should be carried out to find the root cause of the problem and address it as exposure to the VOCs may harm health. Indoor VOC levels up to 350 ppb are -acceptable. However they should not exceed 500ppb. – CO2 ppm range of -450 to 2000 ppm (parts per million). +acceptable. However they should not exceed 500ppb. + + + 400 ppm: average outdoor air level. + 400–1,000 ppm: typical level found in occupied spaces with good air exchange. + 1,000–2,000 ppm: level associated with complaints of drowsiness and poor air. + 2,000–5,000 ppm: level associated with headaches, sleepiness, and stagnant, stale, stuffy air. Poor concentration, loss of attention, increased heart rate and slight nausea may also be present. + 5,000 ppm: this indicates unusual air conditions where high levels of other gases could also be present. Toxicity or oxygen deprivation could occur. This is the permissible exposure limit for daily workplace exposures. + 40,000 ppm: this level is immediately harmful due to oxygen deprivation. + #+END_EXAMPLE *** Particulate matter + PM1 :: are extremely fine particulates with a diameter of fewer than 1 microns. @@ -38,7 +46,7 @@ In gneral I want the stack to be pluggable as much as possible. The current scop + [X] Thermometer - provided by HDC1080 + [X] Humidity sensor - provided by HDC1080 + [-] Particulate matter - - [ ] PM2.5 - i'd to start with it + - [ ] PM2.5 - PS3003 acuired - [ ] PM10 - [ ] PM1 - I might need it to indoor mod + [ ] Preasure sensor diff --git a/include/BMP280Sensor.h b/include/BMP280Sensor.h new file mode 100644 index 0000000..516bf1d --- /dev/null +++ b/include/BMP280Sensor.h @@ -0,0 +1,21 @@ +// BMP280Sensor.h + +#ifndef BMP280SENSOR_H +#define BMP280SENSOR_H +#define BMP280_ADDR (0x76) + +#define SEALEVELPRESSURE_HPA (1013.25) + +#include + +class BMP280Sensor { +public: + BMP280Sensor(uint8_t address = BMP280_ADDR); // Constructor + void init(); + void read_values(float* temperature, float* humidity, float* pressure, float* altitude); +private: + uint8_t _address; // BMP280 wake pin + Adafruit_BME280 bmp280; // BMP280 object +}; + +#endif // BMP280SENSOR_H diff --git a/include/CCS811Sensor.h b/include/CCS811Sensor.h index 921a463..1d9709a 100644 --- a/include/CCS811Sensor.h +++ b/include/CCS811Sensor.h @@ -4,7 +4,7 @@ #define CCS811SENSOR_H #define CCS811_WAK 23 -#include "ccs811.h" // Include CCS811 library +#include // Include CCS811 library class CCS811Sensor { public: diff --git a/include/MS5611Sensor.h b/include/MS5611Sensor.h new file mode 100644 index 0000000..93c1cb8 --- /dev/null +++ b/include/MS5611Sensor.h @@ -0,0 +1,19 @@ +// MS5611Sensor.h + +#ifndef MS5611SENSOR_H +#define MS5611SENSOR_H + +#include + +class MS5611Sensor { +public: + MS5611Sensor(); // Constructor + void init(); // Initialize without temperature and humidity + void read_values(double* temperature, double* preasure, double* altitude); + +private: + uint8_t _address; // MS5611 I2C address + MS5611 ms5611; // MS5611 object +}; + +#endif // MS5611SENSOR_H diff --git a/platformio.ini b/platformio.ini index 7a99824..49dc6b3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,7 +15,9 @@ framework = arduino lib_deps = maarten-pennings/CCS811@^12.0.0 closedcube/ClosedCube HDC1080@^1.3.2 - https://github.com/me-no-dev/ESPAsyncWebServer.git#master + https://github.com/me-no-dev/ESPAsyncWebServer.git#master + https://github.com/jarzebski/Arduino-MS5611 + adafruit/Adafruit BME280 Library monitor_speed = 115200 monitor_filters = default, log2file extra_scripts = populate_static.py diff --git a/src/main.cpp b/src/main.cpp index f6083ac..cac169e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,19 @@ #include "ESPAsyncWebServer.h" #include #include +#include + +// Sensors #include "HDC1080Sensor.h" #include "CCS811Sensor.h" -#include +#include "MS5611Sensor.h" +#include "BMP280Sensor.h" + //#include "src/ESPinfluxdb.h" // https://github.com/hwwong/ESP_influxdb // 14.04.2019 #include "http_static.h" // HTTP pages and JSON request templates // ********************** Config ********************** -// DeepSleep time – send data every 60 seconds +// DeepSleep time send data every 60 seconds const int sleepTimeS = 60; // WiFi Config @@ -24,19 +29,64 @@ struct tm timeinfo; // Create AsyncWebServer object on port 80 AsyncWebServer server(80); -// Globals for HDC1080 +// Globals for HDC1080 :: Temp/Humidity HDC1080Sensor HDC1080_sensors; double hdc1080_temp, hdc1080_humidity; bool hdc1080_err; -// Globals for CCS811 +// Globals for CCS811 :: eTVOC/eCO2 CCS811Sensor CCS811_sensors; -uint16_t eco2, etvoc, errstat, raw; +uint16_t ccs811_eco2, ccs811_etvoc, ccs811_errstat, ccs811_raw; + +// Globals for MS5611 :: Pressure/Altitude +MS5611Sensor MS5611_sensors; +double ms5611_temp, ms5611_pressure, ms5611_altitude; + +// Globals for BMP280 :: Temp/Hum/Pressure +BMP280Sensor BMP280_sensors; +float bmp280_temp, bmp280_humidity, bmp280_pressure, bmp280_altitude; + // loop cycle #define workCycle 60 //seconds // ******************** Config End ******************** +void scanI2CDevices() { + byte error, address; + int nDevices; + + Serial.println("Scanning..."); + + nDevices = 0; + for (address = 1; address < 127; address++) { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) { + Serial.print("I2C device found at address 0x"); + if (address < 16) { + Serial.print("0"); + } + Serial.print(address, HEX); + Serial.println(" !"); + + nDevices++; + } else if (error == 4) { + Serial.print("Unknown error at address 0x"); + if (address < 16) { + Serial.print("0"); + } + Serial.println(address, HEX); + } + } + if (nDevices == 0) { + Serial.println("No I2C devices found\n"); + } else { + Serial.println("done\n"); + } +} + +// ---------- HDC1080 ---------- String readHDC1080Temperature() { // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) if (isnan(hdc1080_temp)) { @@ -58,7 +108,8 @@ String readHDC1080Humidity() { return String(hdc1080_humidity); } } - +// ---------- End HDC1080 ---------- +// ---------- CCS11 ---------- String processCCS811Error(err_t errstat) { if ( errstat == CCS811_ERRSTAT_OK_NODATA ) { Serial.println("CCS811: waiting for (new) data"); @@ -74,20 +125,100 @@ String processCCS811Error(err_t errstat) { } String readCCS811TVOC() { - if( errstat==CCS811_ERRSTAT_OK ){ - return String(etvoc); + if( ccs811_errstat==CCS811_ERRSTAT_OK ){ + return String(ccs811_etvoc); } else { - return processCCS811Error(errstat); + return processCCS811Error(ccs811_errstat); } } String readCCS811ECO2() { - if( errstat==CCS811_ERRSTAT_OK ){ - return String(eco2); + if( ccs811_errstat==CCS811_ERRSTAT_OK ){ + return String(ccs811_eco2); } else { - return processCCS811Error(errstat); + return processCCS811Error(ccs811_errstat); + } +} +// ---------- End CCS11 ---------- + +// ---------- MS5611 ---------- +String readMS5611Temperature() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(ms5611_temp)) { + Serial.println("Failed to read from MS5611 sensor!"); + return "--"; + } + else { + return String(ms5611_temp); } } +String readMS5611Pressure() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(ms5611_pressure)) { + Serial.println("Failed to read from MS5611 sensor!"); + return "--"; + } + else { + return String(ms5611_temp); + } +} + +String readMS5611Altitude() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(ms5611_altitude)) { + Serial.println("Failed to read from MS5611 sensor!"); + return "--"; + } + else { + return String(ms5611_altitude); + } +} +// ---------- End MS5611 ---------- +// ---------- BMP280 ---------- +String readBMP280Temperature() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(bmp280_temp)) { + Serial.println("Failed to read from BMP280 sensor!"); + return "--"; + } + else { + return String(bmp280_temp); + } +} + +String readBMP280Humidity() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(bmp280_humidity)) { + Serial.println("Failed to read from BMP280 sensor!"); + return "--"; + } + else { + return String(bmp280_humidity); + } +} + +String readBMP280Pressure() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(bmp280_pressure)) { + Serial.println("Failed to read from BMP280 sensor!"); + return "--"; + } + else { + return String(bmp280_pressure); + } +} + +String readBMP280Altitude() { + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + if (isnan(bmp280_altitude)) { + Serial.println("Failed to read from BMP280 sensor!"); + return "--"; + } + else { + return String(bmp280_altitude); + } +} +// ---------- End BMP280 ---------- String formatISO8601() { char timestamp[20]; // Buffer for timestamp @@ -163,17 +294,22 @@ void setup() HDC1080_sensors.init(); // eCO2 and eTVOC, temp and humidity needed to adjust values CCS811_sensors.init(&hdc1080_temp, &hdc1080_humidity); - + // Todo add pointer to temp, we might allight Altimeter by another sens + // MS5611_sensors.init(); + // humidity, temperature, pressure and altitude + BMP280_sensors.init(); // Pages and JSONs server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", http_static::index_html, processor); }); + + // Deprecated server.on("/api/sensors.json", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", http_static::sensor_things_resp, processor); }); - // lightweight named endpoints + // DEPRECATED lightweight named endpoints server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/plain", readHDC1080Temperature().c_str()); }); @@ -187,6 +323,19 @@ void setup() request->send_P(200, "text/plain", readCCS811ECO2().c_str()); }); + // For prometheus scrapping. to be deprecated as well + server.on("/metrics", HTTP_GET, [](AsyncWebServerRequest *request) { + String response = "hdc1080_temperature " + String(readHDC1080Temperature()) + "\n"; + response += "hdc1080_humidity " + String(readHDC1080Humidity()) + "\n"; + response += "ccs811_tvoc " + String(readCCS811TVOC()) + "\n"; + response += "ccs811_eco2 " + String(readCCS811ECO2()) + "\n"; + response += "bmp280_temperature " + String(readBMP280Temperature()) + "\n"; + response += "bmp280_humidity " + String(readBMP280Humidity()) + "\n"; + response += "bmp280_pressure " + String(readBMP280Pressure()) + "\n"; + response += "bmp280_altitude " + String(readBMP280Altitude()) + "\n"; + request->send(200, "text/plain", response); +}); + // Start server server.begin(); } @@ -202,16 +351,27 @@ void loop() Serial.println("Failed to obtain time"); return; } - + scanI2CDevices(); Serial.print(&timeinfo, "[%M-%d-%Y--%H:%M:%S]: "); HDC1080_sensors.read_values(&hdc1080_temp, &hdc1080_humidity, &hdc1080_err); - CCS811_sensors.read_values(&eco2, &etvoc, &errstat, &raw); + CCS811_sensors.read_values(&ccs811_eco2, &ccs811_etvoc, &ccs811_errstat, &ccs811_raw); + BMP280_sensors.read_values(&bmp280_temp, &bmp280_humidity, &bmp280_pressure, &bmp280_altitude); - Serial.print("H="); Serial.print(readHDC1080Temperature()); Serial.print(" °C "); - Serial.print("T="); Serial.print(readHDC1080Humidity()); Serial.print(" % "); - Serial.print("eco2="); Serial.print(readCCS811ECO2()); Serial.print(" ppm "); - Serial.print("etvoc="); Serial.print(readCCS811TVOC()); Serial.print(" ppb "); + Serial.print("T="); + Serial.print(readHDC1080Temperature()); + Serial.print(" / "); + Serial.print(readBMP280Temperature()); + Serial.print(" °C "); + Serial.print("H="); + Serial.print(readHDC1080Humidity()); + Serial.print(" / "); + Serial.print(readBMP280Humidity()); + Serial.print(" % "); + Serial.print("eco2="); Serial.print(readCCS811ECO2()); Serial.print(" ppm "); + Serial.print("etvoc="); Serial.print(readCCS811TVOC()); Serial.print(" ppb "); + Serial.print("pressure="); Serial.print(readBMP280Pressure()); Serial.print(" Pa "); + Serial.print("alt="); Serial.print(readBMP280Altitude()); Serial.print(" m "); Serial.println(""); // Wait delay(workCycle*1000); diff --git a/src/sensors/BMP280Sensor.cpp b/src/sensors/BMP280Sensor.cpp new file mode 100644 index 0000000..833c63b --- /dev/null +++ b/src/sensors/BMP280Sensor.cpp @@ -0,0 +1,30 @@ +// BMP280Sensor.cpp + +#include "BMP280Sensor.h" + +BMP280Sensor::BMP280Sensor(uint8_t address) : _address(address) { + // Constructor sets the address +} + +void BMP280Sensor::init() { + // Enable BMP280 + bool ok = bmp280.begin(_address); + + /* weather monitoring settings from driver lib */ + bmp280.setSampling(Adafruit_BME280::MODE_FORCED, + Adafruit_BME280::SAMPLING_X1, // temperature + Adafruit_BME280::SAMPLING_X1, // pressure + Adafruit_BME280::SAMPLING_X1, // humidity + Adafruit_BME280::FILTER_OFF ); +} + + +void BMP280Sensor::read_values(float* temperature, float* humidity, float* pressure, float* altitude) { + // Only needed in forced mode! In normal mode, you can remove the next line. + bmp280.takeForcedMeasurement(); // has no effect in normal mode + + if (humidity) *humidity = bmp280.readHumidity(); + if (temperature) *temperature = bmp280.readTemperature(); + if (pressure) *pressure = bmp280.readPressure() / 100.0F; + if (altitude) *altitude = bmp280.readAltitude(SEALEVELPRESSURE_HPA); +} diff --git a/src/sensors/MS5611Sensor.cpp b/src/sensors/MS5611Sensor.cpp new file mode 100644 index 0000000..fa214c3 --- /dev/null +++ b/src/sensors/MS5611Sensor.cpp @@ -0,0 +1,25 @@ +// MS5611Sensor.cpp + +#include "MS5611Sensor.h" + +// Initialize MS5611 sensor +// Ultra high resolution: MS5611_ULTRA_HIGH_RES +// (default) High resolution: MS5611_HIGH_RES +// Standard: MS5611_STANDARD +// Low power: MS5611_LOW_POWER +// Ultra low power: MS5611_ULTRA_LOW_POWER + +MS5611Sensor::MS5611Sensor() { + // Constructor - No need to do anything here +} + +void MS5611Sensor::init() { + ms5611.begin(MS5611_ULTRA_HIGH_RES); +} + +void MS5611Sensor::read_values(double* temperature, double* preasure, double* altitude) { + if (temperature) *temperature = ms5611.readTemperature(true); + if (preasure) *preasure = ms5611.readPressure(true); + + if (altitude) *altitude = ms5611.getAltitude(*preasure); // second arg double seaLevelPressure = 101325 +} diff --git a/src/static/sensor_things_tmpl.json b/src/static/sensor_things_tmpl.json index b5f3295..ad57b9a 100644 --- a/src/static/sensor_things_tmpl.json +++ b/src/static/sensor_things_tmpl.json @@ -19,7 +19,7 @@ "definition": "http://www.qudt.org/qudt/owl/1.0.0/quantity/Instances.html#Temperature" }, "Sensor": { - "name": "Temperature Sensor", + "name": "temperature", "description": "Sensor for measuring temperature" }, "Observations": [ @@ -43,7 +43,7 @@ "definition": "http://www.qudt.org/qudt/owl/1.0.0/quantity/Instances.html#RelativeHumidity" }, "Sensor": { - "name": "Humidity Sensor", + "name": "humidity", "description": "Sensor for measuring humidity" }, "Observations": [ @@ -67,7 +67,7 @@ "definition": "http://www.qudt.org/qudt/owl/1.0.0/quantity/Instances.html#TotalVolatileOrganicCompounds" }, "Sensor": { - "name": "TVOC Sensor", + "name": "etcov", "description": "Sensor for measuring Total Volatile Organic Compounds" }, "Observations": [ @@ -91,7 +91,7 @@ "definition": "http://www.qudt.org/qudt/owl/1.0.0/quantity/Instances.html#CarbonDioxideConcentration" }, "Sensor": { - "name": "eCO2 Sensor", + "name": "eco2", "description": "Sensor for measuring Equivalent Carbon Dioxide" }, "Observations": [