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