Skip to content

Commit e322422

Browse files
committed
Use code from KinDR007#139
1 parent 646c1e6 commit e322422

File tree

3 files changed

+133
-4
lines changed

3 files changed

+133
-4
lines changed

components/victron/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
CODEOWNERS = ["@KinDR007"]
1111

1212
MULTI_CONF = True
13+
CONF_ASYNC_UART = "async_uart"
1314

1415
victron_ns = cg.esphome_ns.namespace("victron")
1516
VictronComponent = victron_ns.class_("VictronComponent", uart.UARTDevice, cg.Component)
@@ -20,6 +21,7 @@
2021
{
2122
cv.GenerateID(): cv.declare_id(VictronComponent),
2223
cv.Optional(CONF_THROTTLE, default="1s"): cv.positive_time_period_milliseconds,
24+
cv.Optional(CONF_ASYNC_UART, default=False): cv.boolean,
2325
}
2426
)
2527

@@ -30,3 +32,4 @@ def to_code(config):
3032
yield uart.register_uart_device(var, config)
3133

3234
cg.add(var.set_throttle(config[CONF_THROTTLE]))
35+
cg.add(var.set_async_uart(config[CONF_ASYNC_UART]))

components/victron/victron.cpp

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ void VictronComponent::dump_config() { // NOLINT(google-readability-function-si
8787
}
8888

8989
void VictronComponent::loop() {
90+
if (async_uart_) {
91+
async_loop();
92+
} else {
93+
blocking_loop();
94+
}
95+
}
96+
97+
void VictronComponent::blocking_loop() {
9098
const uint32_t now = millis();
9199
const uint8_t elapsed_time = now - last_transmission_;
92100
if ((state_ > 0) && (elapsed_time >= 200)) {
@@ -143,7 +151,7 @@ void VictronComponent::loop() {
143151
ESP_LOGD(TAG, "v:%s", value_.c_str());
144152
if (this->publishing_) {
145153
ESP_LOGD(TAG, "pub:%s", label_.c_str());
146-
handle_value_();
154+
handle_value_(label_, value_);
147155
}
148156
state_ = 0;
149157
} else {
@@ -163,6 +171,115 @@ void VictronComponent::loop() {
163171
}
164172
}
165173

174+
175+
void VictronComponent::async_loop() {
176+
// publish one value at a time to yield to esphome between
177+
// each value and avoid blocking too long
178+
if (publishing_ && recv_buffer_.size() > 0) {
179+
std::pair<std::string, std::string> p = recv_buffer_.back();
180+
handle_value_(p.first, p.second);
181+
recv_buffer_.pop_back();
182+
if (recv_buffer_.size() == 0) {
183+
publishing_ = false;
184+
}
185+
return;
186+
}
187+
// reset publishing in case buffer is empty while publishing
188+
publishing_ = false;
189+
const uint32_t now = millis();
190+
if ((state_ > 0) && (now - last_transmission_ >= 200)) {
191+
// last transmission too long ago. Reset RX index.
192+
ESP_LOGW(TAG, "Last transmission too long ago");
193+
state_ = 0;
194+
}
195+
if (!available())
196+
return;
197+
198+
last_transmission_ = now;
199+
uint8_t c;
200+
read_byte(&c);
201+
// checksum is calculated as the sum of all bytes in a frame
202+
// the final checksum should be a multiple of 256 (0 in 8 bit value)
203+
checksum_ += c;
204+
if (state_ == 0) {
205+
if (c == '\r' || c == '\n') {
206+
return;
207+
}
208+
// reset label/value
209+
label_.clear();
210+
value_.clear();
211+
state_ = 1;
212+
if (begin_frame_ == 0) {
213+
begin_frame_ = now;
214+
}
215+
}
216+
// read label
217+
if (state_ == 1) {
218+
// Start of a ve.direct hex frame
219+
if (c == ':') {
220+
state_ = 3;
221+
return;
222+
}
223+
if (c == '\t') {
224+
// end of label received, start reading value
225+
state_ = 2;
226+
} else {
227+
// update label
228+
label_.push_back(c);
229+
}
230+
return;
231+
}
232+
// read value
233+
if (state_ == 2) {
234+
// The checksum is used as end of frame indicator
235+
if (label_ == "Checksum") {
236+
state_ = 0;
237+
if (begin_frame_ - this->last_publish_ >= this->throttle_) {
238+
// check that checksum value is accurate
239+
if (checksum_ != 0) {
240+
// invalid checksum, drop frame
241+
ESP_LOGW(TAG, "Received invalid checksum, dropping frame: recv %d, calc %d", c, checksum_);
242+
checksum_ = 0;
243+
for (std::pair<std::string, std::string> element : recv_buffer_) {
244+
ESP_LOGD(TAG, ">> %s: %s", element.first.c_str(), element.second.c_str());
245+
}
246+
// clear buffer with invalid data
247+
recv_buffer_.clear();
248+
return;
249+
}
250+
this->last_publish_ = begin_frame_;
251+
// full buffer received, with valid checksum
252+
// set state to publishing to publish the values in the buffer
253+
publishing_ = true;
254+
} else {
255+
// frame is throttled, clear buffer and skip publishing
256+
ESP_LOGD(TAG, "recv throttled, drop frame");
257+
recv_buffer_.clear();
258+
}
259+
// reset checksum and frame
260+
checksum_ = 0;
261+
begin_frame_ = now;
262+
return;
263+
}
264+
if (c == '\r' || c == '\n') {
265+
// end of value received, add label/value to buffer
266+
recv_buffer_.insert(recv_buffer_.begin(), std::make_pair(label_, value_));
267+
state_ = 0;
268+
} else {
269+
// update value
270+
value_.push_back(c);
271+
}
272+
}
273+
// Discard ve.direct hex frame
274+
if (state_ == 3) {
275+
if (c == '\r' || c == '\n') {
276+
state_ = 0;
277+
checksum_ = 0;
278+
recv_buffer_.clear();
279+
}
280+
}
281+
}
282+
166283
static std::string charging_mode_text(int value) {
167284
switch (value) {
168285
case 0:
@@ -663,7 +780,7 @@ static std::string off_reason_text(uint32_t mask) {
663780
return value_list;
664781
}
665782

666-
void VictronComponent::handle_value_() {
783+
void VictronComponent::handle_value_(std::string label_, std::string value_) {
667784
int value;
668785

669786
if (label_ == "V") {

components/victron/victron.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <vector>
4+
#include <utility>
35
#include "esphome/core/component.h"
46
#include "esphome/components/binary_sensor/binary_sensor.h"
57
#include "esphome/components/sensor/sensor.h"
@@ -15,6 +17,7 @@ class VictronComponent : public uart::UARTDevice, public Component {
1517
void set_load_state_binary_sensor(binary_sensor::BinarySensor *load_state_binary_sensor) {
1618
load_state_binary_sensor_ = load_state_binary_sensor;
1719
}
20+
void set_async_uart(bool async_uart) { this->async_uart_ = async_uart; }
1821
void set_relay_state_binary_sensor(binary_sensor::BinarySensor *relay_state_binary_sensor) {
1922
relay_state_binary_sensor_ = relay_state_binary_sensor;
2023
}
@@ -205,11 +208,13 @@ class VictronComponent : public uart::UARTDevice, public Component {
205208

206209
void dump_config() override;
207210
void loop() override;
211+
void async_loop();
212+
void blocking_loop();
208213

209214
float get_setup_priority() const override { return setup_priority::DATA; }
210215

211216
protected:
212-
void handle_value_();
217+
void handle_value_(std::string label_, std::string value_);
213218
void publish_state_(binary_sensor::BinarySensor *binary_sensor, const bool &state);
214219
void publish_state_(sensor::Sensor *sensor, float value);
215220
void publish_state_(text_sensor::TextSensor *text_sensor, const std::string &state);
@@ -287,13 +292,17 @@ class VictronComponent : public uart::UARTDevice, public Component {
287292
text_sensor::TextSensor *alarm_reason_text_sensor_{nullptr};
288293
text_sensor::TextSensor *model_description_text_sensor_{nullptr};
289294

290-
bool publishing_{true};
291295
int state_{0};
296+
bool async_uart_{false};
297+
bool publishing_{false};
292298
std::string label_;
293299
std::string value_;
300+
uint32_t begin_frame_{0};
294301
uint32_t last_transmission_{0};
295302
uint32_t last_publish_{0};
296303
uint32_t throttle_{0};
304+
uint8_t checksum_{0};
305+
std::vector<std::pair<std::string, std::string>> recv_buffer_{};
297306
};
298307

299308
} // namespace victron

0 commit comments

Comments
 (0)