ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
abbwelcome_protocol.h
Go to the documentation of this file.
1#pragma once
2
4#include "esphome/core/hal.h"
6#include "remote_base.h"
7#include <array>
8#include <cinttypes>
9#include <utility>
10#include <vector>
11
12namespace esphome::remote_base {
13
14static constexpr uint8_t MAX_DATA_LENGTH = 15;
15static constexpr uint8_t DATA_LENGTH_MASK = 0x3f;
16
17/*
18Message Format:
19 2 bytes: Sync (0x55FF)
20 1 bit: Retransmission flag (High means retransmission)
21 1 bit: Address length flag (Low means 2 bytes, High means 3 bytes)
22 2 bits: Unknown
23 4 bits: Data length (in bytes)
24 1 bit: Reply flag (High means this is a reply to a previous message with the same message type)
25 7 bits: Message type
26 2-3 bytes: Destination address
27 2-3 bytes: Source address
28 1 byte: Message ID (randomized, does not change for retransmissions)
29 0-? bytes: Data
30 1 byte: Checksum
31*/
32
34 public:
35 // Make default
36 ABBWelcomeData() : data_{0x55, 0xff} {}
37 // Make from initializer_list
38 ABBWelcomeData(std::initializer_list<uint8_t> data) : data_{} {
39 std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
40 }
41 // Make from vector
42 ABBWelcomeData(const std::vector<uint8_t> &data) : data_{} {
43 std::copy_n(data.begin(), std::min(data.size(), this->data_.size()), this->data_.begin());
44 }
45 // Default copy constructor
46 ABBWelcomeData(const ABBWelcomeData &) = default;
47
48 bool auto_message_id{false};
49
50 uint8_t *data() { return this->data_.data(); }
51 const uint8_t *data() const { return this->data_.data(); }
52 uint8_t size() const {
53 return std::min(static_cast<uint8_t>(6 + (2 * this->get_address_length()) + (this->data_[2] & DATA_LENGTH_MASK)),
54 static_cast<uint8_t>(this->data_.size()));
55 }
56 bool is_valid() const {
57 return this->data_[0] == 0x55 && this->data_[1] == 0xff &&
58 ((this->data_[2] & DATA_LENGTH_MASK) <= MAX_DATA_LENGTH) &&
59 (this->data_[this->size() - 1] == this->calc_cs_());
60 }
61 void set_retransmission(bool retransmission) {
62 if (retransmission) {
63 this->data_[2] |= 0x80;
64 } else {
65 this->data_[2] &= 0x7f;
66 }
67 }
68 bool get_retransmission() const { return this->data_[2] & 0x80; }
69 // set_three_byte_address must be called before set_source_address, set_destination_address, set_message_id and
70 // set_data!
71 void set_three_byte_address(bool three_byte_address) {
72 if (three_byte_address) {
73 this->data_[2] |= 0x40;
74 } else {
75 this->data_[2] &= 0xbf;
76 }
77 }
78 uint8_t get_three_byte_address() const { return (this->data_[2] & 0x40); }
79 uint8_t get_address_length() const { return this->get_three_byte_address() ? 3 : 2; }
80 void set_message_type(uint8_t message_type) { this->data_[3] = message_type; }
81 uint8_t get_message_type() const { return this->data_[3]; }
83 if (this->get_address_length() == 2) {
84 this->data_[4] = (address >> 8) & 0xff;
85 this->data_[5] = address & 0xff;
86 } else {
87 this->data_[4] = (address >> 16) & 0xff;
88 this->data_[5] = (address >> 8) & 0xff;
89 this->data_[6] = address & 0xff;
90 }
91 }
93 if (this->get_address_length() == 2) {
94 return encode_uint16(this->data_[4], this->data_[5]);
95 }
96 return encode_uint24(this->data_[4], this->data_[5], this->data_[6]);
97 }
99 if (this->get_address_length() == 2) {
100 this->data_[6] = (address >> 8) & 0xff;
101 this->data_[7] = address & 0xff;
102 } else {
103 this->data_[7] = (address >> 16) & 0xff;
104 this->data_[8] = (address >> 8) & 0xff;
105 this->data_[9] = address & 0xff;
106 }
107 }
109 if (this->get_address_length() == 2) {
110 return encode_uint16(this->data_[6], this->data_[7]);
111 }
112 return encode_uint24(this->data_[7], this->data_[8], this->data_[9]);
113 }
114 void set_message_id(uint8_t message_id) { this->data_[4 + 2 * this->get_address_length()] = message_id; }
116 void set_fixed_message_id(uint8_t message_id) {
117 this->set_message_id(message_id);
118 this->auto_message_id = false;
119 }
120 uint8_t get_message_id() const { return this->data_[4 + 2 * this->get_address_length()]; }
122 void set_data(const uint8_t *data, size_t len) {
123 uint8_t size = static_cast<uint8_t>(std::min<size_t>(MAX_DATA_LENGTH, len));
124 this->data_[2] &= (0xff ^ DATA_LENGTH_MASK);
125 this->data_[2] |= (size & DATA_LENGTH_MASK);
126 if (size)
127 progmem_memcpy(this->data_.data() + 5 + 2 * this->get_address_length(), data, size);
128 }
129 void set_data(const std::vector<uint8_t> &data) { this->set_data(data.data(), data.size()); }
130 std::vector<uint8_t> get_data() const {
131 std::vector<uint8_t> data(this->data_.begin() + 5 + 2 * this->get_address_length(),
132 this->data_.begin() + 5 + 2 * this->get_address_length() + this->get_data_size());
133 return data;
134 }
135 uint8_t get_data_size() const {
136 return std::min(MAX_DATA_LENGTH, static_cast<uint8_t>(this->data_[2] & DATA_LENGTH_MASK));
137 }
138 void finalize() {
139 if (this->auto_message_id && !this->get_retransmission() && !(this->data_[3] & 0x80)) {
140 this->set_message_id(static_cast<uint8_t>(random_uint32()));
141 }
142 this->data_[0] = 0x55;
143 this->data_[1] = 0xff;
144 this->data_[this->size() - 1] = this->calc_cs_();
145 }
146 // Buffer size: max raw hex output (27*3-1=80) + space(1) + type_info(27) + data(52) + null(1) = 161, rounded up
147 static constexpr size_t FORMAT_BUFFER_SIZE = 192;
148
149 template<size_t N> char *format_to(char (&buffer)[N], uint8_t max_print_bytes = 255) const {
150 static_assert(N >= FORMAT_BUFFER_SIZE, "Buffer too small for format_to()");
151 return this->format_to_internal_(buffer, max_print_bytes);
152 }
153 bool operator==(const ABBWelcomeData &rhs) const {
154 if (std::equal(this->data_.begin(), this->data_.begin() + this->size(), rhs.data_.begin()))
155 return true;
156 return (this->auto_message_id || rhs.auto_message_id) && this->is_valid() && rhs.is_valid() &&
157 (this->get_message_type() == rhs.get_message_type()) &&
158 (this->get_source_address() == rhs.get_source_address()) &&
159 (this->get_destination_address() == rhs.get_destination_address()) && (this->get_data() == rhs.get_data());
160 }
161 uint8_t &operator[](size_t idx) { return this->data_[idx]; }
162 const uint8_t &operator[](size_t idx) const { return this->data_[idx]; }
163
164 protected:
165 std::array<uint8_t, 12 + MAX_DATA_LENGTH> data_;
166 // Calculate checksum
167 uint8_t calc_cs_() const;
168 // Internal format implementation - buffer guaranteed >= FORMAT_BUFFER_SIZE by caller
169 char *format_to_internal_(char *buffer, uint8_t max_print_bytes) const {
170 char *ptr = buffer;
171 char *end = buffer + FORMAT_BUFFER_SIZE;
172
173 uint8_t print_bytes = std::min(this->size(), max_print_bytes);
174 if (print_bytes) {
175 char raw_hex[format_hex_pretty_size(12 + MAX_DATA_LENGTH)];
176 format_hex_pretty_to(raw_hex, this->data_.data(), print_bytes, '.');
177 ptr += snprintf(ptr, end - ptr, "%s ", raw_hex);
178 }
179
180 if (this->is_valid()) {
181 ptr += snprintf(ptr, end - ptr,
182 this->get_three_byte_address() ? "[%06" PRIX32 " %s %06" PRIX32 "] Type: %02X"
183 : "[%04" PRIX32 " %s %04" PRIX32 "] Type: %02X",
184 this->get_source_address(), this->get_retransmission() ? "ยป" : ">",
186 if (this->get_data_size()) {
187 char data_hex[format_hex_pretty_size(MAX_DATA_LENGTH)];
188 format_hex_pretty_to(data_hex, this->data_.data() + 5 + 2 * this->get_address_length(), this->get_data_size(),
189 '.');
190 snprintf(ptr, end - ptr, ", Data: %s", data_hex);
191 }
192 } else {
193 snprintf(ptr, end - ptr, "[Invalid]");
194 }
195
196 return buffer;
197 }
198};
199
200class ABBWelcomeProtocol : public RemoteProtocol<ABBWelcomeData> {
201 public:
202 void encode(RemoteTransmitData *dst, const ABBWelcomeData &src);
203 optional<ABBWelcomeData> decode(RemoteReceiveData src);
204 void dump(const ABBWelcomeData &data);
205
206 protected:
207 void encode_byte_(RemoteTransmitData *dst, uint8_t data) const;
208 bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data);
209};
210
212 public:
214 auto data = ABBWelcomeProtocol().decode(src);
215 return data.has_value() && data.value() == this->data_;
216 }
217 void set_source_address(const uint32_t source_address) { this->data_.set_source_address(source_address); }
218 void set_destination_address(const uint32_t destination_address) {
219 this->data_.set_destination_address(destination_address);
220 }
221 void set_retransmission(const bool retransmission) { this->data_.set_retransmission(retransmission); }
222 void set_three_byte_address(const bool three_byte_address) { this->data_.set_three_byte_address(three_byte_address); }
223 void set_message_type(const uint8_t message_type) { this->data_.set_message_type(message_type); }
224 void set_message_id(const uint8_t message_id) { this->data_.set_message_id(message_id); }
225 void set_auto_message_id(const bool auto_message_id) { this->data_.auto_message_id = auto_message_id; }
226 void set_data(const std::vector<uint8_t> &data) { this->data_.set_data(data); }
227 void finalize() { this->data_.finalize(); }
228
229 protected:
231};
232
235
236} // namespace esphome::remote_base
uint8_t address
Definition bl0906.h:4
bool matches(RemoteReceiveData src) override
void set_retransmission(const bool retransmission)
void set_destination_address(const uint32_t destination_address)
void set_auto_message_id(const bool auto_message_id)
void set_message_type(const uint8_t message_type)
void set_data(const std::vector< uint8_t > &data)
void set_source_address(const uint32_t source_address)
void set_three_byte_address(const bool three_byte_address)
bool operator==(const ABBWelcomeData &rhs) const
ABBWelcomeData(std::initializer_list< uint8_t > data)
void set_retransmission(bool retransmission)
void set_destination_address(uint32_t address)
void set_three_byte_address(bool three_byte_address)
char * format_to_internal_(char *buffer, uint8_t max_print_bytes) const
const uint8_t & operator[](size_t idx) const
std::array< uint8_t, 12+MAX_DATA_LENGTH > data_
char * format_to(char(&buffer)[N], uint8_t max_print_bytes=255) const
void set_data(const uint8_t *data, size_t len)
data may be a PROGMEM table
void set_data(const std::vector< uint8_t > &data)
std::vector< uint8_t > get_data() const
void set_message_type(uint8_t message_type)
void set_fixed_message_id(uint8_t message_id)
A configured id, which finalize() keeps instead of picking one.
ABBWelcomeData(const ABBWelcomeData &)=default
ABBWelcomeData(const std::vector< uint8_t > &data)
void encode(RemoteTransmitData *dst, const ABBWelcomeData &src)
void encode_byte_(RemoteTransmitData *dst, uint8_t data) const
bool decode_byte_(RemoteReceiveData &src, bool &done, uint8_t &data)
optional< ABBWelcomeData > decode(RemoteReceiveData src)
uint32_t buffer
Definition gif.cpp:50
void progmem_memcpy(void *dst, const void *src, size_t len)
Definition hal.h:56
const void size_t len
Definition hal.h:68
constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
Encode a 24-bit value given three bytes in most to least significant byte order.
Definition helpers.h:1002
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
Definition helpers.cpp:498
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Definition helpers.h:1609
const void * src
Definition hal.h:68
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:998
static void uint32_t
uint8_t end[39]
Definition sun_gtil2.cpp:17