From cbe2dc624f75bce20c33c098108ff098776af160 Mon Sep 17 00:00:00 2001 From: Oliver Bell Date: Fri, 23 Nov 2018 21:48:22 +0000 Subject: [PATCH] Add co2 library --- lib/MQ135/MQ135.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++ lib/MQ135/MQ135.h | 55 ++++++++++++++++++++ src/main.cpp | 119 ++++++++++++++++++++++++++---------------- 3 files changed, 252 insertions(+), 45 deletions(-) create mode 100644 lib/MQ135/MQ135.cpp create mode 100644 lib/MQ135/MQ135.h diff --git a/lib/MQ135/MQ135.cpp b/lib/MQ135/MQ135.cpp new file mode 100644 index 0000000..676ed31 --- /dev/null +++ b/lib/MQ135/MQ135.cpp @@ -0,0 +1,123 @@ +/**************************************************************************/ +/*! +@file MQ135.cpp +@author G.Krocker (Mad Frog Labs) +@license GNU GPLv3 + +First version of an Arduino Library for the MQ135 gas sensor +TODO: Review the correction factor calculation. This currently relies on +the datasheet but the information there seems to be wrong. + +@section HISTORY + +v1.0 - First release +*/ +/**************************************************************************/ + +#include "MQ135.h" + +/**************************************************************************/ +/*! +@brief Default constructor + +@param[in] pin The analog input pin for the readout of the sensor +*/ +/**************************************************************************/ + +MQ135::MQ135(uint8_t pin) { + _pin = pin; +} + + +/**************************************************************************/ +/*! +@brief Get the correction factor to correct for temperature and humidity + +@param[in] t The ambient air temperature +@param[in] h The relative humidity + +@return The calculated correction factor +*/ +/**************************************************************************/ +float MQ135::getCorrectionFactor(float t, float h) { + return CORA * t * t - CORB * t + CORC - (h-33.)*CORD; +} + +/**************************************************************************/ +/*! +@brief Get the resistance of the sensor, ie. the measurement value + +@return The sensor resistance in kOhm +*/ +/**************************************************************************/ +float MQ135::getResistance() { + int val = analogRead(_pin); + return ((1023./(float)val) * 5. - 1.)*RLOAD; +} + +/**************************************************************************/ +/*! +@brief Get the resistance of the sensor, ie. the measurement value corrected + for temp/hum + +@param[in] t The ambient air temperature +@param[in] h The relative humidity + +@return The corrected sensor resistance kOhm +*/ +/**************************************************************************/ +float MQ135::getCorrectedResistance(float t, float h) { + return getResistance()/getCorrectionFactor(t, h); +} + +/**************************************************************************/ +/*! +@brief Get the ppm of CO2 sensed (assuming only CO2 in the air) + +@return The ppm of CO2 in the air +*/ +/**************************************************************************/ +float MQ135::getPPM() { + return PARA * pow((getResistance()/RZERO), -PARB); +} + +/**************************************************************************/ +/*! +@brief Get the ppm of CO2 sensed (assuming only CO2 in the air), corrected + for temp/hum + +@param[in] t The ambient air temperature +@param[in] h The relative humidity + +@return The ppm of CO2 in the air +*/ +/**************************************************************************/ +float MQ135::getCorrectedPPM(float t, float h) { + return PARA * pow((getCorrectedResistance(t, h)/RZERO), -PARB); +} + +/**************************************************************************/ +/*! +@brief Get the resistance RZero of the sensor for calibration purposes + +@return The sensor resistance RZero in kOhm +*/ +/**************************************************************************/ +float MQ135::getRZero() { + return getResistance() * pow((ATMOCO2/PARA), (1./PARB)); +} + +/**************************************************************************/ +/*! +@brief Get the corrected resistance RZero of the sensor for calibration + purposes + +@param[in] t The ambient air temperature +@param[in] h The relative humidity + +@return The corrected sensor resistance RZero in kOhm +*/ +/**************************************************************************/ +float MQ135::getCorrectedRZero(float t, float h) { + return getCorrectedResistance(t, h) * pow((ATMOCO2/PARA), (1./PARB)); +} diff --git a/lib/MQ135/MQ135.h b/lib/MQ135/MQ135.h new file mode 100644 index 0000000..ea74537 --- /dev/null +++ b/lib/MQ135/MQ135.h @@ -0,0 +1,55 @@ +/**************************************************************************/ +/*! +@file MQ135.h +@author G.Krocker (Mad Frog Labs) +@license GNU GPLv3 + +First version of an Arduino Library for the MQ135 gas sensor +TODO: Review the correction factor calculation. This currently relies on +the datasheet but the information there seems to be wrong. + +@section HISTORY + +v1.0 - First release +*/ +/**************************************************************************/ +#ifndef MQ135_H +#define MQ135_H +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +/// The load resistance on the board +#define RLOAD 10.0 +/// Calibration resistance at atmospheric CO2 level +#define RZERO 76.63 +/// Parameters for calculating ppm of CO2 from sensor resistance +#define PARA 116.6020682 +#define PARB 2.769034857 + +/// Parameters to model temperature and humidity dependence +#define CORA 0.00035 +#define CORB 0.02718 +#define CORC 1.39538 +#define CORD 0.0018 + +/// Atmospheric CO2 level for calibration purposes +#define ATMOCO2 397.13 + +class MQ135 { + private: + uint8_t _pin; + + public: + MQ135(uint8_t pin); + float getCorrectionFactor(float t, float h); + float getResistance(); + float getCorrectedResistance(float t, float h); + float getPPM(); + float getCorrectedPPM(float t, float h); + float getRZero(); + float getCorrectedRZero(float t, float h); +}; +#endif diff --git a/src/main.cpp b/src/main.cpp index 649b47d..32a7ed9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,4 @@ -#include // Only needed for Arduino 1.6.5 and earlier +#include // Only needed for Arduino 1.6.5 and earlier #include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"` #include "SD.h" @@ -11,10 +11,15 @@ #include "ArduinoJson.h" #include "NTPClient.h" #include +#include "MQ135.h" + +#define GAS_SENSOR_PIN A0 const char *ssid = WIFI_SSID; const char *password = WIFI_PASSWORD; +MQ135 gasSensor = MQ135(GAS_SENSOR_PIN); + // Generate client name from the sensor ID String clientName; @@ -28,19 +33,19 @@ WiFiUDP ntpUDP; IPAddress server(192, 168, 0, 103); PubSubClient mqClient(server, 1883, net); -BME280I2C bme; // Default : forced mode, standby time = 1000 ms - // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off, +BME280I2C bme; // Default : forced mode, standby time = 1000 ms + // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off, NTPClient ntpClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); - // Initialize the OLED display using Wire library // This also initialising wire on pins D0 and D1 SSD1306Wire display(0x3c, D3, D5); bool displayInitialised = false; /* Display some text on the screen */ -void displayText(const String text) { +void displayText(const String text) +{ display.clear(); display.setTextAlignment(TEXT_ALIGN_LEFT); display.setFont(ArialMT_Plain_10); @@ -52,7 +57,8 @@ void displayText(const String text) { * Poll the BME sensor, print the results to the screen * and publish the results to the MQTT broker */ -void pollSensor() { +void pollSensor() +{ float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); @@ -60,29 +66,32 @@ void pollSensor() { bme.read(pres, temp, hum, tempUnit, presUnit); + float co2 = gasSensor.getCorrectedPPM(temp, hum); + String dataStr = ""; dataStr += "Temp: "; dataStr += temp; - dataStr += "°"+ String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F'); + dataStr += "°" + String(tempUnit == BME280::TempUnit_Celsius ? 'C' : 'F'); dataStr += "\n Humidity: "; dataStr += hum; dataStr += "% RH"; - dataStr += "\n Pressure: "; - dataStr += pres / 1000 ; - dataStr += "kPa"; + + dataStr += "\n CO2: "; + dataStr += co2; // Display the sensor info on the screen displayText(dataStr); StaticJsonBuffer<128> jsonBuffer; - JsonObject& data = jsonBuffer.createObject(); + JsonObject &data = jsonBuffer.createObject(); data["sensorId"] = SENSOR_ID; data["temp"] = temp; data["hum"] = hum; data["pres"] = pres; - data["tempUnit"] = tempUnit == BME280::TempUnit_Celsius ? "c" :"f"; + data["tempUnit"] = tempUnit == BME280::TempUnit_Celsius ? "c" : "f"; + data["co2"] = co2; // Make json string char dataBuffer[100]; @@ -92,14 +101,17 @@ void pollSensor() { mqClient.publish(clientName.c_str(), dataBuffer); } -void generateClientName() { +void generateClientName() +{ clientName += "sensor-"; clientName += SENSOR_ID; } -void initialiseDisplay() { +void initialiseDisplay() +{ // Initialising the UI will init the display too. - if (displayInitialised) { + if (displayInitialised) + { return; } display.init(); @@ -107,21 +119,24 @@ void initialiseDisplay() { displayInitialised = true; } -void ensureWifi() { - if (WiFi.status() == WL_CONNECTED) { +void ensureWifi() +{ + if (WiFi.status() == WL_CONNECTED) + { return; } // Open wifi connection wifi settings in config WiFi.begin(ssid, password); - + String conn_message = "Connecting to "; conn_message += ssid; displayText(conn_message); // Show a status for when we are connecting to the wifi - while (WiFi.status() != WL_CONNECTED) { + while (WiFi.status() != WL_CONNECTED) + { delay(500); conn_message += "."; Serial.println(conn_message); @@ -133,8 +148,10 @@ void ensureWifi() { delay(300); } -void ensureMqtt() { - if (mqClient.connected()) { +void ensureMqtt() +{ + if (mqClient.connected()) + { return; } @@ -146,8 +163,9 @@ void ensureMqtt() { Serial.print(server); Serial.print(" as "); Serial.println(clientName); - - while (!mqClient.connect(clientName.c_str())) { + + while (!mqClient.connect(clientName.c_str())) + { m_conn_str += "."; displayText(m_conn_str); delay(250); @@ -156,23 +174,27 @@ void ensureMqtt() { displayText("Connected MQTT broker"); } -void syncTime() { +void syncTime() +{ displayText("Syncing clock"); ntpClient.begin(); // Display the time for a few seconds - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) + { ntpClient.update(); displayText(ntpClient.getFormattedTime()); delay(500); } } -void setupSensor() { +void setupSensor() +{ displayText("Finding BME280 sensor"); delay(200); - - while (!bme.begin()) { + + while (!bme.begin()) + { Serial.println("Could not find BME280 sensor!"); displayText("Could not find BME280 sensor! Trying again"); delay(1000); @@ -180,16 +202,17 @@ void setupSensor() { String sensorMessage; // Print what our sensor has - switch (bme.chipModel()) { - case BME280::ChipModel_BME280: - sensorMessage = "Found BME280 sensor! Success."; - break; - case BME280::ChipModel_BMP280: - sensorMessage = "Found BMP280 sensor! No Humidity available."; - break; - default: - sensorAvailable = false; - sensorMessage = "Found UNKNOWN sensor! Error!"; + switch (bme.chipModel()) + { + case BME280::ChipModel_BME280: + sensorMessage = "Found BME280 sensor! Success."; + break; + case BME280::ChipModel_BMP280: + sensorMessage = "Found BMP280 sensor! No Humidity available."; + break; + default: + sensorAvailable = false; + sensorMessage = "Found UNKNOWN sensor! Error!"; } displayText(sensorMessage); @@ -204,24 +227,26 @@ void setupSensor() { * to the MQTT broker, make a reading * and send that reading to the MQTT broker */ -void setup() { +void setup() +{ Serial.begin(9600); delay(100); initialiseDisplay(); // The display could be off from the last deep sleep display.displayOn(); - + // Only need to generate the client name // if it is not already in ram - if (clientName.length() == 0) { + if (clientName.length() == 0) + { generateClientName(); } - + ensureWifi(); syncTime(); - + // Initialse a connection to the BME sensor setupSensor(); @@ -229,7 +254,8 @@ void setup() { // Get the sensor data and // send it to the broker - if (sensorAvailable) { + if (sensorAvailable) + { pollSensor(); } @@ -246,4 +272,7 @@ void setup() { // Loop as unused as we use the setup as a loop // after deep sleep -void loop() {} +void loop() +{ + //pollSensor(); +}