P145 plugin updates & refactoring

This commit is contained in:
flashmark
2023-02-22 23:11:23 +01:00
parent b2ade65e7b
commit 7bb92fbfaa
3 changed files with 195 additions and 161 deletions
+33 -6
View File
@@ -59,12 +59,12 @@
// P145_PCONFIG_RLOAD PCONFIG_FLOAT(0) RLOAD [Ohm]
// P145_PCONFIG_RZERO PCONFIG_FLOAT(1) RZERO [Ohm]
// P145_PCONFIG_REF PCONFIG_FLOAT(2) REF. level [ppm]
// P145_PCONFIG_FLAGS PCONFIG(0) Enable compensation & Enable Calibration & use low Vcc
// P145_PCONFIG_TEMP_TASK PCONFIG(1) Temperature compensation task
// P145_PCONFIG_TEMP_VAL PCONFIG(2) Temperature compensation value
// P145_PCONFIG_HUM_TASK PCONFIG(3) Humidity compensation task
// P145_PCONFIG_HUM_VAL PCONFIG(4) Humidity compensation value
// P145_PCONFIG_SENSORT PCONFIG(5) Sensor type
// P145_PCONFIG_FLAGS PCONFIG(0) Enable compensation & Enable Calibration & use low Vcc
// P145_PCONFIG_TEMP_TASK PCONFIG(1) Temperature compensation task
// P145_PCONFIG_TEMP_VAL PCONFIG(2) Temperature compensation value
// P145_PCONFIG_HUM_TASK PCONFIG(3) Humidity compensation task
// P145_PCONFIG_HUM_VAL PCONFIG(4) Humidity compensation value
// P145_PCONFIG_SENSORT PCONFIG(5) Sensor type
//#######################################################################################################
#include "_Plugin_Helper.h"
@@ -119,6 +119,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
switch (function)
{
// Make the plugin known with its options
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_145;
@@ -135,18 +136,39 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
break;
}
// Return the DEVICENAME for this plugin
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_145);
break;
}
// Setup the value names for the task
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_145));
break;
}
// Add custom GPIO description on device overview page
case PLUGIN_WEBFORM_SHOW_GPIO_DESCR:
{
string = F("AIN: ");
# ifdef ESP32
string += formatGpioLabel(P145_CONFIG_PIN_AIN, false);
#else
string += F("ADC (TOUT)");
#endif
if (validGpio(P145_CONFIG_PIN_HEATER))
{
string += F("<BR>HEAT: ");
string += formatGpioLabel(P145_CONFIG_PIN_HEATER, false);
}
success = true;
break;
}
// Setup the web form for the task
case PLUGIN_WEBFORM_LOAD:
{
bool compensate = P145_PCONFIG_FLAGS & 0x0001; // Compensation enable flag
@@ -226,6 +248,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
break;
}
// Save setting from web form for the task
case PLUGIN_WEBFORM_SAVE:
{
P145_PCONFIG_SENSORT = getFormItemInt(F(P145_GUID_TYPE));
@@ -256,6 +279,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
break;
}
// Initialize the task
case PLUGIN_INIT:
{
P145_data_struct *P145_data = static_cast<P145_data_struct *>(getPluginTaskData(event->TaskIndex));
@@ -275,6 +299,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
break;
}
// Execute at 10Hz
case PLUGIN_TEN_PER_SECOND:
{
P145_data_struct *P145_data = static_cast<P145_data_struct *>(getPluginTaskData(event->TaskIndex));
@@ -285,6 +310,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
break;
}
// Read fresh data from the task
case PLUGIN_READ:
{
P145_data_struct *P145_data = static_cast<P145_data_struct *>(getPluginTaskData(event->TaskIndex));
@@ -305,6 +331,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string)
break;
}
// Execute each second
case PLUGIN_ONCE_A_SECOND:
{
P145_data_struct *P145_data = static_cast<P145_data_struct *>(getPluginTaskData(event->TaskIndex));
+161 -155
View File
@@ -21,24 +21,24 @@
// Below the defines used to represent these enums
// Gas type identifiers used to condense the sensorDefs table to fixed format
#define GAS_USER 0
#define GAS_CO2 1
#define GAS_H2 2
#define GAS_ALCOHOL 3
#define GAS_CH4 4
#define GAS_LPG 5
#define GAS_CO 6
#define P145_GASUSER 0
#define P145_GASCO2 1
#define P145_GASH2 2
#define P145_GASALCOHOL 3
#define P145_GASCH4 4
#define P145_GASLPG 5
#define P145_GASCO 6
// Sensor type identifier used to condense the SensorDefs table to fixed format
#define SENS_USER 0
#define SENS_MQ135 1
#define SENS_MQ2 2
#define SENS_MQ3 3
#define SENS_MQ4 4
#define SENS_MQ5 5
#define SENS_MQ6 6
#define SENS_MQ7 7
#define SENS_MQ8 8
#define P145_SENSUSER 0
#define P145_SENSMQ135 1
#define P145_SENSMQ2 2
#define P145_SENSMQ3 3
#define P145_SENSMQ4 4
#define P145_SENSMQ5 5
#define P145_SENSMQ6 6
#define P145_SENSMQ7 7
#define P145_SENSMQ8 8
@@ -62,8 +62,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgNone, // preferred/tuned algorithm
SENS_USER, // Name
GAS_USER, // gas
P145_SENSUSER, // Name
P145_GASUSER, // gas
},
// *** MQ-135 - CO2 ***
{
@@ -78,8 +78,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
-0.001923077f,// CORF
1.130128205f, // CORG
p145AlgA, // preferred/tuned algorithm
SENS_MQ135, // Name
GAS_CO2, // gas
P145_SENSMQ135, // Name
P145_GASCO2, // gas
},
// *** MQ-2 - H2 ***
{
@@ -94,8 +94,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ2, // Name
GAS_H2, // gas
P145_SENSMQ2, // Name
P145_GASH2, // gas
},
// *** MQ-3 - alcohol ***
{
@@ -110,8 +110,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ3, // Name
GAS_ALCOHOL, // gas
P145_SENSMQ3, // Name
P145_GASALCOHOL, // gas
},
// *** MQ-4 - CH4 ***
{
@@ -126,8 +126,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ4, // Name
GAS_CH4, // gas
P145_SENSMQ4, // Name
P145_GASCH4, // gas
},
// *** MQ-5 - H2 ***
{
@@ -142,8 +142,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ5, // Name
GAS_CH4, // gas
P145_SENSMQ5, // Name
P145_GASCH4, // gas
},
// *** MQ-5 - LPG ***
{
@@ -158,8 +158,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ5, // Name
GAS_LPG, // gas
P145_SENSMQ5, // Name
P145_GASLPG, // gas
},
// *** MQ-6 - LPG ***
{
@@ -174,8 +174,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ6, // Name
GAS_LPG, // gas
P145_SENSMQ6, // Name
P145_GASLPG, // gas
},
// *** MQ-7 - CO ***
{
@@ -190,8 +190,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ7, // Name
GAS_CO, // gas
P145_SENSMQ7, // Name
P145_GASCO, // gas
},
// *** MQ-8 - H2 ***
{
@@ -206,8 +206,8 @@ const struct P145_SENSORDEF sensorDefs[] PROGMEM =
0.0f, // CORF
0.0f, // CORG
p145AlgB, // preferred/tuned algorithm
SENS_MQ8, // Name
GAS_H2, // gas
P145_SENSMQ8, // Name
P145_GASH2, // gas
}
};
/// @brief The number of types stored in the sensorDefs[] table
@@ -223,47 +223,6 @@ constexpr const int nbrOfTypes = (int)(sizeof(sensorDefs) / sizeof(struct P145_S
#define HEATER_OFF_TIME (60*1000)
#define HEATER_MEAS_TIME (30*1000)
/*****************************************************************************/
/*!
@brief Perform the calibration algorithm
@param[in] currentRcal Value for Rzero determined with current measurement
@return Void
@note Collect the highest Rzero during the calibration period
At the end of the period take mean value of current and calculated Rzero
*/
/*****************************************************************************/
void P145_data_struct::calibrate (float currentRcal)
{
unsigned long now = millis();
long time = timePassedSince(last_cal);
float lastRcal = cal_data; // Last calculated Rcal this calibration sequence
if ((currentRcal > lastRcal) || lastRcal <= 0.0f)
{
cal_data = currentRcal;
}
const bool doit = (time > P145_CALIBRATION_INTERVAL) && (cal_data > 0.0f);
if (doit)
{
rzero = cal_data; // Update Rzero as determined by calibration
cal_data = 0.0f; // Restart calibration cycle with no value
last_cal = now; // Remember calibration moment
}
if (loglevelActiveFor(LOG_LEVEL_INFO))
{
#ifdef P145_DEBUG
// Calculated Rzero if calibration concentration is applied
// Rcal as calculated previous sample
addLog(LOG_LEVEL_INFO, concat(concat(F("MQ-xx: Calibration with Rcal = "), currentRcal), concat(F(" Rlast = "), lastRcal)));
#endif
if (doit)
{
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: ***Calibrating*** Rzero = "), cal_data));
}
}
}
/*****************************************************************************/
/*!
@brief Get the resistance of the sensor, ie. the property to be measured
@@ -347,21 +306,8 @@ float P145_data_struct::getRZero(float rSensor) const
/*****************************************************************************/
float P145_data_struct::getCorrectedRZero(float rSensor, float temperature, float humidity) const
{
float c; // Correction factor
if (sensordef.cora <= 0.0f)
{
return (getRZero(rSensor));
}
else if (temperature < 20.0f) //TODO this is an ugly constant associated with the MQ-135 sensor only
{
c = sensordef.cora * temperature * temperature - sensordef.corb * temperature + sensordef.corc - (humidity - 33.0f) * sensordef.cord;
}
else
{
c = sensordef.core * temperature + sensordef.corf * humidity + sensordef.corg;
}
return (rSensor / c) * powf((refLevel / sensordef.para), (1.0f / sensordef.parb));
float c = getTempHumCorrection(temperature, humidity);
return getRZero(rSensor/c);
}
/*****************************************************************************/
@@ -407,31 +353,48 @@ 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)
float c = getTempHumCorrection(temperature, humidity);
return getPPM(rSensor/c);
}
/*****************************************************************************/
/*!
@brief Get the correction factor for temperature & humidity correction
@param[in] temperature The ambient air temperature
@param[in] humidity The relative humidity
@return The correction factor for Rzero to compensate temperature & humidity
@note Datasheet suggests temp & hum relation to Rzero is a multiplication factor c
here we try to calculate this factor.
*/
/*****************************************************************************/
float P145_data_struct::getTempHumCorrection(float temperature, float humidity) const
{
float c = 1.0f; // Correction factor, default no correction, factor is 1.0
if (compensation)
{
case p145AlgA:
if (temperature < 20.0f)
{
c = sensordef.cora * temperature * temperature - sensordef.corb * temperature + sensordef.corc - (humidity - 33.0f) * sensordef.cord;
}
else
{
c = sensordef.core * temperature + sensordef.corf * humidity + sensordef.corg;
}
break;
case p145AlgB:
case p145AlgC:
// Assume a 2nd order polynomial for temperature and 1st order poynomial for humidit
// A + B*t + C*t*t + D*h
c = (((sensordef.corc * temperature ) + sensordef.corb) * temperature) + sensordef.cora + (sensordef.cord * humidity);
break;
case p145AlgNone:
// no correction
break;
default:
// No correction
c = 1.0f;
switch (algorithm)
{
case p145AlgA:
if (temperature < 20.0f)
{
c = sensordef.cora * temperature * temperature - sensordef.corb * temperature + sensordef.corc - (humidity - 33.0f) * sensordef.cord;
}
else
{
c = sensordef.core * temperature + sensordef.corf * humidity + sensordef.corg;
}
break;
case p145AlgB:
case p145AlgC:
// Assume a 2nd order polynomial for temperature and 1st order poynomial for humidit
// A + B*t + C*t*t + D*h
c = (((sensordef.corc * temperature ) + sensordef.corb) * temperature) + sensordef.cora + (sensordef.cord * humidity);
break;
case p145AlgNone:
default:
// No correction
c = 1.0f;
}
}
if (c <=0.0f) // In case no or wrong correction factors are specified disable correction (multiplier = 1.0)
{
@@ -440,7 +403,7 @@ float P145_data_struct::getCorrectedPPM(float rSensor, float temperature, float
#ifdef P145_DEBUG
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: correction= "), c));
#endif
return getPPM(rSensor/c); // Default value should never be returned
return c;
}
/*****************************************************************************/
@@ -487,8 +450,8 @@ float P145_data_struct::getAnalogValue()
/*****************************************************************************/
void P145_data_struct::setSensorData(int stype, bool comp, bool cal, bool vcclow, float load, float zero, float ref)
{
/* Each MQ-xxx sensor comes with its own set cof constants */
/* Copy the correct set from program meory space */
/* Each MQ-xxx sensor comes with its own set of constants */
/* Copy the correct set from program meory space */
if ((stype != sensorType) && (stype < nbrOfTypes) && (stype >= 0))
{
memcpy_P(&sensordef, &sensorDefs[stype], sizeof(struct P145_SENSORDEF));
@@ -501,13 +464,15 @@ void P145_data_struct::setSensorData(int stype, bool comp, bool cal, bool vcclow
rload = load; // Rload, load resistor [Ohm]
rzero = zero; // R0, reference resistance [Ohm]
refLevel = ref; // Reference level for calibration [ppm]
rcal_act = 0.0f; // Reset Rzero estimation
cal_data = 0.0f; // Reset autocalibration
}
/*****************************************************************************/
/*!
@brief Set the connection pins for the sensor
@param[in] analogPin Analog pin for the analog sensor input value
@param[in] heaterPin Ootput pin for heater control
@param[in] aPin Analog pin for the analog sensor input value
@param[in] hPin Ootput pin for heater control
@note These values must be set before the plugin can measure the level
They are determined by the plugin configuration
/*****************************************************************************/
@@ -535,7 +500,9 @@ float P145_data_struct::readValue(float temperature, float humidity)
float ain = 0.0f; // Analog measured and filtered data
float rSensor = 0.0f; // Sensor resistance Rs
float value = 0.0f; // Return value
#ifdef P145_DEBUG
uint ovs = 0; // Oversampling count (for debugging)
#endif
#ifdef P145_TEST
static float injector = 50.0f;
static float injstep = 50.0f;
@@ -557,27 +524,24 @@ float P145_data_struct::readValue(float temperature, float humidity)
else
{
ain = getAnalogValue(); // Use acually being measured value
#ifdef P145_DEBUG
ovs = ovs_cnt;
#endif
resetOversampling(); // Reset the oversampling variables.
}
#endif
rSensor = getResistance(ain); // Convert to Rsensor value
if (compensation) // Check if temperature/Humidity compensation is selected
if (ain > 0.0f) // Skip unsuccesful measurements
{
value = getCorrectedPPM(rSensor, temperature, humidity);
rcal_act = getCorrectedRZero(rSensor, temperature, humidity);
}
else
{
value = getPPM(rSensor);
rcal_act = getRZero(rSensor);
}
rSensor = getResistance(ain); // Convert to Rsensor value
value = getCorrectedPPM(rSensor, temperature, humidity);
rcal_act = getCorrectedRZero(rSensor, temperature, humidity);
// Perform calibration if functionality is enabled
if (calibration && (rcal_act < 1.0e6f))
{
// Perform calibration if functionality is enabled
if (calibration && (rcal_act < 1.0e6f))
{
calibrate(rcal_act);
}
}
if (loglevelActiveFor(LOG_LEVEL_INFO))
{
#ifdef P145_DEBUG
@@ -593,7 +557,7 @@ float P145_data_struct::readValue(float temperature, float humidity)
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: ovs= "), ovs)); // Oversampling count
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: algorithm= "), algorithm)); // Conversion algorithm
if (calibration)
{
@@ -718,6 +682,7 @@ void P145_data_struct::dump() const
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: CORF "), sensordef.corf));
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: CORG "), sensordef.corg));
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: rzero "), rzero));
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: rcal "), rcal_act));
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: analog PIN: "), analogPin));
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: heater PIN: "), heaterPin));
}
@@ -736,16 +701,16 @@ const String P145_data_struct::getTypeName(int stype)
{
switch (sensorDefs[stype].name)
{
case SENS_MQ135: return F("MQ135");
case SENS_MQ2: return F("MQ-2");
case SENS_MQ3: return F("MQ-3");
case SENS_MQ4: return F("MQ-4");
case SENS_MQ5: return F("MQ-5");
case SENS_MQ6: return F("MQ-6");
case SENS_MQ7: return F("MQ-7");
case SENS_MQ8: return F("MQ-8");
case SENS_USER: return F("user");
default: return F("invalid");
case P145_SENSMQ135: return F("MQ135");
case P145_SENSMQ2: return F("MQ-2");
case P145_SENSMQ3: return F("MQ-3");
case P145_SENSMQ4: return F("MQ-4");
case P145_SENSMQ5: return F("MQ-5");
case P145_SENSMQ6: return F("MQ-6");
case P145_SENSMQ7: return F("MQ-7");
case P145_SENSMQ8: return F("MQ-8");
case P145_SENSUSER: return F("user");
default: return F("invalid");
}
}
@@ -761,14 +726,14 @@ const String P145_data_struct::getGasName(int stype)
{
switch (sensorDefs[stype].gas)
{
case GAS_USER: return F("user");
case GAS_CH4: return F("CH4");
case GAS_CO2: return F("CO2");
case GAS_CO: return F("CO");
case GAS_H2: return F("H2");
case GAS_LPG: return F("LPG");
case GAS_ALCOHOL: return F("alcohol");
default: return F("invalid");
case P145_GASUSER: return F("user");
case P145_GASCH4: return F("CH4");
case P145_GASCO2: return F("CO2");
case P145_GASCO: return F("CO");
case P145_GASH2: return F("H2");
case P145_GASLPG: return F("LPG");
case P145_GASALCOHOL: return F("alcohol");
default: return F("invalid");
}
}
@@ -783,6 +748,47 @@ int P145_data_struct::getNbrOfTypes()
return nbrOfTypes;
}
/*****************************************************************************/
/*!
@brief Perform the calibration algorithm
@param[in] currentRcal Value for Rzero determined with current measurement
@return Void
@note Collect the highest Rzero during the calibration period
At the end of the period use it to set the Rzero for next calculations
*/
/*****************************************************************************/
void P145_data_struct::calibrate (float currentRcal)
{
unsigned long now = millis();
long time = timePassedSince(last_cal);
float lastRcal = cal_data; // Last calculated Rcal this calibration sequence
if (currentRcal > lastRcal)
{
cal_data = currentRcal; // Remember highest estimated Rzero during calibration period
}
const bool doit = (time > P145_CALIBRATION_INTERVAL) && (cal_data > 0.0f);
if (doit)
{
rzero = cal_data; // Update Rzero as determined by calibration
cal_data = 0.0f; // Restart calibration cycle with no value
last_cal = now; // Remember calibration moment
}
if (loglevelActiveFor(LOG_LEVEL_INFO))
{
#ifdef P145_DEBUG
// Calculated Rzero if calibration concentration is applied
// Rcal as calculated previous sample
addLog(LOG_LEVEL_INFO, concat(concat(F("MQ-xx: Calibration with Rcal = "), currentRcal), concat(F(" Rlast = "), lastRcal)));
#endif
if (doit)
{
addLog(LOG_LEVEL_INFO, concat(F("MQ-xx: ***Calibrating*** Rzero = "), cal_data));
}
}
}
/**************************************************************************/
/* @brief Evaluate the heater control algorithm
@return void
+1
View File
@@ -117,6 +117,7 @@ struct P145_data_struct : public PluginTaskData_base
float getAnalogValue();
float getResistance(float val) const;
float getRZero(float rSensor) const;
float getTempHumCorrection(float temperature, float humidity) const;
float getCorrectedRZero(float rSensor, float temperature, float humidity) const;
float getPPM(float rSensor);
float getCorrectedPPM(float rSensor, float temperature, float humidity);