Add MAX31865 sensor support, fix MAX31855 sensor (#832)

* Add MAX31865 sensor support, fix MAX31855 sensor.

# MAX31865

Added support for the MAX31865 RTD-to-Digital Converter to measure PT100 and
similar RTDs. Verified with an Adafruit unit (product ID: 3328) and a PT100
probe.

# MAX31855

This was setup for incorrect SPI clock polarity and phase, and would return bad
data due to a race condition measuring on the wrong edge (verified with Saleae
Logic scope). Selecting the correct configuration fixes that problem.

Re-wrote the decode off the datasheet to handle error states better (sends NaN
as an update on failure to read temperature, which shows the value as Unknown
in Home Assistant).

Added the *optional* ability to monitor the internal high-precision temperature
sensor, which can be nice in some applications.

* Tests for MAX31855/MAX38165.

* Update style to match project rules.

Also fix CONF_REFERENCE_RESISTANCE and CONF_REFERENCE_TEMPERATURE being defined
multiple places. Missed this when I added them to const.py.

* Update style to match project rules.

Pylint line limit 101/100 ("missed it by that much").
Also apparently I can't read and patched the wrong line in max31855.cpp.

* Minor string/style cleanup.

There was a copy-paste leftover in max31855.cpp and max31865/sensor.py had
unnecessary whitespace.

* Improve MAX31865 fault detection and logging.

Log levels are more in-line with the documented descriptions.

Fault detection code is improved. A transient fault between reads is still
reported, but now only faults *during* a read cause the sensor to fail and
return NAN ("unknown" in Home Assistant).

* Update style to match project rules.

I just now realized the .clang-format and pylintrc files are included. D'oh!

* MAX31855 & MAX31865 code style alignment.

@OttoWinter caught some style mismatches, updated to match project better.

* Fix a lost '\' in max31865/sensor.py.
This commit is contained in:
DAVe3283 2019-11-06 05:56:43 -07:00 committed by Otto Winter
parent 1ed8e63d59
commit f94e9b6b1e
10 changed files with 397 additions and 51 deletions

View File

@ -1,10 +1,11 @@
#include "max31855.h"
#include "esphome/core/log.h"
namespace esphome {
namespace max31855 {
static const char *TAG = "max31855";
static const char* TAG = "max31855";
void MAX31855Sensor::update() {
this->enable();
@ -22,9 +23,15 @@ void MAX31855Sensor::setup() {
this->spi_setup();
}
void MAX31855Sensor::dump_config() {
LOG_SENSOR("", "MAX31855", this);
ESP_LOGCONFIG(TAG, "MAX31855:");
LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Thermocouple", this);
if (this->temperature_reference_) {
LOG_SENSOR(" ", "Reference", this->temperature_reference_);
} else {
ESP_LOGCONFIG(TAG, " Reference temperature disabled.");
}
}
float MAX31855Sensor::get_setup_priority() const { return setup_priority::DATA; }
void MAX31855Sensor::read_data_() {
@ -32,53 +39,68 @@ void MAX31855Sensor::read_data_() {
delay(1);
uint8_t data[4];
this->read_array(data, 4);
// val is 14 bits of signed temperature data followed by 2 bits of status flags
int16_t val = data[1] | data[0] << 8;
// test data from MAX31855 datasheet
// val = 0x6400 // 1600.00°C
// val = 0x3E80 // 1000.00°C
// val = 0x064C // 100.75°C
// val = 0x0190 // 25.00°C
// val = 0x0000 // 0.00°C
// val = 0xFFFC // -0.25°C
// val = 0xFFF0 // -1.00°C
// val = 0xF060 // -250.00°C
this->disable();
if ((data[3] & 0x01) != 0) {
ESP_LOGW(TAG, "Got thermocouple not connected from MAX31855Sensor (0x%04X) (0x%04X)", val, data[3] | data[2] << 8);
this->status_set_warning();
return;
}
if ((data[3] & 0x02) != 0) {
ESP_LOGW(TAG, "Got short circuit to ground from MAX31855Sensor (0x%04X) (0x%04X)", val, data[3] | data[2] << 8);
this->status_set_warning();
return;
}
if ((data[3] & 0x04) != 0) {
ESP_LOGW(TAG, "Got short circuit to power from MAX31855Sensor (0x%04X) (0x%04X)", val, data[3] | data[2] << 8);
this->status_set_warning();
return;
}
if ((data[1] & 0x01) != 0) {
ESP_LOGW(TAG, "Got faulty reading from MAX31855Sensor (0x%04X) (0x%04X)", val, data[3] | data[2] << 8);
this->status_set_warning();
return;
}
if ((val & 0x8000) != 0) {
// Negative value, drop the lower 2 bits and explicitly extend sign bits.
val = 0xE000 | ((val >> 2) & 0x1FFF);
const uint32_t mem = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3] << 0;
// Verify we got data
if (mem != 0 && mem != 0xFFFFFFFF) {
this->status_clear_error();
} else {
// Positive value, just drop the lower 2 bits.
val >>= 2;
ESP_LOGE(TAG, "No data received from MAX31855 (0x%08X). Check wiring!", mem);
this->publish_state(NAN);
if (this->temperature_reference_) {
this->temperature_reference_->publish_state(NAN);
}
this->status_set_error();
return;
}
float temperature = float(val) / 4.0f;
ESP_LOGD(TAG, "'%s': Got temperature=%.1f°C", this->name_.c_str(), temperature);
this->publish_state(temperature);
// Internal reference temperature always works
if (this->temperature_reference_) {
int16_t val = (mem & 0x0000FFF0) >> 4;
if (val & 0x0800) {
val |= 0xF000; // Pad out 2's complement
}
const float t_ref = float(val) * 0.0625f;
ESP_LOGD(TAG, "Got reference temperature: %.4f°C", t_ref);
this->temperature_reference_->publish_state(t_ref);
}
// Check thermocouple faults
if (mem & 0x00000001) {
ESP_LOGW(TAG, "Thermocouple open circuit (not connected) fault from MAX31855 (0x%08X)", mem);
this->publish_state(NAN);
this->status_set_warning();
return;
}
if (mem & 0x00000002) {
ESP_LOGW(TAG, "Thermocouple short circuit to ground fault from MAX31855 (0x%08X)", mem);
this->publish_state(NAN);
this->status_set_warning();
return;
}
if (mem & 0x00000004) {
ESP_LOGW(TAG, "Thermocouple short circuit to VCC fault from MAX31855 (0x%08X)", mem);
this->publish_state(NAN);
this->status_set_warning();
return;
}
if (mem & 0x00010000) {
ESP_LOGW(TAG, "Got faulty reading from MAX31855 (0x%08X)", mem);
this->publish_state(NAN);
this->status_set_warning();
return;
}
// Decode thermocouple temperature
int16_t val = (mem & 0xFFFC0000) >> 18;
if (val & 0x2000) {
val |= 0xC000; // Pad out 2's complement
}
const float t_sense = float(val) * 0.25f;
ESP_LOGD(TAG, "Got thermocouple temperature: %.2f°C", t_sense);
this->publish_state(t_sense);
this->status_clear_warning();
}

View File

@ -9,9 +9,11 @@ namespace max31855 {
class MAX31855Sensor : public sensor::Sensor,
public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_4MHZ> {
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_4MHZ> {
public:
void set_reference_sensor(sensor::Sensor *temperature_sensor) { temperature_reference_ = temperature_sensor; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
@ -20,6 +22,7 @@ class MAX31855Sensor : public sensor::Sensor,
protected:
void read_data_();
sensor::Sensor *temperature_reference_{nullptr};
};
} // namespace max31855

View File

@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS
from esphome.const import CONF_ID, CONF_REFERENCE_TEMPERATURE, ICON_THERMOMETER, UNIT_CELSIUS
max31855_ns = cg.esphome_ns.namespace('max31855')
MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.Sensor, cg.PollingComponent,
@ -9,6 +9,8 @@ MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.Sensor, cg.PollingC
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({
cv.GenerateID(): cv.declare_id(MAX31855Sensor),
cv.Optional(CONF_REFERENCE_TEMPERATURE):
sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2),
}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA)
@ -17,3 +19,6 @@ def to_code(config):
yield cg.register_component(var, config)
yield spi.register_spi_device(var, config)
yield sensor.register_sensor(var, config)
if CONF_REFERENCE_TEMPERATURE in config:
tc_ref = yield sensor.new_sensor(config[CONF_REFERENCE_TEMPERATURE])
cg.add(var.set_reference_sensor(tc_ref))

View File

View File

@ -0,0 +1,214 @@
#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
write_register_(CONFIGURATION_REG, 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_register_(CONFIGURATION_REG, 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 configuration
uint8_t config = 0b00000010;
config |= (filter_ & 1) << 0;
if (rtd_wires_ == 3) {
config |= 1 << 4;
}
this->write_register_(CONFIGURATION_REG, 0b11111111, config);
}
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_register_(CONFIGURATION_REG, 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_register_(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) {
uint8_t value = this->read_register_(reg);
value &= (~mask);
value |= (bits << start_position);
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

View File

@ -0,0 +1,56 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace max31865 {
enum MAX31865RegisterMasks { SPI_WRITE_M = 0x80 };
enum MAX31865Registers {
CONFIGURATION_REG = 0x00,
RTD_RESISTANCE_MSB_REG = 0x01,
RTD_RESISTANCE_LSB_REG = 0x02,
FAULT_THRESHOLD_H_MSB_REG = 0x03,
FAULT_THRESHOLD_H_LSB_REG = 0x04,
FAULT_THRESHOLD_L_MSB_REG = 0x05,
FAULT_THRESHOLD_L_LSB_REG = 0x06,
FAULT_STATUS_REG = 0x07,
};
enum MAX31865ConfigFilter {
FILTER_60HZ = 0,
FILTER_50HZ = 1,
};
class MAX31865Sensor : public sensor::Sensor,
public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_4MHZ> {
public:
void set_reference_resistance(float reference_resistance) { reference_resistance_ = reference_resistance; }
void set_nominal_resistance(float nominal_resistance) { rtd_nominal_resistance_ = nominal_resistance; }
void set_filter(MAX31865ConfigFilter filter) { filter_ = filter; }
void set_num_rtd_wires(uint8_t rtd_wires) { rtd_wires_ = rtd_wires; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
float reference_resistance_;
float rtd_nominal_resistance_;
MAX31865ConfigFilter filter_;
uint8_t rtd_wires_;
bool has_fault_ = false;
bool has_warn_ = false;
void read_data_();
void write_register_(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position = 0);
const uint8_t read_register_(uint8_t reg);
const uint16_t read_register_16_(uint8_t reg);
float calc_temperature_(const float& rtd_ratio);
};
} // namespace max31865
} // namespace esphome

View File

@ -0,0 +1,34 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
from esphome.const import CONF_ID, CONF_MAINS_FILTER, CONF_REFERENCE_RESISTANCE, \
CONF_RTD_NOMINAL_RESISTANCE, CONF_RTD_WIRES, ICON_THERMOMETER, UNIT_CELSIUS
max31865_ns = cg.esphome_ns.namespace('max31865')
MAX31865Sensor = max31865_ns.class_('MAX31865Sensor', sensor.Sensor, cg.PollingComponent,
spi.SPIDevice)
MAX31865ConfigFilter = max31865_ns.enum('MAX31865ConfigFilter')
FILTER = {
'50HZ': MAX31865ConfigFilter.FILTER_50HZ,
'60HZ': MAX31865ConfigFilter.FILTER_60HZ,
}
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2).extend({
cv.GenerateID(): cv.declare_id(MAX31865Sensor),
cv.Required(CONF_REFERENCE_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=10000)),
cv.Required(CONF_RTD_NOMINAL_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=1000)),
cv.Optional(CONF_MAINS_FILTER, default='60HZ'): cv.enum(FILTER, upper=True, space=''),
cv.Optional(CONF_RTD_WIRES, default=4): cv.int_range(min=2, max=4),
}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield spi.register_spi_device(var, config)
yield sensor.register_sensor(var, config)
cg.add(var.set_reference_resistance(config[CONF_REFERENCE_RESISTANCE]))
cg.add(var.set_nominal_resistance(config[CONF_RTD_NOMINAL_RESISTANCE]))
cg.add(var.set_filter(config[CONF_MAINS_FILTER]))
cg.add(var.set_num_rtd_wires(config[CONF_RTD_WIRES]))

View File

@ -4,15 +4,14 @@ from math import log
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components import sensor
from esphome.const import UNIT_CELSIUS, ICON_THERMOMETER, CONF_SENSOR, CONF_TEMPERATURE, \
CONF_VALUE, CONF_CALIBRATION, CONF_ID
from esphome.const import CONF_CALIBRATION, CONF_ID, CONF_REFERENCE_RESISTANCE, \
CONF_REFERENCE_TEMPERATURE, CONF_SENSOR, CONF_TEMPERATURE, CONF_VALUE, ICON_THERMOMETER, \
UNIT_CELSIUS
ntc_ns = cg.esphome_ns.namespace('ntc')
NTC = ntc_ns.class_('NTC', cg.Component, sensor.Sensor)
CONF_B_CONSTANT = 'b_constant'
CONF_REFERENCE_TEMPERATURE = 'reference_temperature'
CONF_REFERENCE_RESISTANCE = 'reference_resistance'
CONF_A = 'a'
CONF_B = 'b'
CONF_C = 'c'

View File

@ -224,6 +224,7 @@ CONF_LOGS = 'logs'
CONF_LOW = 'low'
CONF_LOW_VOLTAGE_REFERENCE = 'low_voltage_reference'
CONF_MAC_ADDRESS = 'mac_address'
CONF_MAINS_FILTER = 'mains_filter'
CONF_MAKE_ID = 'make_id'
CONF_MANUAL_IP = 'manual_ip'
CONF_MASK_DISTURBER = 'mask_disturber'
@ -343,6 +344,8 @@ CONF_RAW = 'raw'
CONF_REBOOT_TIMEOUT = 'reboot_timeout'
CONF_RECEIVE_TIMEOUT = 'receive_timeout'
CONF_RED = 'red'
CONF_REFERENCE_RESISTANCE = 'reference_resistance'
CONF_REFERENCE_TEMPERATURE = 'reference_temperature'
CONF_REPEAT = 'repeat'
CONF_REPOSITORY = 'repository'
CONF_RESET_PIN = 'reset_pin'
@ -358,6 +361,8 @@ CONF_RGBW = 'rgbw'
CONF_RISING_EDGE = 'rising_edge'
CONF_ROTATION = 'rotation'
CONF_RS_PIN = 'rs_pin'
CONF_RTD_NOMINAL_RESISTANCE = 'rtd_nominal_resistance'
CONF_RTD_WIRES = 'rtd_wires'
CONF_RUN_CYCLES = 'run_cycles'
CONF_RUN_DURATION = 'run_duration'
CONF_RW_PIN = 'rw_pin'

View File

@ -451,6 +451,14 @@ sensor:
name: "Den Temperature"
cs_pin: GPIO23
update_interval: 15s
reference_temperature:
name: "MAX31855 Internal Temperature"
- platform: max31865
name: "Water Tank Temperature"
cs_pin: GPIO23
update_interval: 15s
reference_resistance: "430 Ω"
rtd_nominal_resistance: "100 Ω"
- platform: mhz19
co2:
name: "MH-Z19 CO2 Value"