[MAX31888] Add temperature sensor (#13567)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
JiriPrchal
2026-10-12 12:45:08 -10:00
committed by GitHub
co-authored by pre-commit-ci-lite[bot] J. Nick Koston
parent a68cf669aa
commit f9f41ef16e
14 changed files with 211 additions and 0 deletions
+1
View File
@@ -343,6 +343,7 @@ esphome/components/mapping/* @clydebarrow
esphome/components/matrix_keypad/* @ssieb
esphome/components/max17043/* @blacknell
esphome/components/max31865/* @DAVe3283
esphome/components/max31888/* @JiriPrchal
esphome/components/max44009/* @berfenger
esphome/components/max6956/* @looping40
esphome/components/max7219digit/* @rspaargaren
+2
View File
@@ -0,0 +1,2 @@
CODEOWNERS = ["@JiriPrchal"]
DOMAIN = "max31888"
+95
View File
@@ -0,0 +1,95 @@
#include "max31888.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::max31888 {
ESPHOME_LOG_TAG(TAG, "max31888");
static constexpr uint32_t CONVERSION_TIMEOUT_ID = 0;
static constexpr uint8_t MAX31888_COMMAND_START_CONVERSION = 0x44;
static constexpr uint8_t MAX31888_COMMAND_READ = 0x33;
static constexpr uint8_t MAX31888_COMMAND_SOFT_RESET = 0x82;
static constexpr uint8_t MAX31888_REGISTER_FIFO_DATA = 0x08;
static constexpr uint8_t MAX31888_FIFO_READ_LENGTH = 0x01; // number of bytes to read, minus one
static constexpr uint16_t MAX31888_CONVERSION_TIME_MS = 20;
static constexpr float MAX31888_DEGREES_PER_LSB = 0.005f;
// Sends a command and checks the checksum the device appends to it (Maxim CRC-16 over the command byte,
// sent inverted and low byte first)
bool MAX31888Sensor::send_checked_command_(uint8_t command) {
if (!this->send_command_(command)) {
this->status_set_warning(LOG_STR("bus reset failed"));
return false;
}
const uint8_t low = this->bus_->read8();
const uint8_t high = this->bus_->read8();
if (crc16(&command, 1, 0, 0xa001, false, true) != encode_uint16(high, low)) {
this->status_set_warning(LOG_STR("command checksum invalid"));
return false;
}
return true;
}
void MAX31888Sensor::setup() {
if (!this->check_address_or_index_())
return;
if (!this->send_checked_command_(MAX31888_COMMAND_SOFT_RESET)) {
this->mark_failed();
}
}
void MAX31888Sensor::dump_config() {
ESP_LOGCONFIG(TAG, "MAX31888 Sensor:");
if (this->address_ == 0) {
ESP_LOGW(TAG, " Unable to select an address");
return;
}
LOG_ONE_WIRE_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
}
void MAX31888Sensor::update() {
if (this->address_ == 0)
return;
if (!this->send_checked_command_(MAX31888_COMMAND_START_CONVERSION)) {
this->publish_state(NAN);
return;
}
this->set_timeout(CONVERSION_TIMEOUT_ID, MAX31888_CONVERSION_TIME_MS, [this] {
int16_t raw;
if (!this->read_temperature_(raw)) {
this->publish_state(NAN);
return;
}
const float temperature = raw * MAX31888_DEGREES_PER_LSB;
ESP_LOGD(TAG, "'%s': Got Temperature=%.3f°C", this->get_name().c_str(), temperature);
this->status_clear_warning();
this->publish_state(temperature);
});
}
bool MAX31888Sensor::read_temperature_(int16_t &raw) {
// The checksum covers the command and its two arguments as well as the data
uint8_t frame[7] = {MAX31888_COMMAND_READ, MAX31888_REGISTER_FIFO_DATA, MAX31888_FIFO_READ_LENGTH};
if (!this->send_command_(frame[0])) {
this->status_set_warning(LOG_STR("bus reset failed"));
return false;
}
this->bus_->write8(frame[1]);
this->bus_->write8(frame[2]);
for (size_t i = 3; i < sizeof(frame); i++) {
frame[i] = this->bus_->read8();
}
if (crc16(frame, 5, 0, 0xa001, false, true) != encode_uint16(frame[6], frame[5])) {
this->status_set_warning(LOG_STR("checksum invalid"));
return false;
}
raw = static_cast<int16_t>(encode_uint16(frame[3], frame[4]));
return true;
}
} // namespace esphome::max31888
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/one_wire/one_wire.h"
namespace esphome::max31888 {
class MAX31888Sensor final : public PollingComponent, public sensor::Sensor, public one_wire::OneWireDevice {
public:
void setup() override;
void update() override;
void dump_config() override;
protected:
bool send_checked_command_(uint8_t command);
/// Reads the latest conversion from the FIFO; false when the bus or the checksum fails
bool read_temperature_(int16_t &raw);
};
} // namespace esphome::max31888
+36
View File
@@ -0,0 +1,36 @@
import esphome.codegen as cg
from esphome.components import one_wire, sensor
import esphome.config_validation as cv
from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
from esphome.types import ConfigType
max31888_ns = cg.esphome_ns.namespace("max31888")
MAX31888Sensor = max31888_ns.class_(
"MAX31888Sensor",
cg.PollingComponent,
sensor.Sensor,
one_wire.OneWireDevice,
)
CONFIG_SCHEMA = (
sensor.sensor_schema(
MAX31888Sensor,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(one_wire.one_wire_device_schema())
.extend(cv.polling_component_schema("60s"))
)
async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await one_wire.register_one_wire_device(var, config)
@@ -10,6 +10,7 @@ static const uint8_t DALLAS_MODEL_DS1822 = 0x22;
static const uint8_t DALLAS_MODEL_DS18B20 = 0x28;
static const uint8_t DALLAS_MODEL_DS1825 = 0x3B;
static const uint8_t DALLAS_MODEL_DS28EA00 = 0x42;
static const uint8_t MAXIM_MODEL_MAX31888 = 0x54;
const uint8_t ONE_WIRE_ROM_SELECT = 0x55;
const uint8_t ONE_WIRE_ROM_SEARCH = 0xF0;
@@ -76,6 +77,8 @@ const LogString *OneWireBus::get_model_str(uint8_t model) {
return LOG_STR("DS1825");
case DALLAS_MODEL_DS28EA00:
return LOG_STR("DS28EA00");
case MAXIM_MODEL_MAX31888:
return LOG_STR("MAX31888");
default:
return LOG_STR("Unknown");
}
+9
View File
@@ -0,0 +1,9 @@
sensor:
- platform: max31888
one_wire_id: one_wire_bus
address: 0x1C0000031EDD2A54
name: MAX31888 Temperature 1
- platform: max31888
one_wire_id: one_wire_bus
name: MAX31888 Temperature 2
index: 1
@@ -0,0 +1,3 @@
packages:
one_wire: !include ../../test_build_components/common/one_wire/esp32-idf.yaml
max31888: !include common.yaml
@@ -0,0 +1,3 @@
packages:
one_wire: !include ../../test_build_components/common/one_wire/esp8266-ard.yaml
max31888: !include common.yaml
@@ -0,0 +1,3 @@
packages:
one_wire: !include ../../test_build_components/common/one_wire/rp2040-ard.yaml
max31888: !include common.yaml
@@ -125,6 +125,11 @@ Components using `modbus` packages automatically get `uart` as well.
- **RP2040**: SCL=GPIO5, SDA=GPIO4
- **BK72xx**: SCL=P20, SDA=P21
### 1-Wire
- **ESP32 IDF**: GPIO4
- **ESP8266**: GPIO13
- **RP2040**: GPIO10
### I2C Low Frequency (10kHz)
Same pin allocations as standard I2C, but with 10kHz frequency for components requiring slower speeds.
@@ -0,0 +1,10 @@
# Common 1-Wire configuration for esp32-idf tests
# Provides a shared 1-Wire bus that all components can use
substitutions:
one_wire_pin: GPIO4
one_wire:
- platform: gpio
id: one_wire_bus
pin: ${one_wire_pin}
@@ -0,0 +1,10 @@
# Common 1-Wire configuration for esp8266-ard tests
# Provides a shared 1-Wire bus that all components can use
substitutions:
one_wire_pin: GPIO13
one_wire:
- platform: gpio
id: one_wire_bus
pin: ${one_wire_pin}
@@ -0,0 +1,10 @@
# Common 1-Wire configuration for rp2040-ard tests
# Provides a shared 1-Wire bus that all components can use
substitutions:
one_wire_pin: GPIO10
one_wire:
- platform: gpio
id: one_wire_bus
pin: ${one_wire_pin}