ESPHome  2024.3.1
pulse_meter_sensor.cpp
Go to the documentation of this file.
1 #include "pulse_meter_sensor.h"
2 #include <utility>
3 #include "esphome/core/log.h"
4 
5 namespace esphome {
6 namespace pulse_meter {
7 
8 static const char *const TAG = "pulse_meter";
9 
10 void PulseMeterSensor::set_total_pulses(uint32_t pulses) {
11  this->total_pulses_ = pulses;
12  if (this->total_sensor_ != nullptr) {
14  }
15 }
16 
18  this->pin_->setup();
19  this->isr_pin_ = pin_->to_isr();
20 
21  // Set the last processed edge to now for the first timeout
23 
24  if (this->filter_mode_ == FILTER_EDGE) {
26  } else if (this->filter_mode_ == FILTER_PULSE) {
28  }
29 }
30 
32  // Reset the count in get before we pass it back to the ISR as set
33  this->get_->count_ = 0;
34 
35  // Swap out set and get to get the latest state from the ISR
36  // The ISR could interrupt on any of these lines and the results would be consistent
37  auto *temp = this->set_;
38  this->set_ = this->get_;
39  this->get_ = temp;
40 
41  // Check if we detected a pulse this loop
42  if (this->get_->count_ > 0) {
43  // Keep a running total of pulses if a total sensor is configured
44  if (this->total_sensor_ != nullptr) {
45  this->total_pulses_ += this->get_->count_;
46  const uint32_t total = this->total_pulses_;
47  this->total_sensor_->publish_state(total);
48  }
49 
50  // We need to detect at least two edges to have a valid pulse width
51  switch (this->meter_state_) {
53  case MeterState::TIMED_OUT: {
55  } break;
56  case MeterState::RUNNING: {
57  uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_;
58  float pulse_width_us = delta_us / float(this->get_->count_);
59  this->publish_state((60.0f * 1000000.0f) / pulse_width_us);
60  } break;
61  }
62 
64  }
65  // No detected edges this loop
66  else {
67  const uint32_t now = micros();
68  const uint32_t time_since_valid_edge_us = now - this->last_processed_edge_us_;
69 
70  switch (this->meter_state_) {
71  // Running and initial states can timeout
73  case MeterState::RUNNING: {
74  if (time_since_valid_edge_us > this->timeout_us_) {
76  ESP_LOGD(TAG, "No pulse detected for %" PRIu32 "s, assuming 0 pulses/min",
77  time_since_valid_edge_us / 1000000);
78  this->publish_state(0.0f);
79  }
80  } break;
81  default:
82  break;
83  }
84  }
85 }
86 
88 
90  LOG_SENSOR("", "Pulse Meter", this);
91  LOG_PIN(" Pin: ", this->pin_);
92  if (this->filter_mode_ == FILTER_EDGE) {
93  ESP_LOGCONFIG(TAG, " Filtering rising edges less than %" PRIu32 " µs apart", this->filter_us_);
94  } else {
95  ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %" PRIu32 " µs", this->filter_us_);
96  }
97  ESP_LOGCONFIG(TAG, " Assuming 0 pulses/min after not receiving a pulse for %" PRIu32 "s",
98  this->timeout_us_ / 1000000);
99 }
100 
102  // This is an interrupt handler - we can't call any virtual method from this method
103  // Get the current time before we do anything else so the measurements are consistent
104  const uint32_t now = micros();
105 
106  if ((now - sensor->last_edge_candidate_us_) >= sensor->filter_us_) {
107  sensor->last_edge_candidate_us_ = now;
108  sensor->set_->last_detected_edge_us_ = now;
109  sensor->set_->count_++;
110  }
111 }
112 
114  // This is an interrupt handler - we can't call any virtual method from this method
115  // Get the current time before we do anything else so the measurements are consistent
116  const uint32_t now = micros();
117  const bool pin_val = sensor->isr_pin_.digital_read();
118 
119  // A pulse occurred faster than we can detect
120  if (sensor->last_pin_val_ == pin_val) {
121  // If we haven't reached the filter length yet we need to reset our last_intr_ to now
122  // otherwise we can consider this noise as the "pulse" was certainly less than filter_us_
123  if (now - sensor->last_intr_ < sensor->filter_us_) {
124  sensor->last_intr_ = now;
125  }
126  } else {
127  // Check if the last interrupt was long enough in the past
128  if (now - sensor->last_intr_ > sensor->filter_us_) {
129  // High pulse of filter length now falling (therefore last_intr_ was the rising edge)
130  if (!sensor->in_pulse_ && sensor->last_pin_val_) {
131  sensor->last_edge_candidate_us_ = sensor->last_intr_;
132  sensor->in_pulse_ = true;
133  }
134  // Low pulse of filter length now rising (therefore last_intr_ was the falling edge)
135  else if (sensor->in_pulse_ && !sensor->last_pin_val_) {
137  sensor->set_->count_++;
138  sensor->in_pulse_ = false;
139  }
140  }
141 
142  sensor->last_intr_ = now;
143  sensor->last_pin_val_ = pin_val;
144  }
145 }
146 
147 } // namespace pulse_meter
148 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
static void edge_intr(PulseMeterSensor *sensor)
virtual void setup()=0
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
static void pulse_intr(PulseMeterSensor *sensor)
virtual ISRInternalGPIOPin to_isr() const =0
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition: gpio.h:81