From 4cfcba18ee6d81483d9d35a5f4ba90243be30a9f Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 26 Sep 2025 08:47:31 +0200 Subject: [PATCH] analogsensors pulse output #2624 and frequncy input #2631 --- interface/src/app/main/Sensors.tsx | 3 +- .../src/app/main/SensorsAnalogDialog.tsx | 40 +++++++- interface/src/app/main/types.ts | 18 +++- src/core/analogsensor.cpp | 91 ++++++++++++++++--- src/core/analogsensor.h | 13 ++- src/core/emsdevicevalue.cpp | 8 +- src/core/emsdevicevalue.h | 3 +- src/core/locale_common.h | 4 +- src/web/WebDataService.cpp | 5 +- 9 files changed, 159 insertions(+), 26 deletions(-) diff --git a/interface/src/app/main/Sensors.tsx b/interface/src/app/main/Sensors.tsx index 8332f42c5..c8a37fd91 100644 --- a/interface/src/app/main/Sensors.tsx +++ b/interface/src/app/main/Sensors.tsx @@ -439,7 +439,8 @@ const Sensors = () => { {a.n} {AnalogTypeNames[a.t]} {(a.t === AnalogType.DIGITAL_OUT && a.g !== 25 && a.g !== 26) || - a.t === AnalogType.DIGITAL_IN ? ( + a.t === AnalogType.DIGITAL_IN || + a.t === AnalogType.PULSE ? ( {a.v ? LL.ON() : LL.OFF()} ) : ( {a.t ? formatValue(a.v, a.u) : ''} diff --git a/interface/src/app/main/SensorsAnalogDialog.tsx b/interface/src/app/main/SensorsAnalogDialog.tsx index 6ee9e17e3..719505aa3 100644 --- a/interface/src/app/main/SensorsAnalogDialog.tsx +++ b/interface/src/app/main/SensorsAnalogDialog.tsx @@ -132,7 +132,9 @@ const SensorsAnalogDialog = ({ ))} - {editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE && ( + {((editItem.t >= AnalogType.COUNTER && editItem.t <= AnalogType.RATE) || + (editItem.t >= AnalogType.FREQ_0 && + editItem.t <= AnalogType.FREQ_2)) && ( )} + {editItem.t === AnalogType.PULSE && ( + <> + + + {LL.ACTIVEHIGH()} + {LL.ACTIVELOW()} + + + + s + ) + }, + htmlInput: { min: '0', max: '10000', step: '0.1' } + }} + /> + + + )} diff --git a/interface/src/app/main/types.ts b/interface/src/app/main/types.ts index 924d57a5a..5c86daf4f 100644 --- a/interface/src/app/main/types.ts +++ b/interface/src/app/main/types.ts @@ -188,7 +188,8 @@ export enum DeviceValueUOM { VOLTS, MBAR, LH, - CTKWH + CTKWH, + HZ } export const DeviceValueUOM_s = [ @@ -218,7 +219,8 @@ export const DeviceValueUOM_s = [ 'V', 'mbar', 'l/h', - 'ct/kWh' + 'ct/kWh', + 'Hz' ]; export enum AnalogType { @@ -234,7 +236,11 @@ export enum AnalogType { PWM_1 = 8, PWM_2 = 9, NTC = 10, - RGB = 11 + RGB = 11, + PULSE = 12, + FREQ_0 = 13, + FREQ_1 = 14, + FREQ_2 = 15 } export const AnalogTypeNames = [ @@ -249,7 +255,11 @@ export const AnalogTypeNames = [ 'PWM 1', 'PWM 2', 'NTC Temp.', - 'RGB Led' + 'RGB Led', + 'Pulse', + 'Freq 0', + 'Freq 1', + 'Freq 2' ]; type BoardProfiles = Record; diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp index dadebe4e4..c7acd095a 100644 --- a/src/core/analogsensor.cpp +++ b/src/core/analogsensor.cpp @@ -23,6 +23,29 @@ namespace emsesp { uuid::log::Logger AnalogSensor::logger_{F_(analogsensor), uuid::log::Facility::DAEMON}; +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; +unsigned long AnalogSensor::edge[] = {0, 0, 0}; +unsigned long AnalogSensor::edgecnt[] = {0, 0, 0}; + +void IRAM_ATTR AnalogSensor::freqIrq0() { + portENTER_CRITICAL_ISR(&mux); + edgecnt[0]++; + edge[0] = micros(); + portEXIT_CRITICAL_ISR(&mux); +} +void IRAM_ATTR AnalogSensor::freqIrq1() { + portENTER_CRITICAL_ISR(&mux); + edgecnt[1]++; + edge[1] = micros(); + portEXIT_CRITICAL_ISR(&mux); +} +void IRAM_ATTR AnalogSensor::freqIrq2() { + portENTER_CRITICAL_ISR(&mux); + edgecnt[2]++; + edge[2] = micros(); + portEXIT_CRITICAL_ISR(&mux); +} + void AnalogSensor::start(const bool factory_settings) { // if (factory_settings && EMSESP::nvs_.getString("boot").equals("E32V2_2") && EMSESP::nvs_.getString("hwrevision").equals("3.0")) { if (factory_settings && analogReadMilliVolts(39) > 700) { // core voltage > 2.6V @@ -92,7 +115,7 @@ void AnalogSensor::reload(bool get_nvs) { for (const auto & sensor : settings.analogCustomizations) { // search customlist if (sensor_.gpio() == sensor.gpio) { // for output sensors set value to new start-value - if (sensor.type >= AnalogType::DIGITAL_OUT + if (sensor.type >= AnalogType::DIGITAL_OUT && sensor.type <= AnalogType::PWM_2 && (sensor_.type() != sensor.type || sensor_.offset() != sensor.offset || sensor_.factor() != sensor.factor)) { sensor_.set_value(sensor.offset); } @@ -134,7 +157,7 @@ void AnalogSensor::reload(bool get_nvs) { } } if (sensor.type == AnalogType::COUNTER || (sensor.type >= AnalogType::DIGITAL_OUT && sensor.type <= AnalogType::PWM_2) - || sensor.type == AnalogType::RGB) { + || sensor.type == AnalogType::RGB || sensor.type == AnalogType::PULSE) { Command::add( EMSdevice::DeviceType::ANALOGSENSOR, sensor.name.c_str(), @@ -142,6 +165,7 @@ void AnalogSensor::reload(bool get_nvs) { sensor.type == AnalogType::COUNTER ? FL_(counter) : sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out) : sensor.type == AnalogType::RGB ? FL_(RGB) + : sensor.type == AnalogType::PULSE ? FL_(pulse) : FL_(pwm), CommandFlag::ADMIN_ONLY); } @@ -204,6 +228,16 @@ void AnalogSensor::reload(bool get_nvs) { sensor.set_offset(0); sensor.set_value(0); publish_sensor(sensor); + } else if (sensor.type() >= AnalogType::FREQ_0 && sensor.type() <= AnalogType::FREQ_2) { + LOG_DEBUG("Frequency on GPIO %02d", sensor.gpio()); + pinMode(sensor.gpio(), INPUT_PULLUP); + sensor.set_offset(0); + sensor.set_value(0); + publish_sensor(sensor); + auto index = sensor.type() - AnalogType::FREQ_0; + attachInterrupt(sensor.gpio(), index == 0 ? freqIrq0 : index == 1 ? freqIrq1 : freqIrq2, FALLING); + lastedge[index] = edge[index] = micros(); + edgecnt[index] = 0; } else if (sensor.type() == AnalogType::DIGITAL_IN) { LOG_DEBUG("Digital Read on GPIO %02d", sensor.gpio()); pinMode(sensor.gpio(), INPUT_PULLUP); @@ -260,6 +294,11 @@ void AnalogSensor::reload(bool get_nvs) { sensor.set_value(sensor.offset()); } publish_sensor(sensor); + } else if (sensor.type() == AnalogType::PULSE) { + LOG_DEBUG("Pulse on GPIO %02d", sensor.gpio()); + pinMode(sensor.gpio(), OUTPUT); + digitalWrite(sensor.gpio(), (sensor.offset() == 1) ^ (sensor.value() == 1)); + sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0; } else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) { LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio()); #if ESP_IDF_VERSION_MAJOR >= 5 @@ -330,6 +369,23 @@ void AnalogSensor::measure() { changed_ = true; publish_sensor(sensor); } + } else if (sensor.type() >= AnalogType::FREQ_0 && sensor.type() <= AnalogType::FREQ_2) { + auto index = sensor.type() - AnalogType::FREQ_0; + auto oldval = sensor.value(); + if (edge[index] != lastedge[index] && edgecnt[index] > 0) { + portENTER_CRITICAL_ISR(&mux); + auto t = (edge[index] - lastedge[index]) / edgecnt[index]; + lastedge[index] = edge[index]; + edgecnt[index] = 0; + portEXIT_CRITICAL_ISR(&mux); + sensor.set_value(sensor.factor() * 1000000.0 / t); + } else if (micros() - edge[index] > 10000000ul && sensor.value() > 0) { + sensor.set_value(0); + } + if (sensor.value() != oldval) { + changed_ = true; + publish_sensor(sensor); + } } } } @@ -337,9 +393,9 @@ void AnalogSensor::measure() { // poll digital io every time with debounce // go through the list of digital sensors for (auto & sensor : sensors_) { + auto old_value = sensor.value(); // remember current value before reading if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) { - auto old_value = sensor.value(); // remember current value before reading auto current_reading = digitalRead(sensor.gpio()); if (sensor.poll_ != current_reading) { // check for pinchange sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange @@ -362,13 +418,17 @@ void AnalogSensor::measure() { sensor.last_polltime_ = sensor.polltime_; } } - - // see if there is a change and increment # reads - if (old_value != sensor.value()) { - sensorreads_++; - changed_ = true; - publish_sensor(sensor); - } + } + if (sensor.type() == AnalogType::PULSE && sensor.value() && sensor.polltime_ && sensor.polltime_ < uuid::get_uptime()) { + sensor.set_value(0); + digitalWrite(sensor.gpio(), sensor.offset()); + sensor.polltime_ = 0; + } + // see if there is a change and increment # reads + if (old_value != sensor.value()) { + sensorreads_++; + changed_ = true; + publish_sensor(sensor); } } @@ -562,6 +622,9 @@ void AnalogSensor::publish_values(const bool force) { case AnalogType::PWM_0: case AnalogType::PWM_1: case AnalogType::PWM_2: + case AnalogType::FREQ_0: + case AnalogType::FREQ_1: + case AnalogType::FREQ_2: case AnalogType::RGB: case AnalogType::NTC: dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // double @@ -762,7 +825,7 @@ void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) { output["analog"] = FL_(list_sensortype)[sensor.type()]; output["value"] = sensor.value(); output["readable"] = true; - output["writeable"] = sensor.type() == AnalogType::COUNTER || sensor.type() >= AnalogType::RGB + output["writeable"] = sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::RGB || sensor.type() == AnalogType::PULSE || (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2); output["visible"] = true; if (sensor.type() == AnalogType::COUNTER) { @@ -842,6 +905,12 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) { rgbLedWrite(sensor.gpio(), 2 * r, 2 * g, 2 * b); #endif LOG_DEBUG("RGB set to %d, %d, %d", r, g, b); + } else if (sensor.type() == AnalogType::PULSE) { + uint8_t v = val; + sensor.set_value(v); + pinMode(sensor.gpio(), OUTPUT); + digitalWrite(sensor.gpio(), (sensor.offset() != 0) ^ (sensor.value() != 0)); + sensor.polltime_ = sensor.value() != 0 ? uuid::get_uptime() + (sensor.factor() * 1000) : 0; } else if (sensor.type() == AnalogType::DIGITAL_OUT) { uint8_t v = val; #if CONFIG_IDF_TARGET_ESP32 diff --git a/src/core/analogsensor.h b/src/core/analogsensor.h index 32b31a0e6..18b400a1f 100644 --- a/src/core/analogsensor.h +++ b/src/core/analogsensor.h @@ -124,7 +124,11 @@ class AnalogSensor { PWM_1 = 8, PWM_2 = 9, NTC = 10, - RGB = 11 + RGB = 11, + PULSE = 12, + FREQ_0 = 13, + FREQ_1 = 14, + FREQ_2 = 15 }; void start(const bool factory_settings = false); @@ -190,6 +194,13 @@ class AnalogSensor { bool changed_ = true; // this will force a publish of all sensors when initialising uint32_t sensorfails_ = 0; uint32_t sensorreads_ = 0; + + static void IRAM_ATTR freqIrq0(); + static void IRAM_ATTR freqIrq1(); + static void IRAM_ATTR freqIrq2(); + static unsigned long edge[3]; + static unsigned long edgecnt[3]; + unsigned long lastedge[3] = {0, 0, 0}; }; } // namespace emsesp diff --git a/src/core/emsdevicevalue.cpp b/src/core/emsdevicevalue.cpp index 82bb45732..d62ca4628 100644 --- a/src/core/emsdevicevalue.cpp +++ b/src/core/emsdevicevalue.cpp @@ -108,10 +108,10 @@ DeviceValue::DeviceValue(uint8_t device_type, const char * DeviceValue::DeviceValueUOM_s[] = { F_(uom_blank), // 0 - F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], - FL_(minutes)[0], F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], - F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), - F_(uom_k), F_(uom_volts), F_(uom_mbar), F_(uom_lh), F_(uom_ctkwh), F_(uom_blank) + F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0], + F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), + F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_volts), F_(uom_mbar), + F_(uom_lh), F_(uom_ctkwh), F_(uom_hz), F_(uom_blank) }; diff --git a/src/core/emsdevicevalue.h b/src/core/emsdevicevalue.h index ec4bf4e76..49d509e8b 100644 --- a/src/core/emsdevicevalue.h +++ b/src/core/emsdevicevalue.h @@ -75,7 +75,8 @@ class DeviceValue { MBAR, // 24 - mbar LH, // 25 - l/h CTKWH, // 26 - ct/kWh - CONNECTIVITY // 27 - used in HA + HZ, // 27 - Hz + CONNECTIVITY // 28 - used in HA }; // TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp diff --git a/src/core/locale_common.h b/src/core/locale_common.h index 36a072937..f83cac0d0 100644 --- a/src/core/locale_common.h +++ b/src/core/locale_common.h @@ -264,6 +264,7 @@ MAKE_WORD_CUSTOM(uom_volts, "V") MAKE_WORD_CUSTOM(uom_mbar, "mbar") MAKE_WORD_CUSTOM(uom_lh, "l/h") MAKE_WORD_CUSTOM(uom_ctkwh, "ct/kWh") +MAKE_WORD_CUSTOM(uom_hz, "Hz") // MQTT topics and prefixes MAKE_WORD_CUSTOM(heating_active, "heating_active") @@ -277,7 +278,8 @@ MAKE_ENUM_FIXED(list_syslog_level, "off", "emerg", "alert", "crit", "error", "wa MAKE_ENUM_FIXED(counter, "counter") MAKE_ENUM_FIXED(digital_out, "digital_out") MAKE_ENUM_FIXED(RGB, "RGB") -MAKE_ENUM_FIXED(list_sensortype, "disabled", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2", "NTC Temp", "RGB Led") +MAKE_ENUM_FIXED(pulse, "pulse") +MAKE_ENUM_FIXED(list_sensortype, "disabled", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2", "NTC Temp", "RGB Led", "pulse", "freq 0", "freq 1", "freq 2") // watch MAKE_ENUM_FIXED(list_watch, "off", "on", "raw", "unknown") diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 13489e7c8..735a35182 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -436,7 +436,8 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) { obj["v"] = Helpers::transformNumFloat(sensor.value()); } else #endif - if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN) { + if (sensor.type() == AnalogSensor::AnalogType::DIGITAL_OUT || sensor.type() == AnalogSensor::AnalogType::DIGITAL_IN + || sensor.type() == AnalogSensor::AnalogType::PULSE) { char s[12]; dv["v"] = Helpers::render_boolean(s, sensor.value() != 0, true); JsonArray l = dv["l"].to(); @@ -448,7 +449,7 @@ void WebDataService::dashboard_data(AsyncWebServerRequest * request) { } if (sensor.type() == AnalogSensor::AnalogType::COUNTER || (sensor.type() >= AnalogSensor::AnalogType::DIGITAL_OUT && sensor.type() <= AnalogSensor::AnalogType::PWM_2) - || sensor.type() == AnalogSensor::AnalogType::RGB) { + || sensor.type() == AnalogSensor::AnalogType::RGB || sensor.type() == AnalogSensor::AnalogType::PULSE) { dv["c"] = sensor.name(); } }