diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index 1ebc7fa3d8..f242b6b36d 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -1,85 +1,49 @@ #include "sdm_meter.h" #include "sdm_meter_registers.h" -#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome::sdm_meter { static const char *const TAG = "sdm_meter"; -static const uint8_t MODBUS_REGISTER_COUNT = 80; // 74 x 16-bit registers +static const uint8_t MODBUS_REGISTER_COUNT = 80; // 80 x 16-bit registers (40 float values) -void SDMMeter::on_response(std::span request_pdu, std::span response_pdu) { - auto data = modbus::helpers::server_pdu_payload(response_pdu); - if (data.size() < MODBUS_REGISTER_COUNT * 2) { - ESP_LOGW(TAG, "Invalid size for SDMMeter!"); - return; - } +void SDMMeter::on_read_input_registers(uint16_t start_address, std::span registers, + modbus::ResponseStatus status) { + if (!modbus::succeeded(status)) + return; // the hub already logs exception responses - auto sdm_meter_get_float = [&](size_t i) -> float { - uint32_t temp = encode_uint32(data[i], data[i + 1], data[i + 2], data[i + 3]); - float f; - memcpy(&f, &temp, sizeof(f)); - return f; + // Publish a sensor if both of its registers are in this response; skipping absent registers keeps + // this correct for any read range, so the poll may be split into multiple requests. + auto publish = [&](uint16_t reg, sensor::Sensor *sensor) { + constexpr auto value_type = modbus::helpers::SensorValueType::FP32; + if (sensor == nullptr || reg < start_address) + return; + size_t offset = reg - start_address; + if (offset + modbus::helpers::register_width_for(value_type) > registers.size()) + return; + sensor->publish_state(modbus::helpers::registers_to_value(registers.data() + offset)); }; for (uint8_t i = 0; i < 3; i++) { - auto phase = this->phases_[i]; + auto &phase = this->phases_[i]; if (!phase.setup) continue; - - float voltage = sdm_meter_get_float(SDM_PHASE_1_VOLTAGE * 2 + (i * 4)); - float current = sdm_meter_get_float(SDM_PHASE_1_CURRENT * 2 + (i * 4)); - float active_power = sdm_meter_get_float(SDM_PHASE_1_ACTIVE_POWER * 2 + (i * 4)); - float apparent_power = sdm_meter_get_float(SDM_PHASE_1_APPARENT_POWER * 2 + (i * 4)); - float reactive_power = sdm_meter_get_float(SDM_PHASE_1_REACTIVE_POWER * 2 + (i * 4)); - float power_factor = sdm_meter_get_float(SDM_PHASE_1_POWER_FACTOR * 2 + (i * 4)); - float phase_angle = sdm_meter_get_float(SDM_PHASE_1_ANGLE * 2 + (i * 4)); - - ESP_LOGD( - TAG, - "SDMMeter Phase %c: V=%.3f V, I=%.3f A, Active P=%.3f W, Apparent P=%.3f VA, Reactive P=%.3f var, PF=%.3f, " - "PA=%.3f °", - i + 'A', voltage, current, active_power, apparent_power, reactive_power, power_factor, phase_angle); - if (phase.voltage_sensor_ != nullptr) - phase.voltage_sensor_->publish_state(voltage); - if (phase.current_sensor_ != nullptr) - phase.current_sensor_->publish_state(current); - if (phase.active_power_sensor_ != nullptr) - phase.active_power_sensor_->publish_state(active_power); - if (phase.apparent_power_sensor_ != nullptr) - phase.apparent_power_sensor_->publish_state(apparent_power); - if (phase.reactive_power_sensor_ != nullptr) - phase.reactive_power_sensor_->publish_state(reactive_power); - if (phase.power_factor_sensor_ != nullptr) - phase.power_factor_sensor_->publish_state(power_factor); - if (phase.phase_angle_sensor_ != nullptr) - phase.phase_angle_sensor_->publish_state(phase_angle); + publish(SDM_PHASE_1_VOLTAGE + i * 2, phase.voltage_sensor_); + publish(SDM_PHASE_1_CURRENT + i * 2, phase.current_sensor_); + publish(SDM_PHASE_1_ACTIVE_POWER + i * 2, phase.active_power_sensor_); + publish(SDM_PHASE_1_APPARENT_POWER + i * 2, phase.apparent_power_sensor_); + publish(SDM_PHASE_1_REACTIVE_POWER + i * 2, phase.reactive_power_sensor_); + publish(SDM_PHASE_1_POWER_FACTOR + i * 2, phase.power_factor_sensor_); + publish(SDM_PHASE_1_ANGLE + i * 2, phase.phase_angle_sensor_); } - float total_power = sdm_meter_get_float(SDM_TOTAL_SYSTEM_POWER * 2); - float frequency = sdm_meter_get_float(SDM_FREQUENCY * 2); - float import_active_energy = sdm_meter_get_float(SDM_IMPORT_ACTIVE_ENERGY * 2); - float export_active_energy = sdm_meter_get_float(SDM_EXPORT_ACTIVE_ENERGY * 2); - float import_reactive_energy = sdm_meter_get_float(SDM_IMPORT_REACTIVE_ENERGY * 2); - float export_reactive_energy = sdm_meter_get_float(SDM_EXPORT_REACTIVE_ENERGY * 2); - - ESP_LOGD(TAG, "SDMMeter: F=%.3f Hz, Im.A.E=%.3f Wh, Ex.A.E=%.3f Wh, Im.R.E=%.3f VARh, Ex.R.E=%.3f VARh, T.P=%.3f W", - frequency, import_active_energy, export_active_energy, import_reactive_energy, export_reactive_energy, - total_power); - - if (this->total_power_sensor_ != nullptr) - this->total_power_sensor_->publish_state(total_power); - if (this->frequency_sensor_ != nullptr) - this->frequency_sensor_->publish_state(frequency); - if (this->import_active_energy_sensor_ != nullptr) - this->import_active_energy_sensor_->publish_state(import_active_energy); - if (this->export_active_energy_sensor_ != nullptr) - this->export_active_energy_sensor_->publish_state(export_active_energy); - if (this->import_reactive_energy_sensor_ != nullptr) - this->import_reactive_energy_sensor_->publish_state(import_reactive_energy); - if (this->export_reactive_energy_sensor_ != nullptr) - this->export_reactive_energy_sensor_->publish_state(export_reactive_energy); + publish(SDM_TOTAL_SYSTEM_POWER, this->total_power_sensor_); + publish(SDM_FREQUENCY, this->frequency_sensor_); + publish(SDM_IMPORT_ACTIVE_ENERGY, this->import_active_energy_sensor_); + publish(SDM_EXPORT_ACTIVE_ENERGY, this->export_active_energy_sensor_); + publish(SDM_IMPORT_REACTIVE_ENERGY, this->import_reactive_energy_sensor_); + publish(SDM_EXPORT_REACTIVE_ENERGY, this->export_reactive_energy_sensor_); } void SDMMeter::update() { this->read_input_registers(0, MODBUS_REGISTER_COUNT); } diff --git a/esphome/components/sdm_meter/sdm_meter.h b/esphome/components/sdm_meter/sdm_meter.h index e09b74bbc0..80370010bd 100644 --- a/esphome/components/sdm_meter/sdm_meter.h +++ b/esphome/components/sdm_meter/sdm_meter.h @@ -55,7 +55,8 @@ class SDMMeter final : public PollingComponent, public modbus::ModbusClientDevic void update() override; - void on_response(std::span request_pdu, std::span response_pdu) override; + void on_read_input_registers(uint16_t start_address, std::span registers, + modbus::ResponseStatus status) override; void dump_config() override;