P145 code cleanup,small GUI changes, documentation updates

This commit is contained in:
flashmark
2023-01-14 21:00:32 +01:00
parent 1e55ddb08e
commit 4e3ff774e3
5 changed files with 53 additions and 35 deletions
+20 -8
View File
@@ -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
View File
@@ -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
+5 -4
View File
@@ -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
+17 -17
View File
@@ -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