2022-07-19 15:27:15 +02:00
// force the compiler to show a warning to confirm that this file is included
# warning **** Included USERMOD_BME280 version 2.0 ****
2020-12-25 21:17:30 +01:00
# pragma once
# include "wled.h"
# include <Arduino.h>
# include <Wire.h>
# include <BME280I2C.h> // BME280 sensor
# include <EnvironmentCalculations.h> // BME280 extended measurements
class UsermodBME280 : public Usermod
{
private :
// User-defined configuration
# define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit
2022-07-19 15:27:15 +02:00
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
2020-12-25 21:17:30 +01:00
# 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 } ;
2021-04-12 10:31:59 +02:00
uint8_t sensorType ;
2020-12-25 21:17:30 +01:00
// Measurement timers
long timer ;
long lastTemperatureMeasure = 0 ;
long lastPressureMeasure = 0 ;
// Current sensor values
2021-04-12 10:31:59 +02:00
float sensorTemperature ;
float sensorHumidity ;
float sensorHeatIndex ;
float sensorDewPoint ;
float sensorPressure ;
2020-12-25 21:17:30 +01:00
// Track previous sensor values
float lastTemperature ;
float lastHumidity ;
float lastHeatIndex ;
float lastDewPoint ;
float lastPressure ;
2022-07-19 15:27:15 +02:00
// MQTT topic strings for publishing Home Assistant discovery topics
bool mqttInitialized = false ;
String mqttTemperatureTopic = " " ;
String mqttHumidityTopic = " " ;
String mqttPressureTopic = " " ;
String mqttHeatIndexTopic = " " ;
String mqttDewPointTopic = " " ;
2020-12-25 21:17:30 +01:00
// 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 ) ;
2021-04-12 10:31:59 +02:00
sensorTemperature = _temperature ;
sensorHumidity = _humidity ;
sensorPressure = _pressure ;
if ( sensorType = = 1 )
2020-12-25 21:17:30 +01:00
{
2021-04-12 10:31:59 +02:00
sensorHeatIndex = EnvironmentCalculations : : HeatIndex ( _temperature , _humidity , envTempUnit ) ;
sensorDewPoint = EnvironmentCalculations : : DewPoint ( _temperature , _humidity , envTempUnit ) ;
2020-12-25 21:17:30 +01:00
}
}
2022-07-19 15:27:15 +02:00
// 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 " ;
2022-07-19 22:47:56 +02:00
# ifdef Celsius
_createMqttSensor ( " Temperature " , mqttTemperatureTopic , " temperature " , " °C " ) ;
# else
_createMqttSensor ( " Temperature " , mqttTemperatureTopic , " temperature " , " °F " ) ;
# endif
2022-07-19 15:27:15 +02:00
_createMqttSensor ( " Pressure " , mqttPressureTopic , " pressure " , " hPa " ) ;
_createMqttSensor ( " Humidity " , mqttHumidityTopic , " humidity " , " % " ) ;
2022-07-19 22:47:56 +02:00
# 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
2022-07-19 15:27:15 +02:00
}
// 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 ( ) ) ;
}
2020-12-25 21:17:30 +01:00
public :
void setup ( )
{
Wire . begin ( SDA_PIN , SCL_PIN ) ;
if ( ! bme . begin ( ) )
{
2021-04-12 10:31:59 +02:00
sensorType = 0 ;
2020-12-25 21:17:30 +01:00
Serial . println ( " Could not find BME280I2C sensor! " ) ;
}
else
{
switch ( bme . chipModel ( ) )
{
case BME280 : : ChipModel_BME280 :
2021-04-12 10:31:59 +02:00
sensorType = 1 ;
2020-12-25 21:17:30 +01:00
Serial . println ( " Found BME280 sensor! Success. " ) ;
break ;
case BME280 : : ChipModel_BMP280 :
2021-04-12 10:31:59 +02:00
sensorType = 2 ;
2020-12-25 21:17:30 +01:00
Serial . println ( " Found BMP280 sensor! No Humidity available. " ) ;
break ;
default :
2021-04-12 10:31:59 +02:00
sensorType = 0 ;
2020-12-25 21:17:30 +01:00
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
2022-07-19 15:27:15 +02:00
if ( sensorType ! = 0 & & WLED_MQTT_CONNECTED )
2020-12-25 21:17:30 +01:00
{
// Timer to fetch new temperature, humidity and pressure data at intervals
timer = millis ( ) ;
if ( timer - lastTemperatureMeasure > = TemperatureInterval * 1000 | | mqttTemperaturePub = = 0 )
{
lastTemperatureMeasure = timer ;
2021-04-12 10:31:59 +02:00
UpdateBME280Data ( sensorType ) ;
2020-12-25 21:17:30 +01:00
2021-04-12 10:31:59 +02:00
float temperature = roundf ( sensorTemperature * pow ( 10 , TemperatureDecimals ) ) / pow ( 10 , TemperatureDecimals ) ;
float humidity , heatIndex , dewPoint ;
2020-12-25 21:17:30 +01:00
2022-07-19 15:27:15 +02:00
if ( WLED_MQTT_CONNECTED & & ! mqttInitialized & & HomeAssistantDiscovery )
{
_mqttInitialize ( ) ;
mqttInitialized = true ;
}
2020-12-25 21:17:30 +01:00
// 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
2021-04-12 10:31:59 +02:00
if ( temperature ! = lastTemperature | | PublishAlways )
2020-12-25 21:17:30 +01:00
{
String topic = String ( mqttDeviceTopic ) + " /temperature " ;
2021-04-12 10:31:59 +02:00
mqttTemperaturePub = mqtt - > publish ( topic . c_str ( ) , 0 , false , String ( temperature , TemperatureDecimals ) . c_str ( ) ) ;
2020-12-25 21:17:30 +01:00
}
2021-04-12 10:31:59 +02:00
lastTemperature = temperature ; // Update last sensor temperature for next loop
2020-12-25 21:17:30 +01:00
2021-04-12 10:31:59 +02:00
if ( sensorType = = 1 ) // Only if sensor is a BME280
2020-12-25 21:17:30 +01:00
{
2021-04-12 10:31:59 +02:00
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 ) ;
2020-12-25 21:17:30 +01:00
2021-04-12 10:31:59 +02:00
if ( humidity ! = lastHumidity | | PublishAlways )
2020-12-25 21:17:30 +01:00
{
String topic = String ( mqttDeviceTopic ) + " /humidity " ;
2021-04-12 10:31:59 +02:00
mqtt - > publish ( topic . c_str ( ) , 0 , false , String ( humidity , HumidityDecimals ) . c_str ( ) ) ;
2020-12-25 21:17:30 +01:00
}
2021-04-12 10:31:59 +02:00
if ( heatIndex ! = lastHeatIndex | | PublishAlways )
2020-12-25 21:17:30 +01:00
{
String topic = String ( mqttDeviceTopic ) + " /heat_index " ;
2021-04-12 10:31:59 +02:00
mqtt - > publish ( topic . c_str ( ) , 0 , false , String ( heatIndex , TemperatureDecimals ) . c_str ( ) ) ;
2020-12-25 21:17:30 +01:00
}
2021-04-12 10:31:59 +02:00
if ( dewPoint ! = lastDewPoint | | PublishAlways )
2020-12-25 21:17:30 +01:00
{
String topic = String ( mqttDeviceTopic ) + " /dew_point " ;
2021-04-12 10:31:59 +02:00
mqtt - > publish ( topic . c_str ( ) , 0 , false , String ( dewPoint , TemperatureDecimals ) . c_str ( ) ) ;
2020-12-25 21:17:30 +01:00
}
2021-04-12 10:31:59 +02:00
lastHumidity = humidity ;
lastHeatIndex = heatIndex ;
lastDewPoint = dewPoint ;
2020-12-25 21:17:30 +01:00
}
}
if ( timer - lastPressureMeasure > = PressureInterval * 1000 | | mqttPressurePub = = 0 )
{
lastPressureMeasure = timer ;
2021-04-12 10:31:59 +02:00
float pressure = roundf ( sensorPressure * pow ( 10 , PressureDecimals ) ) / pow ( 10 , PressureDecimals ) ;
2020-12-25 21:17:30 +01:00
2021-04-12 10:31:59 +02:00
if ( pressure ! = lastPressure | | PublishAlways )
2020-12-25 21:17:30 +01:00
{
String topic = String ( mqttDeviceTopic ) + " /pressure " ;
2021-04-12 10:31:59 +02:00
mqttPressurePub = mqtt - > publish ( topic . c_str ( ) , 0 , true , String ( pressure , PressureDecimals ) . c_str ( ) ) ;
2020-12-25 21:17:30 +01:00
}
2021-04-12 10:31:59 +02:00
lastPressure = pressure ;
2020-12-25 21:17:30 +01:00
}
}
}
2022-07-19 22:47:56 +02:00
/*
* 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 ;
}
2022-07-19 15:27:15 +02:00
// Publish Sensor Information to Info Page
void addToJsonInfo ( JsonObject & root )
{
JsonObject user = root [ F ( " u " ) ] ;
2022-07-19 22:47:56 +02:00
if ( user . isNull ( ) ) user = root . createNestedObject ( F ( " u " ) ) ;
2022-07-19 15:27:15 +02:00
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 ) ;
2022-07-19 22:47:56 +02:00
# ifdef Celsius
temperature_json . add ( F ( " °C " ) ) ;
# else
temperature_json . add ( F ( " °F " ) ) ;
# endif
2022-07-19 15:27:15 +02:00
humidity_json . add ( sensorHumidity ) ;
humidity_json . add ( F ( " % " ) ) ;
pressure_json . add ( sensorPressure ) ;
2022-07-19 22:47:56 +02:00
pressure_json . add ( F ( " hPa " ) ) ;
2022-07-19 15:27:15 +02:00
heatindex_json . add ( sensorHeatIndex ) ;
2022-07-19 22:47:56 +02:00
# ifdef Celsius
heatindex_json . add ( F ( " °C " ) ) ;
# else
heatindex_json . add ( F ( " °F " ) ) ;
# endif
2022-07-19 15:27:15 +02:00
dewpoint_json . add ( sensorDewPoint ) ;
2022-07-19 22:47:56 +02:00
# ifdef Celsius
dewpoint_json . add ( F ( " °C " ) ) ;
# else
dewpoint_json . add ( F ( " °F " ) ) ;
# endif
2022-07-19 15:27:15 +02:00
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 ;
}
2020-12-25 21:17:30 +01:00
} ;