Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[P164] add AQI to taskvalues #5184

Open
wants to merge 4 commits into
base: mega
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/source/Plugin/P105.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ When selecting the **AHT1x** Sensor model, an extra option is made available:

* **AHT10 Alternative initialization**: Some AHT10 clone sensors do not seem to like the regular AHT10/AHT15 initialization sequence. They do however accept a soft reset command. By enabling this checkbox, only the soft reset is sent to the device. Only available for AHT1x devices.

* **Temperature offset** Depending on the sensor and the location of the sensor, it may be required to apply some temperature compensation. This can be set in steps of 0.1 degree. This also applies a compensation to the **Humidity** reading.


Data Acquisition
^^^^^^^^^^^^^^^^

Expand All @@ -97,6 +100,7 @@ Change log

.. versionchanged:: 2.0
...
|changed| 2024/12/21 Add temperature offset

|changed| 2024-12-03 Add alternative initialization option

Expand Down
Binary file modified docs/source/Plugin/P105_DeviceConfiguration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion docs/source/Plugin/P164.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ This group of settings, **Single event with all values** and **Send to Controlle
Values
^^^^^^

The plugin provides the ``TVOC`` and ``eCO2`` values. A formula can be set to recalculate. The number of decimals can be set as desired, and defaults to 2.
The plugin provides the ``TVOC``, ``eCO2`` and ``AQI`` values. A formula can be set to recalculate. The number of decimals can be set as desired, and defaults to 0.

In selected builds, per Value is a **Stats** checkbox available, that when checked, gathers the data and presents recent data in a graph, as described here: :ref:`Task Value Statistics: <Task Value Statistics>`

Expand All @@ -112,6 +112,8 @@ Change log
.. versionchanged:: 2.0
...

|changed| 2024-12-21 Added AQI value

|added|
2023-12-27 Initial release version.

Binary file modified docs/source/Plugin/P164_DeviceConfiguration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 30 additions & 17 deletions src/_P105_AHT.ino
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/

/** History:
* 2024-12-21 chromoxdor: Add temperature offset + simple humidity compensation
* 2024-12-03 tonhuisman: Add alternative initialization for AHT10 (clone), see https://github.com/letscontrolit/ESPEasy/issues/5172
* Small code optimization.
* 2024-04-28 tonhuisman: Update plugin name and documentation as DHT20 and AM2301B actually contain an AHT20!
Expand All @@ -48,6 +49,11 @@
# define PLUGIN_VALUENAME2_105 "Humidity"


# define P105_I2C_ADRESS PCONFIG(0)
# define P105_AHT_TYPE PCONFIG(1)
# define P105_ALT_INIT PCONFIG(2)
# define P105_TEMPERATURE_OFFSET PCONFIG(3)

boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string)
{
boolean success = false;
Expand Down Expand Up @@ -87,7 +93,7 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string)
const uint8_t i2cAddressValues[2] = { 0x38, 0x39 };

if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) {
addFormSelectorI2C(F("i2c_addr"), 2, i2cAddressValues, PCONFIG(0));
addFormSelectorI2C(F("i2c_addr"), 2, i2cAddressValues, P105_I2C_ADRESS);
addFormNote(F("SDO Low=0x38, High=0x39. NB: Only available on AHT1x sensors."));
} else {
success = intArrayContains(2, i2cAddressValues, event->Par1);
Expand All @@ -99,21 +105,22 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string)
# if FEATURE_I2C_GET_ADDRESS
case PLUGIN_I2C_GET_ADDRESS:
{
event->Par1 = PCONFIG(0);
event->Par1 = P105_I2C_ADRESS;
success = true;
break;
}
# endif // if FEATURE_I2C_GET_ADDRESS

case PLUGIN_SET_DEFAULTS:
{
PCONFIG(1) = static_cast<int>(AHTx_device_type::AHT20_DEVICE);
P105_AHT_TYPE = static_cast<int>(AHTx_device_type::AHT20_DEVICE);
P105_TEMPERATURE_OFFSET = 0;
break;
}

case PLUGIN_WEBFORM_LOAD:
{
if (static_cast<AHTx_device_type>(PCONFIG(1)) == AHTx_device_type::AHT10_DEVICE) {
if (static_cast<AHTx_device_type>(P105_AHT_TYPE) == AHTx_device_type::AHT10_DEVICE) {
bool hasOtherI2CDevices = false;

for (taskIndex_t x = 0; validTaskIndex(x) && !hasOtherI2CDevices; ++x) {
Expand Down Expand Up @@ -143,37 +150,43 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string)
const int indices[] = { static_cast<int>(AHTx_device_type::AHT10_DEVICE),
static_cast<int>(AHTx_device_type::AHT20_DEVICE),
static_cast<int>(AHTx_device_type::AHT21_DEVICE) };
addFormSelector(F("Sensor model"), F("ahttype"), 3, options, indices, PCONFIG(1), true);
addFormSelector(F("Sensor model"), F("ahttype"), 3, options, indices, P105_AHT_TYPE, true);
addFormNote(F("Changing Sensor model will reload the page."));

if (static_cast<int>(AHTx_device_type::AHT10_DEVICE) == PCONFIG(1)) {
addFormCheckBox(F("AHT10 Alternative initialization"), F("altinit"), PCONFIG(2));
if (static_cast<int>(AHTx_device_type::AHT10_DEVICE) == P105_AHT_TYPE) {
addFormCheckBox(F("AHT10 Alternative initialization"), F("altinit"), P105_ALT_INIT);
}
}

addFormNumericBox(F("Temperature offset"), F("tempoffset"), P105_TEMPERATURE_OFFSET);
addUnit(F("x 0.1C"));
addFormNote("Offset in units of 0.1 degree Celsius and also corrects humidity.");
success = true;

break;
}

case PLUGIN_WEBFORM_SAVE:
{
PCONFIG(1) = getFormItemInt(F("ahttype"));
P105_AHT_TYPE = getFormItemInt(F("ahttype"));

if (static_cast<AHTx_device_type>(PCONFIG(1)) != AHTx_device_type::AHT10_DEVICE) {
PCONFIG(0) = 0x38; // AHT20/AHT21 only support a single I2C address.
if (static_cast<AHTx_device_type>(P105_AHT_TYPE) != AHTx_device_type::AHT10_DEVICE) {
P105_I2C_ADRESS = 0x38; // AHT20/AHT21 only support a single I2C address.
} else {
PCONFIG(0) = getFormItemInt(F("i2c_addr"));
PCONFIG(2) = isFormItemChecked(F("altinit")) ? 1 : 0;
P105_I2C_ADRESS = getFormItemInt(F("i2c_addr"));
P105_ALT_INIT = isFormItemChecked(F("altinit")) ? 1 : 0;
}
success = true;

P105_TEMPERATURE_OFFSET = getFormItemInt(F("tempoffset"));
success = true;
break;
}

case PLUGIN_INIT:
{
success = initPluginTaskData(
event->TaskIndex,
new (std::nothrow) P105_data_struct(PCONFIG(0), static_cast<AHTx_device_type>(PCONFIG(1)), 1 == PCONFIG(2)));
new (std::nothrow) P105_data_struct(P105_I2C_ADRESS, static_cast<AHTx_device_type>(P105_AHT_TYPE), 1 == P105_ALT_INIT));
break;
}

Expand Down Expand Up @@ -202,11 +215,11 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string)
}
P105_data->state = AHTx_state::AHTx_Values_read;

UserVar.setFloat(event->TaskIndex, 0, P105_data->getTemperature());
UserVar.setFloat(event->TaskIndex, 1, P105_data->getHumidity());
UserVar.setFloat(event->TaskIndex, 0, P105_data->getTemperature() + (P105_TEMPERATURE_OFFSET / 10.0f));
UserVar.setFloat(event->TaskIndex, 1, P105_data->getHumidity() * (1 - 0.005f * P105_TEMPERATURE_OFFSET));

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLogMove(LOG_LEVEL_INFO, strformat(F("%s : Addr: 0x%02x"), P105_data->getDeviceName().c_str(), PCONFIG(0)));
addLogMove(LOG_LEVEL_INFO, strformat(F("%s : Addr: 0x%02x"), P105_data->getDeviceName().c_str(), P105_I2C_ADRESS));
addLogMove(LOG_LEVEL_INFO,
strformat(F("%s : Temperature: %s : Humidity: %s"),
P105_data->getDeviceName().c_str(),
Expand Down
25 changes: 17 additions & 8 deletions src/_P164_gases_ens160.ino
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// #######################################################################################################
// P164 "GASES - ENS16x (TVOC, eCO2)"
// Plugin for ENS160 & ENS161 TVOC and eCO2 sensor with I2C interface from ScioSense
// For documentation of the ENS160 hardware device see
// For documentation of the ENS160 hardware device see
// https://www.sciosense.com/wp-content/uploads/documents/SC-001224-DS-9-ENS160-Datasheet.pdf
//
// PLugin code:
Expand All @@ -20,6 +20,7 @@
# define PLUGIN_NAME_164 "Gases - ENS16x"
# define PLUGIN_VALUENAME1_164 "TVOC"
# define PLUGIN_VALUENAME2_164 "eCO2"
# define PLUGIN_VALUENAME3_164 "AQI"

boolean Plugin_164(uint8_t function, struct EventStruct *event, String& string)
{
Expand All @@ -36,7 +37,7 @@ boolean Plugin_164(uint8_t function, struct EventStruct *event, String& string)
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].ValueCount = 2;
Device[deviceCount].ValueCount = 3;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
Expand All @@ -54,6 +55,11 @@ boolean Plugin_164(uint8_t function, struct EventStruct *event, String& string)
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_164));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_164));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_164));

for (int i = 0; i < 3; ++i) {
ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0;
}
break;
}

Expand Down Expand Up @@ -85,7 +91,7 @@ boolean Plugin_164(uint8_t function, struct EventStruct *event, String& string)
case PLUGIN_SET_DEFAULTS:
{
P164_PCONFIG_I2C_ADDR = P164_ENS160_I2CADDR_1;
success = true;
success = true;
break;
}

Expand All @@ -107,20 +113,22 @@ boolean Plugin_164(uint8_t function, struct EventStruct *event, String& string)
break;
}

float temperature = 20.0f; // A reasonable value in case temperature source task is invalid
float humidity = 50.0f; // A reasonable value in case humidity source task is invalid
float tvoc = 0.0f; // tvoc value to be retrieved from device
float eco2 = 0.0f; // eCO2 value to be retrieved from device
float temperature = 20.0f; // A reasonable value in case temperature source task is invalid
float humidity = 50.0f; // A reasonable value in case humidity source task is invalid
float tvoc = 0.0f; // tvoc value to be retrieved from device
float eco2 = 0.0f; // eCO2 value to be retrieved from device
float aqi = 0.0f; // AQI value to be retrieved from device

if (validTaskIndex(P164_PCONFIG_TEMP_TASK) && validTaskIndex(P164_PCONFIG_HUM_TASK))
{
// we're checking a value from other tasks
temperature = UserVar.getFloat(P164_PCONFIG_TEMP_TASK, P164_PCONFIG_TEMP_VAL); // in degrees C
humidity = UserVar.getFloat(P164_PCONFIG_HUM_TASK, P164_PCONFIG_HUM_VAL); // in % relative
}
success = P164_data->read(tvoc, eco2, temperature, humidity);
success = P164_data->read(tvoc, eco2, aqi, temperature, humidity);
UserVar.setFloat(event->TaskIndex, 0, tvoc);
UserVar.setFloat(event->TaskIndex, 1, eco2);
UserVar.setFloat(event->TaskIndex, 2, aqi);
break;
}

Expand All @@ -140,6 +148,7 @@ boolean Plugin_164(uint8_t function, struct EventStruct *event, String& string)
{
P164_data_struct *P164_data =
static_cast<P164_data_struct *>(getPluginTaskData(event->TaskIndex));

if (nullptr == P164_data) {
break;
}
Expand Down
6 changes: 4 additions & 2 deletions src/src/PluginStructs/P164_data_struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,23 +155,25 @@ bool P164_data_struct::begin()
///////////////////////////////////////////////////////////////////////////////////////////////////
// Fetch the processed device values as stored in the software object //
///////////////////////////////////////////////////////////////////////////////////////////////////
bool P164_data_struct::read(float& tvoc, float& eco2)
bool P164_data_struct::read(float& tvoc, float& eco2, float& aqi)
{
bool success = measure(); // Read measurement values from device
tvoc = (float)_data_tvoc; // Latest acquired TVOC value
eco2 = (float)_data_eco2; // Latest aquired eCO2 value
aqi = (float)_data_aqi; // Latest aquired AQI value
return success;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Fetch the processed device values as stored in the software object using compensation //
///////////////////////////////////////////////////////////////////////////////////////////////////
bool P164_data_struct::read(float& tvoc, float& eco2, float temp, float hum)
bool P164_data_struct::read(float& tvoc, float& eco2, float& aqi, float temp, float hum)
{
this->set_envdata(temp, hum); // Write new compensation temp & hum to device
bool success = measure(); // Read measurement values from device
tvoc = (float)_data_tvoc; // Latest acquired TVOC value
eco2 = (float)_data_eco2; // Latest aquired eCO2 value
aqi = (float)_data_aqi; // Latest aquired AQI value
return success;
}

Expand Down
4 changes: 2 additions & 2 deletions src/src/PluginStructs/P164_data_struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ struct P164_data_struct : public PluginTaskData_base {
virtual ~P164_data_struct() = default;

bool begin();
bool read(float& tvoc, float& eco2);
bool read(float& tvoc, float& eco2, float temp, float hum);
bool read(float& tvoc, float& eco2, float& aqi);
bool read(float& tvoc, float& eco2, float& aqi, float temp, float hum);
static bool webformLoad(struct EventStruct *event);
static bool webformSave(struct EventStruct *event);
bool tenPerSecond(struct EventStruct *event);
Expand Down