ESPHome  2022.6.3
pulse_meter_sensor.cpp
Go to the documentation of this file.
1 #include "pulse_meter_sensor.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace pulse_meter {
6 
7 static const char *const TAG = "pulse_meter";
8 
10  this->pin_->setup();
11  this->isr_pin_ = pin_->to_isr();
13 
14  this->last_detected_edge_us_ = 0;
15  this->last_valid_low_edge_us_ = 0;
16  this->last_valid_high_edge_us_ = 0;
17  this->sensor_is_high_ = this->isr_pin_.digital_read();
18 }
19 
21  const uint32_t now = micros();
22 
23  // Check to see if we should filter this edge out
24  if (this->filter_mode_ == FILTER_EDGE) {
25  if ((this->last_detected_edge_us_ - this->last_valid_high_edge_us_) >= this->filter_us_) {
26  // Don't measure the first valid pulse (we need at least two pulses to measure the width)
27  if (this->last_valid_high_edge_us_ != 0) {
29  }
30  this->total_pulses_++;
32  }
33  } else {
34  // Make sure the signal has been stable long enough
35  if ((now - this->last_detected_edge_us_) >= this->filter_us_) {
36  // Only consider HIGH pulses and "new" edges if sensor state is LOW
37  if (!this->sensor_is_high_ && this->isr_pin_.digital_read() &&
39  // Don't measure the first valid pulse (we need at least two pulses to measure the width)
40  if (this->last_valid_high_edge_us_ != 0) {
42  }
43  this->sensor_is_high_ = true;
44  this->total_pulses_++;
46  }
47  // Only consider LOW pulses and "new" edges if sensor state is HIGH
48  else if (this->sensor_is_high_ && !this->isr_pin_.digital_read() &&
50  this->sensor_is_high_ = false;
52  }
53  }
54  }
55 
56  // If we've exceeded our timeout interval without receiving any pulses, assume 0 pulses/min until
57  // we get at least two valid pulses.
58  const uint32_t time_since_valid_edge_us = now - this->last_valid_high_edge_us_;
59  if ((this->last_valid_high_edge_us_ != 0) && (time_since_valid_edge_us > this->timeout_us_) &&
60  (this->pulse_width_us_ != 0)) {
61  ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000);
62  this->pulse_width_us_ = 0;
63  }
64 
65  // We quantize our pulse widths to 1 ms to avoid unnecessary jitter
66  const uint32_t pulse_width_ms = this->pulse_width_us_ / 1000;
67  if (this->pulse_width_dedupe_.next(pulse_width_ms)) {
68  if (pulse_width_ms == 0) {
69  // Treat 0 pulse width as 0 pulses/min (normally because we've not detected any pulses for a while)
70  this->publish_state(0);
71  } else {
72  // Calculate pulses/min from the pulse width in ms
73  this->publish_state((60.0f * 1000.0f) / pulse_width_ms);
74  }
75  }
76 
77  if (this->total_sensor_ != nullptr) {
78  const uint32_t total = this->total_pulses_;
79  if (this->total_dedupe_.next(total)) {
80  this->total_sensor_->publish_state(total);
81  }
82  }
83 }
84 
85 void PulseMeterSensor::set_total_pulses(uint32_t pulses) { this->total_pulses_ = pulses; }
86 
88  LOG_SENSOR("", "Pulse Meter", this);
89  LOG_PIN(" Pin: ", this->pin_);
90  if (this->filter_mode_ == FILTER_EDGE) {
91  ESP_LOGCONFIG(TAG, " Filtering rising edges less than %u µs apart", this->filter_us_);
92  } else {
93  ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %u µs", this->filter_us_);
94  }
95  ESP_LOGCONFIG(TAG, " Assuming 0 pulses/min after not receiving a pulse for %us", this->timeout_us_ / 1000000);
96 }
97 
99  // This is an interrupt handler - we can't call any virtual method from this method
100 
101  // Get the current time before we do anything else so the measurements are consistent
102  const uint32_t now = micros();
103 
104  // We only look at rising edges in EDGE mode, and all edges in PULSE mode
105  if (sensor->filter_mode_ == FILTER_EDGE) {
106  if (sensor->isr_pin_.digital_read()) {
107  sensor->last_detected_edge_us_ = now;
108  }
109  } else {
110  sensor->last_detected_edge_us_ = now;
111  }
112 }
113 
114 } // namespace pulse_meter
115 } // namespace esphome
bool next(T value)
Feeds the next item in the series to the deduplicator and returns whether this is a duplicate...
Definition: helpers.h:476
virtual void setup()=0
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:28
static void gpio_intr(PulseMeterSensor *sensor)
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:70
virtual ISRInternalGPIOPin to_isr() const =0
Deduplicator< uint32_t > pulse_width_dedupe_
Definition: a4988.cpp:4
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition: gpio.h:81