From 68f6b3452e6f68c13fbeee187dab097b2ba8c0fb Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Mon, 13 May 2024 20:09:31 +0200 Subject: [PATCH 1/9] Initial implementation of the AHT10/AHT15/AHT20 sensors --- usermods/AHT10_v2/README.md | 34 +++++ usermods/AHT10_v2/usermod_aht10.h | 201 ++++++++++++++++++++++++++++++ wled00/usermods_list.cpp | 8 ++ 3 files changed, 243 insertions(+) create mode 100644 usermods/AHT10_v2/README.md create mode 100644 usermods/AHT10_v2/usermod_aht10.h diff --git a/usermods/AHT10_v2/README.md b/usermods/AHT10_v2/README.md new file mode 100644 index 0000000000..e6f16ddc49 --- /dev/null +++ b/usermods/AHT10_v2/README.md @@ -0,0 +1,34 @@ +# Usermod AHT10 +This Usermod is designed to read a `AHT10`, `AHT15` or `AHT20` sensor and output the following: +- Temperature +- Humidity + +Configuration is performed via the Usermod menu. The following settings can be configured in the Usermod Menu: +- I2CAddress: The i2c address in decimal. Set it to either 56 (0x38, the default) or 57 (0x39). +- SensorType, one of: + - 0 - AHT10 + - 1 - AHT15 + - 2 - AHT20 +- CheckInterval: Number of seconds between readings +- Decimals: Number of decimals to put in the output + +Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`). +- Libraries + - `enjoyneering/AHT10@~1.1.0` (by [enjoyneering](https://registry.platformio.org/libraries/enjoyneering/AHT10)) + - `Wire` + +# Compiling + +To enable, compile with `USERMOD_AHT10` defined (e.g. in `platformio_override.ini`) +```ini +[env:aht10_example] +extends = env:esp32dev +build_flags = + ${common.build_flags} ${esp32.build_flags} + -D USERMOD_AHT10 + ; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal +lib_deps = + ${esp32.lib_deps} + enjoyneering/AHT10@~1.1.0 + Wire +``` diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h new file mode 100644 index 0000000000..d6e44a87b2 --- /dev/null +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -0,0 +1,201 @@ +#pragma once + +#include "wled.h" +#include + +#define AHT10_SUCCESS 1 + +class UsermodAHT10 : public Usermod { + private: + static const char _name[]; + + unsigned long _lastLoopCheck = 0; + bool _initDone = false; + + // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime + bool _enabled = false; + uint8_t _i2cAddress = AHT10_ADDRESS_0X38; + ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR; + uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds + float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..) + + uint8_t _lastStatus = 0; + float _lastHumidity = 0; + float _lastTemperature = 0; + + AHT10* _aht = nullptr; + + float truncateDecimals(float val) { + return roundf(val * _decimalFactor) / _decimalFactor; + } + + void initializeAht() { + if (_aht != nullptr) { + delete _aht; + } + + _aht = new AHT10(_i2cAddress, _ahtType); + + _lastStatus = 0; + _lastHumidity = 0; + _lastTemperature = 0; + } + + ~UsermodAHT10() { + delete _aht; + _aht = nullptr; + } + + public: + void setup() { + initializeAht(); + } + + void loop() { + // if usermod is disabled or called during strip updating just exit + // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly + if (!_enabled || strip.isUpdating()) + return; + + // do your magic here + unsigned long currentTime = millis(); + + if (currentTime - _lastLoopCheck < _checkInterval) + return; + _lastLoopCheck = currentTime; + + _lastStatus = _aht->readRawData(); + + if (_lastStatus == AHT10_ERROR) + { + // Perform softReset and retry + DEBUG_PRINTLN("AHTxx returned error, doing softReset"); + if (!_aht->softReset()) + { + DEBUG_PRINTLN("softReset failed"); + return; + } + + _lastStatus = _aht->readRawData(); + } + + if (_lastStatus == AHT10_SUCCESS) + { + float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA)); + float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA)); + + // Push to MQTT + + // Store + _lastHumidity = humidity; + _lastTemperature = temperature; + } + } + + void addToJsonInfo(JsonObject& root) override + { + // if "u" object does not exist yet wee need to create it + JsonObject user = root["u"]; + if (user.isNull()) + user = root.createNestedObject("u"); + +#ifdef USERMOD_AHT10_DEBUG + JsonArray temp = user.createNestedArray(F("status")); + temp.add(_lastLoopCheck); + temp.add(F(" / ")); + temp.add(_lastStatus); +#endif + + JsonArray jsonTemp = user.createNestedArray(F("AHT Temperature")); + JsonArray jsonHumidity = user.createNestedArray(F("AHT Humidity")); + + if (_lastLoopCheck == 0) + { + // Before first run + jsonTemp.add(F("Not read yet")); + jsonHumidity.add(F("Not read yet")); + return; + } + + if (_lastStatus != AHT10_SUCCESS) + { + jsonTemp.add(F("An error occurred")); + jsonHumidity.add(F("An error occurred")); + return; + } + + jsonTemp.add(_lastTemperature); + jsonTemp.add(F("°C")); + + jsonHumidity.add(_lastHumidity); + jsonHumidity.add(F("%")); + } + + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject(FPSTR(_name)); + top[F("Enabled")] = _enabled; + top[F("I2CAddress")] = static_cast(_i2cAddress); + top[F("SensorType")] = _ahtType; + top[F("CheckInterval")] = _checkInterval / 1000; + top[F("Decimals")] = log10f(_decimalFactor); + + DEBUG_PRINTLN(F("AHT10 config saved.")); + } + + bool readFromConfig(JsonObject& root) override + { + // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor + // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) + + JsonObject top = root[FPSTR(_name)]; + + bool configComplete = !top.isNull(); + if (!configComplete) + return false; + + configComplete &= getJsonValue(top["Enabled"], _enabled); + configComplete &= getJsonValue(top["I2CAddress"], _i2cAddress); + configComplete &= getJsonValue(top["CheckInterval"], _checkInterval); + if (configComplete) + { + if (1 <= _checkInterval && _checkInterval <= 600) + _checkInterval *= 1000; + else + // Invalid input + _checkInterval = 60000; + } + + configComplete &= getJsonValue(top["Decimals"], _decimalFactor); + if (configComplete) + { + if (0 <= _decimalFactor && _decimalFactor <= 5) + _decimalFactor = pow10f(_decimalFactor); + else + // Invalid input + _decimalFactor = 100; + } + + uint8_t tmpAhtType; + configComplete &= getJsonValue(top["SensorType"], tmpAhtType); + if (configComplete) + { + if (0 <= tmpAhtType && tmpAhtType <= 2) + _ahtType = static_cast(tmpAhtType); + else + // Invalid input + _ahtType = 0; + } + + if (_initDone) + { + // Reloading config + initializeAht(); + } + + _initDone = true; + return configComplete; + } +}; + +const char UsermodAHT10::_name[] PROGMEM = "AHTxx"; \ No newline at end of file diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 3f34d18fef..91455e986f 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -217,6 +217,10 @@ #include "../usermods/TetrisAI_v2/usermod_v2_tetrisai.h" #endif +#ifdef USERMOD_AHT10 + #include "../usermods/AHT10_v2/usermod_aht10.h" +#endif + void registerUsermods() { /* @@ -421,4 +425,8 @@ void registerUsermods() #ifdef USERMOD_TETRISAI usermods.add(new TetrisAIUsermod()); #endif + + #ifdef USERMOD_AHT10 + usermods.add(new UsermodAHT10()); + #endif } From f71d839009fc5e539572318fa0983e29da881c5f Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Tue, 14 May 2024 17:54:43 +0200 Subject: [PATCH 2/9] Address comments --- platformio_override.sample.ini | 3 + usermods/AHT10_v2/README.md | 3 + usermods/AHT10_v2/usermod_aht10.h | 326 ++++++++++++++++-------------- wled00/const.h | 1 + 4 files changed, 176 insertions(+), 157 deletions(-) diff --git a/platformio_override.sample.ini b/platformio_override.sample.ini index 05bd1983e2..14508de72a 100644 --- a/platformio_override.sample.ini +++ b/platformio_override.sample.ini @@ -94,6 +94,9 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} ; -D USERMOD_AUTO_SAVE ; -D AUTOSAVE_AFTER_SEC=90 ; +; Use AHT10/AHT15/AHT20 usermod +; -D USERMOD_AHT10 +; ; Use 4 Line Display usermod with SPI display ; -D USERMOD_FOUR_LINE_DISPLAY ; -D USE_ALT_DISPlAY # mandatory diff --git a/usermods/AHT10_v2/README.md b/usermods/AHT10_v2/README.md index e6f16ddc49..650a595328 100644 --- a/usermods/AHT10_v2/README.md +++ b/usermods/AHT10_v2/README.md @@ -17,6 +17,9 @@ Dependencies, These must be added under `lib_deps` in your `platform.ini` (or `p - `enjoyneering/AHT10@~1.1.0` (by [enjoyneering](https://registry.platformio.org/libraries/enjoyneering/AHT10)) - `Wire` +## Author +[@LordMike](https://github.com/LordMike) + # Compiling To enable, compile with `USERMOD_AHT10` defined (e.g. in `platformio_override.ini`) diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index d6e44a87b2..984905a21d 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -5,197 +5,209 @@ #define AHT10_SUCCESS 1 -class UsermodAHT10 : public Usermod { - private: - static const char _name[]; - - unsigned long _lastLoopCheck = 0; - bool _initDone = false; - - // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime - bool _enabled = false; - uint8_t _i2cAddress = AHT10_ADDRESS_0X38; - ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR; - uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds - float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..) - - uint8_t _lastStatus = 0; - float _lastHumidity = 0; - float _lastTemperature = 0; - - AHT10* _aht = nullptr; - - float truncateDecimals(float val) { - return roundf(val * _decimalFactor) / _decimalFactor; +class UsermodAHT10 : public Usermod +{ +private: + static const char _name[]; + + unsigned long _lastLoopCheck = 0; + bool _initDone = false; + + // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime + bool _enabled = false; + uint8_t _i2cAddress = AHT10_ADDRESS_0X38; + ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR; + uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds + float _decimalFactor = 100; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..) + + uint8_t _lastStatus = 0; + float _lastHumidity = 0; + float _lastTemperature = 0; + + AHT10* _aht = nullptr; + + float truncateDecimals(float val) + { + return roundf(val * _decimalFactor) / _decimalFactor; + } + + void initializeAht() + { + if (_aht != nullptr) + { + delete _aht; } - void initializeAht() { - if (_aht != nullptr) { - delete _aht; - } + _aht = new AHT10(_i2cAddress, _ahtType); - _aht = new AHT10(_i2cAddress, _ahtType); + _lastStatus = 0; + _lastHumidity = 0; + _lastTemperature = 0; + } - _lastStatus = 0; - _lastHumidity = 0; - _lastTemperature = 0; - } + ~UsermodAHT10() + { + delete _aht; + _aht = nullptr; + } - ~UsermodAHT10() { - delete _aht; - _aht = nullptr; - } +public: + void setup() + { + initializeAht(); + } - public: - void setup() { - initializeAht(); - } + void loop() + { + // if usermod is disabled or called during strip updating just exit + // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly + if (!_enabled || strip.isUpdating()) + return; - void loop() { - // if usermod is disabled or called during strip updating just exit - // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly - if (!_enabled || strip.isUpdating()) - return; + // do your magic here + unsigned long currentTime = millis(); - // do your magic here - unsigned long currentTime = millis(); + if (currentTime - _lastLoopCheck < _checkInterval) + return; + _lastLoopCheck = currentTime; - if (currentTime - _lastLoopCheck < _checkInterval) - return; - _lastLoopCheck = currentTime; + _lastStatus = _aht->readRawData(); - _lastStatus = _aht->readRawData(); - - if (_lastStatus == AHT10_ERROR) + if (_lastStatus == AHT10_ERROR) + { + // Perform softReset and retry + DEBUG_PRINTLN("AHTxx returned error, doing softReset"); + if (!_aht->softReset()) { - // Perform softReset and retry - DEBUG_PRINTLN("AHTxx returned error, doing softReset"); - if (!_aht->softReset()) - { - DEBUG_PRINTLN("softReset failed"); - return; - } - - _lastStatus = _aht->readRawData(); + DEBUG_PRINTLN("softReset failed"); + return; } - if (_lastStatus == AHT10_SUCCESS) - { - float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA)); - float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA)); + _lastStatus = _aht->readRawData(); + } - // Push to MQTT + if (_lastStatus == AHT10_SUCCESS) + { + float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA)); + float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA)); - // Store - _lastHumidity = humidity; - _lastTemperature = temperature; - } + // Push to MQTT + + // Store + _lastHumidity = humidity; + _lastTemperature = temperature; } + } - void addToJsonInfo(JsonObject& root) override - { - // if "u" object does not exist yet wee need to create it - JsonObject user = root["u"]; - if (user.isNull()) - user = root.createNestedObject("u"); + uint16_t getId() + { + return USERMOD_ID_AHT10; + } + + void addToJsonInfo(JsonObject& root) override + { + // if "u" object does not exist yet wee need to create it + JsonObject user = root[F("u")]; + if (user.isNull()) + user = root.createNestedObject(F("u")); #ifdef USERMOD_AHT10_DEBUG - JsonArray temp = user.createNestedArray(F("status")); - temp.add(_lastLoopCheck); - temp.add(F(" / ")); - temp.add(_lastStatus); + JsonArray temp = user.createNestedArray(F("status")); + temp.add(_lastLoopCheck); + temp.add(F(" / ")); + temp.add(_lastStatus); #endif - JsonArray jsonTemp = user.createNestedArray(F("AHT Temperature")); - JsonArray jsonHumidity = user.createNestedArray(F("AHT Humidity")); - - if (_lastLoopCheck == 0) - { - // Before first run - jsonTemp.add(F("Not read yet")); - jsonHumidity.add(F("Not read yet")); - return; - } - - if (_lastStatus != AHT10_SUCCESS) - { - jsonTemp.add(F("An error occurred")); - jsonHumidity.add(F("An error occurred")); - return; - } - - jsonTemp.add(_lastTemperature); - jsonTemp.add(F("°C")); + JsonArray jsonTemp = user.createNestedArray(F("AHT Temperature")); + JsonArray jsonHumidity = user.createNestedArray(F("AHT Humidity")); - jsonHumidity.add(_lastHumidity); - jsonHumidity.add(F("%")); + if (_lastLoopCheck == 0) + { + // Before first run + jsonTemp.add(F("Not read yet")); + jsonHumidity.add(F("Not read yet")); + return; } - void addToConfig(JsonObject& root) + if (_lastStatus != AHT10_SUCCESS) { - JsonObject top = root.createNestedObject(FPSTR(_name)); - top[F("Enabled")] = _enabled; - top[F("I2CAddress")] = static_cast(_i2cAddress); - top[F("SensorType")] = _ahtType; - top[F("CheckInterval")] = _checkInterval / 1000; - top[F("Decimals")] = log10f(_decimalFactor); - - DEBUG_PRINTLN(F("AHT10 config saved.")); + jsonTemp.add(F("An error occurred")); + jsonHumidity.add(F("An error occurred")); + return; } - bool readFromConfig(JsonObject& root) override - { - // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor - // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) + jsonTemp.add(_lastTemperature); + jsonTemp.add(F("°C")); - JsonObject top = root[FPSTR(_name)]; + jsonHumidity.add(_lastHumidity); + jsonHumidity.add(F("%")); + } - bool configComplete = !top.isNull(); - if (!configComplete) - return false; + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject(FPSTR(_name)); + top[F("Enabled")] = _enabled; + top[F("I2CAddress")] = static_cast(_i2cAddress); + top[F("SensorType")] = _ahtType; + top[F("CheckInterval")] = _checkInterval / 1000; + top[F("Decimals")] = log10f(_decimalFactor); - configComplete &= getJsonValue(top["Enabled"], _enabled); - configComplete &= getJsonValue(top["I2CAddress"], _i2cAddress); - configComplete &= getJsonValue(top["CheckInterval"], _checkInterval); - if (configComplete) - { - if (1 <= _checkInterval && _checkInterval <= 600) - _checkInterval *= 1000; - else - // Invalid input - _checkInterval = 60000; - } + DEBUG_PRINTLN(F("AHT10 config saved.")); + } - configComplete &= getJsonValue(top["Decimals"], _decimalFactor); - if (configComplete) - { - if (0 <= _decimalFactor && _decimalFactor <= 5) - _decimalFactor = pow10f(_decimalFactor); - else - // Invalid input - _decimalFactor = 100; - } + bool readFromConfig(JsonObject& root) override + { + // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor + // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) - uint8_t tmpAhtType; - configComplete &= getJsonValue(top["SensorType"], tmpAhtType); - if (configComplete) - { - if (0 <= tmpAhtType && tmpAhtType <= 2) - _ahtType = static_cast(tmpAhtType); - else - // Invalid input - _ahtType = 0; - } + JsonObject top = root[FPSTR(_name)]; - if (_initDone) - { - // Reloading config - initializeAht(); - } + bool configComplete = !top.isNull(); + if (!configComplete) + return false; + + configComplete &= getJsonValue(top[F("Enabled")], _enabled); + configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress); + configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval); + if (configComplete) + { + if (1 <= _checkInterval && _checkInterval <= 600) + _checkInterval *= 1000; + else + // Invalid input + _checkInterval = 60000; + } + + configComplete &= getJsonValue(top[F("Decimals")], _decimalFactor); + if (configComplete) + { + if (0 <= _decimalFactor && _decimalFactor <= 5) + _decimalFactor = pow10f(_decimalFactor); + else + // Invalid input + _decimalFactor = 100; + } - _initDone = true; - return configComplete; + uint8_t tmpAhtType; + configComplete &= getJsonValue(top[F("SensorType")], tmpAhtType); + if (configComplete) + { + if (0 <= tmpAhtType && tmpAhtType <= 2) + _ahtType = static_cast(tmpAhtType); + else + // Invalid input + _ahtType = ASAIR_I2C_SENSOR::AHT10_SENSOR; } + + if (_initDone) + { + // Reloading config + initializeAht(); + } + + _initDone = true; + return configComplete; + } }; -const char UsermodAHT10::_name[] PROGMEM = "AHTxx"; \ No newline at end of file +const char UsermodAHT10::_name[] PROGMEM = "AHTxx"; \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index a44e67598f..5173afd24a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -178,6 +178,7 @@ #define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 //usermod "usermod_v2_HttpPullLightControl.h" #define USERMOD_ID_TETRISAI 47 //Usermod "usermod_v2_tetris.h" #define USERMOD_ID_MAX17048 48 //Usermod "usermod_max17048.h" +#define USERMOD_ID_AHT10 49 //Usermod "usermod_aht10.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot From 02bf5902f03fced548f9aaa4f628eb28412ae673 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Tue, 14 May 2024 22:53:03 +0200 Subject: [PATCH 3/9] Add MQTT and HASS support for AHT10 --- usermods/AHT10_v2/usermod_aht10.h | 106 +++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index 984905a21d..c85b3b5439 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -15,6 +15,9 @@ class UsermodAHT10 : public Usermod // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime bool _enabled = false; + bool _mqttPublish = false; + bool _mqttPublishAlways = false; + bool _enableHomeassistantDiscovery = false; uint8_t _i2cAddress = AHT10_ADDRESS_0X38; ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR; uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds @@ -24,7 +27,7 @@ class UsermodAHT10 : public Usermod float _lastHumidity = 0; float _lastTemperature = 0; - AHT10* _aht = nullptr; + AHT10 *_aht = nullptr; float truncateDecimals(float val) { @@ -51,6 +54,64 @@ class UsermodAHT10 : public Usermod _aht = nullptr; } +#ifndef WLED_DISABLE_MQTT + void mqttInitialize() + { + // This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt + if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_enableHomeassistantDiscovery) + return; + + char topic[128]; + snprintf_P(topic, 127, "%s/temperature", mqttDeviceTopic); + mqttCreateHassSensor(F("Temperature"), topic, F("temperature"), F("°C")); + + snprintf_P(topic, 127, "%s/humidity", mqttDeviceTopic); + mqttCreateHassSensor(F("Humidity"), topic, F("humidity"), F("%")); + } + + void mqttPublishIfChanged(const __FlashStringHelper *topic, float lastState, float state) + { + // Check if MQTT Connected, otherwise it will crash the 8266 + if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || lastState != state)) + { + char subuf[128]; + snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic); + mqtt->publish(subuf, 0, false, String(state).c_str()); + } + } + + // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. + void mqttCreateHassSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) + { + String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); + + StaticJsonDocument<600> doc; + + doc[F("name")] = name; + doc[F("state_topic")] = topic; + doc[F("unique_id")] = String(mqttClientID) + name; + if (unitOfMeasurement != "") + doc[F("unit_of_measurement")] = unitOfMeasurement; + if (deviceClass != "") + doc[F("device_class")] = deviceClass; + doc[F("expire_after")] = 1800; + + JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device + device[F("name")] = serverDescription; + device[F("identifiers")] = "wled-sensor-" + String(mqttClientID); + device[F("manufacturer")] = F(WLED_BRAND); + device[F("model")] = F(WLED_PRODUCT_NAME); + device[F("sw_version")] = versionString; + + String temp; + serializeJson(doc, temp); + DEBUG_PRINTLN(t); + DEBUG_PRINTLN(temp); + + mqtt->publish(t.c_str(), 0, true, temp.c_str()); + } +#endif + public: void setup() { @@ -91,20 +152,31 @@ class UsermodAHT10 : public Usermod float temperature = truncateDecimals(_aht->readTemperature(AHT10_USE_READ_DATA)); float humidity = truncateDecimals(_aht->readHumidity(AHT10_USE_READ_DATA)); +#ifndef WLED_DISABLE_MQTT // Push to MQTT + mqttPublishIfChanged(F("temperature"), _lastTemperature, temperature); + mqttPublishIfChanged(F("humidity"), _lastHumidity, humidity); +#endif // Store - _lastHumidity = humidity; _lastTemperature = temperature; + _lastHumidity = humidity; } } +#ifndef WLED_DISABLE_MQTT + void onMqttConnect(bool sessionPresent) + { + mqttInitialize(); + } +#endif + uint16_t getId() { return USERMOD_ID_AHT10; } - void addToJsonInfo(JsonObject& root) override + void addToJsonInfo(JsonObject &root) override { // if "u" object does not exist yet wee need to create it JsonObject user = root[F("u")]; @@ -112,14 +184,15 @@ class UsermodAHT10 : public Usermod user = root.createNestedObject(F("u")); #ifdef USERMOD_AHT10_DEBUG - JsonArray temp = user.createNestedArray(F("status")); + JsonArray temp = user.createNestedArray(F("AHT last loop")); temp.add(_lastLoopCheck); - temp.add(F(" / ")); + + temp = user.createNestedArray(F("AHT last status")); temp.add(_lastStatus); #endif - JsonArray jsonTemp = user.createNestedArray(F("AHT Temperature")); - JsonArray jsonHumidity = user.createNestedArray(F("AHT Humidity")); + JsonArray jsonTemp = user.createNestedArray(F("Temperature")); + JsonArray jsonHumidity = user.createNestedArray(F("Humidity")); if (_lastLoopCheck == 0) { @@ -143,7 +216,7 @@ class UsermodAHT10 : public Usermod jsonHumidity.add(F("%")); } - void addToConfig(JsonObject& root) + void addToConfig(JsonObject &root) { JsonObject top = root.createNestedObject(FPSTR(_name)); top[F("Enabled")] = _enabled; @@ -151,11 +224,16 @@ class UsermodAHT10 : public Usermod top[F("SensorType")] = _ahtType; top[F("CheckInterval")] = _checkInterval / 1000; top[F("Decimals")] = log10f(_decimalFactor); +#ifndef WLED_DISABLE_MQTT + top[F("MqttPublish")] = _mqttPublish; + top[F("MqttPublishAlways")] = _mqttPublishAlways; + top[F("MqttHomeAssistantDiscovery")] = _enableHomeassistantDiscovery; +#endif DEBUG_PRINTLN(F("AHT10 config saved.")); } - bool readFromConfig(JsonObject& root) override + bool readFromConfig(JsonObject &root) override { // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) @@ -199,10 +277,20 @@ class UsermodAHT10 : public Usermod _ahtType = ASAIR_I2C_SENSOR::AHT10_SENSOR; } +#ifndef WLED_DISABLE_MQTT + configComplete &= getJsonValue(top[F("MqttPublish")], _mqttPublish); + configComplete &= getJsonValue(top[F("MqttPublishAlways")], _mqttPublishAlways); + configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], _enableHomeassistantDiscovery); +#endif + if (_initDone) { // Reloading config initializeAht(); + +#ifndef WLED_DISABLE_MQTT + mqttInitialize(); +#endif } _initDone = true; From 74e273274c3cf433e4db9cb5723abc3dab7ed396 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Tue, 14 May 2024 23:15:59 +0200 Subject: [PATCH 4/9] Replace 5 booleans with 1 byte flags field --- usermods/AHT10_v2/usermod_aht10.h | 55 +++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index c85b3b5439..249c594d30 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -5,19 +5,26 @@ #define AHT10_SUCCESS 1 +#define STATE_SETTING_ENABLED 0b00000001 +#define STATE_SETTING_MQTT_PUBLISH 0b00000010 +#define STATE_SETTING_MQTT_PUBLISHALWAYS 0b00000100 +#define STATE_SETTING_MQTT_HOMEASSISTANT 0b00001000 +#define STATE_INITDONE 0b00010000 + +#define STATE_ISSET(stateFlags, state) (((stateFlags) & (state)) == (state)) +#define STATE_SET(stateFlags, state, set) ((set) ? ((stateFlags) |= (state)) : ((stateFlags) &= ~(state))) + class UsermodAHT10 : public Usermod { private: static const char _name[]; unsigned long _lastLoopCheck = 0; - bool _initDone = false; + + // To avoid storing N bools, this flags field will handle all of those + uint8_t _stateFlags = 0; // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime - bool _enabled = false; - bool _mqttPublish = false; - bool _mqttPublishAlways = false; - bool _enableHomeassistantDiscovery = false; uint8_t _i2cAddress = AHT10_ADDRESS_0X38; ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR; uint16_t _checkInterval = 60000; // milliseconds, user settings is in seconds @@ -58,7 +65,7 @@ class UsermodAHT10 : public Usermod void mqttInitialize() { // This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt - if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_enableHomeassistantDiscovery) + if (!WLED_MQTT_CONNECTED || !STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISH) || !STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_HOMEASSISTANT)) return; char topic[128]; @@ -72,7 +79,7 @@ class UsermodAHT10 : public Usermod void mqttPublishIfChanged(const __FlashStringHelper *topic, float lastState, float state) { // Check if MQTT Connected, otherwise it will crash the 8266 - if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || lastState != state)) + if (WLED_MQTT_CONNECTED && STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISH) && (STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISHALWAYS) || lastState != state)) { char subuf[128]; snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic); @@ -122,7 +129,7 @@ class UsermodAHT10 : public Usermod { // if usermod is disabled or called during strip updating just exit // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly - if (!_enabled || strip.isUpdating()) + if (!STATE_ISSET(_stateFlags, STATE_SETTING_ENABLED) || strip.isUpdating()) return; // do your magic here @@ -219,15 +226,15 @@ class UsermodAHT10 : public Usermod void addToConfig(JsonObject &root) { JsonObject top = root.createNestedObject(FPSTR(_name)); - top[F("Enabled")] = _enabled; + top[F("Enabled")] = STATE_ISSET(_stateFlags, STATE_SETTING_ENABLED); top[F("I2CAddress")] = static_cast(_i2cAddress); top[F("SensorType")] = _ahtType; top[F("CheckInterval")] = _checkInterval / 1000; top[F("Decimals")] = log10f(_decimalFactor); #ifndef WLED_DISABLE_MQTT - top[F("MqttPublish")] = _mqttPublish; - top[F("MqttPublishAlways")] = _mqttPublishAlways; - top[F("MqttHomeAssistantDiscovery")] = _enableHomeassistantDiscovery; + top[F("MqttPublish")] = STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISH); + top[F("MqttPublishAlways")] = STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISHALWAYS); + top[F("MqttHomeAssistantDiscovery")] = STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_HOMEASSISTANT); #endif DEBUG_PRINTLN(F("AHT10 config saved.")); @@ -244,7 +251,11 @@ class UsermodAHT10 : public Usermod if (!configComplete) return false; - configComplete &= getJsonValue(top[F("Enabled")], _enabled); + bool tmpBool = false; + configComplete &= getJsonValue(top[F("Enabled")], tmpBool); + if (configComplete) + STATE_SET(_stateFlags, STATE_SETTING_ENABLED, tmpBool); + configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress); configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval); if (configComplete) @@ -278,12 +289,20 @@ class UsermodAHT10 : public Usermod } #ifndef WLED_DISABLE_MQTT - configComplete &= getJsonValue(top[F("MqttPublish")], _mqttPublish); - configComplete &= getJsonValue(top[F("MqttPublishAlways")], _mqttPublishAlways); - configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], _enableHomeassistantDiscovery); + configComplete &= getJsonValue(top[F("MqttPublish")], tmpBool); + if (configComplete) + STATE_SET(_stateFlags, STATE_SETTING_MQTT_PUBLISH, tmpBool); + + configComplete &= getJsonValue(top[F("MqttPublishAlways")], tmpBool); + if (configComplete) + STATE_SET(_stateFlags, STATE_SETTING_MQTT_PUBLISHALWAYS, tmpBool); + + configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool); + if (configComplete) + STATE_SET(_stateFlags, STATE_SETTING_MQTT_HOMEASSISTANT, tmpBool); #endif - if (_initDone) + if (STATE_ISSET(_stateFlags, STATE_INITDONE)) { // Reloading config initializeAht(); @@ -293,7 +312,7 @@ class UsermodAHT10 : public Usermod #endif } - _initDone = true; + STATE_SET(_stateFlags, STATE_INITDONE, true); return configComplete; } }; From 2eff389ffff68824f3fd57b4dd5c5ce3769d2b39 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Wed, 15 May 2024 18:35:33 +0200 Subject: [PATCH 5/9] Handle feedback - Reduce some strings - Use an unnamed struct to pack the bitfield instead --- usermods/AHT10_v2/usermod_aht10.h | 56 +++++++++++++++---------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index 249c594d30..3b8ab1dba2 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -5,15 +5,6 @@ #define AHT10_SUCCESS 1 -#define STATE_SETTING_ENABLED 0b00000001 -#define STATE_SETTING_MQTT_PUBLISH 0b00000010 -#define STATE_SETTING_MQTT_PUBLISHALWAYS 0b00000100 -#define STATE_SETTING_MQTT_HOMEASSISTANT 0b00001000 -#define STATE_INITDONE 0b00010000 - -#define STATE_ISSET(stateFlags, state) (((stateFlags) & (state)) == (state)) -#define STATE_SET(stateFlags, state, set) ((set) ? ((stateFlags) |= (state)) : ((stateFlags) &= ~(state))) - class UsermodAHT10 : public Usermod { private: @@ -21,8 +12,15 @@ class UsermodAHT10 : public Usermod unsigned long _lastLoopCheck = 0; - // To avoid storing N bools, this flags field will handle all of those - uint8_t _stateFlags = 0; + struct + { + bool settingEnabled : 1; // Enable the usermod + bool mqttPublish : 1; // Publish mqtt values + bool mqttPublishAlways : 1; // Publish always, regardless if there is a change + bool mqttHomeAssistant : 1; // Enable Home Assistant docs + bool initDone : 1; // Initialization is done + unsigned : 3; + } _stateFlags; // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime uint8_t _i2cAddress = AHT10_ADDRESS_0X38; @@ -65,7 +63,7 @@ class UsermodAHT10 : public Usermod void mqttInitialize() { // This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt - if (!WLED_MQTT_CONNECTED || !STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISH) || !STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_HOMEASSISTANT)) + if (!WLED_MQTT_CONNECTED || !_stateFlags.mqttPublish || !_stateFlags.mqttHomeAssistant) return; char topic[128]; @@ -79,7 +77,7 @@ class UsermodAHT10 : public Usermod void mqttPublishIfChanged(const __FlashStringHelper *topic, float lastState, float state) { // Check if MQTT Connected, otherwise it will crash the 8266 - if (WLED_MQTT_CONNECTED && STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISH) && (STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISHALWAYS) || lastState != state)) + if (WLED_MQTT_CONNECTED && _stateFlags.mqttPublish && (_stateFlags.mqttPublishAlways || lastState != state)) { char subuf[128]; snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic); @@ -90,7 +88,7 @@ class UsermodAHT10 : public Usermod // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. void mqttCreateHassSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) { - String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config"); + String t = String(F("homeassistant/sensor/")) + mqttClientID + "/" + name + F("/config"); StaticJsonDocument<600> doc; @@ -129,7 +127,7 @@ class UsermodAHT10 : public Usermod { // if usermod is disabled or called during strip updating just exit // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly - if (!STATE_ISSET(_stateFlags, STATE_SETTING_ENABLED) || strip.isUpdating()) + if (!_stateFlags.settingEnabled || strip.isUpdating()) return; // do your magic here @@ -144,10 +142,10 @@ class UsermodAHT10 : public Usermod if (_lastStatus == AHT10_ERROR) { // Perform softReset and retry - DEBUG_PRINTLN("AHTxx returned error, doing softReset"); + DEBUG_PRINTLN(F("AHTxx returned error, doing softReset")); if (!_aht->softReset()) { - DEBUG_PRINTLN("softReset failed"); + DEBUG_PRINTLN(F("softReset failed")); return; } @@ -186,9 +184,9 @@ class UsermodAHT10 : public Usermod void addToJsonInfo(JsonObject &root) override { // if "u" object does not exist yet wee need to create it - JsonObject user = root[F("u")]; + JsonObject user = root["u"]; if (user.isNull()) - user = root.createNestedObject(F("u")); + user = root.createNestedObject("u"); #ifdef USERMOD_AHT10_DEBUG JsonArray temp = user.createNestedArray(F("AHT last loop")); @@ -226,15 +224,15 @@ class UsermodAHT10 : public Usermod void addToConfig(JsonObject &root) { JsonObject top = root.createNestedObject(FPSTR(_name)); - top[F("Enabled")] = STATE_ISSET(_stateFlags, STATE_SETTING_ENABLED); + top[F("Enabled")] = _stateFlags.settingEnabled; top[F("I2CAddress")] = static_cast(_i2cAddress); top[F("SensorType")] = _ahtType; top[F("CheckInterval")] = _checkInterval / 1000; top[F("Decimals")] = log10f(_decimalFactor); #ifndef WLED_DISABLE_MQTT - top[F("MqttPublish")] = STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISH); - top[F("MqttPublishAlways")] = STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_PUBLISHALWAYS); - top[F("MqttHomeAssistantDiscovery")] = STATE_ISSET(_stateFlags, STATE_SETTING_MQTT_HOMEASSISTANT); + top[F("MqttPublish")] = _stateFlags.mqttPublish; + top[F("MqttPublishAlways")] = _stateFlags.mqttPublishAlways; + top[F("MqttHomeAssistantDiscovery")] = _stateFlags.mqttHomeAssistant; #endif DEBUG_PRINTLN(F("AHT10 config saved.")); @@ -254,7 +252,7 @@ class UsermodAHT10 : public Usermod bool tmpBool = false; configComplete &= getJsonValue(top[F("Enabled")], tmpBool); if (configComplete) - STATE_SET(_stateFlags, STATE_SETTING_ENABLED, tmpBool); + _stateFlags.settingEnabled = tmpBool; configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress); configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval); @@ -291,18 +289,18 @@ class UsermodAHT10 : public Usermod #ifndef WLED_DISABLE_MQTT configComplete &= getJsonValue(top[F("MqttPublish")], tmpBool); if (configComplete) - STATE_SET(_stateFlags, STATE_SETTING_MQTT_PUBLISH, tmpBool); + _stateFlags.mqttPublish = tmpBool; configComplete &= getJsonValue(top[F("MqttPublishAlways")], tmpBool); if (configComplete) - STATE_SET(_stateFlags, STATE_SETTING_MQTT_PUBLISHALWAYS, tmpBool); + _stateFlags.mqttPublishAlways = tmpBool; configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool); if (configComplete) - STATE_SET(_stateFlags, STATE_SETTING_MQTT_HOMEASSISTANT, tmpBool); + _stateFlags.mqttHomeAssistant = tmpBool; #endif - if (STATE_ISSET(_stateFlags, STATE_INITDONE)) + if (_stateFlags.initDone) { // Reloading config initializeAht(); @@ -312,7 +310,7 @@ class UsermodAHT10 : public Usermod #endif } - STATE_SET(_stateFlags, STATE_INITDONE, true); + _stateFlags.initDone = true; return configComplete; } }; From f51da4f0c46920711a08825976d8cdfa540b8d61 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Wed, 15 May 2024 18:56:45 +0200 Subject: [PATCH 6/9] Change reporting to be on significant changes --- usermods/AHT10_v2/usermod_aht10.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index 3b8ab1dba2..dde27a5887 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -74,10 +74,11 @@ class UsermodAHT10 : public Usermod mqttCreateHassSensor(F("Humidity"), topic, F("humidity"), F("%")); } - void mqttPublishIfChanged(const __FlashStringHelper *topic, float lastState, float state) + void mqttPublishIfChanged(const __FlashStringHelper *topic, float lastState, float state, float minChange) { // Check if MQTT Connected, otherwise it will crash the 8266 - if (WLED_MQTT_CONNECTED && _stateFlags.mqttPublish && (_stateFlags.mqttPublishAlways || lastState != state)) + // Only report if the change is larger than the required diff + if (WLED_MQTT_CONNECTED && _stateFlags.mqttPublish && (_stateFlags.mqttPublishAlways || fabsf(lastState - state) > minChange)) { char subuf[128]; snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic); @@ -159,8 +160,13 @@ class UsermodAHT10 : public Usermod #ifndef WLED_DISABLE_MQTT // Push to MQTT - mqttPublishIfChanged(F("temperature"), _lastTemperature, temperature); - mqttPublishIfChanged(F("humidity"), _lastHumidity, humidity); + + // We can avoid reporting if the change is insignificant. The threshold chosen is below the level of accuracy, but way above 0.01 which is the precision of the value provided. + // The AHT10/15/20 has an accuracy of 0.3C in the temperature readings + mqttPublishIfChanged(F("temperature"), _lastTemperature, temperature, 0.1f); + + // The AHT10/15/20 has an accuracy in the humidity sensor of 2% + mqttPublishIfChanged(F("humidity"), _lastHumidity, humidity, 0.5f); #endif // Store From f9467ceaf155b5dcce7add362138eef0074afe23 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Wed, 15 May 2024 21:26:30 +0200 Subject: [PATCH 7/9] Fix state issue on change reporting, add override sample ini --- .gitignore | 2 +- usermods/AHT10_v2/README.md | 1 - usermods/AHT10_v2/platformio_override.ini | 9 +++++++++ usermods/AHT10_v2/usermod_aht10.h | 13 ++++++++++--- 4 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 usermods/AHT10_v2/platformio_override.ini diff --git a/.gitignore b/.gitignore index 8a2319a72c..0a7bb16020 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ .vscode esp01-update.sh -platformio_override.ini +/platformio_override.ini replace_fs.py wled-update.sh diff --git a/usermods/AHT10_v2/README.md b/usermods/AHT10_v2/README.md index 650a595328..69fab46717 100644 --- a/usermods/AHT10_v2/README.md +++ b/usermods/AHT10_v2/README.md @@ -33,5 +33,4 @@ build_flags = lib_deps = ${esp32.lib_deps} enjoyneering/AHT10@~1.1.0 - Wire ``` diff --git a/usermods/AHT10_v2/platformio_override.ini b/usermods/AHT10_v2/platformio_override.ini new file mode 100644 index 0000000000..30240f2224 --- /dev/null +++ b/usermods/AHT10_v2/platformio_override.ini @@ -0,0 +1,9 @@ +[env:aht10_example] +extends = env:esp32dev +build_flags = + ${common.build_flags} ${esp32.build_flags} + -D USERMOD_AHT10 + ; -D USERMOD_AHT10_DEBUG ; -- add a debug status to the info modal +lib_deps = + ${esp32.lib_deps} + enjoyneering/AHT10@~1.1.0 \ No newline at end of file diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index dde27a5887..2680c82e27 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -32,6 +32,11 @@ class UsermodAHT10 : public Usermod float _lastHumidity = 0; float _lastTemperature = 0; +#ifndef WLED_MQTT_DISABLE + float _lastHumiditySent = 0; + float _lastTemperatureSent = 0; +#endif + AHT10 *_aht = nullptr; float truncateDecimals(float val) @@ -74,7 +79,7 @@ class UsermodAHT10 : public Usermod mqttCreateHassSensor(F("Humidity"), topic, F("humidity"), F("%")); } - void mqttPublishIfChanged(const __FlashStringHelper *topic, float lastState, float state, float minChange) + void mqttPublishIfChanged(const __FlashStringHelper *topic, float &lastState, float state, float minChange) { // Check if MQTT Connected, otherwise it will crash the 8266 // Only report if the change is larger than the required diff @@ -83,6 +88,8 @@ class UsermodAHT10 : public Usermod char subuf[128]; snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic); mqtt->publish(subuf, 0, false, String(state).c_str()); + + lastState = state; } } @@ -163,10 +170,10 @@ class UsermodAHT10 : public Usermod // We can avoid reporting if the change is insignificant. The threshold chosen is below the level of accuracy, but way above 0.01 which is the precision of the value provided. // The AHT10/15/20 has an accuracy of 0.3C in the temperature readings - mqttPublishIfChanged(F("temperature"), _lastTemperature, temperature, 0.1f); + mqttPublishIfChanged(F("temperature"), _lastTemperatureSent, temperature, 0.1f); // The AHT10/15/20 has an accuracy in the humidity sensor of 2% - mqttPublishIfChanged(F("humidity"), _lastHumidity, humidity, 0.5f); + mqttPublishIfChanged(F("humidity"), _lastHumiditySent, humidity, 0.5f); #endif // Store From 1dd9c6754c569d4113e9b80c15360ad95d88d749 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Wed, 15 May 2024 21:32:08 +0200 Subject: [PATCH 8/9] Cleanup unecessary struct --- usermods/AHT10_v2/usermod_aht10.h | 40 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/usermods/AHT10_v2/usermod_aht10.h b/usermods/AHT10_v2/usermod_aht10.h index 2680c82e27..b5dc1841db 100644 --- a/usermods/AHT10_v2/usermod_aht10.h +++ b/usermods/AHT10_v2/usermod_aht10.h @@ -12,15 +12,11 @@ class UsermodAHT10 : public Usermod unsigned long _lastLoopCheck = 0; - struct - { - bool settingEnabled : 1; // Enable the usermod - bool mqttPublish : 1; // Publish mqtt values - bool mqttPublishAlways : 1; // Publish always, regardless if there is a change - bool mqttHomeAssistant : 1; // Enable Home Assistant docs - bool initDone : 1; // Initialization is done - unsigned : 3; - } _stateFlags; + bool _settingEnabled : 1; // Enable the usermod + bool _mqttPublish : 1; // Publish mqtt values + bool _mqttPublishAlways : 1; // Publish always, regardless if there is a change + bool _mqttHomeAssistant : 1; // Enable Home Assistant docs + bool _initDone : 1; // Initialization is done // Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime uint8_t _i2cAddress = AHT10_ADDRESS_0X38; @@ -68,7 +64,7 @@ class UsermodAHT10 : public Usermod void mqttInitialize() { // This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt - if (!WLED_MQTT_CONNECTED || !_stateFlags.mqttPublish || !_stateFlags.mqttHomeAssistant) + if (!WLED_MQTT_CONNECTED || !_mqttPublish || !_mqttHomeAssistant) return; char topic[128]; @@ -83,7 +79,7 @@ class UsermodAHT10 : public Usermod { // Check if MQTT Connected, otherwise it will crash the 8266 // Only report if the change is larger than the required diff - if (WLED_MQTT_CONNECTED && _stateFlags.mqttPublish && (_stateFlags.mqttPublishAlways || fabsf(lastState - state) > minChange)) + if (WLED_MQTT_CONNECTED && _mqttPublish && (_mqttPublishAlways || fabsf(lastState - state) > minChange)) { char subuf[128]; snprintf_P(subuf, 127, PSTR("%s/%s"), mqttDeviceTopic, (const char *)topic); @@ -135,7 +131,7 @@ class UsermodAHT10 : public Usermod { // if usermod is disabled or called during strip updating just exit // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly - if (!_stateFlags.settingEnabled || strip.isUpdating()) + if (!_settingEnabled || strip.isUpdating()) return; // do your magic here @@ -237,15 +233,15 @@ class UsermodAHT10 : public Usermod void addToConfig(JsonObject &root) { JsonObject top = root.createNestedObject(FPSTR(_name)); - top[F("Enabled")] = _stateFlags.settingEnabled; + top[F("Enabled")] = _settingEnabled; top[F("I2CAddress")] = static_cast(_i2cAddress); top[F("SensorType")] = _ahtType; top[F("CheckInterval")] = _checkInterval / 1000; top[F("Decimals")] = log10f(_decimalFactor); #ifndef WLED_DISABLE_MQTT - top[F("MqttPublish")] = _stateFlags.mqttPublish; - top[F("MqttPublishAlways")] = _stateFlags.mqttPublishAlways; - top[F("MqttHomeAssistantDiscovery")] = _stateFlags.mqttHomeAssistant; + top[F("MqttPublish")] = _mqttPublish; + top[F("MqttPublishAlways")] = _mqttPublishAlways; + top[F("MqttHomeAssistantDiscovery")] = _mqttHomeAssistant; #endif DEBUG_PRINTLN(F("AHT10 config saved.")); @@ -265,7 +261,7 @@ class UsermodAHT10 : public Usermod bool tmpBool = false; configComplete &= getJsonValue(top[F("Enabled")], tmpBool); if (configComplete) - _stateFlags.settingEnabled = tmpBool; + _settingEnabled = tmpBool; configComplete &= getJsonValue(top[F("I2CAddress")], _i2cAddress); configComplete &= getJsonValue(top[F("CheckInterval")], _checkInterval); @@ -302,18 +298,18 @@ class UsermodAHT10 : public Usermod #ifndef WLED_DISABLE_MQTT configComplete &= getJsonValue(top[F("MqttPublish")], tmpBool); if (configComplete) - _stateFlags.mqttPublish = tmpBool; + _mqttPublish = tmpBool; configComplete &= getJsonValue(top[F("MqttPublishAlways")], tmpBool); if (configComplete) - _stateFlags.mqttPublishAlways = tmpBool; + _mqttPublishAlways = tmpBool; configComplete &= getJsonValue(top[F("MqttHomeAssistantDiscovery")], tmpBool); if (configComplete) - _stateFlags.mqttHomeAssistant = tmpBool; + _mqttHomeAssistant = tmpBool; #endif - if (_stateFlags.initDone) + if (_initDone) { // Reloading config initializeAht(); @@ -323,7 +319,7 @@ class UsermodAHT10 : public Usermod #endif } - _stateFlags.initDone = true; + _initDone = true; return configComplete; } }; From 77e6a6de613563417e2597bc737e01071b037fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 21 May 2024 18:23:25 +0200 Subject: [PATCH 9/9] Revert .gitignore --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0a7bb16020..8f083e3f6a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ .vscode esp01-update.sh -/platformio_override.ini +platformio_override.ini replace_fs.py wled-update.sh @@ -21,4 +21,4 @@ wled-update.sh /wled00/my_config.h /wled00/Release /wled00/wled00.ino.cpp -/wled00/html_*.h \ No newline at end of file +/wled00/html_*.h