ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
pzemac.cpp
Go to the documentation of this file.
1#include "pzemac.h"
2#include "esphome/core/log.h"
3
4namespace esphome::pzemac {
5
6namespace helpers = modbus::helpers;
7
8static const char *const TAG = "pzemac";
9
10static const uint8_t PZEM_CMD_RESET_ENERGY = 0x42;
11static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers
12
13// Register map, see https://github.com/esphome/feature-requests/issues/49#issuecomment-538636809
14// 32-bit values are two registers, low word first.
15static const uint16_t PZEM_REGISTER_VOLTAGE = 0; // 1 register, 0.1 V
16static const uint16_t PZEM_REGISTER_CURRENT = 1; // 2 registers, 0.001 A
17static const uint16_t PZEM_REGISTER_ACTIVE_POWER = 3; // 2 registers, 0.1 W
18static const uint16_t PZEM_REGISTER_ACTIVE_ENERGY = 5; // 2 registers, 1 Wh
19static const uint16_t PZEM_REGISTER_FREQUENCY = 7; // 1 register, 0.1 Hz
20static const uint16_t PZEM_REGISTER_POWER_FACTOR = 8; // 1 register, 0.01
21
22void PZEMAC::on_read_input_registers(uint16_t start_address, std::span<const uint16_t> registers,
25 return; // the hub already logs exception responses
26
27 // Publish a sensor if its register(s) are in this response; skipping absent registers keeps this
28 // correct for any read range, so the poll may be split into multiple requests.
29 auto publish_1_register = [&](sensor::Sensor *sensor, uint16_t reg, float divisor) -> void {
30 if (sensor == nullptr)
31 return;
33 sensor->publish_state(*value / divisor);
34 };
35
36 auto publish_2_registers = [&](sensor::Sensor *sensor, uint16_t reg, float divisor) -> void {
37 if (sensor == nullptr)
38 return;
40 sensor->publish_state(*value / divisor);
41 };
42
43 publish_1_register(this->voltage_sensor_, PZEM_REGISTER_VOLTAGE, 10.0f);
44 publish_2_registers(this->current_sensor_, PZEM_REGISTER_CURRENT, 1000.0f);
45 publish_2_registers(this->power_sensor_, PZEM_REGISTER_ACTIVE_POWER, 10.0f);
46 publish_2_registers(this->energy_sensor_, PZEM_REGISTER_ACTIVE_ENERGY, 1.0f);
47 publish_1_register(this->frequency_sensor_, PZEM_REGISTER_FREQUENCY, 10.0f);
48 publish_1_register(this->power_factor_sensor_, PZEM_REGISTER_POWER_FACTOR, 100.0f);
49}
50
51void PZEMAC::on_custom_response(std::span<const uint8_t> request_pdu, std::span<const uint8_t> response_pdu,
53 // The only custom request this component sends is the energy reset; acknowledge its echo here so
54 // the default unhandled-response warning stays meaningful.
55 if (!request_pdu.empty() && request_pdu[0] == PZEM_CMD_RESET_ENERGY) {
57 ESP_LOGD(TAG, "Energy reset acknowledged");
58 } else {
59 ESP_LOGW(TAG, "Energy reset rejected");
60 }
61 return;
62 }
64}
65
66void PZEMAC::update() { this->read_input_registers(0, PZEM_REGISTER_COUNT); }
68 ESP_LOGCONFIG(TAG,
69 "PZEMAC:\n"
70 " Address: 0x%02X",
71 this->address_);
72 LOG_SENSOR("", "Voltage", this->voltage_sensor_);
73 LOG_SENSOR("", "Current", this->current_sensor_);
74 LOG_SENSOR("", "Power", this->power_sensor_);
75 LOG_SENSOR("", "Energy", this->energy_sensor_);
76 LOG_SENSOR("", "Frequency", this->frequency_sensor_);
77 LOG_SENSOR("", "Power Factor", this->power_factor_sensor_);
78}
79
81 const uint8_t pdu[] = {PZEM_CMD_RESET_ENERGY};
82 this->queue_pdu(pdu);
83}
84
85} // namespace esphome::pzemac
uint8_t status
Definition bl0942.h:8
virtual void on_custom_response(std::span< const uint8_t > request_pdu, std::span< const uint8_t > response_pdu, ResponseStatus status)
Catch-all for custom function codes and anything that is not a standard-conformant transaction (see d...
Definition modbus.cpp:1379
sensor::Sensor * frequency_sensor_
Definition pzemac.h:38
sensor::Sensor * voltage_sensor_
Definition pzemac.h:34
void on_custom_response(std::span< const uint8_t > request_pdu, std::span< const uint8_t > response_pdu, modbus::ResponseStatus status) override
Definition pzemac.cpp:51
void on_read_input_registers(uint16_t start_address, std::span< const uint16_t > registers, modbus::ResponseStatus status) override
Definition pzemac.cpp:22
void update() override
Definition pzemac.cpp:66
sensor::Sensor * energy_sensor_
Definition pzemac.h:37
sensor::Sensor * power_sensor_
Definition pzemac.h:36
void dump_config() override
Definition pzemac.cpp:67
sensor::Sensor * power_factor_sensor_
Definition pzemac.h:39
sensor::Sensor * current_sensor_
Definition pzemac.h:35
Base-class for all sensors.
Definition sensor.h:47
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:68
constexpr std::optional< RegisterValueType< VALUE_TYPE > > value_at(std::span< const uint16_t > registers, uint16_t start_address, uint16_t address)
The value stored at an absolute register address, or nullopt when it is not wholly inside this respon...
std::optional< ExceptionCode > ResponseStatus
Definition modbus.h:306
bool succeeded(ResponseStatus status)
True when a transaction carried no exception.
Definition modbus.h:309