ESPHome  2024.4.0
max31865.cpp
Go to the documentation of this file.
1 #include "max31865.h"
2 
3 #include "esphome/core/log.h"
4 #include <cmath>
5 #include <cinttypes>
6 
7 namespace esphome {
8 namespace max31865 {
9 
10 static const char *const TAG = "max31865";
11 
13  // Check new faults since last measurement
14  if (!has_fault_) {
15  const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
16  if (faults & 0b11111100) {
17  if (faults & (1 << 2)) {
18  ESP_LOGW(TAG, "Overvoltage/undervoltage fault between measurements");
19  }
20  if (faults & (1 << 3)) {
21  ESP_LOGW(TAG, "RTDIN- < 0.85 x V_BIAS (FORCE- open) between measurements");
22  }
23  if (faults & (1 << 4)) {
24  ESP_LOGW(TAG, "REFIN- < 0.85 x V_BIAS (FORCE- open) between measurements");
25  }
26  if (faults & (1 << 5)) {
27  ESP_LOGW(TAG, "REFIN- > 0.85 x V_BIAS between measurements");
28  }
29  if (!has_warn_) {
30  if (faults & (1 << 6)) {
31  ESP_LOGW(TAG, "RTD Low Threshold between measurements");
32  }
33  if (faults & (1 << 7)) {
34  ESP_LOGW(TAG, "RTD High Threshold between measurements");
35  }
36  }
37  }
38  }
39 
40  // Run fault detection
41  this->write_config_(0b11101110, 0b10000110);
42  const uint32_t start_time = micros();
43  uint8_t config;
44  uint32_t fault_detect_time;
45  do {
46  config = this->read_register_(CONFIGURATION_REG);
47  fault_detect_time = micros() - start_time;
48  if ((fault_detect_time >= 6000) && (config & 0b00001100)) {
49  ESP_LOGE(TAG,
50  "Fault detection incomplete (0x%02X) after %" PRIu32 "μs (datasheet spec is 600μs max)! Aborting read.",
51  config, fault_detect_time);
52  this->publish_state(NAN);
53  this->status_set_error();
54  return;
55  }
56  } while (config & 0b00001100);
57  ESP_LOGV(TAG, "Fault detection completed in %" PRIu32 "μs.", fault_detect_time);
58 
59  // Start 1-shot conversion
60  this->write_config_(0b11100000, 0b10100000);
61 
62  // Datasheet max conversion time is 55ms for 60Hz / 66ms for 50Hz
63  auto f = std::bind(&MAX31865Sensor::read_data_, this);
64  this->set_timeout("value", filter_ == FILTER_60HZ ? 55 : 66, f);
65 }
66 
68  ESP_LOGCONFIG(TAG, "Setting up MAX31865Sensor '%s'...", this->name_.c_str());
69  this->spi_setup();
70 
71  // Build base configuration
72  base_config_ = 0b00000000;
73  base_config_ |= (filter_ & 1) << 0;
74  if (rtd_wires_ == 3) {
75  base_config_ |= 1 << 4;
76  }
77 
78  // Clear any existing faults & set base config
79  this->write_config_(0b00000010, 0b00000010);
80 }
81 
83  LOG_SENSOR("", "MAX31865", this);
84  LOG_PIN(" CS Pin: ", this->cs_);
85  LOG_UPDATE_INTERVAL(this);
86  ESP_LOGCONFIG(TAG, " Reference Resistance: %.2fΩ", reference_resistance_);
87  ESP_LOGCONFIG(TAG, " RTD: %u-wire %.2fΩ", rtd_wires_, rtd_nominal_resistance_);
88  ESP_LOGCONFIG(TAG, " Mains Filter: %s",
89  (filter_ == FILTER_60HZ ? "60 Hz" : (filter_ == FILTER_50HZ ? "50 Hz" : "Unknown!")));
90 }
91 
93 
95  // Read temperature, disable V_BIAS (save power)
96  const uint16_t rtd_resistance_register = this->read_register_16_(RTD_RESISTANCE_MSB_REG);
97  this->write_config_(0b11000000, 0b00000000);
98 
99  // Check for bad connection
100  if (rtd_resistance_register == 0b0000000000000000 || rtd_resistance_register == 0b1111111111111111) {
101  ESP_LOGE(TAG, "SPI bus read all 0 or all 1 (0x%04X), check MAX31865 wiring & power.", rtd_resistance_register);
102  this->publish_state(NAN);
103  this->status_set_error();
104  return;
105  }
106 
107  // Check faults
108  const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
109  if ((has_fault_ = faults & 0b00111100)) {
110  if (faults & (1 << 2)) {
111  ESP_LOGE(TAG, "Overvoltage/undervoltage fault");
112  }
113  if (faults & (1 << 3)) {
114  ESP_LOGE(TAG, "RTDIN- < 0.85 x V_BIAS (FORCE- open)");
115  }
116  if (faults & (1 << 4)) {
117  ESP_LOGE(TAG, "REFIN- < 0.85 x V_BIAS (FORCE- open)");
118  }
119  if (faults & (1 << 5)) {
120  ESP_LOGE(TAG, "REFIN- > 0.85 x V_BIAS");
121  }
122  this->publish_state(NAN);
123  this->status_set_error();
124  return;
125  } else {
126  this->status_clear_error();
127  }
128  if ((has_warn_ = faults & 0b11000000)) {
129  if (faults & (1 << 6)) {
130  ESP_LOGW(TAG, "RTD Low Threshold");
131  }
132  if (faults & (1 << 7)) {
133  ESP_LOGW(TAG, "RTD High Threshold");
134  }
135  this->status_set_warning();
136  } else {
137  this->status_clear_warning();
138  }
139 
140  // Process temperature
141  if (rtd_resistance_register & 0x0001) {
142  ESP_LOGW(TAG, "RTD Resistance Registers fault bit set! (0x%04X)", rtd_resistance_register);
143  this->status_set_warning();
144  }
145  const float rtd_ratio = static_cast<float>(rtd_resistance_register >> 1) / static_cast<float>((1 << 15) - 1);
146  const float temperature = this->calc_temperature_(rtd_ratio);
147  ESP_LOGV(TAG, "RTD read complete. %.5f (ratio) * %.1fΩ (reference) = %.2fΩ --> %.2f°C", rtd_ratio,
148  reference_resistance_, reference_resistance_ * rtd_ratio, temperature);
149  this->publish_state(temperature);
150 }
151 
152 void MAX31865Sensor::write_config_(uint8_t mask, uint8_t bits, uint8_t start_position) {
153  uint8_t value = base_config_;
154 
155  value &= (~mask);
156  value |= (bits << start_position);
157 
158  this->write_register_(CONFIGURATION_REG, value);
159 }
160 
161 void MAX31865Sensor::write_register_(uint8_t reg, uint8_t value) {
162  this->enable();
163  this->write_byte(reg |= SPI_WRITE_M);
164  this->write_byte(value);
165  this->disable();
166  ESP_LOGVV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value);
167 }
168 
169 uint8_t MAX31865Sensor::read_register_(uint8_t reg) {
170  this->enable();
171  this->write_byte(reg);
172  const uint8_t value(this->read_byte());
173  this->disable();
174  ESP_LOGVV(TAG, "read_register_ 0x%02X: 0x%02X", reg, value);
175  return value;
176 }
177 
178 uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) {
179  this->enable();
180  this->write_byte(reg);
181  const uint8_t msb(this->read_byte());
182  const uint8_t lsb(this->read_byte());
183  this->disable();
184  const uint16_t value((msb << 8) | lsb);
185  ESP_LOGVV(TAG, "read_register_16_ 0x%02X: 0x%04X", reg, value);
186  return value;
187 }
188 
189 float MAX31865Sensor::calc_temperature_(float rtd_ratio) {
190  // Based loosely on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31865
191  // Mainly based on formulas provided by Analog:
192  // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
193 
194  const float a = 3.9083e-3;
195  const float b = -5.775e-7;
196  const float z1 = -a;
197  const float z2 = a * a - 4 * b;
198  const float z3 = 4 * b / rtd_nominal_resistance_;
199  const float z4 = 2 * b;
200 
201  float rtd_resistance = rtd_ratio * reference_resistance_;
202 
203  // ≥ 0°C Formula
204  const float pos_temp = (z1 + std::sqrt(z2 + (z3 * rtd_resistance))) / z4;
205  if (pos_temp >= 0) {
206  return pos_temp;
207  }
208 
209  // < 0°C Formula
210  if (rtd_nominal_resistance_ != 100) {
211  // Normalize RTD to 100Ω
212  rtd_resistance /= rtd_nominal_resistance_;
213  rtd_resistance *= 100;
214  }
215  float rpoly = rtd_resistance;
216  float neg_temp = -242.02f;
217  neg_temp += 2.2228f * rpoly;
218  rpoly *= rtd_resistance; // square
219  neg_temp += 2.5859e-3f * rpoly;
220  rpoly *= rtd_resistance; // ^3
221  neg_temp -= 4.8260e-6f * rpoly;
222  rpoly *= rtd_resistance; // ^4
223  neg_temp -= 2.8183e-8f * rpoly;
224  rpoly *= rtd_resistance; // ^5
225  neg_temp += 1.5243e-10f * rpoly;
226  return neg_temp;
227 }
228 
229 } // namespace max31865
230 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
uint16_t read_register_16_(uint8_t reg)
Definition: max31865.cpp:178
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
float get_setup_priority() const override
Definition: max31865.cpp:92
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
GPIOPin * cs_
Definition: spi.h:395
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
void write_register_(uint8_t reg, uint8_t value)
Definition: max31865.cpp:161
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
void status_clear_warning()
Definition: component.cpp:166
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
float calc_temperature_(float rtd_ratio)
Definition: max31865.cpp:189
void write_config_(uint8_t mask, uint8_t bits, uint8_t start_position=0)
Definition: max31865.cpp:152
constexpr const char * c_str() const
Definition: string_ref.h:68
MAX31865ConfigFilter filter_
Definition: max31865.h:44
void status_clear_error()
Definition: component.cpp:172
uint8_t read_register_(uint8_t reg)
Definition: max31865.cpp:169
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7