ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
pylontech.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cstdint>
5
11
12namespace esphome::pylontech {
13
14static constexpr uint8_t TEXT_SENSOR_MAX_LEN = 14;
15// Battery numbers run 1 to MAX_BATTERIES; bit n - 1 of a battery_mask_t stands for battery n
16static constexpr uint8_t MAX_BATTERIES = 16;
17using battery_mask_t = uint16_t;
18static_assert(MAX_BATTERIES <= sizeof(battery_mask_t) * 8, "battery mask too narrow");
19// A battery reports cells 0 to MAX_CELLS - 1; bit c of a cell_mask_t stands for cell c
20static constexpr uint8_t MAX_CELLS = 15;
21using cell_mask_t = uint16_t;
22static_assert(MAX_CELLS <= sizeof(cell_mask_t) * 8, "cell mask too narrow");
23
25 public:
26 // No default initializers: the hub's slots stay plain zeroed storage; a parse writes every field
27 struct LineContents {
29 uint16_t volt, vlow, vhigh; // mV
30 uint8_t bat_num, coulomb;
31 char base_st[TEXT_SENSOR_MAX_LEN], volt_st[TEXT_SENSOR_MAX_LEN], curr_st[TEXT_SENSOR_MAX_LEN],
32 temp_st[TEXT_SENSOR_MAX_LEN];
33 };
34 struct CellContents {
35 int16_t tempr; // 0.01 °C
36 uint16_t volt; // mV
37 };
38 using CellArray = std::array<CellContents, MAX_CELLS>;
39
40 explicit PylontechListener(uint8_t bat_num) : bat_num_(bat_num) {}
41 uint8_t get_battery() const { return this->bat_num_; }
42
44 virtual void on_line_read(LineContents *line) = 0;
45#ifdef PYLONTECH_CELL_BATTERY_COUNT
48 virtual void on_cells_read(const CellArray &cells, cell_mask_t read) {}
49#endif
50 virtual void dump_config() = 0;
51
52 protected:
53 uint8_t bat_num_;
54};
55
57 public:
59
61 void update() override;
63 void loop() override;
65 void setup() override;
66 void dump_config() override;
67
69 void set_cell_polling_enabled(bool enabled) {
70#ifdef PYLONTECH_CELL_BATTERY_COUNT
71 this->cell_polling_enabled_ = enabled;
72#endif
73 }
74
75#ifdef PYLONTECH_LISTENER_COUNT
77 this->listeners_.push_back(listener);
78 this->batteries_ |= 1u << (listener->get_battery() - 1);
79 }
80#ifdef PYLONTECH_CELL_BATTERY_COUNT
82 void add_cell_battery(uint8_t bat_num) { this->cell_batteries_ |= 1u << (bat_num - 1); }
83#endif
84
85 protected:
86 void process_line_(const char *buffer);
87 void parse_header_(const char *buffer);
88 void dispatch_one_();
89 void reject_line_() {
90 if (this->rejected_ < UINT8_MAX) {
91 this->rejected_++;
92 }
93 }
94
95#ifdef PYLONTECH_CELL_BATTERY_COUNT
96 void parse_cell_line_(const char *buffer);
97 void send_next_bat_();
98#endif
99
100 // Members are ordered by alignment, widest first, so the small ones pack without padding
102 // One slot per listened battery, in battery order; parsed while the UART drains, published one per
103 // loop once it is idle. A newer line for a battery replaces the unpublished one.
104 static_assert(PYLONTECH_BATTERY_COUNT <= MAX_BATTERIES, "more slots than batteries");
105 std::array<PylontechListener::LineContents, PYLONTECH_BATTERY_COUNT> slots_;
106#ifdef PYLONTECH_CELL_BATTERY_COUNT
107 // One cell slot per battery with cell sensors, in battery order, filled by its "bat N" reply and published
108 // to that battery's listeners once the reply has ended and the UART is idle
109 std::array<PylontechListener::CellArray, PYLONTECH_CELL_BATTERY_COUNT> cell_slots_;
110 uint32_t bat_command_at_{0}; // loop time the pause before the next "bat N" ends at
111#endif
112 battery_mask_t batteries_{0}; // batteries with a listener
113 battery_mask_t pending_{0}; // bit i set: slots_[i] is waiting to be published
114#ifdef PYLONTECH_CELL_BATTERY_COUNT
115 std::array<cell_mask_t, PYLONTECH_CELL_BATTERY_COUNT> cells_read_; // cells the current reply has filled
116 battery_mask_t cell_batteries_{0}; // batteries whose cells are read
117 battery_mask_t cells_pending_{0}; // batteries whose cell slot is waiting to be published
118#endif
119 bool has_tlow_id_{false};
120 uint8_t rejected_{0}; // malformed lines since the last idle pass
121#ifdef PYLONTECH_CELL_BATTERY_COUNT
122 uint8_t bat_reply_{0}; // battery whose "bat N" reply is arriving, 0 while the "pwr" reply arrives
123 bool pwr_reply_{false}; // a "pwr" header arrived and its "$$" has not
124 bool bat_command_due_{false}; // a "bat N" waits for the pause after the last reply
125 bool late_reply_{false}; // an abandoned "bat N" may still answer; its lines are dropped until "pwr" answers
127#endif
128 // The longest line, the newer firmware header, is 182 characters
130#endif
131};
132
133} // namespace esphome::pylontech
Collects bytes into NUL terminated lines of up to N - 1 characters; IGNORE_CHAR bytes are skipped and...
Definition line_reader.h:15
This class simplifies creating components that periodically check a state.
Definition component.h:519
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:286
StaticVector< PylontechListener *, PYLONTECH_LISTENER_COUNT > listeners_
Definition pylontech.h:101
void process_line_(const char *buffer)
void loop() override
Read data once available.
void add_cell_battery(uint8_t bat_num)
Read the cells of this battery with "bat N" after every "pwr" reply.
Definition pylontech.h:82
void parse_header_(const char *buffer)
std::array< PylontechListener::CellArray, PYLONTECH_CELL_BATTERY_COUNT > cell_slots_
Definition pylontech.h:109
void update() override
Schedule data readings.
void setup() override
Setup the sensor and test for a connection.
std::array< cell_mask_t, PYLONTECH_CELL_BATTERY_COUNT > cells_read_
Definition pylontech.h:115
std::array< PylontechListener::LineContents, PYLONTECH_BATTERY_COUNT > slots_
Definition pylontech.h:105
void register_listener(PylontechListener *listener)
Definition pylontech.h:76
void set_cell_polling_enabled(bool enabled)
Turn the "bat N" cell readings after each "pwr" reply off or on; nothing to turn off without cell sen...
Definition pylontech.h:69
void parse_cell_line_(const char *buffer)
virtual void on_line_read(LineContents *line)=0
Called only with records for this listener's battery.
std::array< CellContents, MAX_CELLS > CellArray
Definition pylontech.h:38
virtual void on_cells_read(const CellArray &cells, cell_mask_t read)
Called with the cells of this listener's battery after a "bat N" reply; bit c of read is set when cel...
Definition pylontech.h:48
uint32_t buffer
Definition gif.cpp:50
uint16_t battery_mask_t
Definition pylontech.h:17
uint16_t cell_mask_t
Definition pylontech.h:21
static void uint32_t