Merge pull request #4620 from gsieben/USERMOD-BME68X-Update-to-Version-1.0.2

Update USERMOD BME68X to version 1.0.2
This commit is contained in:
Will Miles 2025-03-29 11:09:17 -04:00 committed by GitHub
commit b0b3196e52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 1158 additions and 1134 deletions

View File

@ -2,48 +2,45 @@
* @file usermod_BMW68X.h
* @author Gabriel A. Sieben (GeoGab)
* @brief Usermod for WLED to implement the BME680/BME688 sensor
* @version 1.0.0
* @date 19 Feb 2024
* @version 1.0.2
* @date 28 March 2025
*/
#warning ********************Included USERMOD_BME68X ********************
#define UMOD_DEVICE "ESP32" // NOTE - Set your hardware here
#define HARDWARE_VERSION "1.0" // NOTE - Set your hardware version here
#define UMOD_BME680X_SW_VERSION "1.0.2" // NOTE - Version of the User Mod
#define CALIB_FILE_NAME "/BME680X-Calib.hex" // NOTE - Calibration file name
#define UMOD_NAME "BME680X" // NOTE - User module name
#define UMOD_DEBUG_NAME "UM-BME680X: " // NOTE - Debug print module name addon
#define UMOD_DEVICE "ESP32" // NOTE - Set your hardware here
#define HARDWARE_VERSION "1.0" // NOTE - Set your hardware version here
#define UMOD_BME680X_SW_VERSION "1.0.1" // NOTE - Version of the User Mod
#define CALIB_FILE_NAME "/BME680X-Calib.hex" // NOTE - Calibration file name
#define UMOD_NAME "BME680X" // NOTE - User module name
#define UMOD_DEBUG_NAME "UM-BME680X: " // NOTE - Debug print module name addon
#define ESC "\033"
#define ESC_CSI ESC "["
#define ESC_STYLE_RESET ESC_CSI "0m"
#define ESC_CURSOR_COLUMN(n) ESC_CSI #n "G"
/* Debug Print Text Coloring */
#define ESC "\033"
#define ESC_CSI ESC "["
#define ESC_STYLE_RESET ESC_CSI "0m"
#define ESC_CURSOR_COLUMN(n) ESC_CSI #n "G"
#define ESC_FGCOLOR_BLACK ESC_CSI "30m"
#define ESC_FGCOLOR_RED ESC_CSI "31m"
#define ESC_FGCOLOR_GREEN ESC_CSI "32m"
#define ESC_FGCOLOR_YELLOW ESC_CSI "33m"
#define ESC_FGCOLOR_BLUE ESC_CSI "34m"
#define ESC_FGCOLOR_MAGENTA ESC_CSI "35m"
#define ESC_FGCOLOR_CYAN ESC_CSI "36m"
#define ESC_FGCOLOR_WHITE ESC_CSI "37m"
#define ESC_FGCOLOR_DEFAULT ESC_CSI "39m"
#define ESC_FGCOLOR_BLACK ESC_CSI "30m"
#define ESC_FGCOLOR_RED ESC_CSI "31m"
#define ESC_FGCOLOR_GREEN ESC_CSI "32m"
#define ESC_FGCOLOR_YELLOW ESC_CSI "33m"
#define ESC_FGCOLOR_BLUE ESC_CSI "34m"
#define ESC_FGCOLOR_MAGENTA ESC_CSI "35m"
#define ESC_FGCOLOR_CYAN ESC_CSI "36m"
#define ESC_FGCOLOR_WHITE ESC_CSI "37m"
#define ESC_FGCOLOR_DEFAULT ESC_CSI "39m"
/* Debug Print Special Text */
#define INFO_COLUMN ESC_CURSOR_COLUMN(60)
#define GOGAB_OK INFO_COLUMN "[" ESC_FGCOLOR_GREEN "OK" ESC_STYLE_RESET "]"
#define GOGAB_FAIL INFO_COLUMN "[" ESC_FGCOLOR_RED "FAIL" ESC_STYLE_RESET "]"
#define GOGAB_WARN INFO_COLUMN "[" ESC_FGCOLOR_YELLOW "WARN" ESC_STYLE_RESET "]"
#define GOGAB_DONE INFO_COLUMN "[" ESC_FGCOLOR_CYAN "DONE" ESC_STYLE_RESET "]"
/* Debug Print Special Text */
#define INFO_COLUMN ESC_CURSOR_COLUMN(60)
#define OK INFO_COLUMN "[" ESC_FGCOLOR_GREEN "OK" ESC_STYLE_RESET "]"
#define FAIL INFO_COLUMN "[" ESC_FGCOLOR_RED "FAIL" ESC_STYLE_RESET "]"
#define WARN INFO_COLUMN "[" ESC_FGCOLOR_YELLOW "WARN" ESC_STYLE_RESET "]"
#define DONE INFO_COLUMN "[" ESC_FGCOLOR_CYAN "DONE" ESC_STYLE_RESET "]"
#include "bsec.h" // Bosch sensor library
#include "wled.h"
#include <Arduino.h>
#include "bsec.h" // Bosch sensor library
#include "wled.h"
#include <Arduino.h>
/* UsermodBME68X class definition */
class UsermodBME68X : public Usermod {
/* UsermodBME68X class definition */
class UsermodBME68X : public Usermod {
public:
/* Public: Functions */
@ -250,85 +247,85 @@ class UsermodBME68X : public Usermod {
static const char _unitCelsius[];
static const char _unitFahrenheit[];
}; // UsermodBME68X class definition End
}; // UsermodBME68X class definition End
/*** Setting C O N S T A N T S ***/
/* Private: Settings Strings*/
const char UsermodBME68X::_enabled[] PROGMEM = "Enabled";
const char UsermodBME68X::_hadtopic[] PROGMEM = "homeassistant/sensor/";
/*** Setting C O N S T A N T S ***/
/* Private: Settings Strings*/
const char UsermodBME68X::_enabled[] PROGMEM = "Enabled";
const char UsermodBME68X::_hadtopic[] PROGMEM = "homeassistant/sensor/";
const char UsermodBME68X::_nameI2CAdr[] PROGMEM = "i2C Address";
const char UsermodBME68X::_nameInterval[] PROGMEM = "Interval";
const char UsermodBME68X::_nameMaxAge[] PROGMEM = "Max Age";
const char UsermodBME68X::_namePublishChange[] PROGMEM = "Pub changes only";
const char UsermodBME68X::_namePubAc[] PROGMEM = "Pub Accuracy";
const char UsermodBME68X::_namePubSenState[] PROGMEM = "Pub Calib State";
const char UsermodBME68X::_namePubAfterCalib[] PROGMEM = "Pub After Calib";
const char UsermodBME68X::_nameTempScale[] PROGMEM = "Temp Scale";
const char UsermodBME68X::_nameTempOffset[] PROGMEM = "Temp Offset";
const char UsermodBME68X::_nameHADisc[] PROGMEM = "HA Discovery";
const char UsermodBME68X::_nameDelCalib[] PROGMEM = "Del Calibration Hist";
const char UsermodBME68X::_namePauseOnActWL[] PROGMEM = "Pause while WLED active";
const char UsermodBME68X::_nameI2CAdr[] PROGMEM = "i2C Address";
const char UsermodBME68X::_nameInterval[] PROGMEM = "Interval";
const char UsermodBME68X::_nameMaxAge[] PROGMEM = "Max Age";
const char UsermodBME68X::_namePublishChange[] PROGMEM = "Pub changes only";
const char UsermodBME68X::_namePubAc[] PROGMEM = "Pub Accuracy";
const char UsermodBME68X::_namePubSenState[] PROGMEM = "Pub Calib State";
const char UsermodBME68X::_namePubAfterCalib[] PROGMEM = "Pub After Calib";
const char UsermodBME68X::_nameTempScale[] PROGMEM = "Temp Scale";
const char UsermodBME68X::_nameTempOffset[] PROGMEM = "Temp Offset";
const char UsermodBME68X::_nameHADisc[] PROGMEM = "HA Discovery";
const char UsermodBME68X::_nameDelCalib[] PROGMEM = "Del Calibration Hist";
const char UsermodBME68X::_namePauseOnActWL[] PROGMEM = "Pause while WLED active";
/* Private: Sensor names / Sensor short name */
const char UsermodBME68X::_nameTemp[] PROGMEM = "Temperature";
const char UsermodBME68X::_nameHum[] PROGMEM = "Humidity";
const char UsermodBME68X::_namePress[] PROGMEM = "Pressure";
const char UsermodBME68X::_nameGasRes[] PROGMEM = "Gas-Resistance";
const char UsermodBME68X::_nameAHum[] PROGMEM = "Absolute-Humidity";
const char UsermodBME68X::_nameDrewP[] PROGMEM = "Drew-Point";
const char UsermodBME68X::_nameIaq[] PROGMEM = "IAQ";
const char UsermodBME68X::_nameIaqVerb[] PROGMEM = "IAQ-Verbal";
const char UsermodBME68X::_nameStaticIaq[] PROGMEM = "Static-IAQ";
const char UsermodBME68X::_nameStaticIaqVerb[] PROGMEM = "Static-IAQ-Verbal";
const char UsermodBME68X::_nameCo2[] PROGMEM = "CO2";
const char UsermodBME68X::_nameVoc[] PROGMEM = "VOC";
const char UsermodBME68X::_nameGasPer[] PROGMEM = "Gas-Percentage";
const char UsermodBME68X::_nameIaqAc[] PROGMEM = "IAQ-Accuracy";
const char UsermodBME68X::_nameStaticIaqAc[] PROGMEM = "Static-IAQ-Accuracy";
const char UsermodBME68X::_nameCo2Ac[] PROGMEM = "CO2-Accuracy";
const char UsermodBME68X::_nameVocAc[] PROGMEM = "VOC-Accuracy";
const char UsermodBME68X::_nameGasPerAc[] PROGMEM = "Gas-Percentage-Accuracy";
const char UsermodBME68X::_nameStabStatus[] PROGMEM = "Stab-Status";
const char UsermodBME68X::_nameRunInStatus[] PROGMEM = "Run-In-Status";
/* Private: Sensor names / Sensor short name */
const char UsermodBME68X::_nameTemp[] PROGMEM = "Temperature";
const char UsermodBME68X::_nameHum[] PROGMEM = "Humidity";
const char UsermodBME68X::_namePress[] PROGMEM = "Pressure";
const char UsermodBME68X::_nameGasRes[] PROGMEM = "Gas-Resistance";
const char UsermodBME68X::_nameAHum[] PROGMEM = "Absolute-Humidity";
const char UsermodBME68X::_nameDrewP[] PROGMEM = "Drew-Point";
const char UsermodBME68X::_nameIaq[] PROGMEM = "IAQ";
const char UsermodBME68X::_nameIaqVerb[] PROGMEM = "IAQ-Verbal";
const char UsermodBME68X::_nameStaticIaq[] PROGMEM = "Static-IAQ";
const char UsermodBME68X::_nameStaticIaqVerb[] PROGMEM = "Static-IAQ-Verbal";
const char UsermodBME68X::_nameCo2[] PROGMEM = "CO2";
const char UsermodBME68X::_nameVoc[] PROGMEM = "VOC";
const char UsermodBME68X::_nameGasPer[] PROGMEM = "Gas-Percentage";
const char UsermodBME68X::_nameIaqAc[] PROGMEM = "IAQ-Accuracy";
const char UsermodBME68X::_nameStaticIaqAc[] PROGMEM = "Static-IAQ-Accuracy";
const char UsermodBME68X::_nameCo2Ac[] PROGMEM = "CO2-Accuracy";
const char UsermodBME68X::_nameVocAc[] PROGMEM = "VOC-Accuracy";
const char UsermodBME68X::_nameGasPerAc[] PROGMEM = "Gas-Percentage-Accuracy";
const char UsermodBME68X::_nameStabStatus[] PROGMEM = "Stab-Status";
const char UsermodBME68X::_nameRunInStatus[] PROGMEM = "Run-In-Status";
/* Private Units */
const char UsermodBME68X::_unitTemp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
const char UsermodBME68X::_unitHum[] PROGMEM = "%";
const char UsermodBME68X::_unitPress[] PROGMEM = "hPa";
const char UsermodBME68X::_unitGasres[] PROGMEM = "";
const char UsermodBME68X::_unitAHum[] PROGMEM = "g/m³";
const char UsermodBME68X::_unitDrewp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
const char UsermodBME68X::_unitIaq[] PROGMEM = " "; // No unit
const char UsermodBME68X::_unitStaticIaq[] PROGMEM = " "; // No unit
const char UsermodBME68X::_unitCo2[] PROGMEM = "ppm";
const char UsermodBME68X::_unitVoc[] PROGMEM = "ppm";
const char UsermodBME68X::_unitGasPer[] PROGMEM = "%";
const char UsermodBME68X::_unitNone[] PROGMEM = "";
/* Private Units */
const char UsermodBME68X::_unitTemp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
const char UsermodBME68X::_unitHum[] PROGMEM = "%";
const char UsermodBME68X::_unitPress[] PROGMEM = "hPa";
const char UsermodBME68X::_unitGasres[] PROGMEM = "";
const char UsermodBME68X::_unitAHum[] PROGMEM = "g/m³";
const char UsermodBME68X::_unitDrewp[] PROGMEM = " "; // NOTE - Is set with the selectable temperature unit
const char UsermodBME68X::_unitIaq[] PROGMEM = " "; // No unit
const char UsermodBME68X::_unitStaticIaq[] PROGMEM = " "; // No unit
const char UsermodBME68X::_unitCo2[] PROGMEM = "ppm";
const char UsermodBME68X::_unitVoc[] PROGMEM = "ppm";
const char UsermodBME68X::_unitGasPer[] PROGMEM = "%";
const char UsermodBME68X::_unitNone[] PROGMEM = "";
const char UsermodBME68X::_unitCelsius[] PROGMEM = "°C"; // Symbol for Celsius
const char UsermodBME68X::_unitFahrenheit[] PROGMEM = "°F"; // Symbol for Fahrenheit
const char UsermodBME68X::_unitCelsius[] PROGMEM = "°C"; // Symbol for Celsius
const char UsermodBME68X::_unitFahrenheit[] PROGMEM = "°F"; // Symbol for Fahrenheit
/* Load Sensor Settings */
const uint8_t UsermodBME68X::bsec_config_iaq[] = {
/* Load Sensor Settings */
const uint8_t UsermodBME68X::bsec_config_iaq[] = {
#include "config/generic_33v_3s_28d/bsec_iaq.txt" // Allow 28 days for calibration because the WLED module normally stays in the same place anyway
};
};
/************************************************************************************************************/
/********************************************* M A I N C O D E *********************************************/
/************************************************************************************************************/
/************************************************************************************************************/
/********************************************* M A I N C O D E *********************************************/
/************************************************************************************************************/
/**
/**
* @brief Called by WLED: Setup of the usermod
*/
void UsermodBME68X::setup() {
void UsermodBME68X::setup() {
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME ESC_FGCOLOR_CYAN "Initialize" ESC_STYLE_RESET));
/* Check, if i2c is activated */
if (i2c_scl < 0 || i2c_sda < 0) {
settings.enabled = false; // Disable usermod once i2c is not running
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "I2C is not activated. Please activate I2C first." FAIL));
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "I2C is not activated. Please activate I2C first." GOGAB_FAIL));
return;
}
@ -352,14 +349,14 @@ void UsermodBME68X::setup() {
loadState(); // Load the old calibration data
checkIaqSensorStatus(); // Check the sensor status
// HomeAssistantDiscovery();
DEBUG_PRINTLN(F(INFO_COLUMN DONE));
}
DEBUG_PRINTLN(F(INFO_COLUMN GOGAB_DONE));
}
/**
/**
* @brief Called by WLED: Main loop called by WLED
*
*/
void UsermodBME68X::loop() {
void UsermodBME68X::loop() {
if (!settings.enabled || strip.isUpdating() || !flags.InitSuccessful) return; // Leave if not enabled or string is updating or init failed
if (settings.pauseOnActiveWled && strip.getBrightness()) return; // Workarround Known Issue: handing led update - Leave once pause on activ wled is active and wled is active
@ -452,13 +449,13 @@ void UsermodBME68X::loop() {
if (flags.SaveState) saveState(); // Save if the save state flag is set
}
}
}
}
/**
/**
* @brief Retrieves the sensor data and truncates it to the requested decimal places
*
*/
void UsermodBME68X::getValues() {
void UsermodBME68X::getValues() {
/* Swap the point to the data structures */
swap = PrevValuesPtr;
PrevValuesPtr = ValuesPtr;
@ -500,27 +497,27 @@ void UsermodBME68X::getValues() {
ValuesPtr->gasPercAccuracy = iaqSensor.gasPercentageAccuracy;
ValuesPtr->stabStatus = iaqSensor.stabStatus;
ValuesPtr->runInStatus = iaqSensor.runInStatus;
}
}
/**
/**
* @brief Sends the current sensor data via MQTT
* @param topic Suptopic of the sensor as const char
* @param value Current sensor value as float
*/
void UsermodBME68X::MQTT_publish(const char* topic, const float& value, const int8_t& dig) {
void UsermodBME68X::MQTT_publish(const char* topic, const float& value, const int8_t& dig) {
if (dig<0) return;
if (WLED_MQTT_CONNECTED) {
snprintf_P(charbuffer, 127, PSTR("%s/%s"), mqttDeviceTopic, topic);
mqtt->publish(charbuffer, 0, false, String(value, dig).c_str());
}
}
}
/**
/**
* @brief Called by WLED: Initialize the MQTT parts when the connection to the MQTT server is established.
* @param bool Session Present
*/
void UsermodBME68X::onMqttConnect(bool sessionPresent) {
void UsermodBME68X::onMqttConnect(bool sessionPresent) {
DEBUG_PRINTLN(UMOD_DEBUG_NAME "OnMQTTConnect event fired");
HomeAssistantDiscovery();
@ -528,13 +525,13 @@ void UsermodBME68X::onMqttConnect(bool sessionPresent) {
flags.MqttInitialized=true;
DEBUG_PRINTLN(UMOD_DEBUG_NAME "MQTT first connect");
}
}
}
/**
/**
* @brief MQTT initialization to generate the mqtt topic strings. This initialization also creates the HomeAssistat device configuration (HA Discovery), which home assinstant automatically evaluates to create a device.
*/
void UsermodBME68X::HomeAssistantDiscovery() {
void UsermodBME68X::HomeAssistantDiscovery() {
if (!settings.HomeAssistantDiscovery || !flags.InitSuccessful || !settings.enabled) return; // Leave once HomeAssistant Discovery is inactive
DEBUG_PRINTLN(UMOD_DEBUG_NAME ESC_FGCOLOR_CYAN "Creating HomeAssistant Discovery Mqtt-Entrys" ESC_STYLE_RESET);
@ -564,10 +561,10 @@ void UsermodBME68X::HomeAssistantDiscovery() {
MQTT_PublishHASensor(_nameStabStatus, "", _unitNone, settings.publishSensorState - 1, 1);
MQTT_PublishHASensor(_nameRunInStatus, "", _unitNone, settings.publishSensorState - 1, 1);
DEBUG_PRINTLN(UMOD_DEBUG_NAME DONE);
}
DEBUG_PRINTLN(UMOD_DEBUG_NAME GOGAB_DONE);
}
/**
/**
* @brief These MQTT entries are responsible for the Home Assistant Discovery of the sensors. HA is shown here where to look for the sensor data. This entry therefore only needs to be sent once.
* Important note: In order to find everything that is sent from this device to Home Assistant via MQTT under the same device name, the "device/identifiers" entry must be the same.
* I use the MQTT device name here. If other user mods also use the HA Discovery, it is recommended to set the identifier the same. Otherwise you would have several devices,
@ -578,7 +575,7 @@ void UsermodBME68X::HomeAssistantDiscovery() {
* @param digs Number of decimal places
* @param option Set to true if the sensor is part of diagnostics (dafault 0)
*/
void UsermodBME68X::MQTT_PublishHASensor(const String& name, const String& deviceClass, const String& unitOfMeasurement, const int8_t& digs, const uint8_t& option) {
void UsermodBME68X::MQTT_PublishHASensor(const String& name, const String& deviceClass, const String& unitOfMeasurement, const int8_t& digs, const uint8_t& option) {
DEBUG_PRINT(UMOD_DEBUG_NAME "\t" + name);
snprintf_P(charbuffer, 127, PSTR("%s/%s"), mqttDeviceTopic, name.c_str()); // Current values will be posted here
@ -626,13 +623,13 @@ void UsermodBME68X::MQTT_PublishHASensor(const String& name, const String& devic
DEBUG_PRINTLN(INFO_COLUMN "published");
}
}
}
}
/**
/**
* @brief Called by WLED: Publish Sensor Information to Info Page
* @param JsonObject Pointer
*/
void UsermodBME68X::addToJsonInfo(JsonObject& root) {
void UsermodBME68X::addToJsonInfo(JsonObject& root) {
//DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "Add to info event"));
JsonObject user = root[F("u")];
@ -678,9 +675,9 @@ void UsermodBME68X::addToJsonInfo(JsonObject& root) {
InfoHelper(user, _nameRunInStatus, ValuesPtr->runInStatus, 0, " ");
}
}
}
}
/**
/**
* @brief Info Page helper function
* @param root JSON object
* @param name Name of the sensor as char
@ -688,36 +685,36 @@ void UsermodBME68X::addToJsonInfo(JsonObject& root) {
* @param decimals Decimal places of the value
* @param unit Unit of the sensor
*/
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const float& sensorvalue, const int8_t& decimals, const char* unit) {
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const float& sensorvalue, const int8_t& decimals, const char* unit) {
if (decimals > -1) {
JsonArray sub_json = root.createNestedArray(name);
sub_json.add(roundf(sensorvalue * powf(10, decimals)) / powf(10, decimals));
sub_json.add(unit);
}
}
}
/**
/**
* @brief Info Page helper function (overload)
* @param root JSON object
* @param name Name of the sensor
* @param sensorvalue Value of the sensor as string
* @param status Status of the value (active/inactive)
*/
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const String& sensorvalue, const bool& status) {
void UsermodBME68X::InfoHelper(JsonObject& root, const char* name, const String& sensorvalue, const bool& status) {
if (status) {
JsonArray sub_json = root.createNestedArray(name);
sub_json.add(sensorvalue);
}
}
}
/**
/**
* @brief Called by WLED: Adds the usermodul neends on the config page for user modules
* @param JsonObject Pointer
*
* @see Usermod::addToConfig()
* @see UsermodManager::addToConfig()
*/
void UsermodBME68X::addToConfig(JsonObject& root) {
void UsermodBME68X::addToConfig(JsonObject& root) {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Creating configuration pages content: "));
JsonObject top = root.createNestedObject(FPSTR(UMOD_NAME));
@ -750,15 +747,15 @@ void UsermodBME68X::addToConfig(JsonObject& root) {
sensors_json[FPSTR(_nameVoc)] = settings.decimals.Voc;
sensors_json[FPSTR(_nameGasPer)] = settings.decimals.gasPerc;
DEBUG_PRINTLN(F(OK));
}
DEBUG_PRINTLN(F(GOGAB_OK));
}
/**
/**
* @brief Called by WLED: Add dropdown and additional infos / structure
* @see Usermod::appendConfigData()
* @see UsermodManager::appendConfigData()
*/
void UsermodBME68X::appendConfigData() {
void UsermodBME68X::appendConfigData() {
// snprintf_P(charbuffer, 127, PSTR("addInfo('%s:%s',1,'read interval [seconds]');"), UMOD_NAME, _nameInterval); oappend(charbuffer);
// snprintf_P(charbuffer, 127, PSTR("addInfo('%s:%s',1,'only if value changes');"), UMOD_NAME, _namePublishChange); oappend(charbuffer);
// snprintf_P(charbuffer, 127, PSTR("addInfo('%s:%s',1,'maximum age of a message in seconds');"), UMOD_NAME, _nameMaxAge); oappend(charbuffer);
@ -782,9 +779,9 @@ void UsermodBME68X::appendConfigData() {
oappend(F("');"));
oappend(F("addOption(dd,'0x76',0x76);"));
oappend(F("addOption(dd,'0x77',0x77);"));
}
}
/**
/**
* @brief Called by WLED: Read Usermod Config Settings 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)
@ -797,7 +794,7 @@ void UsermodBME68X::appendConfigData() {
* @see Usermod::readFromConfig()
* @see UsermodManager::readFromConfig()
*/
bool UsermodBME68X::readFromConfig(JsonObject& root) {
bool UsermodBME68X::readFromConfig(JsonObject& root) {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Reading configuration: "));
JsonObject top = root[FPSTR(UMOD_NAME)];
@ -831,7 +828,7 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
configComplete &= getJsonValue(top["Sensors"][FPSTR(_nameVoc)], settings.decimals.Voc, 0 );
configComplete &= getJsonValue(top["Sensors"][FPSTR(_nameGasPer)], settings.decimals.gasPerc, 0 );
DEBUG_PRINTLN(F(OK));
DEBUG_PRINTLN(F(GOGAB_OK));
/* Set the selected temperature unit */
if (settings.tempScale) {
@ -845,10 +842,10 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Deleting Calibration File"));
flags.DeleteCaibration = false;
if (WLED_FS.remove(CALIB_FILE_NAME)) {
DEBUG_PRINTLN(F(OK));
DEBUG_PRINTLN(F(GOGAB_OK));
}
else {
DEBUG_PRINTLN(F(FAIL));
DEBUG_PRINTLN(F(GOGAB_FAIL));
}
}
@ -856,169 +853,169 @@ bool UsermodBME68X::readFromConfig(JsonObject& root) {
iaqSensor.setTemperatureOffset(settings.tempOffset); // Set Temp Offset
return configComplete;
}
}
/**
/**
* @brief Called by WLED: Retunrs the user modul id number
*
* @return uint16_t User module number
*/
uint16_t UsermodBME68X::getId() {
uint16_t UsermodBME68X::getId() {
return USERMOD_ID_BME68X;
}
}
/**
/**
* @brief Returns the current temperature in the scale which is choosen in settings
* @return Temperature value (°C or °F as choosen in settings)
*/
inline float UsermodBME68X::getTemperature() {
*/
inline float UsermodBME68X::getTemperature() {
return ValuesPtr->temperature;
}
}
/**
/**
* @brief Returns the current humidity
* @return Humididty value (%)
*/
inline float UsermodBME68X::getHumidity() {
*/
inline float UsermodBME68X::getHumidity() {
return ValuesPtr->humidity;
}
}
/**
/**
* @brief Returns the current pressure
* @return Pressure value (hPa)
*/
inline float UsermodBME68X::getPressure() {
*/
inline float UsermodBME68X::getPressure() {
return ValuesPtr->pressure;
}
}
/**
/**
* @brief Returns the current gas resistance
* @return Gas resistance value ()
*/
inline float UsermodBME68X::getGasResistance() {
*/
inline float UsermodBME68X::getGasResistance() {
return ValuesPtr->gasResistance;
}
}
/**
/**
* @brief Returns the current absolute humidity
* @return Absolute humidity value (g/m³)
*/
inline float UsermodBME68X::getAbsoluteHumidity() {
*/
inline float UsermodBME68X::getAbsoluteHumidity() {
return ValuesPtr->absHumidity;
}
}
/**
/**
* @brief Returns the current dew point
* @return Dew point (°C or °F as choosen in settings)
*/
inline float UsermodBME68X::getDewPoint() {
*/
inline float UsermodBME68X::getDewPoint() {
return ValuesPtr->drewPoint;
}
}
/**
/**
* @brief Returns the current iaq (Indoor Air Quallity)
* @return Iaq value (0-500)
*/
inline float UsermodBME68X::getIaq() {
*/
inline float UsermodBME68X::getIaq() {
return ValuesPtr->iaq;
}
}
/**
/**
* @brief Returns the current static iaq (Indoor Air Quallity) (NOTE: Static iaq is the better choice than iaq for fixed devices such as the wled module)
* @return Static iaq value (float)
*/
inline float UsermodBME68X::getStaticIaq() {
*/
inline float UsermodBME68X::getStaticIaq() {
return ValuesPtr->staticIaq;
}
}
/**
/**
* @brief Returns the current co2
* @return Co2 value (ppm)
*/
inline float UsermodBME68X::getCo2() {
*/
inline float UsermodBME68X::getCo2() {
return ValuesPtr->co2;
}
}
/**
/**
* @brief Returns the current voc (Breath VOC concentration estimate [ppm])
* @return Voc value (ppm)
*/
inline float UsermodBME68X::getVoc() {
*/
inline float UsermodBME68X::getVoc() {
return ValuesPtr->Voc;
}
}
/**
/**
* @brief Returns the current gas percentage
* @return Gas percentage value (%)
*/
inline float UsermodBME68X::getGasPerc() {
*/
inline float UsermodBME68X::getGasPerc() {
return ValuesPtr->gasPerc;
}
}
/**
/**
* @brief Returns the current iaq accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
* @return Iaq accuracy value (0-3)
*/
inline uint8_t UsermodBME68X::getIaqAccuracy() {
*/
inline uint8_t UsermodBME68X::getIaqAccuracy() {
return ValuesPtr->iaqAccuracy ;
}
}
/**
/**
* @brief Returns the current static iaq accuracy accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
* @return Static iaq accuracy value (0-3)
*/
inline uint8_t UsermodBME68X::getStaticIaqAccuracy() {
*/
inline uint8_t UsermodBME68X::getStaticIaqAccuracy() {
return ValuesPtr->staticIaqAccuracy;
}
}
/**
/**
* @brief Returns the current co2 accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
* @return Co2 accuracy value (0-3)
*/
inline uint8_t UsermodBME68X::getCo2Accuracy() {
*/
inline uint8_t UsermodBME68X::getCo2Accuracy() {
return ValuesPtr->co2Accuracy;
}
}
/**
/**
* @brief Returns the current voc accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
* @return Voc accuracy value (0-3)
*/
inline uint8_t UsermodBME68X::getVocAccuracy() {
*/
inline uint8_t UsermodBME68X::getVocAccuracy() {
return ValuesPtr->VocAccuracy;
}
}
/**
/**
* @brief Returns the current gas percentage accuracy (0 = not calibrated, 2 = being calibrated, 3 = calibrated)
* @return Gas percentage accuracy value (0-3)
*/
inline uint8_t UsermodBME68X::getGasPercAccuracy() {
*/
inline uint8_t UsermodBME68X::getGasPercAccuracy() {
return ValuesPtr->gasPercAccuracy;
}
}
/**
/**
* @brief Returns the current stab status.
* Indicates when the sensor is ready after after switch-on
* @return stab status value (0 = switched on / 1 = stabilized)
*/
inline bool UsermodBME68X::getStabStatus() {
*/
inline bool UsermodBME68X::getStabStatus() {
return ValuesPtr->stabStatus;
}
}
/**
/**
* @brief Returns the current run in status.
* Indicates if the sensor is undergoing initial stabilization during its first use after production
* @return Tun status accuracy value (0 = switched on first time / 1 = stabilized)
*/
inline bool UsermodBME68X::getRunInStatus() {
*/
inline bool UsermodBME68X::getRunInStatus() {
return ValuesPtr->runInStatus;
}
}
/**
/**
* @brief Checks whether the library and the sensor are running.
*/
void UsermodBME68X::checkIaqSensorStatus() {
void UsermodBME68X::checkIaqSensorStatus() {
if (iaqSensor.bsecStatus != BSEC_OK) {
InfoPageStatusLine = "BSEC Library ";
@ -1026,11 +1023,11 @@ void UsermodBME68X::checkIaqSensorStatus() {
flags.InitSuccessful = false;
if (iaqSensor.bsecStatus < BSEC_OK) {
InfoPageStatusLine += " Error Code : " + String(iaqSensor.bsecStatus);
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
InfoPageStatusLine += " Warning Code : " + String(iaqSensor.bsecStatus);
DEBUG_PRINTLN(WARN);
DEBUG_PRINTLN(GOGAB_WARN);
}
}
else {
@ -1041,57 +1038,57 @@ void UsermodBME68X::checkIaqSensorStatus() {
flags.InitSuccessful = false;
if (iaqSensor.bme68xStatus < BME68X_OK) {
InfoPageStatusLine += "error code: " + String(iaqSensor.bme68xStatus);
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
InfoPageStatusLine += "warning code: " + String(iaqSensor.bme68xStatus);
DEBUG_PRINTLN(WARN);
DEBUG_PRINTLN(GOGAB_WARN);
}
}
else {
InfoPageStatusLine += F("OK");
DEBUG_PRINTLN(OK);
DEBUG_PRINTLN(GOGAB_OK);
}
}
}
}
/**
/**
* @brief Loads the calibration data from the file system of the device
*/
void UsermodBME68X::loadState() {
void UsermodBME68X::loadState() {
if (WLED_FS.exists(CALIB_FILE_NAME)) {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Read the calibration file: "));
File file = WLED_FS.open(CALIB_FILE_NAME, FILE_READ);
if (!file) {
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
file.read(bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.close();
DEBUG_PRINTLN(OK);
DEBUG_PRINTLN(GOGAB_OK);
iaqSensor.setState(bsecState);
}
}
else {
DEBUG_PRINTLN(F(UMOD_DEBUG_NAME "Calibration file not found."));
}
}
}
/**
/**
* @brief Saves the calibration data from the file system of the device
*/
void UsermodBME68X::saveState() {
void UsermodBME68X::saveState() {
DEBUG_PRINT(F(UMOD_DEBUG_NAME "Write the calibration file "));
File file = WLED_FS.open(CALIB_FILE_NAME, FILE_WRITE);
if (!file) {
DEBUG_PRINTLN(FAIL);
DEBUG_PRINTLN(GOGAB_FAIL);
}
else {
iaqSensor.getState(bsecState);
file.write(bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.close();
stateUpdateCounter++;
DEBUG_PRINTF("(saved %d times)" OK "\n", stateUpdateCounter);
DEBUG_PRINTF("(saved %d times)" GOGAB_OK "\n", stateUpdateCounter);
flags.SaveState = false; // Clear save state flag
char contbuffer[30];
@ -1110,8 +1107,8 @@ void UsermodBME68X::saveState() {
snprintf_P(charbuffer, 127, PSTR("%s/%s"), mqttDeviceTopic, UMOD_NAME "/Calib Count");
if (WLED_MQTT_CONNECTED) mqtt->publish(charbuffer, 0, false, contbuffer);
}
}
}
static UsermodBME68X bme68x_v2;
REGISTER_USERMOD(bme68x_v2);
static UsermodBME68X bme68x_v2;
REGISTER_USERMOD(bme68x_v2);

View File

@ -1,53 +1,58 @@
# Usermod BME68X
This usermod was developed for a BME680/BME68X sensor. The BME68X is not compatible with the BME280/BMP280 chip. It has its own library. The original 'BSEC Software Library' from Bosch was used to develop the code. The measured values are displayed on the WLED info page.
<p align="center"><img src="pics/pic1.png" style="width:60%;"></p>
In addition, the values are published on MQTT if this is active. The topic used for this is: 'wled/[MQTT Client ID]'. The Client ID is set in the WLED MQTT settings.
<p align="center"><img src="pics/pic2.png"></p>
If you use HomeAssistance discovery, the device tree for HomeAssistance is created. This is published under the topic 'homeassistant/sensor/[MQTT Client ID]' via MQTT.
<p align="center"><img src="pics/pic3.png"></p>
A device with the following sensors appears in HomeAssistant. Please note that MQTT must be activated in HomeAssistant.
<p align="center"><img src="pics/pic4.png" style="width:60%;"></p>
## Features
Raw sensor types
Sensor Accuracy Scale Range
--------------------------------------------------------------------------------------------------
Temperature +/- 1.0 °C/°F -40 to 85 °C
Humidity +/- 3 % 0 to 100 %
Pressure +/- 1 hPa 300 to 1100 hPa
Gas Resistance Ohm
Sensor Accuracy Scale Range
-----------------------------
Temperature +/- 1.0 °C/°F -40 to 85 °C
Humidity +/- 3 % 0 to 100 %
Pressure +/- 1 hPa 300 to 1100 hPa
Gas Resistance Ohm
The BSEC Library calculates the following values via the gas resistance
Sensor Accuracy Scale Range
--------------------------------------------------------------------------------------------------
IAQ value between 0 and 500
Static IAQ same as IAQ but for permanently installed devices
CO2 PPM
VOC PPM
Gas-Percentage %
Sensor Accuracy Scale Range
-----------------------------
IAQ value between 0 and 500
Static IAQ same as IAQ but for permanently installed devices
CO2 PPM
VOC PPM
Gas-Percentage %
In addition the usermod calculates
Sensor Accuracy Scale Range
--------------------------------------------------------------------------------------------------
Absolute humidity g/m³
Dew point °C/°F
Sensor Accuracy Scale Range
-----------------------------
Absolute humidity g/m³
Dew point °C/°F
### IAQ (Indoor Air Quality)
The IAQ is divided into the following value groups.
<p align="center"><img src="pics/pic5.png"></p>
For more detailed information, please consult the enclosed Bosch product description (BME680.pdf).
## Calibration of the device
The gas sensor of the BME68X must be calibrated. This differs from the BME280, which does not require any calibration.
@ -67,10 +72,10 @@ The IAQ index is therefore only meaningful if IAQ Accuracy = 3. In addition to t
Reasonably reliable values are therefore only achieved when accuracy displays the value 3.
## Settings
The settings of the usermods are set in the usermod section of wled.
<p align="center"><img src="pics/pic6.png"></p>
The possible settings are
@ -88,6 +93,7 @@ The possible settings are
- **Del Calibration Hist:** If a check mark is set here, the calibration file saved in the file system is deleted when the settings are saved.
### Sensors
Applies to all sensors. The number of decimal places is set here. If the sensor is set to -1, it will no longer be published. In addition, the IAQ values can be activated here in verbal form.
It is recommended to use the Static IAQ for the IAQ values. This is recommended by Bosch for statically placed devices.
@ -99,6 +105,7 @@ Data is published over MQTT - make sure you've enabled the MQTT sync interface.
In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
Methods also exist to read the read/calculated values from other WLED modules through code.
- getTemperature(); The scale °C/°F is depended to the settings
- getHumidity();
- getPressure();
@ -118,15 +125,36 @@ Methods also exist to read the read/calculated values from other WLED modules th
- getStabStatus();
- getRunInStatus();
## Compilation
To enable, compile with `BME68X` in `custom_usermods` (e.g. in `platformio_override.ini`)
Example:
```[env:esp32_mySpecial]
extends = env:esp32dev
custom_usermods = ${env:esp32dev.custom_usermods} BME68X
```
## Revision History
### Version 1.0.0
- First version of the BME68X_v user module
### Version 1.0.1
- Rebased to WELD Version 0.15
- Reworked some default settings
- A problem with the default settings has been fixed
### Version 1.0.2
* Rebased to WELD Version 0.16
* Fixed: Solved compilation problems related to some macro naming interferences.
## Known problems
- MQTT goes online at device start. Shortly afterwards it goes offline and takes quite a while until it goes online again. The problem does not come from this user module, but from the WLED core.
- If you save the settings often, WLED can get stuck.
- If many LEDS are connected to WLED, reading the sensor can cause a small but noticeable hang. The "Pause While WLED Active" option was introduced as a workaround.

View File

@ -1,6 +1,5 @@
{
"name:": "BME68X_v2",
"build": { "libArchive": false},
"name:": "BME68X",
"dependencies": {
"boschsensortec/BSEC Software Library":"^1.8.1492"
}