From 025e8c82abb7210eda2ace842c68fda6f55cc3e6 Mon Sep 17 00:00:00 2001 From: Max Prokhorov Date: Tue, 12 May 2020 21:17:01 +0300 Subject: [PATCH] Load ratios after boot + show pwr defaults with `get` (#2241) * emon: configure ratios without reboot * settings: serialize() support * debug: use vsnprintf from newlib, not from sdk * settings/experimental: show defaults via `get` * emon: override base methods, fix defaults * sensor/emon: expose internal index calculation - refactor configuration to use the correct index when accessing indexed sensor methods. store index value on magnitude, refactor loops to accomodate this new functionality - rename slot(index) -> description(index), since we use 'slot' as numeric value --- code/espurna/alexa.cpp | 2 +- code/espurna/button.cpp | 48 ++++ code/espurna/crash.cpp | 2 +- code/espurna/debug.cpp | 31 ++- code/espurna/influxdb.cpp | 2 +- code/espurna/main.cpp | 2 +- code/espurna/mdns.cpp | 2 +- code/espurna/mqtt.cpp | 4 +- code/espurna/sensor.cpp | 294 +++++++++++++++-------- code/espurna/sensors/ADE7953Sensor.h | 9 +- code/espurna/sensors/AM2320Sensor.h | 2 +- code/espurna/sensors/AnalogSensor.h | 2 +- code/espurna/sensors/BaseEmonSensor.h | 40 ++- code/espurna/sensors/BaseSensor.h | 9 +- code/espurna/sensors/CSE7766Sensor.h | 46 ++-- code/espurna/sensors/DHTSensor.h | 2 +- code/espurna/sensors/DallasSensor.h | 2 +- code/espurna/sensors/DigitalSensor.h | 2 +- code/espurna/sensors/ECH1560Sensor.h | 2 +- code/espurna/sensors/EZOPHSensor.h | 2 +- code/espurna/sensors/EmonADC121Sensor.h | 2 +- code/espurna/sensors/EmonADS1X15Sensor.h | 7 +- code/espurna/sensors/EmonSensor.h | 31 ++- code/espurna/sensors/EventSensor.h | 2 +- code/espurna/sensors/GUVAS12SDSensor.h | 2 +- code/espurna/sensors/GeigerSensor.h | 2 +- code/espurna/sensors/HDC1080Sensor.h | 2 +- code/espurna/sensors/HLW8012Sensor.h | 116 +++++---- code/espurna/sensors/I2CSensor.h | 2 +- code/espurna/sensors/LDRSensor.h | 2 +- code/espurna/sensors/MAX6675Sensor.h | 2 +- code/espurna/sensors/MHZ19Sensor.h | 2 +- code/espurna/sensors/MICS2710Sensor.h | 2 +- code/espurna/sensors/MICS5525Sensor.h | 2 +- code/espurna/sensors/NTCSensor.h | 2 +- code/espurna/sensors/PMSX003Sensor.h | 2 +- code/espurna/sensors/PZEM004TSensor.h | 29 ++- code/espurna/sensors/PulseMeterSensor.h | 2 +- code/espurna/sensors/SDS011Sensor.h | 2 +- code/espurna/sensors/SI1145Sensor.h | 2 +- code/espurna/sensors/SI7021Sensor.h | 2 +- code/espurna/sensors/SenseAirSensor.h | 2 +- code/espurna/sensors/SonarSensor.h | 2 +- code/espurna/sensors/T6613Sensor.h | 2 +- code/espurna/sensors/TMP3XSensor.h | 2 +- code/espurna/sensors/V9261FSensor.h | 2 +- code/espurna/sensors/VEML6075Sensor.h | 2 +- code/espurna/sensors/VL53L1XSensor.h | 2 +- code/espurna/settings.cpp | 16 ++ code/espurna/settings.h | 34 ++- code/espurna/terminal.cpp | 7 +- code/espurna/thingspeak.cpp | 2 +- code/espurna/wifi.cpp | 12 +- 53 files changed, 562 insertions(+), 245 deletions(-) diff --git a/code/espurna/alexa.cpp b/code/espurna/alexa.cpp index 4d53ca09..2e747473 100644 --- a/code/espurna/alexa.cpp +++ b/code/espurna/alexa.cpp @@ -82,7 +82,7 @@ void _alexaBrokerCallback(const String& topic, unsigned char id, unsigned int va // ----------------------------------------------------------------------------- bool alexaEnabled() { - return getSetting("alexaEnabled", 1 == ALEXA_ENABLED); + return getSetting("alexaEnabled", 1 == ALEXA_ENABLED); } void alexaLoop() { diff --git a/code/espurna/button.cpp b/code/espurna/button.cpp index 0babc2bf..84ed6956 100644 --- a/code/espurna/button.cpp +++ b/code/espurna/button.cpp @@ -42,6 +42,21 @@ debounce_event::types::Mode convert(const String& value) { } } +template<> +String serialize(const debounce_event::types::Mode& value) { + String result; + switch (value) { + case debounce_event::types::Mode::Switch: + result = "1"; + break; + case debounce_event::types::Mode::Pushbutton: + default: + result = "0"; + break; + } + return result; +} + template<> debounce_event::types::PinValue convert(const String& value) { switch (value.toInt()) { @@ -53,6 +68,21 @@ debounce_event::types::PinValue convert(const String& value) { } } +template<> +String serialize(const debounce_event::types::PinValue& value) { + String result; + switch (value) { + case debounce_event::types::PinValue::Low: + result = "0"; + break; + case debounce_event::types::PinValue::High: + default: + result = "1"; + break; + } + return result; +} + template<> debounce_event::types::PinMode convert(const String& value) { switch (value.toInt()) { @@ -66,6 +96,24 @@ debounce_event::types::PinMode convert(const String& value) { } } +template<> +String serialize(const debounce_event::types::PinMode& mode) { + String result; + switch (mode) { + case debounce_event::types::PinMode::InputPullup: + result = "1"; + break; + case debounce_event::types::PinMode::InputPulldown: + result = "2"; + break; + case debounce_event::types::PinMode::Input: + default: + result = "0"; + break; + } + return result; +} + } // namespace settings::internal } // namespace settings diff --git a/code/espurna/crash.cpp b/code/espurna/crash.cpp index 7da4b075..be832d1c 100644 --- a/code/espurna/crash.cpp +++ b/code/espurna/crash.cpp @@ -152,7 +152,7 @@ void crashSetup() { // Minumum of 16 and align for column formatter in crashDump() // Maximum of flash sector size minus reserved space at the beginning const uint16_t trace_max = constrain( - abs((getSetting("sysTraceMax", SAVE_CRASH_STACK_TRACE_MAX) + 15) & -16), + abs((getSetting("sysTraceMax", SAVE_CRASH_STACK_TRACE_MAX) + 15) & -16), 0, (SPI_FLASH_SEC_SIZE - crashUsedSpace()) ); diff --git a/code/espurna/debug.cpp b/code/espurna/debug.cpp index 6d11d584..b52af57f 100644 --- a/code/espurna/debug.cpp +++ b/code/espurna/debug.cpp @@ -29,6 +29,8 @@ char _udp_syslog_header[40] = {0}; bool _debug_enabled = false; + + // ----------------------------------------------------------------------------- // printf-like debug methods // ----------------------------------------------------------------------------- @@ -41,7 +43,7 @@ void _debugSendInternal(const char * message, bool add_timestamp = DEBUG_ADD_TIM void _debugSend(const char * format, va_list args) { char temp[DEBUG_SEND_STRING_BUFFER_SIZE]; - int len = ets_vsnprintf(temp, sizeof(temp), format, args); + int len = vsnprintf(temp, sizeof(temp), format, args); // strlen(...) + '\0' already in temp buffer, avoid using malloc when possible if (len < DEBUG_SEND_STRING_BUFFER_SIZE) { @@ -54,7 +56,7 @@ void _debugSend(const char * format, va_list args) { if (!buffer) { return; } - ets_vsnprintf(buffer, len, format, args); + vsnprintf(buffer, len, format, args); _debugSendInternal(buffer); free(buffer); @@ -282,19 +284,29 @@ void debugSetup() { } -String _debugLogModeSerialize(DebugLogMode value) { +namespace settings { +namespace internal { + +template<> +String serialize(const DebugLogMode& value) { + String result; switch (value) { case DebugLogMode::Disabled: - return "0"; + result = "0"; + break; case DebugLogMode::SkipBoot: - return "2"; + result = "2"; + break; default: case DebugLogMode::Enabled: - return "1"; + result = "1"; + break; } + return result; } -DebugLogMode _debugLogModeDeserialize(const String& value) { +template<> +DebugLogMode convert(const String& value) { switch (value.toInt()) { case 0: return DebugLogMode::Disabled; @@ -306,13 +318,16 @@ DebugLogMode _debugLogModeDeserialize(const String& value) { } } +} +} + void debugConfigureBoot() { static_assert( std::is_same::type>::value, "should be able to match DebugLogMode with int" ); - const auto mode = getSetting("dbgLogMode", DEBUG_LOG_MODE); + const auto mode = getSetting("dbgLogMode", DEBUG_LOG_MODE); switch (mode) { case DebugLogMode::SkipBoot: schedule_function([]() { diff --git a/code/espurna/influxdb.cpp b/code/espurna/influxdb.cpp index 4b7b16d8..5c4eb1de 100644 --- a/code/espurna/influxdb.cpp +++ b/code/espurna/influxdb.cpp @@ -210,7 +210,7 @@ void _idbFlush() { if (!wifiConnected()) return; const auto host = getSetting("idbHost", INFLUXDB_HOST); - const auto port = getSetting("idbPort", INFLUXDB_PORT); + const auto port = getSetting("idbPort", static_cast(INFLUXDB_PORT)); // TODO: should we always store specific pairs like tspk keeps relay / sensor readings? // note that we also send heartbeat data, persistent values should be flagged diff --git a/code/espurna/main.cpp b/code/espurna/main.cpp index f8acf103..3c804351 100644 --- a/code/espurna/main.cpp +++ b/code/espurna/main.cpp @@ -131,7 +131,7 @@ void setup() { // Return bogus free heap value for broken devices // XXX: device is likely to trigger other bugs! tread carefuly - wtfHeap(getSetting("wtfHeap", 0)); + wtfHeap(getSetting("wtfHeap", 0)); // Init Serial, SPIFFS and system check systemSetup(); diff --git a/code/espurna/mdns.cpp b/code/espurna/mdns.cpp index 8c858741..2dc4fc9f 100644 --- a/code/espurna/mdns.cpp +++ b/code/espurna/mdns.cpp @@ -45,7 +45,7 @@ void _mdnsServerStart() { void mdnsServerSetup() { #if WEB_SUPPORT - MDNS.addService("http", "tcp", getSetting("webPort", WEB_PORT)); + MDNS.addService("http", "tcp", getSetting("webPort", static_cast(WEB_PORT))); #endif #if TELNET_SUPPORT diff --git a/code/espurna/mqtt.cpp b/code/espurna/mqtt.cpp index 79ca7999..b6c4f56b 100644 --- a/code/espurna/mqtt.cpp +++ b/code/espurna/mqtt.cpp @@ -127,7 +127,7 @@ SecureClientConfig _mqtt_sc_config { return getSetting("mqttFP", MQTT_SSL_FINGERPRINT); }, []() -> uint16_t { - return getSetting("mqttScMFLN", MQTT_SECURE_CLIENT_MFLN); + return getSetting("mqttScMFLN", MQTT_SECURE_CLIENT_MFLN); }, true }; @@ -258,7 +258,7 @@ void _mqttConfigure() { // Enable only when server is set { const String server = getSetting("mqttServer", MQTT_SERVER); - const auto port = getSetting("mqttPort", MQTT_PORT); + const auto port = getSetting("mqttPort", static_cast(MQTT_PORT)); bool enabled = false; if (server.length()) { enabled = getSetting("mqttEnabled", 1 == MQTT_ENABLED); diff --git a/code/espurna/sensor.cpp b/code/espurna/sensor.cpp index f64d870d..df5e9593 100644 --- a/code/espurna/sensor.cpp +++ b/code/espurna/sensor.cpp @@ -201,7 +201,6 @@ Copyright (C) 2016-2019 by Xose PĂ©rez //-------------------------------------------------------------------------------- - struct sensor_magnitude_t { private: @@ -215,17 +214,19 @@ struct sensor_magnitude_t { } sensor_magnitude_t(); - sensor_magnitude_t(unsigned char type, unsigned char local, sensor::Unit units, BaseSensor* sensor); + sensor_magnitude_t(unsigned char slot, unsigned char index_local, unsigned char type, sensor::Unit units, BaseSensor* sensor); BaseSensor * sensor; // Sensor object BaseFilter * filter; // Filter object - unsigned char type; // Type of measurement - unsigned char local; // Local index in its provider - unsigned char global; // Global index in its type - unsigned char decimals; // Number of decimals in textual representation + unsigned char slot; // Sensor slot # taken by the magnitude, used to access the measurement + unsigned char type; // Type of measurement, returned by the BaseSensor::type(slot) + + unsigned char index_local; // N'th magnitude of it's type, local to the sensor + unsigned char index_global; // ... and across all of the active sensors sensor::Unit units; // Units of measurement + unsigned char decimals; // Number of decimals in textual representation double last; // Last raw value from sensor (unfiltered) double reported; // Last reported value @@ -375,7 +376,7 @@ void _sensorApiResetEnergy(const sensor_magnitude_t& magnitude, const char* payl auto* sensor = static_cast(magnitude.sensor); auto energy = _sensorParseEnergy(payload); - sensor->resetEnergy(magnitude.global, energy); + sensor->resetEnergy(magnitude.index_global, energy); } sensor::Energy _sensorEnergyTotal(unsigned char index) { @@ -413,21 +414,21 @@ void _magnitudeSaveEnergyTotal(sensor_magnitude_t& magnitude, bool persistent) { const auto energy = sensor->totalEnergy(); // Always save to RTCMEM - if (magnitude.global < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy))) { - _sensorRtcmemSaveEnergy(magnitude.global, energy); + if (magnitude.index_global < (sizeof(Rtcmem->energy) / sizeof(*Rtcmem->energy))) { + _sensorRtcmemSaveEnergy(magnitude.index_global, energy); } // Save to EEPROM every '_sensor_save_every' readings // Format is `+`, value without `+` is treated as `` if (persistent && _sensor_save_every) { - _sensor_save_count[magnitude.global] = - (_sensor_save_count[magnitude.global] + 1) % _sensor_save_every; + _sensor_save_count[magnitude.index_global] = + (_sensor_save_count[magnitude.index_global] + 1) % _sensor_save_every; - if (0 == _sensor_save_count[magnitude.global]) { + if (0 == _sensor_save_count[magnitude.index_global]) { const String total = String(energy.kwh.value) + "+" + String(energy.ws.value); - setSetting({"eneTotal", magnitude.global}, total); + setSetting({"eneTotal", magnitude.index_global}, total); #if NTP_SUPPORT - if (ntpSynced()) setSetting({"eneTime", magnitude.global}, ntpDateTime()); + if (ntpSynced()) setSetting({"eneTime", magnitude.index_global}, ntpDateTime()); #endif } } @@ -450,11 +451,12 @@ unsigned char _sensor_report_every = SENSOR_REPORT_EVERY; sensor_magnitude_t::sensor_magnitude_t() : sensor(nullptr), filter(nullptr), + slot(0), type(0), - local(0), - global(0), - decimals(0), + index_local(0), + index_global(0), units(sensor::Unit::None), + decimals(0), last(0.0), reported(0.0), min_change(0.0), @@ -462,14 +464,15 @@ sensor_magnitude_t::sensor_magnitude_t() : correction(0.0) {} -sensor_magnitude_t::sensor_magnitude_t(unsigned char type, unsigned char local, sensor::Unit units, BaseSensor* sensor) : +sensor_magnitude_t::sensor_magnitude_t(unsigned char slot, unsigned char index_local, unsigned char type, sensor::Unit units, BaseSensor* sensor) : sensor(sensor), filter(nullptr), + slot(slot), type(type), - local(local), - global(_counts[type]), - decimals(0), + index_local(index_local), + index_global(_counts[type]), units(units), + decimals(0), last(0.0), reported(0.0), min_change(0.0), @@ -784,7 +787,7 @@ 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.local)) { + switch (magnitude.sensor->units(magnitude.slot)) { case sensor::Unit::Celcius: if (magnitude.units == sensor::Unit::Farenheit) { value = (value * 1.8) + 32.0; @@ -822,8 +825,84 @@ double _magnitudeProcess(const sensor_magnitude_t& magnitude, double value) { // ----------------------------------------------------------------------------- +bool _sensorMatchKeyPrefix(const char * key) { + if (strncmp(key, "pwr", 3) == 0) return true; + if (strncmp(key, "sns", 3) == 0) return true; + if (strncmp(key, "tmp", 3) == 0) return true; + if (strncmp(key, "hum", 3) == 0) return true; + if (strncmp(key, "ene", 3) == 0) return true; + if (strncmp(key, "lux", 3) == 0) return true; + return false; +} + +const String _sensorQueryDefault(const String& key) { + + auto get_defaults = [](unsigned char type, BaseSensor* ptr) -> String { + if (!ptr) return String(); + auto* sensor = static_cast(ptr); + switch (type) { + case MAGNITUDE_CURRENT: + return String(sensor->defaultCurrentRatio()); + case MAGNITUDE_VOLTAGE: + return String(sensor->defaultVoltageRatio()); + case MAGNITUDE_POWER_ACTIVE: + return String(sensor->defaultPowerRatio()); + case MAGNITUDE_ENERGY: + return String(sensor->defaultEnergyRatio()); + default: + return String(); + } + }; + + auto magnitude_key = [](const sensor_magnitude_t& magnitude) -> settings_key_t { + switch (magnitude.type) { + case MAGNITUDE_CURRENT: + return {"pwrRatioC", magnitude.index_global}; + case MAGNITUDE_VOLTAGE: + return {"pwrRatioV", magnitude.index_global}; + case MAGNITUDE_POWER_ACTIVE: + return {"pwrRatioP", magnitude.index_global}; + case MAGNITUDE_ENERGY: + return {"pwrRatioE", magnitude.index_global}; + default: + return {}; + } + }; + + unsigned char type = MAGNITUDE_NONE; + BaseSensor* target = nullptr; + + for (auto& magnitude : _magnitudes) { + switch (magnitude.type) { + case MAGNITUDE_CURRENT: + case MAGNITUDE_VOLTAGE: + case MAGNITUDE_POWER_ACTIVE: + case MAGNITUDE_ENERGY: { + auto ratioKey(magnitude_key(magnitude)); + if (ratioKey.match(key)) { + target = magnitude.sensor; + type = magnitude.type; + goto return_defaults; + } + break; + } + default: + break; + } + } + +return_defaults: + + return get_defaults(type, target); + +} + #if WEB_SUPPORT +bool _sensorWebSocketOnKeyCheck(const char* key, JsonVariant&) { + return _sensorMatchKeyPrefix(key); +} + // Used by modules to generate magnitude_id<->module_id mapping for the WebUI void sensorWebSocketMagnitudes(JsonObject& root, const String& prefix) { @@ -848,16 +927,6 @@ void sensorWebSocketMagnitudes(JsonObject& root, const String& prefix) { } } -bool _sensorWebSocketOnKeyCheck(const char * key, JsonVariant& value) { - if (strncmp(key, "pwr", 3) == 0) return true; - if (strncmp(key, "sns", 3) == 0) return true; - if (strncmp(key, "tmp", 3) == 0) return true; - if (strncmp(key, "hum", 3) == 0) return true; - if (strncmp(key, "ene", 3) == 0) return true; - if (strncmp(key, "lux", 3) == 0) return true; - return false; -} - void _sensorWebSocketOnVisible(JsonObject& root) { root["snsVisible"] = 1; @@ -888,14 +957,11 @@ void _sensorWebSocketMagnitudesConfig(JsonObject& root) { if (magnitude.type == MAGNITUDE_EVENT) continue; ++size; - index.add(magnitude.global); + // Note: we use int instead of bool to ever so slightly compress json output + index.add(magnitude.index_global); type.add(magnitude.type); units.add(_magnitudeUnits(magnitude)); - - { - String sensor_desc = magnitude.sensor->slot(magnitude.local); - description.add(sensor_desc); - } + description.add(magnitude.sensor->description(magnitude.slot)); } @@ -928,7 +994,7 @@ void _sensorWebSocketSendData(JsonObject& root) { #if NTP_SUPPORT if ((_sensor_save_every > 0) && (magnitude.type == MAGNITUDE_ENERGY)) { String string = F("Last saved: "); - string += getSetting({"eneTime", magnitude.global}, F("(unknown)")); + string += getSetting({"eneTime", magnitude.index_global}, F("(unknown)")); info.add(string); } else { info.add((uint8_t)0); @@ -1029,7 +1095,7 @@ void _sensorAPISetup() { auto& magnitude = _magnitudes.at(magnitude_id); String topic = magnitudeTopic(magnitude.type); - if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) topic = topic + "/" + String(magnitude.global); + if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) topic = topic + "/" + String(magnitude.index_global); api_get_callback_f get_cb = [&magnitude](char * buffer, size_t len) { double value = _sensor_realtime ? magnitude.last : magnitude.reported; @@ -1065,7 +1131,7 @@ void _sensorMqttCallback(unsigned int type, const char* topic, char* payload) { for (auto& magnitude : _magnitudes) { if (MAGNITUDE_ENERGY != magnitude.type) continue; - if (index != magnitude.global) continue; + if (index != magnitude.index_global) continue; _sensorApiResetEnergy(magnitude, payload); break; } @@ -1099,8 +1165,8 @@ void _sensorInitCommands() { dtostrf(magnitude.reported, 1, magnitude.decimals, reported); DEBUG_MSG_P(PSTR("[SENSOR] %2u * %s/%u @ %s (last:%s, reported:%s)\n"), index, - magnitudeTopic(magnitude.type).c_str(), magnitude.global, - magnitude.sensor->slot(magnitude.local).c_str(), + magnitudeTopic(magnitude.type).c_str(), magnitude.index_global, + magnitude.sensor->description(magnitude.slot).c_str(), last, reported ); } @@ -1207,7 +1273,7 @@ void _sensorLoad() { #if BMX280_SUPPORT { // Support up to two sensors with full auto-discovery. - const unsigned char number = constrain(getSetting("bmx280Number", BMX280_NUMBER), 1, 2); + const unsigned char number = constrain(getSetting("bmx280Number", BMX280_NUMBER), 1, 2); // For second sensor, if BMX280_ADDRESS is 0x00 then auto-discover // otherwise choose the other unnamed sensor address @@ -1742,7 +1808,7 @@ void _sensorReport(unsigned char index, double value) { dtostrf(value, 1, magnitude.decimals, buffer); #if BROKER_SUPPORT - SensorReportBroker::Publish(magnitudeTopic(magnitude.type), magnitude.global, value, buffer); + SensorReportBroker::Publish(magnitudeTopic(magnitude.type), magnitude.index_global, value, buffer); #endif #if MQTT_SUPPORT @@ -1753,9 +1819,9 @@ void _sensorReport(unsigned char index, double value) { char topic[32]; snprintf(topic, sizeof(topic), "%s/%s", SENSOR_ADDRESS_TOPIC, magnitudeTopic(magnitude.type).c_str()); if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) { - mqttSend(topic, magnitude.global, magnitude.sensor->address(magnitude.local).c_str()); + mqttSend(topic, magnitude.index_global, magnitude.sensor->address(magnitude.slot).c_str()); } else { - mqttSend(topic, magnitude.sensor->address(magnitude.local).c_str()); + mqttSend(topic, magnitude.sensor->address(magnitude.slot).c_str()); } #endif // SENSOR_PUBLISH_ADDRESSES @@ -1788,7 +1854,6 @@ void _sensorCallback(unsigned char i, unsigned char type, double value) { void _sensorInit() { _sensors_ready = true; - _sensor_save_every = 0; for (unsigned char i=0; i<_sensors.size(); i++) { @@ -1808,14 +1873,19 @@ void _sensorInit() { for (unsigned char magnitude_index = 0; magnitude_index < _sensors[i]->count(); ++magnitude_index) { const auto magnitude_type = _sensors[i]->type(magnitude_index); + const auto magnitude_local = _sensors[i]->local(magnitude_type); _magnitudes.emplace_back( - magnitude_type, // specific type of the magnitude - magnitude_index, // index local to the sensor - sensor::Unit::None, // set up later, in configuration - _sensors[i] // bind the sensor to allow us to reference it later + magnitude_index, // id of the magnitude, unique to the sensor + magnitude_local, // index_local, # of the magnitude + magnitude_type, // specific type of the magnitude + sensor::Unit::None, // set up later, in configuration + _sensors[i] // bind the sensor to allow us to reference it later ); - if (MAGNITUDE_ENERGY == magnitude_type) { + if (_sensorIsEmon(_sensors[i]) && (MAGNITUDE_ENERGY == magnitude_type)) { + const auto index_global = _magnitudes.back().index_global; + auto* sensor = static_cast(_sensors[i]); + sensor->resetEnergy(magnitude_local, _sensorEnergyTotal(index_global)); _sensor_save_count.push_back(0); } @@ -1846,36 +1916,6 @@ 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]); - - for (size_t index = 0; index < sensor->countDevices(); ++index) { - sensor->resetEnergy(index, _sensorEnergyTotal(index)); - sensor->setCurrentRatio( - index, get_ratio("pwrRatioC", index, sensor->getCurrentRatio(index)) - ); - sensor->setVoltageRatio( - index, get_ratio("pwrRatioV", index, sensor->getVoltageRatio(index)) - ); - sensor->setPowerRatio( - index, get_ratio("pwrRatioP", index, sensor->getPowerRatio(index)) - ); - sensor->setEnergyRatio( - index, get_ratio("pwrRatioE", index, sensor->getEnergyRatio(index)) - ); - sensor->setVoltage( - index, get_ratio("pwrVoltage", index, sensor->getVoltage(index)) - ); - } - - } - } } @@ -1893,6 +1933,11 @@ sensor::Unit convert(const String& string) { return sensor::Unit::None; } +template <> +String serialize(const sensor::Unit& unit) { + return String(static_cast(unit)); +} + } // ns settings::internal } // ns settings @@ -1913,7 +1958,7 @@ void _sensorConfigure() { const auto hum_min_delta = getSetting("humMinDelta", HUMIDITY_MIN_CHANGE); const auto ene_max_delta = getSetting("eneMaxDelta", ENERGY_MAX_CHANGE); - // Specific sensor settings + // Apply settings based on sensor type for (unsigned char index = 0; index < _sensors.size(); ++index) { #if MICS2710_SUPPORT || MICS5525_SUPPORT @@ -1974,8 +2019,6 @@ void _sensorConfigure() { sensor->resetRatios(); } - sensor->setEnergyRatio(getSetting("pwrRatioE", sensor->getEnergyRatio())); - } // is emon? } @@ -1997,34 +2040,73 @@ void _sensorConfigure() { auto& magnitude = _magnitudes.at(index); + // process emon-specific settings first. ensure that settings use global index and we access sensor with the local one + if (_sensorIsEmon(magnitude.sensor)) { + // 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)); + }; + + auto* sensor = static_cast(magnitude.sensor); + + switch (magnitude.type) { + case MAGNITUDE_CURRENT: + sensor->setCurrentRatio( + magnitude.index_local, get_ratio("pwrRatioC", magnitude.index_global, sensor->defaultCurrentRatio()) + ); + break; + case MAGNITUDE_POWER_ACTIVE: + sensor->setPowerRatio( + magnitude.index_local, get_ratio("pwrRatioP", magnitude.index_global, sensor->defaultPowerRatio()) + ); + break; + case MAGNITUDE_VOLTAGE: + sensor->setVoltageRatio( + magnitude.index_local, get_ratio("pwrRatioV", magnitude.index_global, sensor->defaultVoltageRatio()) + ); + sensor->setVoltage( + magnitude.index_local, get_ratio("pwrVoltage", magnitude.index_global, sensor->defaultVoltage()) + ); + break; + case MAGNITUDE_ENERGY: + sensor->setEnergyRatio( + magnitude.index_local, get_ratio("pwrRatioE", magnitude.index_global, sensor->defaultEnergyRatio()) + ); + break; + default: + break; + } + } + + // adjust type-specific units (TODO: try to adjust settings to use type prefixes?) switch (magnitude.type) { case MAGNITUDE_TEMPERATURE: magnitude.units = _magnitudeUnitFilter( magnitude, - getSetting({"tmpUnits", magnitude.global}, tmpUnits) + getSetting({"tmpUnits", magnitude.index_global}, tmpUnits) ); - magnitude.correction = getSetting({"tmpCorrection", magnitude.global}, tmpCorrection); + magnitude.correction = getSetting({"tmpCorrection", magnitude.index_global}, tmpCorrection); break; case MAGNITUDE_HUMIDITY: - magnitude.correction = getSetting({"humCorrection", magnitude.global}, humCorrection); + magnitude.correction = getSetting({"humCorrection", magnitude.index_global}, humCorrection); break; case MAGNITUDE_POWER_ACTIVE: magnitude.units = _magnitudeUnitFilter( magnitude, - getSetting({"pwrUnits", magnitude.global}, pwrUnits) + getSetting({"pwrUnits", magnitude.index_global}, pwrUnits) ); break; case MAGNITUDE_ENERGY: magnitude.units = _magnitudeUnitFilter( magnitude, - getSetting({"eneUnits", magnitude.global}, eneUnits) + getSetting({"eneUnits", magnitude.index_global}, eneUnits) ); break; case MAGNITUDE_LUX: - magnitude.correction = getSetting({"luxCorrection", magnitude.global}, luxCorrection); + magnitude.correction = getSetting({"luxCorrection", magnitude.index_global}, luxCorrection); break; default: - magnitude.units = magnitude.sensor->units(magnitude.local); + magnitude.units = magnitude.sensor->units(magnitude.slot); break; } @@ -2037,6 +2119,7 @@ void _sensorConfigure() { // adjust min & max change delta value to trigger report // TODO: find a proper way to extend this to min/max of any magnitude + // TODO: we can't use index_global b/c we don't specify type in the setting { auto min_default = 0.0; auto max_default = 0.0; @@ -2061,7 +2144,7 @@ void _sensorConfigure() { // in case we don't save energy periodically, purge existing value in ram & settings if ((MAGNITUDE_ENERGY == magnitude.type) && (0 == _sensor_save_every)) { - _sensorResetEnergyTotal(magnitude.global); + _sensorResetEnergyTotal(magnitude.index_global); } } @@ -2086,7 +2169,7 @@ unsigned char magnitudeCount() { String magnitudeName(unsigned char index) { if (index < _magnitudes.size()) { sensor_magnitude_t magnitude = _magnitudes[index]; - return magnitude.sensor->slot(magnitude.local); + return magnitude.sensor->description(magnitude.slot); } return String(); } @@ -2107,7 +2190,7 @@ double magnitudeValue(unsigned char index) { unsigned char magnitudeIndex(unsigned char index) { if (index < _magnitudes.size()) { - return int(_magnitudes[index].global); + return int(_magnitudes[index].index_global); } return 0; } @@ -2117,7 +2200,7 @@ String magnitudeTopicIndex(unsigned char index) { if (index < _magnitudes.size()) { sensor_magnitude_t magnitude = _magnitudes[index]; if (SENSOR_USE_INDEX || (sensor_magnitude_t::counts(magnitude.type) > 1)) { - snprintf(topic, sizeof(topic), "%s/%u", magnitudeTopic(magnitude.type).c_str(), magnitude.global); + snprintf(topic, sizeof(topic), "%s/%u", magnitudeTopic(magnitude.type).c_str(), magnitude.index_global); } else { snprintf(topic, sizeof(topic), "%s", magnitudeTopic(magnitude.type).c_str()); } @@ -2133,7 +2216,7 @@ void _sensorBackwards() { moveSetting("powerUnits", "pwrUnits"); moveSetting("energyUnits", "eneUnits"); - // Energy is now indexed (based on magnitude.global) + // Energy is now indexed (based on magnitude.index_global) moveSetting("eneTotal", "eneTotal0"); // Update PZEM004T energy total across multiple devices @@ -2165,6 +2248,15 @@ void sensorSetup() { // Configure based on settings _sensorConfigure(); + // Allow us to query key default + settingsRegisterDefaults({ + [](const char* key) -> bool { + if (strncmp(key, "pwr", 3) == 0) return true; + return false; + }, + _sensorQueryDefault + }); + // Websockets integration, send sensor readings and configuration #if WEB_SUPPORT wsRegister() @@ -2242,7 +2334,7 @@ void sensorLoop() { // Instant value // ------------------------------------------------------------- - value_raw = magnitude.sensor->value(magnitude.local); + value_raw = magnitude.sensor->value(magnitude.slot); // Completely remove spurious values if relay is OFF #if RELAY_SUPPORT && SENSOR_POWER_CHECK_STATUS @@ -2290,7 +2382,7 @@ void sensorLoop() { { char buffer[64]; dtostrf(value_show, 1, magnitude.decimals, buffer); - SensorReadBroker::Publish(magnitudeTopic(magnitude.type), magnitude.global, value_show, buffer); + SensorReadBroker::Publish(magnitudeTopic(magnitude.type), magnitude.index_global, value_show, buffer); } #endif @@ -2303,7 +2395,7 @@ void sensorLoop() { char buffer[64]; dtostrf(value_show, 1, magnitude.decimals, buffer); DEBUG_MSG_P(PSTR("[SENSOR] %s - %s: %s%s\n"), - magnitude.sensor->slot(magnitude.local).c_str(), + magnitude.sensor->description(magnitude.slot).c_str(), magnitudeTopic(magnitude.type).c_str(), buffer, _magnitudeUnits(magnitude).c_str() diff --git a/code/espurna/sensors/ADE7953Sensor.h b/code/espurna/sensors/ADE7953Sensor.h index bfc607fb..9c1e173c 100644 --- a/code/espurna/sensors/ADE7953Sensor.h +++ b/code/espurna/sensors/ADE7953Sensor.h @@ -75,11 +75,10 @@ class ADE7953Sensor : public I2CSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; - // Pre-read hook (usually to populate registers with up-to-date data) void pre() { uint32_t active_power1 = 0; @@ -150,6 +149,12 @@ class ADE7953Sensor : public I2CSensor { return 0; } + // Convert slot # to a magnitude # + unsigned char local(unsigned char index) override { + if (index == 0) { return 0; } // common voltage + return (index - 1) / countDevices(); // device { energy, current, active power } + } + // Type for slot # index unsigned char type(unsigned char index) { if (index == 0) return MAGNITUDE_VOLTAGE; diff --git a/code/espurna/sensors/AM2320Sensor.h b/code/espurna/sensors/AM2320Sensor.h index b967f4ad..51c1b139 100644 --- a/code/espurna/sensors/AM2320Sensor.h +++ b/code/espurna/sensors/AM2320Sensor.h @@ -65,7 +65,7 @@ class AM2320Sensor : public I2CSensor<> { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/AnalogSensor.h b/code/espurna/sensors/AnalogSensor.h index 45840b79..f38e7e72 100644 --- a/code/espurna/sensors/AnalogSensor.h +++ b/code/espurna/sensors/AnalogSensor.h @@ -78,7 +78,7 @@ class AnalogSensor : public BaseAnalogSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/BaseEmonSensor.h b/code/espurna/sensors/BaseEmonSensor.h index d294929f..4f685459 100644 --- a/code/espurna/sensors/BaseEmonSensor.h +++ b/code/espurna/sensors/BaseEmonSensor.h @@ -67,6 +67,10 @@ class BaseEmonSensor : public BaseSensor { } // --- configuration --- + + virtual double defaultVoltage() { + return 0.0; + } // when sensor needs explicit mains voltage value virtual void setVoltage(double) {} @@ -74,7 +78,7 @@ class BaseEmonSensor : public BaseSensor { setVoltage(value); } virtual double getVoltage() { - return 0.0; + return defaultVoltage(); } virtual double getVoltage(unsigned char index) { return getVoltage(); @@ -100,18 +104,33 @@ class BaseEmonSensor : public BaseSensor { setEnergyRatio(value); } + // Generic ratio configuration, default is a + // no-op and must be implemented by the sensor class + virtual double defaultCurrentRatio() const { + return 0.0; + } + virtual double defaultVoltageRatio() const { + return 0.0; + } + virtual double defaultPowerRatio() const { + return 0.0; + } + virtual double defaultEnergyRatio() const { + return 0.0; + } + // when sensor implements a single device virtual double getCurrentRatio() { - return 0.0; + return defaultCurrentRatio(); } virtual double getVoltageRatio() { - return 0.0; + return defaultVoltageRatio(); } virtual double getPowerRatio() { - return 0.0; + return defaultPowerRatio(); } virtual double getEnergyRatio() { - return 0.0; + return defaultEnergyRatio(); } // when sensor implements more than one device @@ -132,7 +151,16 @@ class BaseEmonSensor : public BaseSensor { virtual void expectedVoltage(unsigned int value) {} virtual void expectedPower(unsigned int value) {} - virtual void resetCalibration(double value) {} + virtual void expectedCurrent(unsigned char index, double value) { + expectedCurrent(value); + } + virtual void expectedVoltage(unsigned char index, unsigned int value) { + expectedVoltage(value); + } + virtual void expectedPower(unsigned char index, unsigned int value) { + expectedPower(value); + } + virtual void resetRatios() {} protected: diff --git a/code/espurna/sensors/BaseSensor.h b/code/espurna/sensors/BaseSensor.h index 5102e16e..e41ed923 100644 --- a/code/espurna/sensors/BaseSensor.h +++ b/code/espurna/sensors/BaseSensor.h @@ -39,12 +39,12 @@ class BaseSensor { // Descriptive name of the sensor virtual String description() = 0; + // Descriptive name of the slot # index + virtual String description(unsigned char index) = 0; + // Address of the sensor (it could be the GPIO or I2C address) virtual String address(unsigned char index) = 0; - // Descriptive name of the slot # index - virtual String slot(unsigned char index) = 0; - // Type of sensor virtual unsigned char type() { return sensor::type::Base; } @@ -84,6 +84,9 @@ class BaseSensor { // Number of available slots unsigned char count() { return _count; } + // Convert slot # index to a magnitude # index + virtual unsigned char local(unsigned char slot) { return 0; } + // Hook for event callback void onEvent(TSensorCallback fn) { _callback = fn; }; diff --git a/code/espurna/sensors/CSE7766Sensor.h b/code/espurna/sensors/CSE7766Sensor.h index a1031662..fd52efc4 100644 --- a/code/espurna/sensors/CSE7766Sensor.h +++ b/code/espurna/sensors/CSE7766Sensor.h @@ -59,50 +59,64 @@ class CSE7766Sensor : public BaseEmonSensor { // --------------------------------------------------------------------- - void expectedCurrent(double expected) { + void expectedCurrent(double expected) override { if ((expected > 0) && (_current > 0)) { _ratioC = _ratioC * (expected / _current); } } - void expectedVoltage(unsigned int expected) { + void expectedVoltage(unsigned int expected) override { if ((expected > 0) && (_voltage > 0)) { _ratioV = _ratioV * (expected / _voltage); } } - void expectedPower(unsigned int expected) { + void expectedPower(unsigned int expected) override { if ((expected > 0) && (_active > 0)) { _ratioP = _ratioP * (expected / _active); } } - void setCurrentRatio(double value) { + double defaultCurrentRatio() const override { + return 1.0; + } + + double defaultVoltageRatio() const override { + return 1.0; + } + + double defaultPowerRatio() const override { + return 1.0; + } + + void setCurrentRatio(double value) override { _ratioC = value; }; - void setVoltageRatio(double value) { + void setVoltageRatio(double value) override { _ratioV = value; }; - void setPowerRatio(double value) { + void setPowerRatio(double value) override { _ratioP = value; }; - double getCurrentRatio() { + double getCurrentRatio() override { return _ratioC; }; - double getVoltageRatio() { + double getVoltageRatio() override { return _ratioV; }; - double getPowerRatio() { + double getPowerRatio() override { return _ratioP; }; - void resetCalibration() { - _ratioC = _ratioV = _ratioP = 1.0; + void resetRatios() override { + _ratioC = defaultCurrentRatio(); + _ratioV = defaultVoltageRatio(); + _ratioP = defaultPowerRatio(); } // --------------------------------------------------------------------- @@ -112,6 +126,8 @@ class CSE7766Sensor : public BaseEmonSensor { // Initialization method, must be idempotent void begin() { + resetRatios(); + if (!_dirty) return; if (_serial) delete _serial; @@ -141,7 +157,7 @@ class CSE7766Sensor : public BaseEmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; @@ -387,9 +403,9 @@ class CSE7766Sensor : public BaseEmonSensor { double _voltage = 0; double _current = 0; - double _ratioV = 1.0; - double _ratioC = 1.0; - double _ratioP = 1.0; + double _ratioV; + double _ratioC; + double _ratioP; unsigned char _data[24]; diff --git a/code/espurna/sensors/DHTSensor.h b/code/espurna/sensors/DHTSensor.h index 589b2f9d..a0288415 100644 --- a/code/espurna/sensors/DHTSensor.h +++ b/code/espurna/sensors/DHTSensor.h @@ -132,7 +132,7 @@ class DHTSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/DallasSensor.h b/code/espurna/sensors/DallasSensor.h index e8646a87..bddefa6c 100644 --- a/code/espurna/sensors/DallasSensor.h +++ b/code/espurna/sensors/DallasSensor.h @@ -239,7 +239,7 @@ class DallasSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { if (index < _count) { char buffer[40]; uint8_t * address = _devices[index].address; diff --git a/code/espurna/sensors/DigitalSensor.h b/code/espurna/sensors/DigitalSensor.h index 884fe9c9..3c779432 100644 --- a/code/espurna/sensors/DigitalSensor.h +++ b/code/espurna/sensors/DigitalSensor.h @@ -70,7 +70,7 @@ class DigitalSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/ECH1560Sensor.h b/code/espurna/sensors/ECH1560Sensor.h index 8187a15c..d19783ab 100644 --- a/code/espurna/sensors/ECH1560Sensor.h +++ b/code/espurna/sensors/ECH1560Sensor.h @@ -94,7 +94,7 @@ class ECH1560Sensor : public BaseEmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/EZOPHSensor.h b/code/espurna/sensors/EZOPHSensor.h index 168ba8a5..eedee0e7 100644 --- a/code/espurna/sensors/EZOPHSensor.h +++ b/code/espurna/sensors/EZOPHSensor.h @@ -81,7 +81,7 @@ class EZOPHSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/EmonADC121Sensor.h b/code/espurna/sensors/EmonADC121Sensor.h index a62bc7bc..c56b33c5 100644 --- a/code/espurna/sensors/EmonADC121Sensor.h +++ b/code/espurna/sensors/EmonADC121Sensor.h @@ -119,7 +119,7 @@ class EmonADC121Sensor : public EmonSensor { // Current value for slot # index double value(unsigned char index) { - unsigned char channel = index / _magnitudes; + unsigned char channel = local(index); unsigned char i=0; #if EMON_REPORT_CURRENT if (index == i++) return _current[channel]; diff --git a/code/espurna/sensors/EmonADS1X15Sensor.h b/code/espurna/sensors/EmonADS1X15Sensor.h index f065ed7c..59b83e63 100644 --- a/code/espurna/sensors/EmonADS1X15Sensor.h +++ b/code/espurna/sensors/EmonADS1X15Sensor.h @@ -146,6 +146,11 @@ class EmonADS1X15Sensor : public EmonSensor { // Sensor API // --------------------------------------------------------------------- + // Convert slot # index to a magnitude # index + unsigned char local(unsigned char index) override { + return (_ports) ? (index / _ports) : 0u; + } + // Initialization method, must be idempotent void begin() { @@ -194,7 +199,7 @@ class EmonADS1X15Sensor : public EmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { char buffer[35]; unsigned char channel = getChannel(index % _ports); snprintf(buffer, sizeof(buffer), "EMON @ ADS1%d15 (A%d) @ I2C (0x%02X)", _type == ADS1X15_CHIP_ADS1015 ? 0 : 1, channel, _address); diff --git a/code/espurna/sensors/EmonSensor.h b/code/espurna/sensors/EmonSensor.h index da487d39..aaf514d5 100644 --- a/code/espurna/sensors/EmonSensor.h +++ b/code/espurna/sensors/EmonSensor.h @@ -41,7 +41,9 @@ class EmonSensor : public I2CSensor { } - void expectedPower(unsigned char channel, unsigned int expected) { + // --------------------------------------------------------------------- + + void expectedPower(unsigned char channel, unsigned int expected) override { if (channel >= _channels) return; unsigned int actual = _current[channel] * _voltage; if (actual == 0) return; @@ -51,9 +53,7 @@ class EmonSensor : public I2CSensor { _dirty = true; } - // --------------------------------------------------------------------- - - void setVoltage(double voltage) { + void setVoltage(double voltage) override { if (_voltage == voltage) return; _voltage = voltage; _dirty = true; @@ -65,7 +65,11 @@ class EmonSensor : public I2CSensor { _dirty = true; } - void setCurrentRatio(unsigned char channel, double current_ratio) { + double defaultCurrentRatio() const override { + return EMON_CURRENT_RATIO; + } + + void setCurrentRatio(unsigned char channel, double current_ratio) override { if (channel >= _channels) return; if (_current_ratio[channel] == current_ratio) return; _current_ratio[channel] = current_ratio; @@ -73,13 +77,15 @@ class EmonSensor : public I2CSensor { _dirty = true; } - void resetRatios() { - setCurrentRatio(0, EMON_CURRENT_RATIO); + void resetRatios() override { + for (unsigned char channel = 0; channel < _channels; ++channel) { + setCurrentRatio(channel, defaultCurrentRatio()); + } } // --------------------------------------------------------------------- - double getVoltage() { + double getVoltage() override { return _voltage; } @@ -87,12 +93,12 @@ class EmonSensor : public I2CSensor { return _reference; } - double getCurrentRatio(unsigned char channel) { + double getCurrentRatio(unsigned char channel) override { if (channel >= _channels) return 0; return _current_ratio[channel]; } - unsigned char getChannels() { + size_t countDevices() override { return _channels; } @@ -128,6 +134,11 @@ class EmonSensor : public I2CSensor { } + // Convert slot # index to a magnitude # index + unsigned char local(unsigned char index) override { + return (_magnitudes) ? (index / _magnitudes) : 0u; + } + protected: // --------------------------------------------------------------------- diff --git a/code/espurna/sensors/EventSensor.h b/code/espurna/sensors/EventSensor.h index cf542eed..53dc2461 100644 --- a/code/espurna/sensors/EventSensor.h +++ b/code/espurna/sensors/EventSensor.h @@ -107,7 +107,7 @@ class EventSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/GUVAS12SDSensor.h b/code/espurna/sensors/GUVAS12SDSensor.h index 7f1e34e0..cb06a0ae 100644 --- a/code/espurna/sensors/GUVAS12SDSensor.h +++ b/code/espurna/sensors/GUVAS12SDSensor.h @@ -91,7 +91,7 @@ class GUVAS12SDSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/GeigerSensor.h b/code/espurna/sensors/GeigerSensor.h index af3bd8b1..7ed39d0d 100644 --- a/code/espurna/sensors/GeigerSensor.h +++ b/code/espurna/sensors/GeigerSensor.h @@ -94,7 +94,7 @@ String description() { } // Descriptive name of the slot # index -String slot(unsigned char index) { +String description(unsigned char index) { char buffer[30]; unsigned char i=0; #if GEIGER_REPORT_CPM diff --git a/code/espurna/sensors/HDC1080Sensor.h b/code/espurna/sensors/HDC1080Sensor.h index b7c7d31a..ced16678 100644 --- a/code/espurna/sensors/HDC1080Sensor.h +++ b/code/espurna/sensors/HDC1080Sensor.h @@ -57,7 +57,7 @@ class HDC1080Sensor : public I2CSensor<> { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/HLW8012Sensor.h b/code/espurna/sensors/HLW8012Sensor.h index 1f1025c7..ef0bf92a 100644 --- a/code/espurna/sensors/HLW8012Sensor.h +++ b/code/espurna/sensors/HLW8012Sensor.h @@ -33,22 +33,6 @@ class HLW8012Sensor : public BaseEmonSensor { delete _hlw8012; } - void expectedCurrent(double expected) { - _hlw8012->expectedCurrent(expected); - } - - void expectedVoltage(unsigned int expected) { - _hlw8012->expectedVoltage(expected); - } - - void expectedPower(unsigned int expected) { - _hlw8012->expectedActivePower(expected); - } - - void resetRatios() { - _hlw8012->resetMultipliers(); - } - // --------------------------------------------------------------------- void setSEL(unsigned char sel) { @@ -73,18 +57,62 @@ class HLW8012Sensor : public BaseEmonSensor { _sel_current = value; } - void setCurrentRatio(double value) { + // --------------------------------------------------------------------- + + void expectedCurrent(double expected) override { + _hlw8012->expectedCurrent(expected); + } + + void expectedVoltage(unsigned int expected) override { + _hlw8012->expectedVoltage(expected); + } + + void expectedPower(unsigned int expected) override { + _hlw8012->expectedActivePower(expected); + } + + double defaultCurrentRatio() const override { + return HLW8012_CURRENT_RATIO; + } + + double defaultVoltageRatio() const override { + return HLW8012_VOLTAGE_RATIO; + } + + double defaultPowerRatio() const override { + return HLW8012_POWER_RATIO; + } + + void resetRatios() override { + _hlw8012->setCurrentMultiplier(_initialRatioC); + _hlw8012->setVoltageMultiplier(_initialRatioV); + _hlw8012->setPowerMultiplier(_initialRatioP); + } + + void setCurrentRatio(double value) override { _hlw8012->setCurrentMultiplier(value); }; - void setVoltageRatio(double value) { + void setVoltageRatio(double value) override { _hlw8012->setVoltageMultiplier(value); }; - void setPowerRatio(double value) { + void setPowerRatio(double value) override { _hlw8012->setPowerMultiplier(value); }; + double getCurrentRatio() override { + return _hlw8012->getCurrentMultiplier(); + }; + + double getVoltageRatio() override { + return _hlw8012->getVoltageMultiplier(); + }; + + double getPowerRatio() override { + return _hlw8012->getPowerMultiplier(); + }; + // --------------------------------------------------------------------- unsigned char getSEL() { @@ -103,18 +131,6 @@ class HLW8012Sensor : public BaseEmonSensor { return _sel_current; } - double getCurrentRatio() { - return _hlw8012->getCurrentMultiplier(); - }; - - double getVoltageRatio() { - return _hlw8012->getVoltageMultiplier(); - }; - - double getPowerRatio() { - return _hlw8012->getPowerMultiplier(); - }; - // --------------------------------------------------------------------- // Sensors API // --------------------------------------------------------------------- @@ -135,17 +151,8 @@ class HLW8012Sensor : public BaseEmonSensor { _hlw8012->begin(_cf, _cf1, _sel, _sel_current, false, 1000000); #endif - // These values are used to calculate current, voltage and power factors as per datasheet formula - // These are the nominal values for the Sonoff POW resistors: - // * The CURRENT_RESISTOR is the 1milliOhm copper-manganese resistor in series with the main line - // * The VOLTAGE_RESISTOR_UPSTREAM are the 5 470kOhm resistors in the voltage divider that feeds the V2P pin in the HLW8012 - // * The VOLTAGE_RESISTOR_DOWNSTREAM is the 1kOhm resistor in the voltage divider that feeds the V2P pin in the HLW8012 - _hlw8012->setResistors(HLW8012_CURRENT_R, HLW8012_VOLTAGE_R_UP, HLW8012_VOLTAGE_R_DOWN); - - // Also, adjust with ratio values that could be set in hardware profile - if (HLW8012_CURRENT_RATIO > 0.0) _hlw8012->setCurrentMultiplier(HLW8012_CURRENT_RATIO); - if (HLW8012_VOLTAGE_RATIO > 0.0) _hlw8012->setVoltageMultiplier(HLW8012_VOLTAGE_RATIO); - if (HLW8012_POWER_RATIO > 0.0) _hlw8012->setPowerMultiplier(HLW8012_POWER_RATIO); + // Adjust with ratio values that could be set in the hardware profile + _defaultRatios(); // Handle interrupts #if HLW8012_USE_INTERRUPTS && (!HLW8012_WAIT_FOR_WIFI) @@ -165,7 +172,7 @@ class HLW8012Sensor : public BaseEmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; @@ -232,6 +239,25 @@ class HLW8012Sensor : public BaseEmonSensor { protected: + void _defaultRatios() { + // These values are used to calculate current, voltage and power factors as per datasheet formula + // These are the nominal values for the Sonoff POW resistors: + // * The CURRENT_RESISTOR is the 1milliOhm copper-manganese resistor in series with the main line + // * The VOLTAGE_RESISTOR_UPSTREAM are the 5 470kOhm resistors in the voltage divider that feeds the V2P pin in the HLW8012 + // * The VOLTAGE_RESISTOR_DOWNSTREAM is the 1kOhm resistor in the voltage divider that feeds the V2P pin in the HLW8012 + _hlw8012->setResistors(HLW8012_CURRENT_R, HLW8012_VOLTAGE_R_UP, HLW8012_VOLTAGE_R_DOWN); + + // Multipliers are already set, but we might have changed default values via the hardware profile + if (defaultCurrentRatio() > 0.0) _hlw8012->setCurrentMultiplier(defaultCurrentRatio()); + if (defaultVoltageRatio() > 0.0) _hlw8012->setVoltageMultiplier(defaultVoltageRatio()); + if (defaultPowerRatio() > 0.0) _hlw8012->setPowerMultiplier(defaultPowerRatio()); + + // Preserve ratios as a fallback + _initialRatioC = getCurrentRatio(); + _initialRatioV = getVoltageRatio(); + _initialRatioP = getPowerRatio(); + } + // --------------------------------------------------------------------- // Interrupt management // --------------------------------------------------------------------- @@ -276,6 +302,10 @@ class HLW8012Sensor : public BaseEmonSensor { // --------------------------------------------------------------------- + double _initialRatioC; + double _initialRatioV; + double _initialRatioP; + unsigned char _sel = GPIO_NONE; unsigned char _cf = GPIO_NONE; unsigned char _cf1 = GPIO_NONE; diff --git a/code/espurna/sensors/I2CSensor.h b/code/espurna/sensors/I2CSensor.h index 3cdef6b0..d7c7e94d 100644 --- a/code/espurna/sensors/I2CSensor.h +++ b/code/espurna/sensors/I2CSensor.h @@ -47,7 +47,7 @@ class I2CSensor : public T { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return static_cast(this)->description(); }; diff --git a/code/espurna/sensors/LDRSensor.h b/code/espurna/sensors/LDRSensor.h index fda9b5ff..1c08b5fd 100644 --- a/code/espurna/sensors/LDRSensor.h +++ b/code/espurna/sensors/LDRSensor.h @@ -125,7 +125,7 @@ class LDRSensor : public AnalogSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); } diff --git a/code/espurna/sensors/MAX6675Sensor.h b/code/espurna/sensors/MAX6675Sensor.h index abf7c006..6a7d0839 100644 --- a/code/espurna/sensors/MAX6675Sensor.h +++ b/code/espurna/sensors/MAX6675Sensor.h @@ -95,7 +95,7 @@ class MAX6675Sensor : public BaseSensor { // Address of the device // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { if (index < _count) { // char buffer[40]; // uint8_t * address = _devices[index].address; diff --git a/code/espurna/sensors/MHZ19Sensor.h b/code/espurna/sensors/MHZ19Sensor.h index f89ac0f2..0faffbfa 100644 --- a/code/espurna/sensors/MHZ19Sensor.h +++ b/code/espurna/sensors/MHZ19Sensor.h @@ -94,7 +94,7 @@ class MHZ19Sensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/MICS2710Sensor.h b/code/espurna/sensors/MICS2710Sensor.h index 12256d24..68af0534 100644 --- a/code/espurna/sensors/MICS2710Sensor.h +++ b/code/espurna/sensors/MICS2710Sensor.h @@ -90,7 +90,7 @@ class MICS2710Sensor : public BaseAnalogSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/MICS5525Sensor.h b/code/espurna/sensors/MICS5525Sensor.h index 7c6bba32..478c9a4a 100644 --- a/code/espurna/sensors/MICS5525Sensor.h +++ b/code/espurna/sensors/MICS5525Sensor.h @@ -62,7 +62,7 @@ class MICS5525Sensor : public BaseAnalogSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/NTCSensor.h b/code/espurna/sensors/NTCSensor.h index 2a67b31c..83c7058f 100644 --- a/code/espurna/sensors/NTCSensor.h +++ b/code/espurna/sensors/NTCSensor.h @@ -61,7 +61,7 @@ class NTCSensor : public AnalogSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); } diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h index 3ce2ab27..2161feb8 100644 --- a/code/espurna/sensors/PMSX003Sensor.h +++ b/code/espurna/sensors/PMSX003Sensor.h @@ -247,7 +247,7 @@ class PMSX003Sensor : public BaseSensor, PMSX003 { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { char buffer[36] = {0}; if (_soft) { snprintf(buffer, sizeof(buffer), "%d @ %s @ SwSerial(%u,%u)", int(index + 1), pms_specs[_type].name, _pin_rx, _pin_tx); diff --git a/code/espurna/sensors/PZEM004TSensor.h b/code/espurna/sensors/PZEM004TSensor.h index 596ccc70..631e3500 100644 --- a/code/espurna/sensors/PZEM004TSensor.h +++ b/code/espurna/sensors/PZEM004TSensor.h @@ -89,21 +89,23 @@ class PZEM004TSensor : public BaseEmonSensor { return PZEM004TSensor::instance; } + // --------------------------------------------------------------------- + // We can't modify PZEM values, just ignore this - void resetEnergy() {} - void resetEnergy(unsigned char) {} - void resetEnergy(unsigned char, sensor::Energy) {} + void resetEnergy() override {} + void resetEnergy(unsigned char) override {} + void resetEnergy(unsigned char, sensor::Energy) override {} // Override Base methods that deal with _energy[] - size_t countDevices() { + size_t countDevices() override { return _addresses.size(); } - double getEnergy(unsigned char index) { + double getEnergy(unsigned char index) override { return _readings[index].energy; } - sensor::Energy totalEnergy(unsigned char index) { + sensor::Energy totalEnergy(unsigned char index) override { return getEnergy(index); } @@ -210,8 +212,8 @@ class PZEM004TSensor : public BaseEmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { - int dev = index / PZ_MAGNITUDE_COUNT; + String description(unsigned char index) { + auto dev = local(index); char buffer[25]; snprintf(buffer, sizeof(buffer), "(%u/%s)", dev, getAddress(dev).c_str()); return description() + String(buffer); @@ -219,14 +221,17 @@ class PZEM004TSensor : public BaseEmonSensor { // Address of the sensor (it could be the GPIO or I2C address) String address(unsigned char index) { - int dev = index / PZ_MAGNITUDE_COUNT; - return _addresses[dev].toString(); + return _addresses[local(index)].toString(); + } + + // Convert slot # to a magnitude # + unsigned char local(unsigned char index) override { + return index / PZ_MAGNITUDE_COUNT; } // Type for slot # index unsigned char type(unsigned char index) { - int dev = index / PZ_MAGNITUDE_COUNT; - index = index - (dev * PZ_MAGNITUDE_COUNT); + index = index - (local(index) * PZ_MAGNITUDE_COUNT); if (index == PZ_MAGNITUDE_CURRENT_INDEX) return MAGNITUDE_CURRENT; if (index == PZ_MAGNITUDE_VOLTAGE_INDEX) return MAGNITUDE_VOLTAGE; if (index == PZ_MAGNITUDE_POWER_ACTIVE_INDEX) return MAGNITUDE_POWER_ACTIVE; diff --git a/code/espurna/sensors/PulseMeterSensor.h b/code/espurna/sensors/PulseMeterSensor.h index 8941b961..17f9bb33 100644 --- a/code/espurna/sensors/PulseMeterSensor.h +++ b/code/espurna/sensors/PulseMeterSensor.h @@ -82,7 +82,7 @@ class PulseMeterSensor : public BaseEmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/SDS011Sensor.h b/code/espurna/sensors/SDS011Sensor.h index 7786641b..21017128 100644 --- a/code/espurna/sensors/SDS011Sensor.h +++ b/code/espurna/sensors/SDS011Sensor.h @@ -83,7 +83,7 @@ class SDS011Sensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/SI1145Sensor.h b/code/espurna/sensors/SI1145Sensor.h index 18b98dba..03775027 100644 --- a/code/espurna/sensors/SI1145Sensor.h +++ b/code/espurna/sensors/SI1145Sensor.h @@ -51,7 +51,7 @@ class SI1145Sensor : public I2CSensor<> { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/SI7021Sensor.h b/code/espurna/sensors/SI7021Sensor.h index 3e9e101d..31cdeafc 100644 --- a/code/espurna/sensors/SI7021Sensor.h +++ b/code/espurna/sensors/SI7021Sensor.h @@ -63,7 +63,7 @@ class SI7021Sensor : public I2CSensor<> { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/SenseAirSensor.h b/code/espurna/sensors/SenseAirSensor.h index d69a14e4..a46c76bf 100644 --- a/code/espurna/sensors/SenseAirSensor.h +++ b/code/espurna/sensors/SenseAirSensor.h @@ -180,7 +180,7 @@ class SenseAirSensor : public BaseSensor, SenseAir { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); } diff --git a/code/espurna/sensors/SonarSensor.h b/code/espurna/sensors/SonarSensor.h index 8ccf3b3d..897d4671 100644 --- a/code/espurna/sensors/SonarSensor.h +++ b/code/espurna/sensors/SonarSensor.h @@ -85,7 +85,7 @@ class SonarSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/T6613Sensor.h b/code/espurna/sensors/T6613Sensor.h index 12d59fe5..1726fdf9 100644 --- a/code/espurna/sensors/T6613Sensor.h +++ b/code/espurna/sensors/T6613Sensor.h @@ -89,7 +89,7 @@ class T6613Sensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/TMP3XSensor.h b/code/espurna/sensors/TMP3XSensor.h index 1dad17fb..22a51d5b 100644 --- a/code/espurna/sensors/TMP3XSensor.h +++ b/code/espurna/sensors/TMP3XSensor.h @@ -56,7 +56,7 @@ class TMP3XSensor : public BaseSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/V9261FSensor.h b/code/espurna/sensors/V9261FSensor.h index ce6dbea4..77c2aa19 100644 --- a/code/espurna/sensors/V9261FSensor.h +++ b/code/espurna/sensors/V9261FSensor.h @@ -84,7 +84,7 @@ class V9261FSensor : public BaseEmonSensor { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/VEML6075Sensor.h b/code/espurna/sensors/VEML6075Sensor.h index 5caaf83d..3bbf8494 100644 --- a/code/espurna/sensors/VEML6075Sensor.h +++ b/code/espurna/sensors/VEML6075Sensor.h @@ -50,7 +50,7 @@ class VEML6075Sensor : public I2CSensor<> { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/sensors/VL53L1XSensor.h b/code/espurna/sensors/VL53L1XSensor.h index dfd3a7b9..16be7dae 100644 --- a/code/espurna/sensors/VL53L1XSensor.h +++ b/code/espurna/sensors/VL53L1XSensor.h @@ -80,7 +80,7 @@ class VL53L1XSensor : public I2CSensor<> { } // Descriptive name of the slot # index - String slot(unsigned char index) { + String description(unsigned char index) { return description(); }; diff --git a/code/espurna/settings.cpp b/code/espurna/settings.cpp index 75f328a6..b6024d52 100644 --- a/code/espurna/settings.cpp +++ b/code/espurna/settings.cpp @@ -241,6 +241,22 @@ std::vector settingsKeys() { return keys; } + +static std::vector _settings_matchers; + +void settingsRegisterDefaults(const settings_key_match_t& matcher) { + _settings_matchers.push_back(matcher); +} + +String settingsQueryDefaults(const String& key) { + for (auto& matcher : _settings_matchers) { + if (matcher.match(key.c_str())) { + return matcher.key(key); + } + } + return String(); +} + // ----------------------------------------------------------------------------- // Key-value API // ----------------------------------------------------------------------------- diff --git a/code/espurna/settings.h b/code/espurna/settings.h index 07823ff2..c168fc6c 100644 --- a/code/espurna/settings.h +++ b/code/espurna/settings.h @@ -38,6 +38,17 @@ class settings_key_t { settings_key_t(const __FlashStringHelper* value) : value(value), index(-1) {} + settings_key_t() : + value(), index(-1) + {} + + bool match(const char* _value) const { + return (value == _value) || (toString() == _value); + } + + bool match(const String& _value) const { + return (value == _value) || (toString() == _value); + } String toString() const; @@ -105,19 +116,38 @@ unsigned short convert(const String& value); template <> unsigned char convert(const String& value); +template +String serialize(const T& value); + +template +String serialize(const T& value) { + return String(value); +} + } // namespace settings::internal } // namespace settings // -------------------------------------------------------------------------- +struct settings_key_match_t { + using match_f = bool(*)(const char* key); + using key_f = const String(*)(const String& key); + + match_f match; + key_f key; +}; + +void settingsRegisterDefaults(const settings_key_match_t& matcher); +String settingsQueryDefaults(const String& key); + +// -------------------------------------------------------------------------- + void moveSetting(const String& from, const String& to); void moveSetting(const String& from, const String& to, unsigned int index); void moveSettings(const String& from, const String& to); -#if 1 template Rfunc = settings::internal::convert> R getSetting(const settings_key_t& key, R defaultValue) __attribute__((noinline)); -#endif template Rfunc = settings::internal::convert> R getSetting(const settings_key_t& key, R defaultValue) { diff --git a/code/espurna/terminal.cpp b/code/espurna/terminal.cpp index 8c4516a9..47d58473 100644 --- a/code/espurna/terminal.cpp +++ b/code/espurna/terminal.cpp @@ -254,7 +254,12 @@ void _terminalInitCommand() { String key = String(e->argv[i]); String value; if (!Embedis::get(key, value)) { - DEBUG_MSG_P(PSTR("> %s =>\n"), key.c_str()); + const auto maybeDefault = settingsQueryDefaults(key); + if (maybeDefault.length()) { + DEBUG_MSG_P(PSTR("> %s => %s (default)\n"), key.c_str(), maybeDefault.c_str()); + } else { + DEBUG_MSG_P(PSTR("> %s =>\n"), key.c_str()); + } continue; } diff --git a/code/espurna/thingspeak.cpp b/code/espurna/thingspeak.cpp index d71ffeb2..f9047437 100644 --- a/code/espurna/thingspeak.cpp +++ b/code/espurna/thingspeak.cpp @@ -404,7 +404,7 @@ void _tspkFlush() { // POST data if any if (_tspk_data.length()) { _tspk_data.concat("&api_key="); - _tspk_data.concat(getSetting("tspkKey", THINGSPEAK_APIKEY)); + _tspk_data.concat(getSetting("tspkKey", THINGSPEAK_APIKEY)); --_tspk_tries; _tspkPost(getSetting("tspkAddress", THINGSPEAK_ADDRESS)); } diff --git a/code/espurna/wifi.cpp b/code/espurna/wifi.cpp index 249b8d36..cd0eda66 100644 --- a/code/espurna/wifi.cpp +++ b/code/espurna/wifi.cpp @@ -25,6 +25,7 @@ unsigned long _wifi_gratuitous_arp_last = 0; // ----------------------------------------------------------------------------- // PRIVATE // ----------------------------------------------------------------------------- + struct wifi_scan_info_t { String ssid_scan; int32_t rssi_scan; @@ -58,7 +59,11 @@ void _wifiCheckAP() { } } -WiFiSleepType_t _wifiSleepModeConvert(const String& value) { +namespace settings { +namespace internal { + +template<> +WiFiSleepType_t convert(const String& value) { switch (value.toInt()) { case 2: return WIFI_MODEM_SLEEP; @@ -70,6 +75,9 @@ WiFiSleepType_t _wifiSleepModeConvert(const String& value) { } } +} +} + void _wifiConfigure() { jw.setHostname(getSetting("hostname").c_str()); @@ -131,7 +139,7 @@ void _wifiConfigure() { jw.enableScan(getSetting("wifiScan", 1 == WIFI_SCAN_NETWORKS)); - const auto sleep_mode = getSetting("wifiSleep", WIFI_SLEEP_MODE); + const auto sleep_mode = getSetting("wifiSleep", WIFI_SLEEP_MODE); WiFi.setSleepMode(sleep_mode); #if WIFI_GRATUITOUS_ARP_SUPPORT