From 732e84c45e7bee04deeaab88bb98c0dd543e7418 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Tue, 28 Apr 2020 07:12:42 +0300 Subject: [PATCH] Various sensor fixes (#2230) * hlw8012: load hardware-specific ratios * hlw8012: read energy in pre() callback * dcz: nvalue should be integer * sns: fix pressure constrain * sns: load ratios based on index too * sns: per-magnitude corrections (still limited by type) * sns: attach units to index --- code/espurna/domoticz.ino | 3 +- code/espurna/sensor.ino | 99 ++++++++++++++++----------- code/espurna/sensors/BaseEmonSensor.h | 16 +++++ code/espurna/sensors/BaseSensor.h | 4 +- code/espurna/sensors/HLW8012Sensor.h | 36 +++++----- 5 files changed, 98 insertions(+), 60 deletions(-) diff --git a/code/espurna/domoticz.ino b/code/espurna/domoticz.ino index 5612f8fd..03fd1a81 100644 --- a/code/espurna/domoticz.ino +++ b/code/espurna/domoticz.ino @@ -218,6 +218,7 @@ void domoticzSendMagnitude(unsigned char type, unsigned char index, double value int nvalue = (buffer[0] >= 48) ? (buffer[0] - 48) : 0; domoticzSend(key, nvalue, buffer); // https://www.domoticz.com/wiki/Domoticz_API/JSON_URL's#Humidity + // nvalue contains HUM (relative humidity) // svalue contains HUM_STAT, one of consts below } else if (MAGNITUDE_HUMIDITY == type) { const char status = 48 + ( @@ -227,7 +228,7 @@ void domoticzSendMagnitude(unsigned char type, unsigned char index, double value HUMIDITY_DRY ); char svalue[2] = {status, '\0'}; - domoticzSend(key, buffer, svalue); + domoticzSend(key, static_cast(value), svalue); // Otherwise, send char string (nvalue is only for integers) } else { domoticzSend(key, 0, buffer); diff --git a/code/espurna/sensor.ino b/code/espurna/sensor.ino index 52f7b20c..64f4ffd3 100644 --- a/code/espurna/sensor.ino +++ b/code/espurna/sensor.ino @@ -49,6 +49,7 @@ struct sensor_magnitude_t { double reported; // Last reported value double min_change; // Minimum value change to report double max_change; // Maximum value change to report + double correction; // Value correction (applied when processing) }; @@ -162,10 +163,6 @@ bool _sensor_realtime = API_REAL_TIME_VALUES; unsigned long _sensor_read_interval = 1000 * SENSOR_READ_INTERVAL; unsigned char _sensor_report_every = SENSOR_REPORT_EVERY; -double _sensor_temperature_correction = SENSOR_TEMPERATURE_CORRECTION; -double _sensor_humidity_correction = SENSOR_HUMIDITY_CORRECTION; -double _sensor_lux_correction = SENSOR_LUX_CORRECTION; - // Energy persistence std::vector _sensor_save_count; unsigned char _sensor_save_every = SENSOR_SAVE_EVERY; @@ -185,7 +182,8 @@ sensor_magnitude_t::sensor_magnitude_t() : last(0.0), reported(0.0), min_change(0.0), - max_change(0.0) + max_change(0.0), + correction(0.0) {} sensor_magnitude_t::sensor_magnitude_t(unsigned char type, unsigned char local, sensor::Unit units, BaseSensor* sensor) : @@ -199,7 +197,8 @@ sensor_magnitude_t::sensor_magnitude_t(unsigned char type, unsigned char local, last(0.0), reported(0.0), min_change(0.0), - max_change(0.0) + max_change(0.0), + correction(0.0) { ++_counts[type]; @@ -509,17 +508,16 @@ sensor::Unit _magnitudeUnitFilter(const sensor_magnitude_t& magnitude, sensor::U double _magnitudeProcess(const sensor_magnitude_t& magnitude, double value) { // Process input (sensor) units and convert to the ones that magnitude specifies as output - switch (magnitude.sensor->units(magnitude.type)) { + switch (magnitude.sensor->units(magnitude.local)) { case sensor::Unit::Celcius: if (magnitude.units == sensor::Unit::Farenheit) { value = (value * 1.8) + 32.0; } else if (magnitude.units == sensor::Unit::Kelvin) { value = value + 273.15; } - value = value + _sensor_temperature_correction; break; - case sensor::Unit::Hectopascal: - value = constrain(value + _sensor_humidity_correction, 0.0, 100.0); + case sensor::Unit::Percentage: + value = constrain(value, 0.0, 100.0); break; case sensor::Unit::Watt: case sensor::Unit::Voltampere: @@ -536,13 +534,12 @@ double _magnitudeProcess(const sensor_magnitude_t& magnitude, double value) { value = value * 3.6e+6; } break; - case sensor::Unit::Lux: - value = value + _sensor_lux_correction; - break; default: break; } + value = value + magnitude.correction; + return roundTo(value, magnitude.decimals); } @@ -739,11 +736,19 @@ void _sensorWebSocketOnConnected(JsonObject& root) { } + if (sensor_magnitude_t::counts(MAGNITUDE_TEMPERATURE)) { + root["tmpCorrection"] = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION); + } + + if (sensor_magnitude_t::counts(MAGNITUDE_HUMIDITY)) { + root["humCorrection"] = getSetting("humCorrection", SENSOR_HUMIDITY_CORRECTION); + } + + if (sensor_magnitude_t::counts(MAGNITUDE_LUX)) { + root["luxCorrection"] = getSetting("luxCorrection", SENSOR_LUX_CORRECTION); + } + if (magnitudeCount()) { - //root["apiRealTime"] = _sensor_realtime; - root["tmpCorrection"] = _sensor_temperature_correction; - root["humCorrection"] = _sensor_humidity_correction; - root["luxCorrection"] = _sensor_lux_correction; root["snsRead"] = _sensor_read_interval / 1000; root["snsReport"] = _sensor_report_every; root["snsSave"] = _sensor_save_every; @@ -1317,6 +1322,9 @@ void _sensorLoad() { sensor->setSEL(getSetting("snsHlw8012SelGPIO", HLW8012_SEL_PIN)); sensor->setCF(getSetting("snsHlw8012CfGPIO", HLW8012_CF_PIN)); sensor->setCF1(getSetting("snsHlw8012Cf1GPIO", HLW8012_CF1_PIN)); + sensor->setCurrentRatio(HLW8012_CURRENT_RATIO); + sensor->setVoltageRatio(HLW8012_VOLTAGE_RATIO); + sensor->setPowerRatio(HLW8012_POWER_RATIO); sensor->setSELCurrent(HLW8012_SEL_CURRENT); _sensors.push_back(sensor); } @@ -1631,25 +1639,32 @@ void _sensorInit() { break; } + // TODO: compatibility proxy, fetch global key before indexed + auto get_ratio = [](const char* key, unsigned char index, double default_value) -> double { + return getSetting({key, index}, getSetting(key, default_value)); + }; + if (_sensorIsEmon(_sensors[i])) { auto* sensor = static_cast(_sensors[i]); - sensor->setCurrentRatio( - getSetting("pwrRatioC", sensor->getCurrentRatio()) - ); - sensor->setVoltageRatio( - getSetting("pwrRatioV", sensor->getVoltageRatio()) - ); - sensor->setPowerRatio( - getSetting("pwrRatioP", sensor->getPowerRatio()) - ); - sensor->setEnergyRatio( - getSetting("pwrRatioE", sensor->getEnergyRatio()) - ); - for (size_t index = 0; index < sensor->countDevices(); ++index) { sensor->resetEnergy(index, _sensorEnergyTotal(index)); + sensor->setCurrentRatio( + get_ratio("pwrRatioC", index, sensor->getCurrentRatio(index)) + ); + sensor->setVoltageRatio( + get_ratio("pwrRatioV", index, sensor->getVoltageRatio(index)) + ); + sensor->setPowerRatio( + get_ratio("pwrRatioP", index, sensor->getPowerRatio(index)) + ); + sensor->setEnergyRatio( + get_ratio("pwrRatioE", index, sensor->getEnergyRatio(index)) + ); + sensor->setVoltage( + get_ratio("pwrVoltage", index, sensor->getVoltage(index)) + ); } } @@ -1683,12 +1698,7 @@ void _sensorConfigure() { _sensor_realtime = getSetting("apiRealTime", 1 == API_REAL_TIME_VALUES); - // Corrections are applied for every magnitude atm, assuming there is no more than one magnitude per type present - _sensor_temperature_correction = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION); - _sensor_humidity_correction = getSetting("humCorrection", SENSOR_HUMIDITY_CORRECTION); - _sensor_lux_correction = getSetting("luxCorrection", SENSOR_LUX_CORRECTION); - - // ... same for delta values + // Per-magnitude min & max delta settings // - min controls whether we report at all when report_count overflows // - max will trigger report as soon as read value is greater than the specified delta // (atm this works best for accumulated magnitudes, like energy) @@ -1766,22 +1776,30 @@ void _sensorConfigure() { // Update magnitude config, filter sizes and reset energy if needed { - // Proxy legacy global setting + // TODO: instead of using global enum, have a local mapping? const auto tmpUnits = getSetting("tmpUnits", SENSOR_TEMPERATURE_UNITS); const auto pwrUnits = getSetting("pwrUnits", SENSOR_POWER_UNITS); const auto eneUnits = getSetting("eneUnits", SENSOR_ENERGY_UNITS); + // TODO: map MAGNITUDE_... type to a specific string? nvm the preprocessor flags, just focus on settings + const auto tmpCorrection = getSetting("tmpCorrection", SENSOR_TEMPERATURE_CORRECTION); + const auto humCorrection = getSetting("humCorrection", SENSOR_HUMIDITY_CORRECTION); + const auto luxCorrection = getSetting("luxCorrection", SENSOR_LUX_CORRECTION); + for (unsigned char index = 0; index < _magnitudes.size(); ++index) { auto& magnitude = _magnitudes.at(index); - // update units based either on hard-coded defaults or runtime settings switch (magnitude.type) { case MAGNITUDE_TEMPERATURE: magnitude.units = _magnitudeUnitFilter( magnitude, getSetting({"tmpUnits", magnitude.global}, tmpUnits) ); + magnitude.correction = getSetting({"tmpCorrection", magnitude.global}, tmpCorrection); + break; + case MAGNITUDE_HUMIDITY: + magnitude.correction = getSetting({"humCorrection", magnitude.global}, humCorrection); break; case MAGNITUDE_POWER_ACTIVE: magnitude.units = _magnitudeUnitFilter( @@ -1795,8 +1813,11 @@ void _sensorConfigure() { getSetting({"eneUnits", magnitude.global}, eneUnits) ); break; + case MAGNITUDE_LUX: + magnitude.correction = getSetting({"luxCorrection", magnitude.global}, luxCorrection); + break; default: - magnitude.units = magnitude.sensor->units(magnitude.type); + magnitude.units = magnitude.sensor->units(magnitude.local); break; } diff --git a/code/espurna/sensors/BaseEmonSensor.h b/code/espurna/sensors/BaseEmonSensor.h index c72272b5..9c12f006 100644 --- a/code/espurna/sensors/BaseEmonSensor.h +++ b/code/espurna/sensors/BaseEmonSensor.h @@ -25,6 +25,8 @@ class BaseEmonSensor : public BaseSensor { return sensor::type::Emon; } + // --- energy monitoring -- + virtual void resizeDevices(size_t devices) { _energy.resize(devices); _devices = devices; @@ -64,11 +66,25 @@ class BaseEmonSensor : public BaseSensor { return getEnergy(0); } + // --- configuration --- + + // when sensor needs explicit mains voltage value + virtual void setVoltage(double) {} + virtual double getVoltage() { return 0.0; } + virtual double getVoltage(unsigned char index) { return getVoltage(); } + + // when sensor implements ratios / multipliers virtual void setCurrentRatio(double) {} virtual void setVoltageRatio(double) {} virtual void setPowerRatio(double) {} virtual void setEnergyRatio(double) {} + // when sensor implements ratios / multipliers + virtual void setCurrentRatio(unsigned char index, double) {} + virtual void setVoltageRatio(unsigned char index, double) {} + virtual void setPowerRatio(unsigned char index, double) {} + virtual void setEnergyRatio(unsigned char index, double) {} + // when sensor implements a single device virtual double getCurrentRatio() { return 0.0; } virtual double getVoltageRatio() { return 0.0; } diff --git a/code/espurna/sensors/BaseSensor.h b/code/espurna/sensors/BaseSensor.h index 77c8087f..5102e16e 100644 --- a/code/espurna/sensors/BaseSensor.h +++ b/code/espurna/sensors/BaseSensor.h @@ -88,8 +88,8 @@ class BaseSensor { void onEvent(TSensorCallback fn) { _callback = fn; }; // Specify units attached to magnitudes - virtual sensor::Unit units(unsigned char type) { - switch (type) { + virtual sensor::Unit units(unsigned char index) { + switch (type(index)) { case MAGNITUDE_TEMPERATURE: return sensor::Unit::Celcius; case MAGNITUDE_HUMIDITY: diff --git a/code/espurna/sensors/HLW8012Sensor.h b/code/espurna/sensors/HLW8012Sensor.h index b582b099..bced2648 100644 --- a/code/espurna/sensors/HLW8012Sensor.h +++ b/code/espurna/sensors/HLW8012Sensor.h @@ -143,11 +143,9 @@ class HLW8012Sensor : public BaseEmonSensor { _hlw8012->setResistors(HLW8012_CURRENT_R, HLW8012_VOLTAGE_R_UP, HLW8012_VOLTAGE_R_DOWN); // Handle interrupts - #if HLW8012_USE_INTERRUPTS - #if HLW8012_WAIT_FOR_WIFI == 0 - _enableInterrupts(false); - _enableInterrupts(true); - #endif + #if HLW8012_USE_INTERRUPTS && (!HLW8012_WAIT_FOR_WIFI) + _enableInterrupts(false); + _enableInterrupts(true); #endif _ready = true; @@ -187,10 +185,7 @@ class HLW8012Sensor : public BaseEmonSensor { } double getEnergyDelta() { - const auto result = _hlw8012->getEnergy(); - _energy[0] += sensor::Ws { result }; - _hlw8012->resetEnergy(); - return result; + return _energy_last; } // Current value for slot # index @@ -207,18 +202,21 @@ class HLW8012Sensor : public BaseEmonSensor { } // Pre-read hook (usually to populate registers with up-to-date data) - #if HLW8012_USE_INTERRUPTS - #if HLW8012_WAIT_FOR_WIFI void pre() { - _enableInterrupts(wifiConnected()); + #if HLW8012_USE_INTERRUPTS && HLW8012_WAIT_FOR_WIFI + _enableInterrupts(wifiConnected()); + #endif + + _energy_last = _hlw8012->getEnergy(); + _energy[0] += sensor::Ws { _energy_last }; + _hlw8012->resetEnergy(); } - #endif - #endif - // Toggle between current and voltage monitoring - #if HLW8012_USE_INTERRUPTS == 0 - // Post-read hook (usually to reset things) - void post() { _hlw8012->toggleMode(); } + #if !HLW8012_USE_INTERRUPTS + // Toggle between current and voltage monitoring after reading + void post() { + _hlw8012->toggleMode(); + } #endif // HLW8012_USE_INTERRUPTS == 0 // Handle interrupt calls @@ -278,6 +276,8 @@ class HLW8012Sensor : public BaseEmonSensor { unsigned char _cf1 = GPIO_NONE; bool _sel_current = true; + uint32_t _energy_last = 0; + HLW8012 * _hlw8012 = NULL; };