diff --git a/usermods/BME280_v2/README.md b/usermods/BME280_v2/README.md new file mode 100644 index 00000000..216ca630 --- /dev/null +++ b/usermods/BME280_v2/README.md @@ -0,0 +1,40 @@ +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. + +- 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. +- 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`) +```ini +build_flags = + ${common.build_flags_esp8266} + -D USERMOD_BME280 +``` +or define `USERMOD_BME280` in `my_config.h` +```c++ +#define USERMOD_BME280 +``` + +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: +Measurement type | MQTT topic +--- | --- +Temperature | `/temperature` +Humidity | `/humidity` +Pressure | `/pressure` +Heat index | `/heat_index` +Dew point | `/dew_point` \ No newline at end of file diff --git a/usermods/BME280_v2/usermod_bme280.h b/usermods/BME280_v2/usermod_bme280.h new file mode 100644 index 00000000..80a31a4f --- /dev/null +++ b/usermods/BME280_v2/usermod_bme280.h @@ -0,0 +1,212 @@ +#pragma once + +#include "wled.h" +#include +#include +#include // BME280 sensor +#include // BME280 extended measurements + +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 0 // 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 + +// 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 + +#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{ + BME280::OSR_X16, // Temperature oversampling x16 + BME280::OSR_X16, // Humidity oversampling x16 + BME280::OSR_X16, // Pressure oversampling x16 + // Defaults + BME280::Mode_Forced, + BME280::StandbyTime_1000ms, + BME280::Filter_Off, + BME280::SpiEnable_False, + BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. Default 0x76 + }; + + BME280I2C bme{settings}; + + uint8_t SensorType; + + // Measurement timers + long timer; + long lastTemperatureMeasure = 0; + long lastPressureMeasure = 0; + + // Current sensor values + float SensorTemperature; + float SensorHumidity; + float SensorHeatIndex; + float SensorDewPoint; + float SensorPressure; + // Track previous sensor values + float lastTemperature; + float lastHumidity; + float lastHeatIndex; + float lastDewPoint; + float lastPressure; + + // Store packet IDs of MQTT publications + uint16_t mqttTemperaturePub = 0; + uint16_t mqttPressurePub = 0; + + void UpdateBME280Data(int SensorType) + { + float _temperature, _humidity, _pressure; + #ifdef Celsius + BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); + EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius); + #else + BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); + EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit); + #endif + BME280::PresUnit presUnit(BME280::PresUnit_hPa); + + 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); + } + } + +public: + void setup() + { + Wire.begin(SDA_PIN, SCL_PIN); + + if (!bme.begin()) + { + SensorType = 0; + Serial.println("Could not find BME280I2C sensor!"); + } + else + { + switch (bme.chipModel()) + { + case BME280::ChipModel_BME280: + SensorType = 1; + Serial.println("Found BME280 sensor! Success."); + break; + case BME280::ChipModel_BMP280: + SensorType = 2; + Serial.println("Found BMP280 sensor! No Humidity available."); + break; + default: + SensorType = 0; + Serial.println("Found UNKNOWN sensor! Error!"); + } + } + } + + void loop() + { + // BME280 sensor MQTT publishing + // Check if sensor present and MQTT Connected, otherwise it will crash the MCU + if (SensorType != 0 && mqtt != nullptr) + { + // Timer to fetch new temperature, humidity and pressure data at intervals + timer = millis(); + + if (timer - lastTemperatureMeasure >= TemperatureInterval * 1000 || mqttTemperaturePub == 0) + { + lastTemperatureMeasure = timer; + + UpdateBME280Data(SensorType); + + float Temperature = roundf(SensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + float Humidity, HeatIndex, DewPoint; + + // 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) + { + String topic = String(mqttDeviceTopic) + "/temperature"; + mqttTemperaturePub = mqtt->publish(topic.c_str(), 0, false, String(Temperature, TemperatureDecimals).c_str()); + } + + lastTemperature = Temperature; // Update last sensor temperature for next loop + + if (SensorType == 1) // Only if sensor is a BME280 + { + Humidity = roundf(SensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals); + HeatIndex = roundf(SensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + DewPoint = roundf(SensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); + + if (Humidity != lastHumidity) + { + String topic = String(mqttDeviceTopic) + "/humidity"; + mqtt->publish(topic.c_str(), 0, false, String(Humidity, HumidityDecimals).c_str()); + } + + if (HeatIndex != lastHeatIndex) + { + String topic = String(mqttDeviceTopic) + "/heat_index"; + mqtt->publish(topic.c_str(), 0, false, String(HeatIndex, TemperatureDecimals).c_str()); + } + + if (DewPoint != lastDewPoint) + { + String topic = String(mqttDeviceTopic) + "/dew_point"; + mqtt->publish(topic.c_str(), 0, false, String(DewPoint, TemperatureDecimals).c_str()); + } + + lastHumidity = Humidity; + lastHeatIndex = HeatIndex; + lastDewPoint = DewPoint; + } + } + + if (timer - lastPressureMeasure >= PressureInterval * 1000 || mqttPressurePub == 0) + { + lastPressureMeasure = timer; + + float Pressure = roundf(SensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals); + + if (Pressure != lastPressure) + { + String topic = String(mqttDeviceTopic) + "/pressure"; + mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(Pressure, PressureDecimals).c_str()); + } + + lastPressure = Pressure; + } + } + } +}; \ No newline at end of file diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 90b18074..fbbe0391 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -14,6 +14,13 @@ #include "../usermods/Temperature/usermod_temperature.h" #endif //#include "usermod_v2_empty.h" +#ifdef USERMOD_BUZZER +#include "../usermods/buzzer/usermod_v2_buzzer.h" +#endif +// BME280 v2 usermod. Define "USERMOD_BME280" in my_config.h +#ifdef USERMOD_BME280 +#include "../usermods/BME280_v2/usermod_bme280.h" +#endif void registerUsermods() { @@ -27,4 +34,10 @@ void registerUsermods() usermods.add(new UsermodTemperature()); #endif //usermods.add(new UsermodRenameMe()); + #ifdef USERMOD_BUZZER + usermods.add(new BuzzerUsermod()); + #endif + #ifdef USERMOD_BME280 + usermods.add(new UsermodBME280()); + #endif } \ No newline at end of file