ESPHome 2026.3.0
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 {
8namespace vbus {
9
10static const char *const TAG = "vbus";
11
12// Maximum bytes to log in verbose hex output (16 frames * 4 bytes = 64 bytes typical)
13static constexpr size_t VBUS_MAX_LOG_BYTES = 64;
14
16 ESP_LOGCONFIG(TAG, "VBus:");
18}
19
20static void septet_spread(uint8_t *data, int start, int count, uint8_t septet) {
21 for (int i = 0; i < count; i++, septet >>= 1) {
22 if (septet & 1)
23 data[start + i] |= 0x80;
24 }
25}
26
27static bool checksum(const uint8_t *data, int start, int count) {
28 uint8_t csum = 0x7f;
29 for (int i = 0; i < count; i++)
30 csum = (csum - data[start + i]) & 0x7f;
31 return csum == 0;
32}
33
34void VBus::loop() {
35 if (!available())
36 return;
37
38 while (available()) {
39 uint8_t c;
40 read_byte(&c);
41
42 if (c == 0xaa) {
43 this->state_ = 1;
44 this->buffer_.clear();
45 continue;
46 }
47 if (c & 0x80) {
48 this->state_ = 0;
49 continue;
50 }
51 if (this->state_ == 0)
52 continue;
53
54 if (this->state_ == 1) {
55 this->buffer_.push_back(c);
56 if (this->buffer_.size() == 7) {
57 this->protocol_ = this->buffer_[4];
58 this->source_ = (this->buffer_[3] << 8) + this->buffer_[2];
59 this->dest_ = (this->buffer_[1] << 8) + this->buffer_[0];
60 this->command_ = (this->buffer_[6] << 8) + this->buffer_[5];
61 }
62 if ((this->protocol_ == 0x20) && (this->buffer_.size() == 15)) {
63 this->state_ = 0;
64 if (!checksum(this->buffer_.data(), 0, 15)) {
65 ESP_LOGE(TAG, "P2 checksum failed");
66 continue;
67 }
68 septet_spread(this->buffer_.data(), 7, 6, this->buffer_[13]);
69 uint16_t id = (this->buffer_[8] << 8) + this->buffer_[7];
70 uint32_t value =
71 (this->buffer_[12] << 24) + (this->buffer_[11] << 16) + (this->buffer_[10] << 8) + this->buffer_[9];
72 ESP_LOGV(TAG, "P1 C%04x %04x->%04x: %04x %04" PRIx32 " (%" PRIu32 ")", this->command_, this->source_,
73 this->dest_, id, value, value);
74 } else if ((this->protocol_ == 0x10) && (this->buffer_.size() == 9)) {
75 if (!checksum(this->buffer_.data(), 0, 9)) {
76 ESP_LOGE(TAG, "P1 checksum failed");
77 this->state_ = 0;
78 continue;
79 }
80 this->frames_ = this->buffer_[7];
81 if (this->frames_) {
82 this->state_ = 2;
83 this->cframe_ = 0;
84 this->fbcount_ = 0;
85 this->buffer_.clear();
86 } else {
87 this->state_ = 0;
88 ESP_LOGD(TAG, "P1 empty message");
89 }
90 } else if (this->buffer_.size() > 15) {
91 ESP_LOGW(TAG, "Unknown protocol 0x%02x, discarding", this->protocol_);
92 this->state_ = 0;
93 }
94 continue;
95 }
96
97 if (this->state_ == 2) {
98 this->fbytes_[this->fbcount_++] = c;
99 if (this->fbcount_ < 6)
100 continue;
101 this->fbcount_ = 0;
102 if (!checksum(this->fbytes_, 0, 6)) {
103 ESP_LOGE(TAG, "frame checksum failed");
104 continue;
105 }
106 septet_spread(this->fbytes_, 0, 4, this->fbytes_[4]);
107 for (int i = 0; i < 4; i++)
108 this->buffer_.push_back(this->fbytes_[i]);
109 if (++this->cframe_ < this->frames_)
110 continue;
111#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
112 char hex_buf[format_hex_size(VBUS_MAX_LOG_BYTES)];
113 size_t log_bytes = std::min(this->buffer_.size(), static_cast<size_t>(VBUS_MAX_LOG_BYTES));
114#endif
115 ESP_LOGV(TAG, "P2 C%04x %04x->%04x: %s", this->command_, this->source_, this->dest_,
116 format_hex_to(hex_buf, this->buffer_.data(), log_bytes));
117 for (auto &listener : this->listeners_)
118 listener->on_message(this->command_, this->source_, this->dest_, this->buffer_);
119 this->state_ = 0;
120 continue;
121 }
122 }
123}
124
125void VBusListener::on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector<uint8_t> &message) {
126 if ((this->command_ != 0xffff) && (this->command_ != command))
127 return;
128 if ((this->source_ != 0xffff) && (this->source_ != source))
129 return;
130 if ((this->dest_ != 0xffff) && (this->dest_ != dest))
131 return;
132 this->handle_message(message);
133}
134
135} // namespace vbus
136} // namespace esphome
media_source::MediaSource * source
uint8_t checksum
Definition bl0906.h:3
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition uart.cpp:16
bool read_byte(uint8_t *data)
Definition uart.h:34
uint8_t frames_
Definition vbus.h:43
uint16_t dest_
Definition vbus.h:41
uint8_t cframe_
Definition vbus.h:44
uint8_t fbytes_[6]
Definition vbus.h:45
uint16_t command_
Definition vbus.h:42
std::vector< VBusListener * > listeners_
Definition vbus.h:47
std::vector< uint8_t > buffer_
Definition vbus.h:38
void dump_config() override
Definition vbus.cpp:15
uint16_t source_
Definition vbus.h:40
void loop() override
Definition vbus.cpp:34
uint8_t protocol_
Definition vbus.h:39
void on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector< uint8_t > &message)
Definition vbus.cpp:125
virtual void handle_message(std::vector< uint8_t > &message)=0
const char * message
Definition component.cpp:38
const char *const TAG
Definition spi.cpp:7
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
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:1174
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:341
static void uint32_t