ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
vbus.cpp
Go to the documentation of this file.
1#include "vbus.h"
3#include "esphome/core/log.h"
4#include <algorithm>
5#include <cinttypes>
6
7namespace esphome::vbus {
8
9ESPHOME_LOG_TAG(TAG, "vbus");
10
11// Maximum bytes to log in verbose hex output (16 frames * 4 bytes = 64 bytes typical)
12static constexpr size_t VBUS_MAX_LOG_BYTES = 64;
13
14void VBus::dump_config() { ESP_LOGCONFIG(TAG, "VBus:"); }
15
16static void septet_spread(uint8_t *data, int start, int count, uint8_t septet) {
17 for (int i = 0; i < count; i++, septet >>= 1) {
18 if (septet & 1)
19 data[start + i] |= 0x80;
20 }
21}
22
23static bool checksum(const uint8_t *data, int start, int count) {
24 uint8_t csum = 0x7f;
25 for (int i = 0; i < count; i++)
26 csum = (csum - data[start + i]) & 0x7f;
27 return csum == 0;
28}
29
30void VBus::loop() {
31 this->read_available([this](uint8_t c) {
32 if (c == 0xaa) {
33 this->state_ = 1;
34 this->buffer_.clear();
35 return;
36 }
37 if (c & 0x80) {
38 this->state_ = 0;
39 return;
40 }
41 if (this->state_ == 0)
42 return;
43
44 if (this->state_ == 1) {
45 this->buffer_.push_back(c);
46 if (this->buffer_.size() == 7) {
47 this->protocol_ = this->buffer_[4];
48 this->source_ = (this->buffer_[3] << 8) + this->buffer_[2];
49 this->dest_ = (this->buffer_[1] << 8) + this->buffer_[0];
50 this->command_ = (this->buffer_[6] << 8) + this->buffer_[5];
51 }
52 if ((this->protocol_ == 0x20) && (this->buffer_.size() == 15)) {
53 this->state_ = 0;
54 if (!checksum(this->buffer_.data(), 0, 15)) {
55 ESP_LOGE(TAG, "P2 checksum failed");
56 return;
57 }
58 septet_spread(this->buffer_.data(), 7, 6, this->buffer_[13]);
59 uint16_t id = (this->buffer_[8] << 8) + this->buffer_[7];
60 uint32_t value = encode_uint32(this->buffer_[12], this->buffer_[11], this->buffer_[10], this->buffer_[9]);
61 ESP_LOGV(TAG, "P1 C%04x %04x->%04x: %04x %04" PRIx32 " (%" PRIu32 ")", this->command_, this->source_,
62 this->dest_, id, value, value);
63 } else if ((this->protocol_ == 0x10) && (this->buffer_.size() == 9)) {
64 if (!checksum(this->buffer_.data(), 0, 9)) {
65 ESP_LOGE(TAG, "P1 checksum failed");
66 this->state_ = 0;
67 return;
68 }
69 this->frames_ = this->buffer_[7];
70 if (this->frames_) {
71 this->state_ = 2;
72 this->cframe_ = 0;
73 this->fbcount_ = 0;
74 this->buffer_.clear();
75 } else {
76 this->state_ = 0;
77 ESP_LOGD(TAG, "P1 empty message");
78 }
79 } else if (this->buffer_.size() > 15) {
80 ESP_LOGW(TAG, "Unknown protocol 0x%02x, discarding", this->protocol_);
81 this->state_ = 0;
82 }
83 return;
84 }
85
86 if (this->state_ == 2) {
87 this->fbytes_[this->fbcount_++] = c;
88 if (this->fbcount_ < 6)
89 return;
90 this->fbcount_ = 0;
91 if (!checksum(this->fbytes_, 0, 6)) {
92 ESP_LOGE(TAG, "frame checksum failed");
93 this->state_ = 0;
94 return;
95 }
96 septet_spread(this->fbytes_, 0, 4, this->fbytes_[4]);
97 for (int i = 0; i < 4; i++)
98 this->buffer_.push_back(this->fbytes_[i]);
99 if (++this->cframe_ < this->frames_)
100 return;
101#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
102 char hex_buf[format_hex_size(VBUS_MAX_LOG_BYTES)];
103 size_t log_bytes = std::min(this->buffer_.size(), static_cast<size_t>(VBUS_MAX_LOG_BYTES));
104#endif
105 ESP_LOGV(TAG, "P2 C%04x %04x->%04x: %s", this->command_, this->source_, this->dest_,
106 format_hex_to(hex_buf, this->buffer_.data(), log_bytes));
107 for (auto &listener : this->listeners_)
108 listener->on_message(this->command_, this->source_, this->dest_, this->buffer_);
109 this->state_ = 0;
110 return;
111 }
112 });
113}
114
115void VBusListener::on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector<uint8_t> &message) {
116 if ((this->command_ != 0xffff) && (this->command_ != command))
117 return;
118 if ((this->source_ != 0xffff) && (this->source_ != source))
119 return;
120 if ((this->dest_ != 0xffff) && (this->dest_ != dest))
121 return;
122 this->handle_message(message);
123}
124
125} // namespace esphome::vbus
uint8_t checksum
Definition bl0906.h:3
bool read_available(F &&on_byte)
Same as read_available_chunks(), calling on_byte(uint8_t) once per byte.
Definition uart.h:77
uint8_t frames_
Definition vbus.h:42
uint16_t dest_
Definition vbus.h:40
uint8_t cframe_
Definition vbus.h:43
uint8_t fbytes_[6]
Definition vbus.h:44
uint16_t command_
Definition vbus.h:41
std::vector< VBusListener * > listeners_
Definition vbus.h:46
std::vector< uint8_t > buffer_
Definition vbus.h:37
void dump_config() override
Definition vbus.cpp:14
uint16_t source_
Definition vbus.h:39
void loop() override
Definition vbus.cpp:30
uint8_t protocol_
Definition vbus.h:38
void on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector< uint8_t > &message)
Definition vbus.cpp:115
virtual void handle_message(std::vector< uint8_t > &message)=0
const LogString * message
Definition component.cpp:35
unsigned count
Definition gif.cpp:51
ESPHOME_LOG_TAG(TAG, "vbus.binary_sensor")
constexpr size_t format_hex_size(size_t byte_count)
Calculate buffer size needed for format_hex_to: "XXXXXXXX...\0" = bytes * 2 + 1.
Definition helpers.h:1583
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:1006
char * format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as lowercase hex to buffer (base implementation).
Definition helpers.cpp:428
static void uint32_t