diff --git a/lib/MQ135/MQ135.cpp b/lib/MQ135/MQ135.cpp index 676ed31..ef235f7 100644 --- a/lib/MQ135/MQ135.cpp +++ b/lib/MQ135/MQ135.cpp @@ -1,123 +1,83 @@ -/**************************************************************************/ -/*! -@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; + this->pin = pin; + pinMode(pin, INPUT); } - -/**************************************************************************/ -/*! -@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; +double MQ135::getVoltage() { + return (double)analogRead(pin) * VStep; } -/**************************************************************************/ -/*! -@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; +double MQ135::getResistance() { + double voltage = getVoltage(); + double rs = ((VIn * RL) / voltage) - RL; + if (rs < 0) { + rs = 0; + } + return rs; } -/**************************************************************************/ -/*! -@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); +double MQ135::getPPM(float a, float b) { + double ratio = getResistance() / R0; + double ppm = a * pow(ratio, b); + if (ppm < 0) { + ppm = 0; + } + return ppm; } -/**************************************************************************/ -/*! -@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); +double MQ135::getPPMLinear(float a, float b) { + double ratio = getResistance() / R0; + double ppm_log = (log10(ratio) - b) / a; + double ppm = pow(10, ppm_log); + if (ppm < 0) { + ppm = 0; + } + return ppm; } -/**************************************************************************/ -/*! -@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); +double MQ135::getAcetona() { + return getPPM(34.668, -3.369); } -/**************************************************************************/ -/*! -@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)); +double MQ135::getAlcohol() { + return getPPM(77.255, -3.18); } -/**************************************************************************/ -/*! -@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)); +double MQ135::getCO2() { + // return getPPMLinear(-0.3525, 0.7142) + ATMOCO2; + return getPPM(110.47, -2.862) + ATMOCO2; +} + +double MQ135::getCO() { + return getPPM(605.18, -3.937); +} + +double MQ135::getNH4() { + return getPPM(102.2, -2.473); +} + +double MQ135::getTolueno() { + return getPPM(44.947, -3.445); +} + +float MQ135::getR0() { + double r0 = getResistance() / 3.6; + return r0; +} + +double MQ135::getR0ByCO2Level(float ppm) { + if (ppm > ATMOCO2) { + ppm -= ATMOCO2; + } + else { + return NAN; + } + double tmp = -(log10(ppm / 110.47) / -2.862) + log10(getResistance()); + return pow(10, tmp); +} + +void MQ135::setR0(float r0) { + R0 = r0; } diff --git a/lib/MQ135/MQ135.h b/lib/MQ135/MQ135.h index 1ac6080..4f3d0fe 100644 --- a/lib/MQ135/MQ135.h +++ b/lib/MQ135/MQ135.h @@ -1,56 +1,66 @@ -/**************************************************************************/ -/*! -@file MQ135.h -@author G.Krocker (Mad Frog Labs) -@license GNU GPLv3 +#ifndef MQ135New_H +#define MQ135New_H -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. +#include "Arduino.h" -@section HISTORY +/// Resistor on Sensor in kΩ +#define RL 10 -v1.0 - First release -*/ -/**************************************************************************/ -#ifndef MQ135_H -#define MQ135_H -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif +/// Voltage on Sensor in V +#define VIn 5 -/// The load resistance on the board -#define RLOAD 10.0 -/// Calibration resistance at atmospheric CO2 level -// #define RZERO 76.63 -#define RZERO 480 -/// Parameters for calculating ppm of CO2 from sensor resistance -#define PARA 116.6020682 -#define PARB 2.769034857 +/// Board analog Input Resolution +/// Default: 2^10 +#define Resolution 1024 -/// 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 +/// CO2 Level in Atmosphere #define ATMOCO2 397.13 +/// Helper to calculate Voltage from Input +/// Voltage = input * Vin / (Resolution - 1) +const double VStep = (double)VIn / (Resolution - 1); + class MQ135 { private: - uint8_t _pin; + /// input pin + uint8_t pin; + /// calibration Resistance + float R0; public: + /// Constructor with analog input Pin 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); + /// Get R0 in default conditions for calibration purposes. + /// Assume CO2 Level is the default Atmospheric Level (~400ppm) + float getR0(); + /// Get R0 in custom conditions for calibration purposes. + /// Can be used, if you know the current CO2 Level. + double getR0ByCO2Level(float ppm); + /// Set R0 Value for calibration. + void setR0(float r0); + + /// Gets the resolved sensor voltage + double getVoltage(); + /// Calculates the Resistance of the Sensor + double getResistance(); + /// Calculates ppm on a exponential curve + /// (Different Gases have different curves) + double getPPM(float a, float b); + /// Calculates ppm on a linear curve + /// (Different Gases have different curves) + double getPPMLinear(float a, float b); + + /// Gets ppm of Acetona in Air (C3H6O) + double getAcetona(); + /// Gets ppm of Alcohol in Air + double getAlcohol(); + /// Gets ppm of CO in Air + double getCO(); + /// Gets ppm of CO2 in Air + double getCO2(); + /// Gets ppm of NH4 in Air + double getNH4(); + /// Gets ppm of Tolueno in Air (CH3) + double getTolueno(); }; #endif diff --git a/platformio.ini b/platformio.ini index ea23b77..ae5769a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,7 +8,10 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html -[env:uno] +[env:mega] platform = atmelavr -board = uno +board = megaatmega2560 framework = arduino +; lib_deps = +; adafruit/Adafruit BME280 Library @ ^2.1.2 +; miguel5612/MQUnifiedsensor @ ^2.0.1 diff --git a/src/main.cpp b/src/main.cpp index 158386b..133cbca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,7 @@ #include #include -#include #define PIN_MQ135 A0 -#define PIN_DHT11 A1 - #define PIN_LED_GREEN DD2 #define PIN_LED_YELLOW DD3 #define PIN_LED_RED DD4 @@ -13,93 +10,48 @@ #define MEASURE_DELAY 1000 #define NOISE_DELAY 100 -MQ135 co2_sensor = MQ135(PIN_MQ135); -dht dht_sensor; -const int maxCount = MEASURE_DELAY / NOISE_DELAY; +const int maxCount = 10; int count = 0; -float ppm = -1; bool noise = false; +bool noiseActive = false; + +float ppm; + +MQ135 co2_sensor(PIN_MQ135); void setup() { Serial.begin(9600); - pinMode(PIN_MQ135, INPUT); - pinMode(PIN_DHT11, INPUT); - pinMode(PIN_LED_GREEN, OUTPUT); - pinMode(PIN_LED_YELLOW, OUTPUT); - pinMode(PIN_LED_RED, OUTPUT); - pinMode(PIN_NOISE, OUTPUT); + co2_sensor.setR0(100); } -float measure() { - Serial.print(RZERO); - Serial.println("---------------------------"); - Serial.print("DHT:\t"); - int chk = dht_sensor.read11(PIN_DHT11); - switch (chk) - { - case DHTLIB_OK: - Serial.print("OK,\t"); - break; - case DHTLIB_ERROR_CHECKSUM: - Serial.print("Checksum error,\t"); - break; - case DHTLIB_ERROR_TIMEOUT: - Serial.print("Time out error,\t"); - break; - case DHTLIB_ERROR_CONNECT: - Serial.print("Connect error,\t"); - break; - case DHTLIB_ERROR_ACK_L: - Serial.print("Ack Low error,\t"); - break; - case DHTLIB_ERROR_ACK_H: - Serial.print("Ack High error,\t"); - break; - default: - Serial.print("Unknown error,\t"); - break; - } - Serial.println(); - - Serial.print ("temperature: "); - Serial.println (dht_sensor.temperature); - Serial.print ("humidity: "); - Serial.println (dht_sensor.humidity); - - float val = analogRead(A0); - Serial.print ("raw = "); - Serial.println (val); - float zero = co2_sensor.getCorrectedRZero(dht_sensor.temperature, dht_sensor.humidity); - Serial.print ("rzero: "); - Serial.println (zero); - float ppm = co2_sensor.getCorrectedPPM(dht_sensor.temperature, dht_sensor.humidity); - Serial.print ("ppm: "); - Serial.println (ppm); - return ppm; -} +void printValues(float ppm, float temp, float humidity); void loop() { - if (count >= maxCount || ppm < 0) { - ppm = measure(); + if (count >= maxCount || count < 0) { + ppm = co2_sensor.getCO2(); + count = 0; if (ppm < 1000) { + noiseActive = false; digitalWrite(PIN_LED_GREEN, 1); digitalWrite(PIN_LED_YELLOW, 0); digitalWrite(PIN_LED_RED, 0); } else if (ppm <= 2000) { + noiseActive = false; digitalWrite(PIN_LED_GREEN, 0); digitalWrite(PIN_LED_YELLOW, 1); digitalWrite(PIN_LED_RED, 0); } else { + noiseActive = true; digitalWrite(PIN_LED_GREEN, 0); digitalWrite(PIN_LED_YELLOW, 0); digitalWrite(PIN_LED_RED, 1); } } - if (ppm > 2000) { + if (noiseActive) { noise = !noise; digitalWrite(PIN_NOISE, noise); }