From 1b4156646e03dacd3dfba02e092023c7d7dd188a Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:22:34 +0200 Subject: [PATCH] Esp32 pulsecounter optional pcnt (#3691) Co-authored-by: RoboMagus <-> --- esphome/components/hlw8012/hlw8012.h | 13 +++++- .../pulse_counter/pulse_counter_sensor.cpp | 23 ++++++---- .../pulse_counter/pulse_counter_sensor.h | 45 ++++++++++++------- esphome/components/pulse_counter/sensor.py | 34 +++++++++----- 4 files changed, 79 insertions(+), 36 deletions(-) diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index 5060957cf1..adb49ffb66 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -16,8 +16,17 @@ enum HLW8012SensorModels { HLW8012_SENSOR_MODEL_BL0937 }; +#ifdef HAS_PCNT +#define USE_PCNT true +#else +#define USE_PCNT false +#endif + class HLW8012Component : public PollingComponent { public: + HLW8012Component() + : cf_store_(*pulse_counter::get_storage(USE_PCNT)), cf1_store_(*pulse_counter::get_storage(USE_PCNT)) {} + void setup() override; void dump_config() override; float get_setup_priority() const override; @@ -49,9 +58,9 @@ class HLW8012Component : public PollingComponent { uint64_t cf_total_pulses_{0}; GPIOPin *sel_pin_; InternalGPIOPin *cf_pin_; - pulse_counter::PulseCounterStorage cf_store_; + pulse_counter::PulseCounterStorageBase &cf_store_; InternalGPIOPin *cf1_pin_; - pulse_counter::PulseCounterStorage cf1_store_; + pulse_counter::PulseCounterStorageBase &cf1_store_; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 002f6dcac9..1f50360fed 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -8,8 +8,16 @@ static const char *const TAG = "pulse_counter"; const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; -#ifndef HAS_PCNT -void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { +#ifdef HAS_PCNT +PulseCounterStorageBase *get_storage(bool hw_pcnt) { + return (hw_pcnt ? (PulseCounterStorageBase *) (new HwPulseCounterStorage) + : (PulseCounterStorageBase *) (new BasicPulseCounterStorage)); +} +#else +PulseCounterStorageBase *get_storage(bool) { return new BasicPulseCounterStorage; } +#endif + +void IRAM_ATTR BasicPulseCounterStorage::gpio_intr(BasicPulseCounterStorage *arg) { const uint32_t now = micros(); const bool discard = now - arg->last_pulse < arg->filter_us; arg->last_pulse = now; @@ -28,23 +36,22 @@ void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { break; } } -bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { +bool BasicPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { this->pin = pin; this->pin->setup(); this->isr_pin = this->pin->to_isr(); - this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); + this->pin->attach_interrupt(BasicPulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); return true; } -pulse_counter_t PulseCounterStorage::read_raw_value() { +pulse_counter_t BasicPulseCounterStorage::read_raw_value() { pulse_counter_t counter = this->counter; pulse_counter_t ret = counter - this->last_value; this->last_value = counter; return ret; } -#endif #ifdef HAS_PCNT -bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { +bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; this->pin = pin; this->pin->setup(); @@ -127,7 +134,7 @@ bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { } return true; } -pulse_counter_t PulseCounterStorage::read_raw_value() { +pulse_counter_t HwPulseCounterStorage::read_raw_value() { pulse_counter_t counter; pcnt_get_counter_value(this->pcnt_unit, &counter); pulse_counter_t ret = counter - this->last_value; diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index f81d20a646..d9be79e403 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -24,31 +24,44 @@ using pulse_counter_t = int16_t; using pulse_counter_t = int32_t; #endif -struct PulseCounterStorage { - bool pulse_counter_setup(InternalGPIOPin *pin); - pulse_counter_t read_raw_value(); - - static void gpio_intr(PulseCounterStorage *arg); - -#ifndef HAS_PCNT - volatile pulse_counter_t counter{0}; - volatile uint32_t last_pulse{0}; -#endif +struct PulseCounterStorageBase { + virtual bool pulse_counter_setup(InternalGPIOPin *pin) = 0; + virtual pulse_counter_t read_raw_value() = 0; InternalGPIOPin *pin; -#ifdef HAS_PCNT - pcnt_unit_t pcnt_unit; -#else - ISRInternalGPIOPin isr_pin; -#endif PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT}; PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE}; uint32_t filter_us{0}; pulse_counter_t last_value{0}; }; +struct BasicPulseCounterStorage : public PulseCounterStorageBase { + static void gpio_intr(BasicPulseCounterStorage *arg); + + bool pulse_counter_setup(InternalGPIOPin *pin) override; + pulse_counter_t read_raw_value() override; + + volatile pulse_counter_t counter{0}; + volatile uint32_t last_pulse{0}; + + ISRInternalGPIOPin isr_pin; +}; + +#ifdef HAS_PCNT +struct HwPulseCounterStorage : public PulseCounterStorageBase { + bool pulse_counter_setup(InternalGPIOPin *pin) override; + pulse_counter_t read_raw_value() override; + + pcnt_unit_t pcnt_unit; +}; +#endif + +PulseCounterStorageBase *get_storage(bool hw_pcnt = false); + class PulseCounterSensor : public sensor::Sensor, public PollingComponent { public: + explicit PulseCounterSensor(bool hw_pcnt = false) : storage_(*get_storage(hw_pcnt)) {} + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; } void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; } @@ -65,7 +78,7 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent { protected: InternalGPIOPin *pin_; - PulseCounterStorage storage_; + PulseCounterStorageBase &storage_; uint32_t last_time_{0}; uint32_t current_total_{0}; sensor::Sensor *total_sensor_; diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index 88f53bdf77..27364a34b3 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -20,6 +20,8 @@ from esphome.const import ( ) from esphome.core import CORE +CONF_USE_PCNT = "use_pcnt" + pulse_counter_ns = cg.esphome_ns.namespace("pulse_counter") PulseCounterCountMode = pulse_counter_ns.enum("PulseCounterCountMode") COUNT_MODES = { @@ -40,11 +42,19 @@ SetTotalPulsesAction = pulse_counter_ns.class_( def validate_internal_filter(value): - value = cv.positive_time_period_microseconds(value) - if CORE.is_esp32: - if value.total_microseconds > 13: - raise cv.Invalid("Maximum internal filter value for ESP32 is 13us") - return value + use_pcnt = value.get(CONF_USE_PCNT) + if CORE.is_esp8266 and use_pcnt: + raise cv.Invalid( + "Using hardware PCNT is only available on ESP32", + [CONF_USE_PCNT], + ) + + if CORE.is_esp32 and use_pcnt: + if value.get(CONF_INTERNAL_FILTER).total_microseconds > 13: + raise cv.Invalid( + "Maximum internal filter value when using ESP32 hardware PCNT is 13us", + [CONF_INTERNAL_FILTER], + ) return value @@ -69,7 +79,7 @@ def validate_count_mode(value): return value -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( sensor.sensor_schema( PulseCounterSensor, unit_of_measurement=UNIT_PULSES_PER_MINUTE, @@ -95,21 +105,25 @@ CONFIG_SCHEMA = ( ), validate_count_mode, ), - cv.Optional(CONF_INTERNAL_FILTER, default="13us"): validate_internal_filter, + cv.SplitDefault(CONF_USE_PCNT, esp32=True): cv.boolean, + cv.Optional( + CONF_INTERNAL_FILTER, default="13us" + ): cv.positive_time_period_microseconds, cv.Optional(CONF_TOTAL): sensor.sensor_schema( unit_of_measurement=UNIT_PULSES, icon=ICON_PULSE, accuracy_decimals=0, state_class=STATE_CLASS_TOTAL_INCREASING, ), - } + }, ) - .extend(cv.polling_component_schema("60s")) + .extend(cv.polling_component_schema("60s")), + validate_internal_filter, ) async def to_code(config): - var = await sensor.new_sensor(config) + var = await sensor.new_sensor(config, config.get(CONF_USE_PCNT)) await cg.register_component(var, config) pin = await cg.gpio_pin_expression(config[CONF_PIN])