ESPHome 2025.6.3
Loading...
Searching...
No Matches
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
7namespace esphome {
8namespace max31865 {
9
10static 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, "Running setup for '%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,
87 " Reference Resistance: %.2fΩ\n"
88 " RTD: %u-wire %.2fΩ\n"
89 " Mains Filter: %s",
91 (filter_ == FILTER_60HZ ? "60 Hz" : (filter_ == FILTER_50HZ ? "50 Hz" : "Unknown!")));
92}
93
95
97 // Read temperature, disable V_BIAS (save power)
98 const uint16_t rtd_resistance_register = this->read_register_16_(RTD_RESISTANCE_MSB_REG);
99 this->write_config_(0b11000000, 0b00000000);
100
101 // Check for bad connection
102 if (rtd_resistance_register == 0b0000000000000000 || rtd_resistance_register == 0b1111111111111111) {
103 ESP_LOGE(TAG, "SPI bus read all 0 or all 1 (0x%04X), check MAX31865 wiring & power.", rtd_resistance_register);
104 this->publish_state(NAN);
105 this->status_set_error();
106 return;
107 }
108
109 // Check faults
110 const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
111 has_fault_ = faults & 0b00111100;
112 if (has_fault_) {
113 if (faults & (1 << 2)) {
114 ESP_LOGE(TAG, "Overvoltage/undervoltage fault");
115 }
116 if (faults & (1 << 3)) {
117 ESP_LOGE(TAG, "RTDIN- < 0.85 x V_BIAS (FORCE- open)");
118 }
119 if (faults & (1 << 4)) {
120 ESP_LOGE(TAG, "REFIN- < 0.85 x V_BIAS (FORCE- open)");
121 }
122 if (faults & (1 << 5)) {
123 ESP_LOGE(TAG, "REFIN- > 0.85 x V_BIAS");
124 }
125 this->publish_state(NAN);
126 this->status_set_error();
127 return;
128 } else {
129 this->status_clear_error();
130 }
131 has_warn_ = faults & 0b11000000;
132 if (has_warn_) {
133 if (faults & (1 << 6)) {
134 ESP_LOGW(TAG, "RTD Low Threshold");
135 }
136 if (faults & (1 << 7)) {
137 ESP_LOGW(TAG, "RTD High Threshold");
138 }
139 this->status_set_warning();
140 } else {
141 this->status_clear_warning();
142 }
143
144 // Process temperature
145 if (rtd_resistance_register & 0x0001) {
146 ESP_LOGW(TAG, "RTD Resistance Registers fault bit set! (0x%04X)", rtd_resistance_register);
147 this->status_set_warning();
148 }
149 const float rtd_ratio = static_cast<float>(rtd_resistance_register >> 1) / static_cast<float>((1 << 15) - 1);
150 const float temperature = this->calc_temperature_(rtd_ratio);
151 ESP_LOGV(TAG, "RTD read complete. %.5f (ratio) * %.1fΩ (reference) = %.2fΩ --> %.2f°C", rtd_ratio,
153 this->publish_state(temperature);
154}
155
156void MAX31865Sensor::write_config_(uint8_t mask, uint8_t bits, uint8_t start_position) {
157 uint8_t value = base_config_;
158
159 value &= (~mask);
160 value |= (bits << start_position);
161
163}
164
165void MAX31865Sensor::write_register_(uint8_t reg, uint8_t value) {
166 this->enable();
167 this->write_byte(reg |= SPI_WRITE_M);
168 this->write_byte(value);
169 this->disable();
170 ESP_LOGVV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value);
171}
172
173uint8_t MAX31865Sensor::read_register_(uint8_t reg) {
174 this->enable();
175 this->write_byte(reg);
176 const uint8_t value(this->read_byte());
177 this->disable();
178 ESP_LOGVV(TAG, "read_register_ 0x%02X: 0x%02X", reg, value);
179 return value;
180}
181
183 this->enable();
184 this->write_byte(reg);
185 const uint8_t msb(this->read_byte());
186 const uint8_t lsb(this->read_byte());
187 this->disable();
188 const uint16_t value((msb << 8) | lsb);
189 ESP_LOGVV(TAG, "read_register_16_ 0x%02X: 0x%04X", reg, value);
190 return value;
191}
192
193float MAX31865Sensor::calc_temperature_(float rtd_ratio) {
194 // Based loosely on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31865
195 // Mainly based on formulas provided by Analog:
196 // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
197
198 const float a = 3.9083e-3;
199 const float b = -5.775e-7;
200 const float z1 = -a;
201 const float z2 = a * a - 4 * b;
202 const float z3 = 4 * b / rtd_nominal_resistance_;
203 const float z4 = 2 * b;
204
205 float rtd_resistance = rtd_ratio * reference_resistance_;
206
207 // ≥ 0°C Formula
208 const float pos_temp = (z1 + std::sqrt(z2 + (z3 * rtd_resistance))) / z4;
209 if (pos_temp >= 0) {
210 return pos_temp;
211 }
212
213 // < 0°C Formula
214 if (rtd_nominal_resistance_ != 100) {
215 // Normalize RTD to 100Ω
216 rtd_resistance /= rtd_nominal_resistance_;
217 rtd_resistance *= 100;
218 }
219 float rpoly = rtd_resistance;
220 float neg_temp = -242.02f;
221 neg_temp += 2.2228f * rpoly;
222 rpoly *= rtd_resistance; // square
223 neg_temp += 2.5859e-3f * rpoly;
224 rpoly *= rtd_resistance; // ^3
225 neg_temp -= 4.8260e-6f * rpoly;
226 rpoly *= rtd_resistance; // ^4
227 neg_temp -= 2.8183e-8f * rpoly;
228 rpoly *= rtd_resistance; // ^5
229 neg_temp += 1.5243e-10f * rpoly;
230 return neg_temp;
231}
232
233} // namespace max31865
234} // namespace esphome
void status_set_warning(const char *message="unspecified")
void status_set_error(const char *message="unspecified")
void status_clear_warning()
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:75
constexpr const char * c_str() const
Definition string_ref.h:69
uint8_t read_register_(uint8_t reg)
Definition max31865.cpp:173
void write_register_(uint8_t reg, uint8_t value)
Definition max31865.cpp:165
uint16_t read_register_16_(uint8_t reg)
Definition max31865.cpp:182
float get_setup_priority() const override
Definition max31865.cpp:94
void write_config_(uint8_t mask, uint8_t bits, uint8_t start_position=0)
Definition max31865.cpp:156
MAX31865ConfigFilter filter_
Definition max31865.h:44
float calc_temperature_(float rtd_ratio)
Definition max31865.cpp:193
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:20
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t IRAM_ATTR HOT micros()
Definition core.cpp:30
uint16_t temperature
Definition sun_gtil2.cpp:12