ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
pulse_counter_sensor.cpp
Go to the documentation of this file.
2#include "esphome/core/log.h"
3
4#ifdef HAS_PCNT
5#include <driver/gpio.h>
6#include <esp_private/esp_clk.h>
7#include <hal/pcnt_ll.h>
8#endif
9
11
12ESPHOME_LOG_TAG(TAG, "pulse_counter");
13
14const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"};
15
16#ifdef HAS_PCNT
21#else // HAS_PCNT
22PulseCounterStorageBase *get_storage(bool) { return new BasicPulseCounterStorage; }
23#endif // HAS_PCNT
24
26 const uint32_t now = micros();
27 const bool discard = now - arg->last_pulse < arg->filter_us;
28 arg->last_pulse = now;
29 if (discard)
30 return;
31
35 }
37 auto x = arg->counter + 1;
38 arg->counter = x;
39 } else if (mode == PULSE_COUNTER_DECREMENT) {
40 auto x = arg->counter - 1;
41 arg->counter = x;
42 }
43}
44
46 this->pin = pin;
47 this->pin->setup();
48 this->isr_pin = this->pin->to_isr();
49 const bool rising = this->rising_edge_mode != PULSE_COUNTER_DISABLE;
50 const bool falling = this->falling_edge_mode != PULSE_COUNTER_DISABLE;
51 if (!rising && !falling) {
52 return true;
53 }
55 if (this->filter_us == 0) {
56 if (!falling) {
58 this->fixed_mode = this->rising_edge_mode;
59 } else if (!rising) {
61 this->fixed_mode = this->falling_edge_mode;
62 } else if (this->rising_edge_mode == this->falling_edge_mode) {
63 this->fixed_mode = this->rising_edge_mode;
64 }
65 }
67 return true;
68}
69
76
77#ifdef HAS_PCNT
79 this->pin = pin;
80 this->pin->setup();
81
82 pcnt_unit_config_t unit_config = {
83 .low_limit = INT16_MIN,
84 .high_limit = INT16_MAX,
85 .flags = {.accum_count = true},
86 };
87 esp_err_t error = pcnt_new_unit(&unit_config, &this->pcnt_unit);
88 if (error != ESP_OK) {
89 ESP_LOGE(TAG, "Creating PCNT unit failed: %s", esp_err_to_name(error));
90 return false;
91 }
92
93 pcnt_chan_config_t chan_config = {
94 .edge_gpio_num = static_cast<gpio_num_t>(this->pin->get_pin()),
95 .level_gpio_num = GPIO_NUM_NC,
96 };
97 error = pcnt_new_channel(this->pcnt_unit, &chan_config, &this->pcnt_channel);
98 if (error != ESP_OK) {
99 ESP_LOGE(TAG, "Creating PCNT channel failed: %s", esp_err_to_name(error));
100 return false;
101 }
102
103 pcnt_channel_edge_action_t rising = PCNT_CHANNEL_EDGE_ACTION_HOLD;
104 pcnt_channel_edge_action_t falling = PCNT_CHANNEL_EDGE_ACTION_HOLD;
105 switch (this->rising_edge_mode) {
107 rising = PCNT_CHANNEL_EDGE_ACTION_HOLD;
108 break;
110 rising = PCNT_CHANNEL_EDGE_ACTION_INCREASE;
111 break;
113 rising = PCNT_CHANNEL_EDGE_ACTION_DECREASE;
114 break;
115 }
116 switch (this->falling_edge_mode) {
118 falling = PCNT_CHANNEL_EDGE_ACTION_HOLD;
119 break;
121 falling = PCNT_CHANNEL_EDGE_ACTION_INCREASE;
122 break;
124 falling = PCNT_CHANNEL_EDGE_ACTION_DECREASE;
125 break;
126 }
127
128 error = pcnt_channel_set_edge_action(this->pcnt_channel, rising, falling);
129 if (error != ESP_OK) {
130 ESP_LOGE(TAG, "Setting PCNT edge action failed: %s", esp_err_to_name(error));
131 return false;
132 }
133
134 if (this->filter_us != 0) {
135 uint32_t max_glitch_ns = PCNT_LL_MAX_GLITCH_WIDTH * 1000u / ((uint32_t) esp_clk_apb_freq() / 1000000u);
136 pcnt_glitch_filter_config_t filter_config = {
137 .max_glitch_ns = std::min(this->filter_us * 1000u, max_glitch_ns),
138 };
139 error = pcnt_unit_set_glitch_filter(this->pcnt_unit, &filter_config);
140 if (error != ESP_OK) {
141 ESP_LOGE(TAG, "Setting PCNT glitch filter failed: %s", esp_err_to_name(error));
142 return false;
143 }
144 }
145
146 error = pcnt_unit_add_watch_point(this->pcnt_unit, INT16_MIN);
147 if (error != ESP_OK) {
148 ESP_LOGE(TAG, "Adding PCNT low limit watch point failed: %s", esp_err_to_name(error));
149 return false;
150 }
151 error = pcnt_unit_add_watch_point(this->pcnt_unit, INT16_MAX);
152 if (error != ESP_OK) {
153 ESP_LOGE(TAG, "Adding PCNT high limit watch point failed: %s", esp_err_to_name(error));
154 return false;
155 }
156
157 error = pcnt_unit_enable(this->pcnt_unit);
158 if (error != ESP_OK) {
159 ESP_LOGE(TAG, "Enabling PCNT unit failed: %s", esp_err_to_name(error));
160 return false;
161 }
162 error = pcnt_unit_clear_count(this->pcnt_unit);
163 if (error != ESP_OK) {
164 ESP_LOGE(TAG, "Clearing PCNT unit failed: %s", esp_err_to_name(error));
165 return false;
166 }
167 error = pcnt_unit_start(this->pcnt_unit);
168 if (error != ESP_OK) {
169 ESP_LOGE(TAG, "Starting PCNT unit failed: %s", esp_err_to_name(error));
170 return false;
171 }
172 return true;
173}
174
176 int count;
177 pcnt_unit_get_count(this->pcnt_unit, &count);
179 this->last_value = count;
180 return ret;
181}
182#endif // HAS_PCNT
183
185 if (this->storage_.pin == nullptr) {
186 return true;
187 }
188 ESP_LOGE(TAG, "Edge modes and filter cannot change after setup");
189 return false;
190}
191
197
203
205 if (this->before_setup_()) {
206 this->storage_.filter_us = filter;
207 }
208}
209
211 if (!this->storage_.pulse_counter_setup(this->pin_)) {
212 this->mark_failed();
213 return;
214 }
215}
216
218 this->current_total_ = pulses;
219 if (this->total_sensor_ != nullptr)
220 this->total_sensor_->publish_state(pulses);
221}
222
224 LOG_SENSOR("", "Pulse Counter", this);
225 LOG_PIN(" Pin: ", this->pin_);
226 ESP_LOGCONFIG(TAG,
227 " Rising Edge: %s\n"
228 " Falling Edge: %s\n"
229 " Filtering pulses shorter than %" PRIu32 " µs",
231 EDGE_MODE_TO_STRING[this->storage_.falling_edge_mode], this->storage_.filter_us);
232 LOG_UPDATE_INTERVAL(this);
233}
234
237 uint32_t now = millis();
238 if (this->last_time_ != 0) {
239 uint32_t interval = now - this->last_time_;
240 float value = (60000.0f * raw) / float(interval); // per minute
241 ESP_LOGD(TAG, "'%s': Retrieved counter: %0.2f pulses/min", LOG_STR_ARG(this->get_log_name()), value);
242 this->publish_state(value);
243 }
244
245 if (this->total_sensor_ != nullptr) {
247 ESP_LOGD(TAG, "'%s': Total : %" PRIu32 " pulses", LOG_STR_ARG(this->get_log_name()), current_total_);
249 }
250 this->last_time_ = now;
251}
252
253} // namespace esphome::pulse_counter
BedjetMode mode
BedJet operating mode.
uint8_t raw[35]
Definition bl0939.h:0
void mark_failed()
Mark this component as failed.
const LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:29
ISRInternalGPIOPin to_isr() const
Definition gpio.cpp:91
void setup() override
Unit of measurement is "pulses/min".
void set_falling_edge_mode(PulseCounterCountMode mode)
void set_rising_edge_mode(PulseCounterCountMode mode)
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
uint8_t type
unsigned count
Definition gif.cpp:51
int ret
@ INTERRUPT_FALLING_EDGE
Definition gpio_pin.h:53
@ INTERRUPT_RISING_EDGE
Definition gpio_pin.h:52
@ INTERRUPT_ANY_EDGE
Definition gpio_pin.h:54
ESPHOME_LOG_TAG(TAG, "pulse_counter")
const char *const EDGE_MODE_TO_STRING[]
PulseCounterStorageBase * get_storage(bool hw_pcnt)
uint32_t IRAM_ATTR HOT micros()
Definition hal.cpp:43
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
static void uint32_t
bool pulse_counter_setup(InternalGPIOPin *pin) override
static void gpio_intr(BasicPulseCounterStorage *arg)
bool pulse_counter_setup(InternalGPIOPin *pin) override
virtual pulse_counter_t read_raw_value()=0
virtual bool pulse_counter_setup(InternalGPIOPin *pin)=0
uint16_t x
Definition tt21100.cpp:5