mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
[tmc2209] Add current and diagnostic sensors (#20638)
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
from esphome.components.tmc22xx import CONF_ANALOG_CURRENT_SCALE
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import TMC2209Stepper, tmc2209_ns
|
||||
|
||||
CONF_ACTUAL_CURRENT = "actual_current"
|
||||
CONF_MOTOR_LOAD = "motor_load"
|
||||
CONF_PWM_GRAD_AUTO = "pwm_grad_auto"
|
||||
CONF_PWM_OFS_AUTO = "pwm_ofs_auto"
|
||||
CONF_PWM_SCALE_AUTO = "pwm_scale_auto"
|
||||
CONF_PWM_SCALE_SUM = "pwm_scale_sum"
|
||||
CONF_STALLGUARD_RESULT = "stallguard_result"
|
||||
CONF_TMC2209_ID = "tmc2209_id"
|
||||
|
||||
TMC2209Sensor = tmc2209_ns.class_("TMC2209Sensor", cg.PollingComponent)
|
||||
|
||||
_diagnostic_schema = sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TMC2209Sensor),
|
||||
cv.GenerateID(CONF_TMC2209_ID): cv.use_id(TMC2209Stepper),
|
||||
cv.Optional(CONF_ACTUAL_CURRENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MOTOR_LOAD): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_STALLGUARD_RESULT): _diagnostic_schema,
|
||||
cv.Optional(CONF_PWM_SCALE_SUM): _diagnostic_schema,
|
||||
cv.Optional(CONF_PWM_SCALE_AUTO): _diagnostic_schema,
|
||||
cv.Optional(CONF_PWM_OFS_AUTO): _diagnostic_schema,
|
||||
cv.Optional(CONF_PWM_GRAD_AUTO): _diagnostic_schema,
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
if CONF_ACTUAL_CURRENT not in config:
|
||||
return config
|
||||
full_config = fv.full_config.get()
|
||||
path = full_config.get_path_for_id(config[CONF_TMC2209_ID])[:-1]
|
||||
if full_config.get_config_for_path(path).get(CONF_ANALOG_CURRENT_SCALE):
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_ACTUAL_CURRENT}' can't be used with '{CONF_ANALOG_CURRENT_SCALE}', "
|
||||
"the current then depends on VREF",
|
||||
path=[CONF_ACTUAL_CURRENT],
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_TMC2209_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], hub)
|
||||
await cg.register_component(var, config)
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_ACTUAL_CURRENT, var.set_actual_current_sensor)
|
||||
await sensors(CONF_STALLGUARD_RESULT, var.set_stallguard_result_sensor)
|
||||
await sensors(CONF_MOTOR_LOAD, var.set_motor_load_sensor)
|
||||
await sensors(CONF_PWM_SCALE_SUM, var.set_pwm_scale_sum_sensor)
|
||||
await sensors(CONF_PWM_SCALE_AUTO, var.set_pwm_scale_auto_sensor)
|
||||
await sensors(CONF_PWM_OFS_AUTO, var.set_pwm_ofs_auto_sensor)
|
||||
await sensors(CONF_PWM_GRAD_AUTO, var.set_pwm_grad_auto_sensor)
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "tmc2209_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace esphome::tmc2209 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "tmc2209.sensor");
|
||||
|
||||
using tmc22xx::TMC22XXStepper;
|
||||
|
||||
// Highest StallGuard result, reached with no load
|
||||
static constexpr float STALLGUARD_RESULT_MAX = 510.0f;
|
||||
|
||||
static void publish_field(sensor::Sensor *sensor, const optional<uint32_t> &data, const RegisterField &field) {
|
||||
if (sensor == nullptr)
|
||||
return;
|
||||
if (!data.has_value()) {
|
||||
sensor->publish_state(NAN);
|
||||
return;
|
||||
}
|
||||
uint32_t value = TMC22XXStepper::extract_field(*data, field);
|
||||
sensor->publish_state(field.is_signed ? static_cast<float>(static_cast<int32_t>(value)) : static_cast<float>(value));
|
||||
}
|
||||
|
||||
void TMC2209Sensor::update() {
|
||||
if (this->parent_->is_failed())
|
||||
return;
|
||||
|
||||
// Each failed read blocks the loop for the UART timeout. Leave the bus alone while the driver is known to be
|
||||
// unreachable or after the first failed read; the remaining sensors then publish NaN.
|
||||
bool reachable = !this->parent_->is_unreachable();
|
||||
auto read = [this, &reachable](uint8_t reg) -> optional<uint32_t> {
|
||||
if (!reachable)
|
||||
return {};
|
||||
auto data = this->parent_->read_register(reg);
|
||||
reachable = data.has_value();
|
||||
return data;
|
||||
};
|
||||
|
||||
if (this->actual_current_sensor_ != nullptr) {
|
||||
auto drv_status = read(tmc22xx::REG_DRV_STATUS);
|
||||
float current = NAN;
|
||||
if (drv_status.has_value()) {
|
||||
auto scale = static_cast<uint8_t>(TMC22XXStepper::extract_field(*drv_status, tmc22xx::CS_ACTUAL));
|
||||
current = this->parent_->scale_to_current(scale);
|
||||
}
|
||||
this->actual_current_sensor_->publish_state(current);
|
||||
}
|
||||
|
||||
// StallGuard only measures the load while the motor turns
|
||||
bool moving = !this->parent_->has_reached_target();
|
||||
if (this->stallguard_result_sensor_ != nullptr || (this->motor_load_sensor_ != nullptr && moving)) {
|
||||
auto sg_result = read(REG_SG_RESULT);
|
||||
publish_field(this->stallguard_result_sensor_, sg_result, SG_RESULT);
|
||||
if (this->motor_load_sensor_ != nullptr) {
|
||||
// 100% is the load at which a stall is reported, where the result drops below twice the threshold
|
||||
float stall_result = 2.0f * this->parent_->get_stallguard_threshold();
|
||||
float load = NAN;
|
||||
if (moving && sg_result.has_value() && stall_result < STALLGUARD_RESULT_MAX) {
|
||||
float result = TMC22XXStepper::extract_field(*sg_result, SG_RESULT);
|
||||
load = (STALLGUARD_RESULT_MAX - result) / (STALLGUARD_RESULT_MAX - stall_result) * 100.0f;
|
||||
}
|
||||
this->motor_load_sensor_->publish_state(load);
|
||||
}
|
||||
} else if (this->motor_load_sensor_ != nullptr) {
|
||||
this->motor_load_sensor_->publish_state(NAN);
|
||||
}
|
||||
|
||||
if (this->pwm_scale_sum_sensor_ != nullptr || this->pwm_scale_auto_sensor_ != nullptr) {
|
||||
auto pwm_scale = read(tmc22xx::REG_PWM_SCALE);
|
||||
publish_field(this->pwm_scale_sum_sensor_, pwm_scale, tmc22xx::PWM_SCALE_SUM);
|
||||
publish_field(this->pwm_scale_auto_sensor_, pwm_scale, tmc22xx::PWM_SCALE_AUTO);
|
||||
}
|
||||
|
||||
if (this->pwm_ofs_auto_sensor_ != nullptr || this->pwm_grad_auto_sensor_ != nullptr) {
|
||||
auto pwm_auto = read(tmc22xx::REG_PWM_AUTO);
|
||||
publish_field(this->pwm_ofs_auto_sensor_, pwm_auto, tmc22xx::PWM_OFS_AUTO);
|
||||
publish_field(this->pwm_grad_auto_sensor_, pwm_auto, tmc22xx::PWM_GRAD_AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
void TMC2209Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "TMC2209 Sensor:");
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Actual Current", this->actual_current_sensor_);
|
||||
LOG_SENSOR(" ", "StallGuard Result", this->stallguard_result_sensor_);
|
||||
LOG_SENSOR(" ", "Motor Load", this->motor_load_sensor_);
|
||||
LOG_SENSOR(" ", "PWM Scale Sum", this->pwm_scale_sum_sensor_);
|
||||
LOG_SENSOR(" ", "PWM Scale Auto", this->pwm_scale_auto_sensor_);
|
||||
LOG_SENSOR(" ", "PWM Offset Auto", this->pwm_ofs_auto_sensor_);
|
||||
LOG_SENSOR(" ", "PWM Gradient Auto", this->pwm_grad_auto_sensor_);
|
||||
}
|
||||
|
||||
} // namespace esphome::tmc2209
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/tmc2209/tmc2209.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome::tmc2209 {
|
||||
|
||||
class TMC2209Sensor : public PollingComponent {
|
||||
public:
|
||||
explicit TMC2209Sensor(TMC2209Stepper *parent) : parent_(parent) {}
|
||||
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_actual_current_sensor(sensor::Sensor *sensor) { this->actual_current_sensor_ = sensor; }
|
||||
void set_stallguard_result_sensor(sensor::Sensor *sensor) { this->stallguard_result_sensor_ = sensor; }
|
||||
void set_motor_load_sensor(sensor::Sensor *sensor) { this->motor_load_sensor_ = sensor; }
|
||||
void set_pwm_scale_sum_sensor(sensor::Sensor *sensor) { this->pwm_scale_sum_sensor_ = sensor; }
|
||||
void set_pwm_scale_auto_sensor(sensor::Sensor *sensor) { this->pwm_scale_auto_sensor_ = sensor; }
|
||||
void set_pwm_ofs_auto_sensor(sensor::Sensor *sensor) { this->pwm_ofs_auto_sensor_ = sensor; }
|
||||
void set_pwm_grad_auto_sensor(sensor::Sensor *sensor) { this->pwm_grad_auto_sensor_ = sensor; }
|
||||
|
||||
protected:
|
||||
TMC2209Stepper *parent_;
|
||||
sensor::Sensor *actual_current_sensor_{nullptr};
|
||||
sensor::Sensor *stallguard_result_sensor_{nullptr};
|
||||
sensor::Sensor *motor_load_sensor_{nullptr};
|
||||
sensor::Sensor *pwm_scale_sum_sensor_{nullptr};
|
||||
sensor::Sensor *pwm_scale_auto_sensor_{nullptr};
|
||||
sensor::Sensor *pwm_ofs_auto_sensor_{nullptr};
|
||||
sensor::Sensor *pwm_grad_auto_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace esphome::tmc2209
|
||||
@@ -9,10 +9,12 @@ using tmc22xx::RegisterField;
|
||||
// StallGuard and CoolStep registers, which the TMC2208 does not have
|
||||
static constexpr uint8_t REG_TCOOLTHRS = 0x14;
|
||||
static constexpr uint8_t REG_SGTHRS = 0x40;
|
||||
static constexpr uint8_t REG_SG_RESULT = 0x41;
|
||||
static constexpr uint8_t REG_COOLCONF = 0x42;
|
||||
|
||||
static constexpr RegisterField TCOOLTHRS{REG_TCOOLTHRS, 0, 20, false};
|
||||
static constexpr RegisterField SGTHRS{REG_SGTHRS, 0, 8, false};
|
||||
static constexpr RegisterField SG_RESULT{REG_SG_RESULT, 0, 10, false};
|
||||
static constexpr RegisterField SEMIN{REG_COOLCONF, 0, 4, false};
|
||||
static constexpr RegisterField SEUP{REG_COOLCONF, 5, 2, false};
|
||||
static constexpr RegisterField SEMAX{REG_COOLCONF, 8, 4, false};
|
||||
@@ -30,6 +32,7 @@ class TMC2209Stepper : public tmc22xx::TMC22XXStepper {
|
||||
|
||||
/// A stall is reported when the StallGuard result drops below twice this value.
|
||||
void set_stallguard_threshold(uint8_t threshold) { this->write_field(SGTHRS, threshold); }
|
||||
uint8_t get_stallguard_threshold() const { return extract_field(this->sgthrs_, SGTHRS); }
|
||||
/// Lower velocity limit, as TSTEP, for CoolStep and the StallGuard output on DIAG.
|
||||
void set_tcool_threshold(uint32_t threshold) { this->write_field(TCOOLTHRS, threshold); }
|
||||
void set_semin(uint8_t semin) { this->write_field(SEMIN, semin); }
|
||||
|
||||
@@ -33,6 +33,8 @@ static constexpr uint8_t REG_VACTUAL = 0x22;
|
||||
static constexpr uint8_t REG_CHOPCONF = 0x6C;
|
||||
static constexpr uint8_t REG_DRV_STATUS = 0x6F;
|
||||
static constexpr uint8_t REG_PWMCONF = 0x70;
|
||||
static constexpr uint8_t REG_PWM_SCALE = 0x71;
|
||||
static constexpr uint8_t REG_PWM_AUTO = 0x72;
|
||||
|
||||
// Readable registers that a driver reset sets back to their defaults; the last written values are restored
|
||||
static constexpr std::array<uint8_t, 4> RESTORED_REGISTERS{REG_GCONF, REG_FACTORY_CONF, REG_CHOPCONF, REG_PWMCONF};
|
||||
@@ -59,7 +61,12 @@ static constexpr RegisterField VSENSE{REG_CHOPCONF, 17, 1, false};
|
||||
static constexpr RegisterField MRES{REG_CHOPCONF, 24, 4, false};
|
||||
static constexpr RegisterField INTPOL{REG_CHOPCONF, 28, 1, false};
|
||||
static constexpr RegisterField DEDGE{REG_CHOPCONF, 29, 1, false};
|
||||
static constexpr RegisterField CS_ACTUAL{REG_DRV_STATUS, 16, 5, false};
|
||||
static constexpr RegisterField FREEWHEEL{REG_PWMCONF, 20, 2, false};
|
||||
static constexpr RegisterField PWM_SCALE_SUM{REG_PWM_SCALE, 0, 8, false};
|
||||
static constexpr RegisterField PWM_SCALE_AUTO{REG_PWM_SCALE, 16, 9, true};
|
||||
static constexpr RegisterField PWM_OFS_AUTO{REG_PWM_AUTO, 0, 8, false};
|
||||
static constexpr RegisterField PWM_GRAD_AUTO{REG_PWM_AUTO, 16, 8, false};
|
||||
|
||||
enum StandstillMode : uint8_t {
|
||||
STANDSTILL_MODE_NORMAL = 0,
|
||||
@@ -129,6 +136,8 @@ class TMC22XXStepper : public stepper::Stepper, public Component, public uart::U
|
||||
/// Enable or disable the motor outputs, using the ENN pin when configured and TOFF otherwise.
|
||||
void set_enabled(bool enabled);
|
||||
bool is_enabled() const { return this->enabled_; }
|
||||
/// True while the driver does not answer, until the status poll reads it again (every 10 s).
|
||||
bool is_unreachable() const { return this->status_poll_skip_ > 0; }
|
||||
|
||||
/// Called with the condition and whether it became active or cleared.
|
||||
template<typename F> void add_on_status_callback(F &&callback) {
|
||||
|
||||
@@ -84,3 +84,26 @@ esphome:
|
||||
target: 1000
|
||||
- tmc2209.disable:
|
||||
id: tmc2209_step_dir
|
||||
|
||||
sensor:
|
||||
- platform: tmc2209
|
||||
tmc2209_id: tmc2209_step_dir
|
||||
update_interval: 10s
|
||||
actual_current:
|
||||
name: Actual Current
|
||||
stallguard_result:
|
||||
name: StallGuard Result
|
||||
motor_load:
|
||||
name: Motor Load
|
||||
pwm_scale_sum:
|
||||
name: PWM Scale Sum
|
||||
pwm_scale_auto:
|
||||
name: PWM Scale Auto
|
||||
pwm_ofs_auto:
|
||||
name: PWM Offset Auto
|
||||
pwm_grad_auto:
|
||||
name: PWM Gradient Auto
|
||||
- platform: tmc2209
|
||||
tmc2209_id: tmc2209_serial
|
||||
stallguard_result:
|
||||
name: Serial StallGuard Result
|
||||
|
||||
Reference in New Issue
Block a user