mirror of
https://github.com/esphome/esphome.git
synced 2026-09-23 21:14:03 +00:00
183 lines
6.1 KiB
C++
183 lines
6.1 KiB
C++
#include "esphome/core/log.h"
|
|
#include "absolute_humidity.h"
|
|
|
|
namespace esphome::absolute_humidity {
|
|
|
|
static const char *const TAG{"absolute_humidity.sensor"};
|
|
|
|
void AbsoluteHumidityComponent::setup() {
|
|
this->temperature_sensor_->add_on_state_callback([this](float state) {
|
|
this->temperature_ = state;
|
|
this->enable_loop();
|
|
});
|
|
ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
|
|
// Get initial value
|
|
if (this->temperature_sensor_->has_state()) {
|
|
this->temperature_ = this->temperature_sensor_->get_state();
|
|
}
|
|
|
|
this->humidity_sensor_->add_on_state_callback([this](float state) {
|
|
this->humidity_ = state;
|
|
this->enable_loop();
|
|
});
|
|
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", this->humidity_sensor_->get_name().c_str());
|
|
// Get initial value
|
|
if (this->humidity_sensor_->has_state()) {
|
|
this->humidity_ = this->humidity_sensor_->get_state();
|
|
}
|
|
}
|
|
|
|
void AbsoluteHumidityComponent::dump_config() {
|
|
LOG_SENSOR("", "Absolute Humidity", this);
|
|
|
|
switch (this->equation_) {
|
|
case BUCK:
|
|
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Buck");
|
|
break;
|
|
case TETENS:
|
|
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Tetens");
|
|
break;
|
|
case WOBUS:
|
|
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Wobus");
|
|
break;
|
|
default:
|
|
ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
|
|
break;
|
|
}
|
|
|
|
ESP_LOGCONFIG(TAG,
|
|
"Sources\n"
|
|
" Temperature: '%s'\n"
|
|
" Relative Humidity: '%s'",
|
|
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
|
|
}
|
|
|
|
void AbsoluteHumidityComponent::loop() {
|
|
// Only run once
|
|
this->disable_loop();
|
|
|
|
// Ensure we have source data
|
|
const bool no_temperature{std::isnan(this->temperature_)};
|
|
const bool no_humidity{std::isnan(this->humidity_)};
|
|
if (no_temperature || no_humidity) {
|
|
if (no_temperature) {
|
|
ESP_LOGW(TAG, "No valid state from temperature sensor!");
|
|
}
|
|
if (no_humidity) {
|
|
ESP_LOGW(TAG, "No valid state from humidity sensor!");
|
|
}
|
|
this->publish_state(NAN);
|
|
this->status_set_warning(LOG_STR("Unable to calculate absolute humidity."));
|
|
return;
|
|
}
|
|
|
|
// Convert to desired units
|
|
const float temperature_c{this->temperature_};
|
|
const float temperature_k{temperature_c + 273.15f};
|
|
const float hr{this->humidity_ / 100.0f};
|
|
|
|
// Calculate saturation vapor pressure
|
|
float es;
|
|
switch (this->equation_) {
|
|
case BUCK:
|
|
es = es_buck(temperature_c);
|
|
break;
|
|
case TETENS:
|
|
es = es_tetens(temperature_c);
|
|
break;
|
|
case WOBUS:
|
|
es = es_wobus(temperature_c);
|
|
break;
|
|
default:
|
|
this->publish_state(NAN);
|
|
this->status_set_error(LOG_STR("Invalid saturation vapor pressure equation selection!"));
|
|
return;
|
|
}
|
|
|
|
// Calculate absolute humidity
|
|
const float absolute_humidity{vapor_density(es, hr, temperature_k)};
|
|
|
|
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa, absolute humidity %f g/m³", es, absolute_humidity);
|
|
|
|
// Publish absolute humidity
|
|
this->status_clear_warning();
|
|
this->publish_state(absolute_humidity);
|
|
}
|
|
|
|
// Buck equation (https://en.wikipedia.org/wiki/Arden_Buck_equation)
|
|
// More accurate than Tetens in normal meteorologic conditions
|
|
float AbsoluteHumidityComponent::es_buck(float temperature_c) {
|
|
float a, b, c, d;
|
|
if (temperature_c >= 0.0f) {
|
|
a = 0.61121f;
|
|
b = 18.678f;
|
|
c = 234.5f;
|
|
d = 257.14f;
|
|
} else {
|
|
a = 0.61115f;
|
|
b = 18.678f;
|
|
c = 233.7f;
|
|
d = 279.82f;
|
|
}
|
|
return a * expf((b - (temperature_c / c)) * (temperature_c / (d + temperature_c)));
|
|
}
|
|
|
|
// Tetens equation (https://en.wikipedia.org/wiki/Tetens_equation)
|
|
float AbsoluteHumidityComponent::es_tetens(float temperature_c) {
|
|
float a, b;
|
|
if (temperature_c >= 0.0f) {
|
|
a = 17.27f;
|
|
b = 237.3f;
|
|
} else {
|
|
a = 21.875f;
|
|
b = 265.5f;
|
|
}
|
|
return 0.61078f * expf((a * temperature_c) / (temperature_c + b));
|
|
}
|
|
|
|
// Wobus equation
|
|
// https://wahiduddin.net/calc/density_altitude.htm
|
|
// https://wahiduddin.net/calc/density_algorithms.htm
|
|
// Calculate the saturation vapor pressure (kPa)
|
|
float AbsoluteHumidityComponent::es_wobus(float t) {
|
|
// THIS FUNCTION RETURNS THE SATURATION VAPOR PRESSURE ESW (MILLIBARS)
|
|
// OVER LIQUID WATER GIVEN THE TEMPERATURE T (CELSIUS). THE POLYNOMIAL
|
|
// APPROXIMATION BELOW IS DUE TO HERMAN WOBUS, A MATHEMATICIAN WHO
|
|
// WORKED AT THE NAVY WEATHER RESEARCH FACILITY, NORFOLK, VIRGINIA,
|
|
// BUT WHO IS NOW RETIRED. THE COEFFICIENTS OF THE POLYNOMIAL WERE
|
|
// CHOSEN TO FIT THE VALUES IN TABLE 94 ON PP. 351-353 OF THE SMITH-
|
|
// SONIAN METEOROLOGICAL TABLES BY ROLAND LIST (6TH EDITION). THE
|
|
// APPROXIMATION IS VALID FOR -50 < T < 100C.
|
|
//
|
|
// Baker, Schlatter 17-MAY-1982 Original version.
|
|
|
|
constexpr float c0{+0.99999683e+00f};
|
|
constexpr float c1{-0.90826951e-02f};
|
|
constexpr float c2{+0.78736169e-04f};
|
|
constexpr float c3{-0.61117958e-06f};
|
|
constexpr float c4{+0.43884187e-08f};
|
|
constexpr float c5{-0.29883885e-10f};
|
|
constexpr float c6{+0.21874425e-12f};
|
|
constexpr float c7{-0.17892321e-14f};
|
|
constexpr float c8{+0.11112018e-16f};
|
|
constexpr float c9{-0.30994571e-19f};
|
|
const float p{c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * (c6 + t * (c7 + t * (c8 + t * (c9)))))))))};
|
|
return 0.61078f / powf(p, 8.0f);
|
|
}
|
|
|
|
// From https://www.environmentalbiophysics.org/chalk-talk-how-to-calculate-absolute-humidity/
|
|
// H/T to https://esphome.io/cookbook/bme280_environment/
|
|
// H/T to https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
|
|
float AbsoluteHumidityComponent::vapor_density(float es, float hr, float ta) {
|
|
// es = saturated vapor pressure (kPa)
|
|
// hr = relative humidity [0-1]
|
|
// ta = absolute temperature (K)
|
|
|
|
const float ea{hr * es * 1000.0f}; // vapor pressure of the air (Pa)
|
|
const float mw{18.01528f}; // molar mass of water (g⋅mol⁻¹)
|
|
const float r{8.31446261815324f}; // molar gas constant (J⋅K⁻¹)
|
|
return (ea * mw) / (r * ta);
|
|
}
|
|
|
|
} // namespace esphome::absolute_humidity
|