From e12f7b67e53edb87e167c3a3a3f000325e5cf91c Mon Sep 17 00:00:00 2001 From: albarlow Date: Tue, 19 Jul 2022 14:27:15 +0100 Subject: [PATCH 1/7] Usermod BME280 Enhancements I added a Usermod interface for key settings. I used a PinArray for the SDA/SCL pins, but you can't name these individually. I have also made a display to show the temperature/humidity values in the web interface's Info screen. I had to change the definition of those items in order to allow these new functions to work. I have not noticed any negative side effects to this change. At the moment, I've not figured out how to make Celsius/Farenheit toggleable due to the way the #define setup works. Finally, I have added a routine to publish MQTT Discovery Topics for Home Assistant (toggleable in the Usermod screen). I've been testing this on the only suitable device I have for a few months and haven't noticed any problems. --- usermods/BME280_v2/usermod_bme280.h | 183 ++++++++++++++++++++++++---- 1 file changed, 156 insertions(+), 27 deletions(-) diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 82eb0887..4f3853bb 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -1,3 +1,6 @@ +// force the compiler to show a warning to confirm that this file is included +#warning **** Included USERMOD_BME280 version 2.0 **** + #pragma once #include "wled.h" @@ -11,32 +14,13 @@ class UsermodBME280 : public Usermod private: // User-defined configuration #define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit -#define TemperatureDecimals 1 // Number of decimal places in published temperaure values -#define HumidityDecimals 2 // Number of decimal places in published humidity values -#define PressureDecimals 2 // Number of decimal places in published pressure values -#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds -#define PressureInterval 300 // Interval to measure pressure in seconds -#define PublishAlways 0 // Publish values even when they have not changed - -// Sanity checks -#if !defined(TemperatureDecimals) || TemperatureDecimals < 0 - #define TemperatureDecimals 0 -#endif -#if !defined(HumidityDecimals) || HumidityDecimals < 0 - #define HumidityDecimals 0 -#endif -#if !defined(PressureDecimals) || PressureDecimals < 0 - #define PressureDecimals 0 -#endif -#if !defined(TemperatureInterval) || TemperatureInterval < 0 - #define TemperatureInterval 1 -#endif -#if !defined(PressureInterval) || PressureInterval < 0 - #define PressureInterval TemperatureInterval -#endif -#if !defined(PublishAlways) - #define PublishAlways 0 -#endif +unsigned long TemperatureDecimals = 0; // Number of decimal places in published temperaure values +unsigned long HumidityDecimals = 0; // Number of decimal places in published humidity values +unsigned long PressureDecimals = 0; // Number of decimal places in published pressure values +unsigned long TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds +unsigned long PressureInterval = 300; // Interval to measure pressure in seconds +bool PublishAlways = false; // Publish values even when they have not changed +bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information #ifdef ARDUINO_ARCH_ESP32 // ESP32 boards uint8_t SCL_PIN = 22; @@ -82,6 +66,14 @@ private: float lastDewPoint; float lastPressure; + // MQTT topic strings for publishing Home Assistant discovery topics + bool mqttInitialized = false; + String mqttTemperatureTopic = ""; + String mqttHumidityTopic = ""; + String mqttPressureTopic = ""; + String mqttHeatIndexTopic = ""; + String mqttDewPointTopic = ""; + // Store packet IDs of MQTT publications uint16_t mqttTemperaturePub = 0; uint16_t mqttPressurePub = 0; @@ -110,6 +102,55 @@ private: } } + // Procedure to define all MQTT Topics + void _mqttInitialize() + { + mqttTemperatureTopic = String(mqttDeviceTopic) + "/temperature"; + mqttPressureTopic = String(mqttDeviceTopic) + "/pressure"; + mqttHumidityTopic = String(mqttDeviceTopic) + "/humidity"; + mqttHeatIndexTopic = String(mqttDeviceTopic) + "/heat_index"; + mqttDewPointTopic = String(mqttDeviceTopic) + "/dew_point"; + + String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config"; + + _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", "°C"); + _createMqttSensor("Pressure", mqttPressureTopic, "pressure", "hPa"); + _createMqttSensor("Humidity", mqttHumidityTopic, "humidity", "%"); + _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", "°C"); + _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", "°C"); + } + + // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. + void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement) + { + String t = String("homeassistant/sensor/") + mqttClientID + "/" + name + "/config"; + + StaticJsonDocument<600> doc; + + doc["name"] = String(serverDescription) + " " + name; + doc["state_topic"] = topic; + doc["unique_id"] = String(mqttClientID) + name; + if (unitOfMeasurement != "") + doc["unit_of_measurement"] = unitOfMeasurement; + if (deviceClass != "") + doc["device_class"] = deviceClass; + doc["expire_after"] = 1800; + + JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device + device["name"] = serverDescription; + device["identifiers"] = "wled-sensor-" + String(mqttClientID); + device["manufacturer"] = "WLED"; + device["model"] = "FOSS"; + device["sw_version"] = versionString; + + String temp; + serializeJson(doc, temp); + Serial.println(t); + Serial.println(temp); + + mqtt->publish(t.c_str(), 0, true, temp.c_str()); + } + public: void setup() { @@ -143,7 +184,7 @@ public: { // BME280 sensor MQTT publishing // Check if sensor present and MQTT Connected, otherwise it will crash the MCU - if (sensorType != 0 && mqtt != nullptr) + if (sensorType != 0 && WLED_MQTT_CONNECTED) { // Timer to fetch new temperature, humidity and pressure data at intervals timer = millis(); @@ -157,6 +198,12 @@ public: float temperature = roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); float humidity, heatIndex, dewPoint; + if (WLED_MQTT_CONNECTED && !mqttInitialized && HomeAssistantDiscovery) + { + _mqttInitialize(); + mqttInitialized = true; + } + // If temperature has changed since last measure, create string populated with device topic // from the UI and values read from sensor, then publish to broker if (temperature != lastTemperature || PublishAlways) @@ -213,4 +260,86 @@ public: } } } + + // Publish Sensor Information to Info Page + void addToJsonInfo(JsonObject &root) + { + JsonObject user = root[F("u")]; + if (user.isNull()) + user = root.createNestedObject(F("u")); + + if (sensorType==0) //No Sensor + { + // if we sensor not detected, let the user know + JsonArray temperature_json = user.createNestedArray("BME/BMP280 Sensor"); + temperature_json.add(F("Not Found")); + } + else if (sensorType==2) //BMP280 + { + + JsonArray temperature_json = user.createNestedArray("Temperature"); + JsonArray pressure_json = user.createNestedArray("Pressure"); + temperature_json.add(sensorTemperature); + temperature_json.add(F("°C")); + pressure_json.add(sensorPressure); + pressure_json.add(F("°C")); + } + else if (sensorType==1) //BME280 + { + JsonArray temperature_json = user.createNestedArray("Temperature"); + JsonArray humidity_json = user.createNestedArray("Humidity"); + JsonArray pressure_json = user.createNestedArray("Pressure"); + JsonArray heatindex_json = user.createNestedArray("Heat Index"); + JsonArray dewpoint_json = user.createNestedArray("Dew Point"); + temperature_json.add(sensorTemperature); + temperature_json.add(F("°C")); + humidity_json.add(sensorHumidity); + humidity_json.add(F("%")); + pressure_json.add(sensorPressure); + pressure_json.add(F("°C")); + heatindex_json.add(sensorHeatIndex); + heatindex_json.add(F("°C")); + dewpoint_json.add(sensorDewPoint); + dewpoint_json.add(F("°C")); + } + return; + } + + // Save Usermod Config Settings + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject("BME280/BMP280"); + top["TemperatureDecimals"] = TemperatureDecimals; + top["HumidityDecimals"] = HumidityDecimals; + top["PressureDecimals"] = PressureDecimals; + top["TemperatureInterval"] = TemperatureInterval; + top["PublishAlways"] = PublishAlways; + top["HomeAssistantDiscovery"] = HomeAssistantDiscovery; + JsonArray pinArray = top.createNestedArray("pin-sda-scl"); + pinArray.add(SDA_PIN); + pinArray.add(SCL_PIN); + } + + // Read Usermod Config Settings + bool readFromConfig(JsonObject& root) + { + // 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["BME280/BMP280"]; + + bool configComplete = !top.isNull(); + + // A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing + configComplete &= getJsonValue(top["TemperatureDecimals"], TemperatureDecimals, 1); + configComplete &= getJsonValue(top["HumidityDecimals"], HumidityDecimals, 0); + configComplete &= getJsonValue(top["PressureDecimals"], PressureDecimals, 0); + configComplete &= getJsonValue(top["TemperatureInterval"], TemperatureInterval, 30); + configComplete &= getJsonValue(top["PublishAlways"], PublishAlways, false); + configComplete &= getJsonValue(top["HomeAssistantDiscovery"], HomeAssistantDiscovery, false); + configComplete &= getJsonValue(top["pin-sda-scl"][0], SDA_PIN, 21); //SDA + configComplete &= getJsonValue(top["pin-sda-scl"][1], SCL_PIN, 22); //SCL + + return configComplete; + } }; \ No newline at end of file From bee48dae7e57ea24dc590c4ddf8aeb893d5214a8 Mon Sep 17 00:00:00 2001 From: albarlow Date: Tue, 19 Jul 2022 14:37:05 +0100 Subject: [PATCH 2/7] Update readme --- usermods/BME280_v2/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md index 216ca630..253ae70c 100644 --- a/usermods/BME280_v2/README.md +++ b/usermods/BME280_v2/README.md @@ -15,6 +15,8 @@ or define `USERMOD_BME280` in `my_config.h` #define USERMOD_BME280 ``` +If you are using Home Assistant, there is an option to create a discoverable device with sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index. This can be disabled if required. + Changes include: - Adjustable measure intervals - Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude From 9d574397bcc4d062e1a7f20b7c94c89c23ba9fbf Mon Sep 17 00:00:00 2001 From: albarlow Date: Tue, 19 Jul 2022 21:47:56 +0100 Subject: [PATCH 3/7] usermod bme280 Added public variables to the BME280 usermod based on those in the Temperature usermod. Only complication is that this usermod utilises different function calls depending on whether user defines celsius or not. I have handled this for the temperature, but the Dew Point and Heat Index are relative to the temperature. I've also addressed some areas where I'd previously assumed Celsius for reporting purposes as my test case is using Farenheit. --- usermods/BME280_v2/usermod_bme280.h | 71 +++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 4f3853bb..353e451b 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -113,11 +113,23 @@ bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Inform String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config"; - _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", "°C"); + #ifdef Celsius + _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", "°C"); + #else + _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", "°F"); + #endif _createMqttSensor("Pressure", mqttPressureTopic, "pressure", "hPa"); _createMqttSensor("Humidity", mqttHumidityTopic, "humidity", "%"); - _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", "°C"); - _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", "°C"); + #ifdef Celsius + _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", "°C"); + #else + _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", "°F"); + #endif + #ifdef Celsius + _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", "°C"); + #else + _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", "°F"); + #endif } // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. @@ -260,13 +272,43 @@ public: } } } + + /* + * API calls te enable data exchange between WLED modules + */ + inline float getTemperatureC() { + #ifdef Celsius + return (float)sensorTemperature; + #else + return (float)sensorTemperature * 1.8f + 32; + #endif + + } + inline float getTemperatureF() { + #ifdef Celsius + return ((float)sensorTemperature -32) * 0.56f; + #else + return (float)sensorTemperature; + #endif + } + inline float getHumidity() { + return (float)sensorHumidity; + } + inline float getPressure() { + return (float)sensorPressure; + } + inline float getDewPoint() { + return (float)sensorDewPoint; + } + inline float getHeatIndex() { + return (float)sensorHeatIndex; + } // Publish Sensor Information to Info Page void addToJsonInfo(JsonObject &root) { JsonObject user = root[F("u")]; - if (user.isNull()) - user = root.createNestedObject(F("u")); + if (user.isNull()) user = root.createNestedObject(F("u")); if (sensorType==0) //No Sensor { @@ -292,14 +334,27 @@ public: JsonArray heatindex_json = user.createNestedArray("Heat Index"); JsonArray dewpoint_json = user.createNestedArray("Dew Point"); temperature_json.add(sensorTemperature); - temperature_json.add(F("°C")); + #ifdef Celsius + temperature_json.add(F("°C")); + #else + temperature_json.add(F("°F")); + #endif humidity_json.add(sensorHumidity); humidity_json.add(F("%")); pressure_json.add(sensorPressure); - pressure_json.add(F("°C")); + pressure_json.add(F("hPa")); heatindex_json.add(sensorHeatIndex); - heatindex_json.add(F("°C")); + #ifdef Celsius + heatindex_json.add(F("°C")); + #else + heatindex_json.add(F("°F")); + #endif dewpoint_json.add(sensorDewPoint); + #ifdef Celsius + dewpoint_json.add(F("°C")); + #else + dewpoint_json.add(F("°F")); + #endif dewpoint_json.add(F("°C")); } return; From 866296fefd1f62e82343c6c4628c03f1efa43cbd Mon Sep 17 00:00:00 2001 From: albarlow Date: Tue, 19 Jul 2022 22:15:26 +0100 Subject: [PATCH 4/7] Update usermod bme280 rounding Apply rounding per usermod settings --- usermods/BME280_v2/usermod_bme280.h | 33 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 353e451b..dcf74893 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -278,30 +278,30 @@ public: */ inline float getTemperatureC() { #ifdef Celsius - return (float)sensorTemperature; + return (float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); #else - return (float)sensorTemperature * 1.8f + 32; + return (float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) * 1.8f + 32; #endif } inline float getTemperatureF() { #ifdef Celsius - return ((float)sensorTemperature -32) * 0.56f; + return ((float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) -32) * 0.56f; #else - return (float)sensorTemperature; + return (float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); #endif } inline float getHumidity() { - return (float)sensorHumidity; + return (float)roundf(sensorHumidity * pow(10, HumidityDecimals)); } inline float getPressure() { - return (float)sensorPressure; + return (float)roundf(sensorPressure * pow(10, PressureDecimals)); } inline float getDewPoint() { - return (float)sensorDewPoint; + return (float)roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); } inline float getHeatIndex() { - return (float)sensorHeatIndex; + return (float)roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); } // Publish Sensor Information to Info Page @@ -321,10 +321,10 @@ public: JsonArray temperature_json = user.createNestedArray("Temperature"); JsonArray pressure_json = user.createNestedArray("Pressure"); - temperature_json.add(sensorTemperature); + temperature_json.add(roundf(sensorTemperature * pow(10, TemperatureDecimals))); temperature_json.add(F("°C")); - pressure_json.add(sensorPressure); - pressure_json.add(F("°C")); + pressure_json.add(roundf(sensorPressure * pow(10, PressureDecimals))); + pressure_json.add(F("hPa")); } else if (sensorType==1) //BME280 { @@ -333,29 +333,28 @@ public: JsonArray pressure_json = user.createNestedArray("Pressure"); JsonArray heatindex_json = user.createNestedArray("Heat Index"); JsonArray dewpoint_json = user.createNestedArray("Dew Point"); - temperature_json.add(sensorTemperature); + temperature_json.add(roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals)); #ifdef Celsius temperature_json.add(F("°C")); #else temperature_json.add(F("°F")); #endif - humidity_json.add(sensorHumidity); + humidity_json.add(roundf(sensorHumidity * pow(10, HumidityDecimals))); humidity_json.add(F("%")); - pressure_json.add(sensorPressure); + pressure_json.add(roundf(sensorPressure * pow(10, PressureDecimals))); pressure_json.add(F("hPa")); - heatindex_json.add(sensorHeatIndex); + heatindex_json.add(roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals)); #ifdef Celsius heatindex_json.add(F("°C")); #else heatindex_json.add(F("°F")); #endif - dewpoint_json.add(sensorDewPoint); + dewpoint_json.add(roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals)); #ifdef Celsius dewpoint_json.add(F("°C")); #else dewpoint_json.add(F("°F")); #endif - dewpoint_json.add(F("°C")); } return; } From 22ac12dc3664b480aa7733f9896a059fa970475c Mon Sep 17 00:00:00 2001 From: albarlow Date: Wed, 20 Jul 2022 13:06:27 +0100 Subject: [PATCH 5/7] Remove compile-time variables and revamp readme.md --- usermods/BME280_v2/README.md | 76 +++++++----- usermods/BME280_v2/usermod_bme280.h | 174 ++++++++++++++++------------ 2 files changed, 149 insertions(+), 101 deletions(-) diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md index 253ae70c..3d41c54b 100644 --- a/usermods/BME280_v2/README.md +++ b/usermods/BME280_v2/README.md @@ -1,42 +1,66 @@ -Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield. +# Usermod BME280 +This Usermod is designed to read a `BME280` or `BMP280` sensor and output the following: +- Temperature +- Humidity (`BME280` only) +- Pressure +- Heat Index (`BME280` only) +- Dew Point (`BME280` only) -- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`). -- Data is published over MQTT so make sure you've enabled the MQTT sync interface. +Configuration is all completed via the Usermod menu. There are no settings to set in code! The following settings can be configured in the Usermod Menu (remember to reboot for any changes made here to take effect): +- Temperature Decimals (number of decimal places to output) +- Humidity Decimals +- Pressure Decimals +- Temperature Interval (how many seconds between reads of temperature and humidity) +- Pressure Interval +- Publish Always (turn off to only publish changes, on to publish whether or not value changed) +- Use Celsius (turn off to use Farenheit) +- Home Assistant Discovery (turn on to sent MQTT Discovery entries for Home Assistant) +- SDA/SCL GPIO Pins + +Dependencies +- Libraries + - `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) + - `Wire` + - These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`). +- Data is published over MQTT - make sure you've enabled the MQTT sync interface. - This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages! -To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`) +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. +- `getTemperatureC()` +- `getTemperatureF()` +- `getHumidity()` +- `getPressure()` +- `getDewPointC()` +- `getDewPointF()` +- `getHeatIndexC()` +- `getHeatIndexF()` + +# Complilation + +To enable, compile with `USERMOD_BME280` defined (e.g. in `platformio_override.ini`) ```ini +[env:usermod_bme280_d1_mini] +extends = env:d1_mini build_flags = ${common.build_flags_esp8266} -D USERMOD_BME280 -``` -or define `USERMOD_BME280` in `my_config.h` -```c++ -#define USERMOD_BME280 +lib_deps = + ${esp8266.lib_deps} + BME280@~3.0.0 + Wire ``` -If you are using Home Assistant, there is an option to create a discoverable device with sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index. This can be disabled if required. -Changes include: -- Adjustable measure intervals - - Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude -- Adjustment of number of decimal places in published sensor values - - Separate adjustment for temperature, humidity and pressure values - - Values are rounded to the specified number of decimal places -- Pressure measured in units of hPa instead of Pa -- Calculation of heat index (apparent temperature) and dew point - - These, along with humidity measurements, are disabled if the sensor is a BMP280 -- 16x oversampling of sensor during measurement -- Values are only published if they are different from the previous value -- Values are published on startup (continually until the MQTT broker acknowledges a successful publication) - -Adjustments are made through preprocessor definitions at the start of the class definition. - -MQTT topics are as follows: +# MQTT +MQTT topics are as follows (`` is set in MQTT section of Sync Setup menu): Measurement type | MQTT topic --- | --- Temperature | `/temperature` Humidity | `/humidity` Pressure | `/pressure` Heat index | `/heat_index` -Dew point | `/dew_point` \ No newline at end of file +Dew point | `/dew_point` + +If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is seperate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index. \ No newline at end of file diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index dcf74893..399cc274 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -12,24 +12,28 @@ class UsermodBME280 : public Usermod { private: -// User-defined configuration -#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit -unsigned long TemperatureDecimals = 0; // Number of decimal places in published temperaure values -unsigned long HumidityDecimals = 0; // Number of decimal places in published humidity values -unsigned long PressureDecimals = 0; // Number of decimal places in published pressure values -unsigned long TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds -unsigned long PressureInterval = 300; // Interval to measure pressure in seconds -bool PublishAlways = false; // Publish values even when they have not changed -bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information + + // NOTE: Do not implement any compile-time variables, anything the user needs to configure + // should be configurable from the Usermod menu using the methods below + // key settings set via usermod menu + unsigned long TemperatureDecimals = 0; // Number of decimal places in published temperaure values + unsigned long HumidityDecimals = 0; // Number of decimal places in published humidity values + unsigned long PressureDecimals = 0; // Number of decimal places in published pressure values + unsigned long TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds + unsigned long PressureInterval = 300; // Interval to measure pressure in seconds + bool PublishAlways = false; // Publish values even when they have not changed + bool UseCelsius = true; // Use Celsius for Reporting + bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information -#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards - uint8_t SCL_PIN = 22; - uint8_t SDA_PIN = 21; -#else // ESP8266 boards - uint8_t SCL_PIN = 5; - uint8_t SDA_PIN = 4; - //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 -#endif + // set the default pins based on the architecture, these get overridden by Usermod menu settings + #ifdef ARDUINO_ARCH_ESP32 // ESP32 boards + uint8_t SCL_PIN = 22; + uint8_t SDA_PIN = 21; + #else // ESP8266 boards + uint8_t SCL_PIN = 5; + uint8_t SDA_PIN = 4; + //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 + #endif // BME280 sensor settings BME280I2C::Settings settings{ @@ -59,6 +63,7 @@ bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Inform float sensorHeatIndex; float sensorDewPoint; float sensorPressure; + String tempScale; // Track previous sensor values float lastTemperature; float lastHumidity; @@ -78,31 +83,47 @@ bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Inform uint16_t mqttTemperaturePub = 0; uint16_t mqttPressurePub = 0; + // Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Farenheit being set in Usermod Menu) void UpdateBME280Data(int SensorType) { float _temperature, _humidity, _pressure; - #ifdef Celsius + + if (UseCelsius) { BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius); - #else + BME280::PresUnit presUnit(BME280::PresUnit_hPa); + + bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit); + + sensorTemperature = _temperature; + sensorHumidity = _humidity; + sensorPressure = _pressure; + tempScale = "°C"; + if (sensorType == 1) + { + sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit); + sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit); + } + } else { BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit); - #endif - BME280::PresUnit presUnit(BME280::PresUnit_hPa); + BME280::PresUnit presUnit(BME280::PresUnit_hPa); - bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit); + bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit); - sensorTemperature = _temperature; - sensorHumidity = _humidity; - sensorPressure = _pressure; - if (sensorType == 1) - { - sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit); - sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit); + sensorTemperature = _temperature; + sensorHumidity = _humidity; + sensorPressure = _pressure; + tempScale = "°F"; + if (sensorType == 1) + { + sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit); + sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit); + } } } - // Procedure to define all MQTT Topics + // Procedure to define all MQTT discovery Topics void _mqttInitialize() { mqttTemperatureTopic = String(mqttDeviceTopic) + "/temperature"; @@ -111,25 +132,15 @@ bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Inform mqttHeatIndexTopic = String(mqttDeviceTopic) + "/heat_index"; mqttDewPointTopic = String(mqttDeviceTopic) + "/dew_point"; - String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config"; + if (HomeAssistantDiscovery) { + String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config"; - #ifdef Celsius - _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", "°C"); - #else - _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", "°F"); - #endif - _createMqttSensor("Pressure", mqttPressureTopic, "pressure", "hPa"); - _createMqttSensor("Humidity", mqttHumidityTopic, "humidity", "%"); - #ifdef Celsius - _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", "°C"); - #else - _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", "°F"); - #endif - #ifdef Celsius - _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", "°C"); - #else - _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", "°F"); - #endif + _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", tempScale); + _createMqttSensor("Pressure", mqttPressureTopic, "pressure", "hPa"); + _createMqttSensor("Humidity", mqttHumidityTopic, "humidity", "%"); + _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", tempScale); + _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", tempScale); + } } // Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop. @@ -210,7 +221,7 @@ public: float temperature = roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); float humidity, heatIndex, dewPoint; - if (WLED_MQTT_CONNECTED && !mqttInitialized && HomeAssistantDiscovery) + if (WLED_MQTT_CONNECTED && !mqttInitialized) { _mqttInitialize(); mqttInitialized = true; @@ -277,19 +288,19 @@ public: * API calls te enable data exchange between WLED modules */ inline float getTemperatureC() { - #ifdef Celsius + if (UseCelsius) { return (float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); - #else + } else { return (float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) * 1.8f + 32; - #endif + } } inline float getTemperatureF() { - #ifdef Celsius + if (UseCelsius) { return ((float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) -32) * 0.56f; - #else + } else { return (float)roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); - #endif + } } inline float getHumidity() { return (float)roundf(sensorHumidity * pow(10, HumidityDecimals)); @@ -297,11 +308,32 @@ public: inline float getPressure() { return (float)roundf(sensorPressure * pow(10, PressureDecimals)); } - inline float getDewPoint() { - return (float)roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + inline float getDewPointC() { + if (UseCelsius) { + return (float)roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + } else { + return (float)roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) * 1.8f + 32; + } } - inline float getHeatIndex() { - return (float)roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + inline float getDewPointF() { + if (UseCelsius) { + return ((float)roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) -32) * 0.56f; + } else { + return (float)roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + } + } + inline float getHeatIndexC() { + if (UseCelsius) { + return (float)roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + } else { + return (float)roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) * 1.8f + 32; + } + }inline float getHeatIndexF() { + if (UseCelsius) { + return ((float)roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals) -32) * 0.56f; + } else { + return (float)roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + } } // Publish Sensor Information to Info Page @@ -322,7 +354,7 @@ public: JsonArray temperature_json = user.createNestedArray("Temperature"); JsonArray pressure_json = user.createNestedArray("Pressure"); temperature_json.add(roundf(sensorTemperature * pow(10, TemperatureDecimals))); - temperature_json.add(F("°C")); + temperature_json.add(tempScale); pressure_json.add(roundf(sensorPressure * pow(10, PressureDecimals))); pressure_json.add(F("hPa")); } @@ -334,27 +366,15 @@ public: JsonArray heatindex_json = user.createNestedArray("Heat Index"); JsonArray dewpoint_json = user.createNestedArray("Dew Point"); temperature_json.add(roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals)); - #ifdef Celsius - temperature_json.add(F("°C")); - #else - temperature_json.add(F("°F")); - #endif + temperature_json.add(tempScale); humidity_json.add(roundf(sensorHumidity * pow(10, HumidityDecimals))); humidity_json.add(F("%")); pressure_json.add(roundf(sensorPressure * pow(10, PressureDecimals))); pressure_json.add(F("hPa")); heatindex_json.add(roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals)); - #ifdef Celsius - heatindex_json.add(F("°C")); - #else - heatindex_json.add(F("°F")); - #endif + heatindex_json.add(tempScale); dewpoint_json.add(roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals)); - #ifdef Celsius - dewpoint_json.add(F("°C")); - #else - dewpoint_json.add(F("°F")); - #endif + dewpoint_json.add(tempScale); } return; } @@ -367,7 +387,9 @@ public: top["HumidityDecimals"] = HumidityDecimals; top["PressureDecimals"] = PressureDecimals; top["TemperatureInterval"] = TemperatureInterval; + top["PressureInterval"] = PressureInterval; top["PublishAlways"] = PublishAlways; + top["UseCelsius"] = UseCelsius; top["HomeAssistantDiscovery"] = HomeAssistantDiscovery; JsonArray pinArray = top.createNestedArray("pin-sda-scl"); pinArray.add(SDA_PIN); @@ -389,7 +411,9 @@ public: configComplete &= getJsonValue(top["HumidityDecimals"], HumidityDecimals, 0); configComplete &= getJsonValue(top["PressureDecimals"], PressureDecimals, 0); configComplete &= getJsonValue(top["TemperatureInterval"], TemperatureInterval, 30); + configComplete &= getJsonValue(top["PressureInterval"], PressureInterval, 30); configComplete &= getJsonValue(top["PublishAlways"], PublishAlways, false); + configComplete &= getJsonValue(top["UseCelsius"], UseCelsius, true); configComplete &= getJsonValue(top["HomeAssistantDiscovery"], HomeAssistantDiscovery, false); configComplete &= getJsonValue(top["pin-sda-scl"][0], SDA_PIN, 21); //SDA configComplete &= getJsonValue(top["pin-sda-scl"][1], SCL_PIN, 22); //SCL From 7308f5993c788842db4d5aa2b34aa92b5a0e52a9 Mon Sep 17 00:00:00 2001 From: albarlow Date: Wed, 20 Jul 2022 22:53:45 +0100 Subject: [PATCH 6/7] Added changelog --- usermods/BME280_v2/README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md index 3d41c54b..395c2039 100644 --- a/usermods/BME280_v2/README.md +++ b/usermods/BME280_v2/README.md @@ -63,4 +63,25 @@ Pressure | `/pressure` Heat index | `/heat_index` Dew point | `/dew_point` -If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is seperate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index. \ No newline at end of file +If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is seperate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index. + +# Revision History +Jul 2022 +- Added Home Assistant Discovery +- Added API interface to output data +- Removed compile-time variables +- Added usermod menu interface +- Added value outputs to info screen +- Updated `readme.md` + +Apr 2021 +- Added `Publish Always` option + +Dec 2020 +- Ported to V2 Usermod format +- Customisable `measure intervals` +- Customisable number of `decimal places` in published sensor values +- Pressure measured in units of hPa instead of Pa +- Calculation of heat index (apparent temperature) and dew point +- `16x oversampling` of sensor during measurement +- Values only published if they are different from the previous value \ No newline at end of file From c96f83b0766efb47ae57ef56fe85432bcab2e786 Mon Sep 17 00:00:00 2001 From: albarlow Date: Thu, 21 Jul 2022 09:48:37 +0100 Subject: [PATCH 7/7] Registered Usermod and Pins Implemented PinManager and rerun setup of usermod after updating pins/ Registered in const.h and pin_manager.h I tried to follow the existing formatting/numbering in these files. Wrapped any strings I could in F() --- usermods/BME280_v2/README.md | 7 +- usermods/BME280_v2/usermod_bme280.h | 143 ++++++++++++++++++---------- wled00/const.h | 1 + wled00/pin_manager.h | 3 +- 4 files changed, 99 insertions(+), 55 deletions(-) diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md index 395c2039..b8718e0e 100644 --- a/usermods/BME280_v2/README.md +++ b/usermods/BME280_v2/README.md @@ -6,7 +6,7 @@ This Usermod is designed to read a `BME280` or `BMP280` sensor and output the fo - Heat Index (`BME280` only) - Dew Point (`BME280` only) -Configuration is all completed via the Usermod menu. There are no settings to set in code! The following settings can be configured in the Usermod Menu (remember to reboot for any changes made here to take effect): +Configuration is all completed via the Usermod menu. There are no settings to set in code! The following settings can be configured in the Usermod Menu: - Temperature Decimals (number of decimal places to output) - Humidity Decimals - Pressure Decimals @@ -15,7 +15,7 @@ Configuration is all completed via the Usermod menu. There are no settings to s - Publish Always (turn off to only publish changes, on to publish whether or not value changed) - Use Celsius (turn off to use Farenheit) - Home Assistant Discovery (turn on to sent MQTT Discovery entries for Home Assistant) -- SDA/SCL GPIO Pins +- SCL/SDA GPIO Pins Dependencies - Libraries @@ -73,6 +73,9 @@ Jul 2022 - Added usermod menu interface - Added value outputs to info screen - Updated `readme.md` +- Registered usermod +- Implemented PinManager for usermod +- Implemented reallocation of pins without reboot Apr 2021 - Added `Publish Always` option diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h index 399cc274..912dce8a 100644 --- a/usermods/BME280_v2/usermod_bme280.h +++ b/usermods/BME280_v2/usermod_bme280.h @@ -27,13 +27,15 @@ private: // set the default pins based on the architecture, these get overridden by Usermod menu settings #ifdef ARDUINO_ARCH_ESP32 // ESP32 boards - uint8_t SCL_PIN = 22; - uint8_t SDA_PIN = 21; + #define HW_PIN_SCL 22 + #define HW_PIN_SDA 21 #else // ESP8266 boards - uint8_t SCL_PIN = 5; - uint8_t SDA_PIN = 4; + #define HW_PIN_SCL 5 + #define HW_PIN_SDA 4 //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8 #endif + int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup() + bool initDone = false; // BME280 sensor settings BME280I2C::Settings settings{ @@ -133,13 +135,11 @@ private: mqttDewPointTopic = String(mqttDeviceTopic) + "/dew_point"; if (HomeAssistantDiscovery) { - String t = String("homeassistant/sensor/") + mqttClientID + "/temperature/config"; - - _createMqttSensor("Temperature", mqttTemperatureTopic, "temperature", tempScale); - _createMqttSensor("Pressure", mqttPressureTopic, "pressure", "hPa"); - _createMqttSensor("Humidity", mqttHumidityTopic, "humidity", "%"); - _createMqttSensor("HeatIndex", mqttHeatIndexTopic, "temperature", tempScale); - _createMqttSensor("DewPoint", mqttDewPointTopic, "temperature", tempScale); + _createMqttSensor(F("Temperature"), mqttTemperatureTopic, F("temperature"), tempScale); + _createMqttSensor(F("Pressure"), mqttPressureTopic, F("pressure"), F("hPa")); + _createMqttSensor(F("Humidity"), mqttHumidityTopic, F("humidity"), F("%")); + _createMqttSensor(F("HeatIndex"), mqttHeatIndexTopic, F("temperature"), tempScale); + _createMqttSensor(F("DewPoint"), mqttDewPointTopic, F("temperature"), tempScale); } } @@ -150,21 +150,21 @@ private: StaticJsonDocument<600> doc; - doc["name"] = String(serverDescription) + " " + name; - doc["state_topic"] = topic; - doc["unique_id"] = String(mqttClientID) + name; + doc[F("name")] = String(serverDescription) + " " + name; + doc[F("state_topic")] = topic; + doc[F("unique_id")] = String(mqttClientID) + name; if (unitOfMeasurement != "") - doc["unit_of_measurement"] = unitOfMeasurement; + doc[F("unit_of_measurement")] = unitOfMeasurement; if (deviceClass != "") - doc["device_class"] = deviceClass; - doc["expire_after"] = 1800; + doc[F("device_class")] = deviceClass; + doc[F("expire_after")] = 1800; - JsonObject device = doc.createNestedObject("device"); // attach the sensor to the same device - device["name"] = serverDescription; - device["identifiers"] = "wled-sensor-" + String(mqttClientID); - device["manufacturer"] = "WLED"; - device["model"] = "FOSS"; - device["sw_version"] = versionString; + 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"); + device[F("model")] = F("FOSS"); + device[F("sw_version")] = versionString; String temp; serializeJson(doc, temp); @@ -177,12 +177,18 @@ private: public: void setup() { - Wire.begin(SDA_PIN, SCL_PIN); + bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used + PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins + PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins + if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + if (!pinManager.allocateMultiplePins(pins, 2, po)) { sensorType=0; return; } + + Wire.begin(ioPin[1], ioPin[0]); if (!bme.begin()) { sensorType = 0; - Serial.println("Could not find BME280I2C sensor!"); + Serial.println(F("Could not find BME280I2C sensor!")); } else { @@ -190,17 +196,18 @@ public: { case BME280::ChipModel_BME280: sensorType = 1; - Serial.println("Found BME280 sensor! Success."); + Serial.println(F("Found BME280 sensor! Success.")); break; case BME280::ChipModel_BMP280: sensorType = 2; - Serial.println("Found BMP280 sensor! No Humidity available."); + Serial.println(F("Found BMP280 sensor! No Humidity available.")); break; default: sensorType = 0; - Serial.println("Found UNKNOWN sensor! Error!"); + Serial.println(F("Found UNKNOWN sensor! Error!")); } } + initDone=true; } void loop() @@ -345,7 +352,7 @@ public: if (sensorType==0) //No Sensor { // if we sensor not detected, let the user know - JsonArray temperature_json = user.createNestedArray("BME/BMP280 Sensor"); + JsonArray temperature_json = user.createNestedArray(F("BME/BMP280 Sensor")); temperature_json.add(F("Not Found")); } else if (sensorType==2) //BMP280 @@ -382,18 +389,19 @@ public: // Save Usermod Config Settings void addToConfig(JsonObject& root) { - JsonObject top = root.createNestedObject("BME280/BMP280"); - top["TemperatureDecimals"] = TemperatureDecimals; - top["HumidityDecimals"] = HumidityDecimals; - top["PressureDecimals"] = PressureDecimals; - top["TemperatureInterval"] = TemperatureInterval; - top["PressureInterval"] = PressureInterval; - top["PublishAlways"] = PublishAlways; - top["UseCelsius"] = UseCelsius; - top["HomeAssistantDiscovery"] = HomeAssistantDiscovery; - JsonArray pinArray = top.createNestedArray("pin-sda-scl"); - pinArray.add(SDA_PIN); - pinArray.add(SCL_PIN); + JsonObject top = root.createNestedObject(F("BME280/BMP280")); + top[F("TemperatureDecimals")] = TemperatureDecimals; + top[F("HumidityDecimals")] = HumidityDecimals; + top[F("PressureDecimals")] = PressureDecimals; + top[F("TemperatureInterval")] = TemperatureInterval; + top[F("PressureInterval")] = PressureInterval; + top[F("PublishAlways")] = PublishAlways; + top[F("UseCelsius")] = UseCelsius; + top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery; + JsonArray io_pin = top.createNestedArray(F("pin")); + for (byte i=0; i<2; i++) io_pin.add(ioPin[i]); + top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page + DEBUG_PRINTLN(F("BME280 config saved.")); } // Read Usermod Config Settings @@ -402,22 +410,53 @@ public: // 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["BME280/BMP280"]; + int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins + + JsonObject top = root[F("BME280/BMP280")]; + if (top.isNull()) { + DEBUG_PRINT(F("BME280/BMP280")); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } bool configComplete = !top.isNull(); // A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing - configComplete &= getJsonValue(top["TemperatureDecimals"], TemperatureDecimals, 1); - configComplete &= getJsonValue(top["HumidityDecimals"], HumidityDecimals, 0); - configComplete &= getJsonValue(top["PressureDecimals"], PressureDecimals, 0); - configComplete &= getJsonValue(top["TemperatureInterval"], TemperatureInterval, 30); - configComplete &= getJsonValue(top["PressureInterval"], PressureInterval, 30); - configComplete &= getJsonValue(top["PublishAlways"], PublishAlways, false); - configComplete &= getJsonValue(top["UseCelsius"], UseCelsius, true); - configComplete &= getJsonValue(top["HomeAssistantDiscovery"], HomeAssistantDiscovery, false); - configComplete &= getJsonValue(top["pin-sda-scl"][0], SDA_PIN, 21); //SDA - configComplete &= getJsonValue(top["pin-sda-scl"][1], SCL_PIN, 22); //SCL + configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1); + configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0); + configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0); + configComplete &= getJsonValue(top[F("TemperatureInterval")], TemperatureInterval, 30); + configComplete &= getJsonValue(top[F("PressureInterval")], PressureInterval, 30); + configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false); + configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true); + configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false); + for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]); + + DEBUG_PRINT(FPSTR(F("BME280/BMP280"))); + if (!initDone) { + // first run: reading from cfg.json + for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; + DEBUG_PRINTLN(F(" config loaded.")); + } else { + DEBUG_PRINTLN(F(" config (re)loaded.")); + // changing parameters from settings page + bool pinsChanged = false; + for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed + if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones + PinOwner po = PinOwner::UM_BME280; + if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins + for (byte i=0; i<2; i++) ioPin[i] = newPin[i]; + setup(); + } + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return !top[F("pin")].isNull(); + } return configComplete; } + + uint16_t getId() { + return USERMOD_ID_BME280; + } }; \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index 6b9517d9..bf119a58 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -76,6 +76,7 @@ #define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h" #define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h" #define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h" +#define USERMOD_ID_BME280 30 //Usermod "usermod_bme280.h //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 5f009425..e9e77c14 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -53,7 +53,8 @@ enum struct PinOwner : uint8_t { // #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h // #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" - UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h" + UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h" + UM_BME280 = USERMOD_ID_BME280 // 0x18 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");