mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 01:24:04 +00:00
P145 code cleanup,small GUI changes, documentation updates
This commit is contained in:
@@ -29,10 +29,12 @@ This plugin supports gas sensors for which the resistance depends on a gas conce
|
||||
The analog value of the sensors is determined by a resistance (Rsensor). The sensor is typically used in series with a load resistor (Rload).
|
||||
The voltage is over Rload is connected to the analog input of the ESP. A sensor and gas specific conversion formula is used to convert the measured voltage to a gas concentration.
|
||||
|
||||
Typical MQ-xxx sensor connection:
|
||||
|
||||
.. image:: P145_MQ-xxx-Diagram.jpg
|
||||
:alt: Basic circuit to connect MQ-xxx sensors
|
||||
|
||||
The plugin can be configured to select one of the predefined sensor types and tune the conversion parameters.
|
||||
The plugin can be configured to select one of the predefined sensor types. Various parameters can be tuned to adapt the conversion.
|
||||
|
||||
Basic algorithm
|
||||
^^^^^^^^^^^^^^^
|
||||
@@ -40,6 +42,8 @@ Basic algorithm
|
||||
The MQ-xxx series of sensors contain a resistor that depends on the concentration of certain gases in the environment. This relation is typically provided in the datasheets as a curve on a logarithmic plot. Figure below shows this relation for the MQ-3 sensor. In the indicated range the plot is close to linear.
|
||||
The plugin uses a conversion algoritm based upon a linear relation in the log-log plot. This results in two parameters parA and parB that determine this linear relation.
|
||||
|
||||
Example MQ-xxx sensor gas level to resistance graph:
|
||||
|
||||
.. image:: P145_SensorPlot.jpg
|
||||
:alt: Relation between gas level and sensor resistance (Rsensor/Rzero)
|
||||
|
||||
@@ -60,6 +64,8 @@ Temperature and humidity compensation
|
||||
The MQ-xxx series of sensors is depending on the environment temperature and humidity. The datasheets often provide a graph that shows the relation between the Rsensor/Rzero ratio and environment temperature for various relative humidity levels. Figure below shows this relation for the MQ-3 sensor.
|
||||
From the plot it is clear that this relation is not linear.
|
||||
|
||||
Example MQ-xxx temperature and humidity influence on sensor resistance
|
||||
|
||||
.. image:: P145_TemHumDependency.jpg
|
||||
:alt: Relation between temperature & humidity versus sensor resistance (Rsensor/Rzero)
|
||||
|
||||
@@ -107,29 +113,35 @@ Hardware
|
||||
|
||||
Sensors can be bought as a component. The internet is full of development boards that contain the sensor with load resistor connected. These boards also provide a comparator that compares the output value with a preset value for a simple digital output.
|
||||
|
||||
Development board with MQ-7 CO sensor:
|
||||
|
||||
.. image:: P145_MQ_hardware.jpeg
|
||||
:alt: MQ-7 development board
|
||||
|
||||
Development board with MQ-7 CO sensor
|
||||
|
||||
Schematics for development board:
|
||||
|
||||
.. image:: P145_MQxxx-sensor-board.jpg
|
||||
:alt: Development board schematics
|
||||
|
||||
Schematics for development board
|
||||
|
||||
Connections
|
||||
^^^^^^^^^^^^
|
||||
|
||||
The sensor analog output is connected to an analog input on the ESP. The digital output provided on development boards is not used by the plugin. You can use the digital input plugin instead (P001).
|
||||
See analog input plugin for more information about the available analog inputs for each ESP version.
|
||||
The plugin uses the standard ESPeasy mechanism to select an analog input pin. Selection options depend on the type of ESP used.
|
||||
See analog input plugin for more information about the available analog inputs for each of the supported ESP versions.
|
||||
|
||||
Load resistor remarks
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The manufactors of the MQ-xxx sensors provide the conversion plots given a set of environment conditions. This includes the supply voltage and a prescribed range for the load resistor Rload.
|
||||
Unfortunately most of the cheap development boards put a standard 1kOhm resistor on the board. For most sensors this value is far too low. The plugin can handle such values.
|
||||
However such a low value for the load resistor decreases the range of the output signal and thus the accuracy of the measurent. It also uses the sensor in a range that is not supported by the manufator.
|
||||
It is advised to replace this sensor by a value fitting the range advised by the manufactor. You can find these in the datasheets.
|
||||
However, a too low value for the load resistor decreases the range of the output voltage signal and thus the accuracy of the measurent. It also uses the sensor in a range that is not supported by the manufator.
|
||||
It is advised to replace this resistor by a value fitting the range advised by the manufactor. You can find the correct range in the datasheet for the sensor.
|
||||
|
||||
Location of Rload on many of the cheap development boards:
|
||||
|
||||
.. image:: P145_loadResistor.png
|
||||
:alt: Load register location on sensor development boards
|
||||
|
||||
Many ESP8266 boards use resistors to divide the analog input value so map the 1 Volt input range of the ESP to the 3.3 Volt supply range. These resistors are in parallel with the Rload on the sensor board.
|
||||
This shall be taken into account when determining the Rload value for the plugin.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
+11
-6
@@ -151,7 +151,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
|
||||
# ifdef ESP32
|
||||
// Analog input selection
|
||||
addRowLabel(F("Analog Pin"));
|
||||
addADC_PinSelect(AdcPinSelectPurpose::ADC_Touch_HallEffect, F("taskdevicepin1"), CONFIG_PIN1);
|
||||
addADC_PinSelect(AdcPinSelectPurpose::ADC_Touch_HallEffect, F("taskdevicepin1"), CONFIG_PIN_AIN);
|
||||
# endif // ifdef ESP32
|
||||
|
||||
addFormFloatNumberBox(F("Load Resistance"), F("plugin_145_RLOAD"), P145_PCONFIG_RLOAD, 0.0f, 10e6f, 2U);
|
||||
@@ -175,10 +175,13 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
|
||||
bool compensate = P145_PCONFIG_FLAGS & 0x0001;
|
||||
bool calibrate = (P145_PCONFIG_FLAGS >> 1) & 0x0001;
|
||||
addFormCheckBox(F("Enable automatic calibration"), F("plugin_145_enable_calibrarion"), calibrate);
|
||||
addFormCheckBox(F("Enable temp/humid compensation"), F("plugin_145_enable_compensation"), compensate);
|
||||
addFormNote(F("If this is enabled, the Temperature and Humidity values below need to be configured."));
|
||||
//if (compensate)
|
||||
//addFormCheckBox(F("Enable temp/humid compensation"), F("plugin_145_enable_compensation"), compensate);
|
||||
addFormSelector_YesNo(F("Enable temp/humid compensation"), F("plugin_145_enable_compensation"), compensate, true);
|
||||
// Above selector will fore reloading the page and thus updating the compensate flag
|
||||
// Show the compensation details only when compensation is enabled
|
||||
if (compensate)
|
||||
{
|
||||
addFormNote(F("If compensation is enabled, the Temperature and Humidity values below need to be configured."));
|
||||
// temperature
|
||||
addRowLabel(F("Temperature"));
|
||||
addTaskSelect(F("plugin_145_temperature_task"), P145_PCONFIG_TEMP_TASK);
|
||||
@@ -210,7 +213,8 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
|
||||
P145_PCONFIG_RLOAD = getFormItemFloat(F("plugin_145_RLOAD"));
|
||||
P145_PCONFIG_RZERO = getFormItemFloat(F("plugin_145_RZERO"));
|
||||
P145_PCONFIG_REF = getFormItemFloat(F("plugin_145_REFLEVEL"));
|
||||
bool compensate = isFormItemChecked(F("plugin_145_enable_compensation") );
|
||||
//bool compensate = isFormItemChecked(F("plugin_145_enable_compensation") );
|
||||
bool compensate = (getFormItemInt(F("plugin_145_enable_compensation")) == 1);
|
||||
bool calibrate = isFormItemChecked(F("plugin_145_enable_calibrarion") );
|
||||
P145_PCONFIG_FLAGS = compensate + (calibrate << 1);
|
||||
P145_PCONFIG_TEMP_TASK = getFormItemInt(F("plugin_145_temperature_task"));
|
||||
@@ -263,7 +267,8 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
float temperature = 20.0f; // A reasonable value in case temperature source task is invalid
|
||||
float humidity = 60.0f; // A reasonable value in case tumidity source task is invalid
|
||||
if (validTaskIndex(P145_PCONFIG_TEMP_TASK) && validTaskIndex(P145_PCONFIG_HUM_TASK))
|
||||
bool compensate = P145_PCONFIG_FLAGS & 0x0001;
|
||||
if (compensate && validTaskIndex(P145_PCONFIG_TEMP_TASK) && validTaskIndex(P145_PCONFIG_HUM_TASK))
|
||||
{
|
||||
// we're checking a var from another task, so calculate that basevar
|
||||
temperature = UserVar[P145_PCONFIG_TEMP_TASK * VARS_PER_TASK + P145_PCONFIG_TEMP_VAL]; // in degrees C
|
||||
|
||||
@@ -270,7 +270,7 @@ float P145_data_struct::getPPM(float rSensor)
|
||||
return (sensordef.para * powf((rSensor/rzero), -sensordef.parb));
|
||||
break;
|
||||
case p145AlgB: // Miquel5612 Exponential
|
||||
return (sensordef.para *powf((rSensor/rzero), sensordef.parb));
|
||||
return (sensordef.para * powf((rSensor/rzero), sensordef.parb));
|
||||
break;
|
||||
case p145AlgC: // Miquel5612 Linear
|
||||
return (powf(10.0f, (log10f(rSensor/rzero)-sensordef.parb)/sensordef.para));
|
||||
@@ -294,10 +294,10 @@ float P145_data_struct::getPPM(float rSensor)
|
||||
/*****************************************************************************/
|
||||
float P145_data_struct::getCorrectedPPM(float rSensor, float temperature, float humidity)
|
||||
{
|
||||
float c = 1.0f; // Correction factor
|
||||
switch (algorithm)
|
||||
{
|
||||
case p145AlgA:
|
||||
float c; // Temperature & humidity correction factor
|
||||
if (temperature < 20.0f)
|
||||
{
|
||||
c = sensordef.cora * temperature * temperature - sensordef.corb * temperature + sensordef.corc - (humidity - 33.0f) * sensordef.cord;
|
||||
@@ -306,7 +306,6 @@ float P145_data_struct::getCorrectedPPM(float rSensor, float temperature, float
|
||||
{
|
||||
c = sensordef.core * temperature + sensordef.corf * humidity + sensordef.corg;
|
||||
}
|
||||
return (sensordef.para * pow(((rSensor / c) / rzero), -sensordef.parb));
|
||||
case p145AlgB:
|
||||
// TODO Still missing a formula to correct for temp/hum when applying this algorithm
|
||||
break;
|
||||
@@ -317,7 +316,7 @@ float P145_data_struct::getCorrectedPPM(float rSensor, float temperature, float
|
||||
// Do nothing, return a default value at the end
|
||||
break;
|
||||
}
|
||||
return 0.0f; // Default value should never be returned
|
||||
return getPPM(rSensor/c); // Default value should never be returned
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -429,6 +428,8 @@ float P145_data_struct::readValue(float temperature, float humidity)
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: RS= "), rSensor)); // Calculated sensor resistance Rsensor
|
||||
#ifdef P145_DEBUG
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: Ref= "), refLevel)); // Reference level for calibration
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: Temp= "), temperature)); // Temperature for compensation algorithm
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: Hum= "), humidity)); // Humidity for compensation algorithm
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: ain= "), ain)); // Measured analog input value
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: ovs= "), ovs_cnt)); // Oversampling count
|
||||
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: algorithm= "), algorithm)); // Conversion algorithm
|
||||
|
||||
@@ -38,19 +38,19 @@ enum P145_algorithm
|
||||
// Members are preferrably sorted by memory alignment
|
||||
struct P145_SENSORDEF
|
||||
{
|
||||
float cleanRatio; // Rs/R0 ratio in clean air
|
||||
float para; // PARA scaling factor value
|
||||
float parb; // PARB exponent value
|
||||
float cora; // CORA
|
||||
float corb; // CORB
|
||||
float corc; // CORC
|
||||
float cord; // CORD
|
||||
float core; // CORE
|
||||
float corf; // CORF
|
||||
float corg; // CORG
|
||||
float cleanRatio; // Rs/R0 ratio in clean air
|
||||
float para; // PARA scaling factor value
|
||||
float parb; // PARB exponent value
|
||||
float cora; // CORA
|
||||
float corb; // CORB
|
||||
float corc; // CORC
|
||||
float cord; // CORD
|
||||
float core; // CORE
|
||||
float corf; // CORF
|
||||
float corg; // CORG
|
||||
P145_algorithm alg; // Preferred/tuned algorithm
|
||||
char name[8]; // Sensor type name
|
||||
char gas[8]; // Measured gas concentration
|
||||
char name[8]; // Sensor type name
|
||||
char gas[8]; // Measured gas concentration
|
||||
};
|
||||
|
||||
struct P145_data_struct : public PluginTaskData_base
|
||||
@@ -65,12 +65,12 @@ struct P145_data_struct : public PluginTaskData_base
|
||||
uint ovs_cnt = 0; // Oversampling algorithm sample counter
|
||||
float last_ain = 0.0; // Oversampling algorithm last measured analog input value
|
||||
/* Calibration static data */
|
||||
ulong last_cal = 0; // Last calibration timestamp
|
||||
float rcal = 0.0; // Rcal, calibration resistance [Ohm]
|
||||
ulong last_cal = 0U; // Last calibration timestamp
|
||||
float rcal = 0.0f; // Rcal, calibration resistance [Ohm]
|
||||
/* Sensor value conversion parameters */
|
||||
float rload = 0.0; // Rload, load resistor [Ohm]
|
||||
float rzero = 0.0; // R0, reference resistance [Ohm]
|
||||
float refLevel = 0.0; // Reference level for calibration [ppm]
|
||||
float rload = 0.0f; // Rload, load resistor [Ohm]
|
||||
float rzero = 0.0f; // R0, reference resistance [Ohm]
|
||||
float refLevel = 0.0f; // Reference level for calibration [ppm]
|
||||
/* Sensor type & user options */
|
||||
bool compensation = false; // Use temperature compensation
|
||||
bool calibration = false; // Perform auto calibration
|
||||
|
||||
Reference in New Issue
Block a user