mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-05-19 12:43:28 +02:00
36ffef083b
In a heavy EMI environment, reading the current config from the MAX31865 can fail, such as switching from a 4-wire sensor to a 3-wire sensor. This causes the temperature value to be off wildly, but still technically valid, so it doesn't get reported as a sensor failure. Since we know what configuration we want, rather than send it to the MAX31865 on setup and ask for it over and over (propagating any error as we write it back), instead store the base configuration and work from that to change modes. This not only avoids propagating any error, it also saves a lot of unnecessary reads from the MAX31865.
221 lines
6.9 KiB
C++
221 lines
6.9 KiB
C++
#include "max31865.h"
|
|
|
|
#include "esphome/core/log.h"
|
|
#include <cmath>
|
|
|
|
namespace esphome {
|
|
namespace max31865 {
|
|
|
|
static const char* TAG = "max31865";
|
|
|
|
void MAX31865Sensor::update() {
|
|
// Check new faults since last measurement
|
|
if (!has_fault_) {
|
|
const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
|
|
if (faults & 0b11111100) {
|
|
if (faults & (1 << 2)) {
|
|
ESP_LOGW(TAG, "Overvoltage/undervoltage fault between measurements");
|
|
}
|
|
if (faults & (1 << 3)) {
|
|
ESP_LOGW(TAG, "RTDIN- < 0.85 x V_BIAS (FORCE- open) between measurements");
|
|
}
|
|
if (faults & (1 << 4)) {
|
|
ESP_LOGW(TAG, "REFIN- < 0.85 x V_BIAS (FORCE- open) between measurements");
|
|
}
|
|
if (faults & (1 << 5)) {
|
|
ESP_LOGW(TAG, "REFIN- > 0.85 x V_BIAS between measurements");
|
|
}
|
|
if (!has_warn_) {
|
|
if (faults & (1 << 6)) {
|
|
ESP_LOGW(TAG, "RTD Low Threshold between measurements");
|
|
}
|
|
if (faults & (1 << 7)) {
|
|
ESP_LOGW(TAG, "RTD High Threshold between measurements");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run fault detection
|
|
this->write_config_(0b11101110, 0b10000110);
|
|
const uint32_t start_time = micros();
|
|
uint8_t config;
|
|
uint32_t fault_detect_time;
|
|
do {
|
|
config = this->read_register_(CONFIGURATION_REG);
|
|
fault_detect_time = micros() - start_time;
|
|
if ((fault_detect_time >= 6000) && (config & 0b00001100)) {
|
|
ESP_LOGE(TAG, "Fault detection incomplete (0x%02X) after %uμs (datasheet spec is 600μs max)! Aborting read.",
|
|
config, fault_detect_time);
|
|
this->publish_state(NAN);
|
|
this->status_set_error();
|
|
return;
|
|
}
|
|
} while (config & 0b00001100);
|
|
ESP_LOGV(TAG, "Fault detection completed in %uμs.", fault_detect_time);
|
|
|
|
// Start 1-shot conversion
|
|
this->write_config_(0b11100000, 0b10100000);
|
|
|
|
// Datasheet max conversion time is 55ms for 60Hz / 66ms for 50Hz
|
|
auto f = std::bind(&MAX31865Sensor::read_data_, this);
|
|
this->set_timeout("value", filter_ == FILTER_60HZ ? 55 : 66, f);
|
|
}
|
|
|
|
void MAX31865Sensor::setup() {
|
|
ESP_LOGCONFIG(TAG, "Setting up MAX31865Sensor '%s'...", this->name_.c_str());
|
|
this->spi_setup();
|
|
|
|
// Build base configuration
|
|
base_config_ = 0b00000000;
|
|
base_config_ |= (filter_ & 1) << 0;
|
|
if (rtd_wires_ == 3) {
|
|
base_config_ |= 1 << 4;
|
|
}
|
|
|
|
// Clear any existing faults & set base config
|
|
this->write_config_(0b00000010, 0b00000010);
|
|
}
|
|
|
|
void MAX31865Sensor::dump_config() {
|
|
LOG_SENSOR("", "MAX31865", this);
|
|
LOG_PIN(" CS Pin: ", this->cs_);
|
|
LOG_UPDATE_INTERVAL(this);
|
|
ESP_LOGCONFIG(TAG, " Reference Resistance: %.2fΩ", reference_resistance_);
|
|
ESP_LOGCONFIG(TAG, " RTD: %u-wire %.2fΩ", rtd_wires_, rtd_nominal_resistance_);
|
|
ESP_LOGCONFIG(TAG, " Filter: %s",
|
|
(filter_ == FILTER_60HZ ? "60 Hz" : (filter_ == FILTER_50HZ ? "50 Hz" : "Unknown!")));
|
|
}
|
|
|
|
float MAX31865Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
|
|
|
void MAX31865Sensor::read_data_() {
|
|
// Read temperature, disable V_BIAS (save power)
|
|
const uint16_t rtd_resistance_register = this->read_register_16_(RTD_RESISTANCE_MSB_REG);
|
|
this->write_config_(0b11000000, 0b00000000);
|
|
|
|
// Check faults
|
|
const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
|
|
if ((has_fault_ = faults & 0b00111100)) {
|
|
if (faults & (1 << 2)) {
|
|
ESP_LOGE(TAG, "Overvoltage/undervoltage fault");
|
|
}
|
|
if (faults & (1 << 3)) {
|
|
ESP_LOGE(TAG, "RTDIN- < 0.85 x V_BIAS (FORCE- open)");
|
|
}
|
|
if (faults & (1 << 4)) {
|
|
ESP_LOGE(TAG, "REFIN- < 0.85 x V_BIAS (FORCE- open)");
|
|
}
|
|
if (faults & (1 << 5)) {
|
|
ESP_LOGE(TAG, "REFIN- > 0.85 x V_BIAS");
|
|
}
|
|
this->publish_state(NAN);
|
|
this->status_set_error();
|
|
return;
|
|
} else {
|
|
this->status_clear_error();
|
|
}
|
|
if ((has_warn_ = faults & 0b11000000)) {
|
|
if (faults & (1 << 6)) {
|
|
ESP_LOGW(TAG, "RTD Low Threshold");
|
|
}
|
|
if (faults & (1 << 7)) {
|
|
ESP_LOGW(TAG, "RTD High Threshold");
|
|
}
|
|
this->status_set_warning();
|
|
} else {
|
|
this->status_clear_warning();
|
|
}
|
|
|
|
// Process temperature
|
|
if (rtd_resistance_register & 0x0001) {
|
|
ESP_LOGW(TAG, "RTD Resistance Registers fault bit set! (0x%04X)", rtd_resistance_register);
|
|
this->status_set_warning();
|
|
}
|
|
const float rtd_ratio = static_cast<float>(rtd_resistance_register >> 1) / static_cast<float>((1 << 15) - 1);
|
|
const float temperature = this->calc_temperature_(rtd_ratio);
|
|
ESP_LOGV(TAG, "RTD read complete. %.5f (ratio) * %.1fΩ (reference) = %.2fΩ --> %.2f°C", rtd_ratio,
|
|
reference_resistance_, reference_resistance_ * rtd_ratio, temperature);
|
|
this->publish_state(temperature);
|
|
}
|
|
|
|
void MAX31865Sensor::write_config_(uint8_t mask, uint8_t bits, uint8_t start_position) {
|
|
uint8_t value = base_config_;
|
|
|
|
value &= (~mask);
|
|
value |= (bits << start_position);
|
|
|
|
this->write_register_(CONFIGURATION_REG, value);
|
|
}
|
|
|
|
void MAX31865Sensor::write_register_(uint8_t reg, uint8_t value) {
|
|
this->enable();
|
|
this->write_byte(reg |= SPI_WRITE_M);
|
|
this->write_byte(value);
|
|
this->disable();
|
|
ESP_LOGVV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value);
|
|
}
|
|
|
|
const uint8_t MAX31865Sensor::read_register_(uint8_t reg) {
|
|
this->enable();
|
|
this->write_byte(reg);
|
|
const uint8_t value(this->read_byte());
|
|
this->disable();
|
|
ESP_LOGVV(TAG, "read_register_ 0x%02X: 0x%02X", reg, value);
|
|
return value;
|
|
}
|
|
|
|
const uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) {
|
|
this->enable();
|
|
this->write_byte(reg);
|
|
const uint8_t msb(this->read_byte());
|
|
const uint8_t lsb(this->read_byte());
|
|
this->disable();
|
|
const uint16_t value((msb << 8) | lsb);
|
|
ESP_LOGVV(TAG, "read_register_16_ 0x%02X: 0x%04X", reg, value);
|
|
return value;
|
|
}
|
|
|
|
float MAX31865Sensor::calc_temperature_(const float& rtd_ratio) {
|
|
// Based loosely on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31865
|
|
// Mainly based on formulas provided by Analog:
|
|
// http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
|
|
|
|
const float a = 3.9083e-3;
|
|
const float b = -5.775e-7;
|
|
const float z1 = -a;
|
|
const float z2 = a * a - 4 * b;
|
|
const float z3 = 4 * b / rtd_nominal_resistance_;
|
|
const float z4 = 2 * b;
|
|
|
|
float rtd_resistance = rtd_ratio * reference_resistance_;
|
|
|
|
// ≥ 0°C Formula
|
|
const float pos_temp = (z1 + std::sqrt(z2 + (z3 * rtd_resistance))) / z4;
|
|
if (pos_temp >= 0) {
|
|
return pos_temp;
|
|
}
|
|
|
|
// < 0°C Formula
|
|
if (rtd_nominal_resistance_ != 100) {
|
|
// Normalize RTD to 100Ω
|
|
rtd_resistance /= rtd_nominal_resistance_;
|
|
rtd_resistance *= 100;
|
|
}
|
|
float rpoly = rtd_resistance;
|
|
float neg_temp = -242.02;
|
|
neg_temp += 2.2228 * rpoly;
|
|
rpoly *= rtd_resistance; // square
|
|
neg_temp += 2.5859e-3 * rpoly;
|
|
rpoly *= rtd_resistance; // ^3
|
|
neg_temp -= 4.8260e-6 * rpoly;
|
|
rpoly *= rtd_resistance; // ^4
|
|
neg_temp -= 2.8183e-8 * rpoly;
|
|
rpoly *= rtd_resistance; // ^5
|
|
neg_temp += 1.5243e-10 * rpoly;
|
|
return neg_temp;
|
|
}
|
|
|
|
} // namespace max31865
|
|
} // namespace esphome
|