ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
ble_device.cpp
Go to the documentation of this file.
1// ble_device.cpp
2//
3// Platform-neutral implementation of the shared BLE advertisement types.
4// Parses raw BLE advertisement data into ESPBTDevice.
5
6#include "ble_device.h"
7
8#include "ble_aes_ccm.h"
9
12#include "esphome/core/log.h"
13
14#include <cstring>
15
17
18static const char *const TAG = "ble_device_base";
19
20// Longest advertisement payload worth hex-dumping at VERY_VERBOSE
21// (legacy advertising: 31-byte adv + 31-byte scan response).
22static constexpr size_t BLE_ADV_MAX_LOG_BYTES = 62;
23
24// ---------------------------------------------------------------------------
25// ESPBTUUID
26// ---------------------------------------------------------------------------
27
29 ESPBTUUID ret;
30 ret.type_ = Type::UUID16;
31 ret.uuid_.uuid16 = uuid;
32 return ret;
33}
34
36 ESPBTUUID ret;
37 ret.type_ = Type::UUID32;
38 ret.uuid_.uuid32 = uuid;
39 return ret;
40}
41
42ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
43 ESPBTUUID ret;
44 ret.type_ = Type::UUID128;
45 memcpy(ret.uuid_.uuid128, data, 16);
46 return ret;
47}
48
50 ESPBTUUID ret;
51 ret.type_ = Type::UUID128;
52 for (int i = 0; i < 16; i++)
53 ret.uuid_.uuid128[i] = data[15 - i];
54 return ret;
55}
56
57ESPBTUUID ESPBTUUID::from_raw(const char *data, size_t length) {
58 // Same text-parsing semantics as the historical esp32_ble::ESPBTUUID::from_raw.
59 ESPBTUUID ret;
60 if (length == 4) {
61 // 16-bit UUID as 4-character hex string
62 auto parsed = parse_hex<uint16_t>(data, length);
63 if (parsed.has_value()) {
64 ret.type_ = Type::UUID16;
65 ret.uuid_.uuid16 = parsed.value();
66 }
67 } else if (length == 8) {
68 // 32-bit UUID as 8-character hex string
69 auto parsed = parse_hex<uint32_t>(data, length);
70 if (parsed.has_value()) {
71 ret.type_ = Type::UUID32;
72 ret.uuid_.uuid32 = parsed.value();
73 }
74 } else if (length == 16) {
75 // 16 raw bytes (little-endian 128-bit UUID)
76 ret.type_ = Type::UUID128;
77 memcpy(ret.uuid_.uuid128, reinterpret_cast<const uint8_t *>(data), 16);
78 } else if (length == 36) {
79 // Dashed text form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
80 ret.type_ = Type::UUID128;
81 int n = 0;
82 for (size_t i = 0; i < length; i += 2) {
83 if (data[i] == '-')
84 i++;
85 uint8_t msb = data[i];
86 uint8_t lsb = data[i + 1];
87 if (msb > '9')
88 msb -= 7;
89 if (lsb > '9')
90 lsb -= 7;
91 ret.uuid_.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F);
92 }
93 } else {
94 ESP_LOGE(TAG, "ERROR: UUID value not 4, 8, 16 or 36 bytes - %s", data);
95 }
96 return ret;
97}
98
99#ifdef USE_ESP32
100ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) {
101 if (uuid.len == ESP_UUID_LEN_16)
102 return ESPBTUUID::from_uint16(uuid.uuid.uuid16);
103 if (uuid.len == ESP_UUID_LEN_32)
104 return ESPBTUUID::from_uint32(uuid.uuid.uuid32);
105 return ESPBTUUID::from_raw(uuid.uuid.uuid128);
106}
107
108esp_bt_uuid_t ESPBTUUID::get_uuid() const {
109 esp_bt_uuid_t ret;
110 switch (this->type_) {
111 case Type::UUID16:
112 ret.len = ESP_UUID_LEN_16;
113 ret.uuid.uuid16 = this->uuid_.uuid16;
114 break;
115 case Type::UUID32:
116 ret.len = ESP_UUID_LEN_32;
117 ret.uuid.uuid32 = this->uuid_.uuid32;
118 break;
119 default:
120 case Type::UUID128:
121 ret.len = ESP_UUID_LEN_128;
122 memcpy(ret.uuid.uuid128, this->uuid_.uuid128, ESP_UUID_LEN_128);
123 break;
124 }
125 return ret;
126}
127
128void ESPBTDevice::parse_scan_rst(const esp32_ble::BLEScanResult &scan_result) {
129 this->scan_result_ = &scan_result;
130 // BLEScanResult's bda is most-significant octet first; the neutral ingest
131 // takes the BLE controller (LSB-first) order, so reverse — address_uint64()/
132 // address_str() then produce exactly the historical esp32 values.
133 uint8_t mac_lsb_first[6];
134 for (uint8_t i = 0; i < 6; i++)
135 mac_lsb_first[i] = scan_result.bda[5 - i];
136 this->from_scan_result(mac_lsb_first, scan_result.rssi, scan_result.ble_addr_type, scan_result.ble_adv,
137 scan_result.adv_data_len + scan_result.scan_rsp_len);
138}
139#endif // USE_ESP32
140
142 if (this->type_ == Type::UUID128)
143 return *this;
144 uint8_t data[16];
145 this->to_128bit_(data);
146 return ESPBTUUID::from_raw(data);
147}
148
149bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const {
150 // Adjacent byte-pair search — identical semantics to esp32_ble::ESPBTUUID::contains.
151 switch (this->type_) {
152 case Type::UUID16:
153 return (this->uuid_.uuid16 >> 8) == data2 && (this->uuid_.uuid16 & 0xFF) == data1;
154 case Type::UUID32:
155 for (uint8_t i = 0; i < 3; i++) {
156 bool a = ((this->uuid_.uuid32 >> i * 8) & 0xFF) == data1;
157 bool b = ((this->uuid_.uuid32 >> (i + 1) * 8) & 0xFF) == data2;
158 if (a && b)
159 return true;
160 }
161 return false;
162 case Type::UUID128:
163 for (uint8_t i = 0; i < 15; i++) {
164 if (this->uuid_.uuid128[i] == data1 && this->uuid_.uuid128[i + 1] == data2)
165 return true;
166 }
167 return false;
168 }
169 return false;
170}
171
172const char *ESPBTUUID::to_str(char *buf) const {
173 // Identical output format to esp32_ble::ESPBTUUID::to_str.
174 char *pos = buf;
175 switch (this->type_) {
176 case Type::UUID16:
177 *pos++ = '0';
178 *pos++ = 'x';
179 *pos++ = format_hex_pretty_char(this->uuid_.uuid16 >> 12);
180 *pos++ = format_hex_pretty_char((this->uuid_.uuid16 >> 8) & 0x0F);
181 *pos++ = format_hex_pretty_char((this->uuid_.uuid16 >> 4) & 0x0F);
182 *pos++ = format_hex_pretty_char(this->uuid_.uuid16 & 0x0F);
183 *pos = 0; // NUL-terminate
184 return buf;
185 case Type::UUID32:
186 *pos++ = '0';
187 *pos++ = 'x';
188 for (int shift = 28; shift >= 0; shift -= 4)
189 *pos++ = format_hex_pretty_char((this->uuid_.uuid32 >> shift) & 0x0F);
190 *pos = 0; // NUL-terminate
191 return buf;
192 default:
193 case Type::UUID128:
194 // Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
195 for (int8_t i = 15; i >= 0; i--) {
196 uint8_t byte = this->uuid_.uuid128[i];
197 *pos++ = format_hex_pretty_char(byte >> 4);
198 *pos++ = format_hex_pretty_char(byte & 0x0F);
199 if (i == 12 || i == 10 || i == 8 || i == 6)
200 *pos++ = '-';
201 }
202 *pos = 0; // NUL-terminate
203 return buf;
204 }
205}
206
207void ESPBTUUID::to_128bit_(uint8_t out[16]) const {
208 // Bluetooth Base UUID 00000000-0000-1000-8000-00805F9B34FB (LSB-first), with the 16/32-bit
209 // value placed at bytes 12..; identical expansion to esp32_ble::ESPBTUUID::as_128bit().
210 static const uint8_t BASE[16] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
211 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
212 if (this->type_ == Type::UUID128) {
213 memcpy(out, this->uuid_.uuid128, 16);
214 return;
215 }
216 memcpy(out, BASE, 16);
217 const uint32_t value = (this->type_ == Type::UUID32) ? this->uuid_.uuid32 : this->uuid_.uuid16;
218 const size_t len = (this->type_ == Type::UUID32) ? 4 : 2;
219 for (size_t i = 0; i < len; i++)
220 out[12 + i] = (value >> (i * 8)) & 0xFF;
221}
222
223bool ESPBTUUID::operator==(const ESPBTUUID &other) const {
224 if (this->type_ == other.type_) {
225 switch (this->type_) {
226 case Type::UUID16:
227 return this->uuid_.uuid16 == other.uuid_.uuid16;
228 case Type::UUID32:
229 return this->uuid_.uuid32 == other.uuid_.uuid32;
230 case Type::UUID128:
231 return memcmp(this->uuid_.uuid128, other.uuid_.uuid128, 16) == 0;
232 }
233 return false;
234 }
235 // Different widths: expand both to the 128-bit Bluetooth Base UUID form and compare, so a
236 // configured 16/32-bit UUID matches the equivalent 128-bit advertisement (esp32 parity).
237 uint8_t a[16];
238 uint8_t b[16];
239 this->to_128bit_(a);
240 other.to_128bit_(b);
241 return memcmp(a, b, 16) == 0;
242}
243
244// ---------------------------------------------------------------------------
245// ESPBLEiBeacon
246// ---------------------------------------------------------------------------
247
248ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(this->beacon_data_)); }
249
250optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
251 // iBeacon manufacturer specific data (after company-ID bytes have been stripped):
252 // [0x02][0x15][16-byte UUID][2-byte major][2-byte minor][1-byte power] = exactly 23 bytes
253 // Parity with esp32_ble_tracker: gate on the Apple company ID and length only.
254 // (Checking the 0x02/0x15 sub-type prefix would be stricter, but is a behavior
255 // change; it belongs to a follow-up, not this refactor.)
256 if (!data.uuid.contains(0x4C, 0x00)) // Apple company ID 0x004C
257 return {};
258 if (data.data.size() != 23)
259 return {};
260 return ESPBLEiBeacon(data.data.data());
261}
262
263// ---------------------------------------------------------------------------
264// ESPBTDevice
265// ---------------------------------------------------------------------------
266
267void ESPBTDevice::from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_type, const uint8_t *data,
268 uint16_t data_len) {
269 // Ingest is BLE controller order (LSB-first); store in printable (MSB-first)
270 // order so the raw address() accessor matches the historical esp32 layout.
271 for (uint8_t i = 0; i < 6; i++)
272 this->address_[i] = mac[5 - i];
273 this->address_type_ = addr_type;
274 this->rssi_ = rssi;
275 this->name_.clear();
276 this->service_uuids_.clear();
277 this->manufacturer_datas_.clear();
278 this->service_datas_.clear();
279 this->tx_powers_.clear();
280 this->appearance_.reset();
281 this->ad_flag_.reset();
282 this->parse_adv_(data, data_len);
283
284#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
285 ESP_LOGVV(TAG, "Parse Result:");
286 const char *address_type;
287 switch (this->address_type_) {
288 case BLE_ADDR_TYPE_PUBLIC:
289 address_type = "PUBLIC";
290 break;
291 case BLE_ADDR_TYPE_RANDOM:
292 address_type = "RANDOM";
293 break;
294 case BLE_ADDR_TYPE_RPA_PUBLIC:
295 address_type = "RPA_PUBLIC";
296 break;
297 case BLE_ADDR_TYPE_RPA_RANDOM:
298 address_type = "RPA_RANDOM";
299 break;
300 default:
301 address_type = "UNKNOWN";
302 break;
303 }
304 char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
305 ESP_LOGVV(TAG, " Address: %s (%s)", this->address_str_to(addr_buf), address_type);
306 ESP_LOGVV(TAG, " RSSI: %d", this->rssi_);
307 ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str());
308 for (auto &it : this->tx_powers_) {
309 ESP_LOGVV(TAG, " TX Power: %d", it);
310 }
311 if (this->appearance_.has_value()) {
312 ESP_LOGVV(TAG, " Appearance: %u", *this->appearance_);
313 }
314 if (this->ad_flag_.has_value()) {
315 ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_);
316 }
317 char uuid_buf[UUID_STR_LEN];
318 for (auto &uuid : this->service_uuids_) {
319 ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_str(uuid_buf));
320 }
321 char hex_buf[format_hex_pretty_size(BLE_ADV_MAX_LOG_BYTES)];
322 for (auto &mfg_data : this->manufacturer_datas_) {
323 auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(mfg_data);
324 if (ibeacon.has_value()) {
325 ESP_LOGVV(TAG, " Manufacturer iBeacon:");
326 ESP_LOGVV(TAG, " UUID: %s", ibeacon.value().get_uuid().to_str(uuid_buf));
327 ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major());
328 ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor());
329 ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power());
330 } else {
331 ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", mfg_data.uuid.to_str(uuid_buf),
332 format_hex_pretty_to(hex_buf, mfg_data.data.data(), mfg_data.data.size()));
333 }
334 }
335 for (auto &svc_data : this->service_datas_) {
336 ESP_LOGVV(TAG, " Service data:");
337 ESP_LOGVV(TAG, " UUID: %s", svc_data.uuid.to_str(uuid_buf));
338 ESP_LOGVV(TAG, " Data: %s", format_hex_pretty_to(hex_buf, svc_data.data.data(), svc_data.data.size()));
339 }
340 ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty_to(hex_buf, data, data_len));
341#endif // ESPHOME_LOG_HAS_VERY_VERBOSE
342}
343
344std::string ESPBTDevice::address_str() const {
346 return std::string(this->address_str_to(buf));
347}
348
349const char *ESPBTDevice::address_str_to(char *buf) const {
350 // address_ is stored in printable (MSB-first) order.
352 return buf;
353}
354
356 // address_ is MSB-first; byte 0 of the result is the LSB (esp32 semantics).
357 uint64_t addr = 0;
358 for (int i = 0; i < 6; i++)
359 addr |= static_cast<uint64_t>(this->address_[i]) << ((5 - i) * 8);
360 return addr;
361}
362
363bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
364#ifdef USE_BLE_DEVICE_IRK
365 // Bluetooth Core 5.x "ah" function: hash = e(IRK, padding | prand)[low 24 bits].
366 // The resolvable private address is prand (top 3 bytes) | hash (bottom 3 bytes).
367 // Uses the portable software AES-128 shared with the CCM decryptor, so IRK
368 // matching behaves identically on every platform (volume is one block per
369 // advertisement from a matching RPA device — software AES is not a cost).
370 uint8_t ecb_plaintext[16] = {0};
371 uint8_t ecb_ciphertext[16];
372 const uint64_t addr64 = this->address_uint64();
373 ecb_plaintext[13] = (addr64 >> 40) & 0xff;
374 ecb_plaintext[14] = (addr64 >> 32) & 0xff;
375 ecb_plaintext[15] = (addr64 >> 24) & 0xff;
376 aes128_encrypt_block(irk, ecb_plaintext, ecb_ciphertext);
377 return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
378 ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
379#else
380 // No sensor configured an irk: in this build; the AES core is compiled out.
381 (void) irk;
382 return false;
383#endif
384}
385
386void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) {
387 // BLE AD structure TLV: [length][type][value...]
388 // length includes the type byte.
389 uint16_t offset = 0;
390 while (offset < len) {
391 uint8_t ad_len = payload[offset++];
392 if (ad_len == 0)
393 continue; // possible zero-padded advertisement data (esp32_ble_tracker skips these too)
394 if (offset + ad_len > len)
395 break;
396 uint8_t ad_type = payload[offset];
397 const uint8_t *ad_data = &payload[offset + 1];
398 uint8_t ad_data_len = ad_len - 1;
399 offset += ad_len;
400
401 switch (ad_type) {
402 case 0x01: // Flags
403 if (ad_data_len >= 1)
404 this->ad_flag_ = ad_data[0];
405 break;
406
407 case 0x08: // Shortened Local Name
408 case 0x09: // Complete Local Name
409 // Keep the longest name seen — a merged adv + scan-response frame may carry both the
410 // shortened and the complete name, and the shortened form must never replace the
411 // complete one (same rule as esp32_ble_tracker's parse_adv_).
412 if (ad_data_len > this->name_.length())
413 this->name_.assign(reinterpret_cast<const char *>(ad_data), ad_data_len);
414 break;
415
416 case 0x0A: // TX Power Level
417 if (ad_data_len >= 1)
418 this->tx_powers_.push_back(static_cast<int8_t>(ad_data[0]));
419 break;
420
421 case 0x19: // Appearance
422 if (ad_data_len >= 2)
423 this->appearance_ = static_cast<uint16_t>(ad_data[0]) | (static_cast<uint16_t>(ad_data[1]) << 8);
424 break;
425
426 case 0x02: // Incomplete List of 16-bit Service UUIDs
427 case 0x03: // Complete List of 16-bit Service UUIDs
428 for (uint8_t i = 0; (i + 1) < ad_data_len; i += 2) {
429 uint16_t uuid = (static_cast<uint16_t>(ad_data[i + 1]) << 8) | ad_data[i];
430 this->service_uuids_.push_back(ESPBTUUID::from_uint16(uuid));
431 }
432 break;
433
434 case 0x04: // Incomplete List of 32-bit Service UUIDs
435 case 0x05: // Complete List of 32-bit Service UUIDs
436 for (uint8_t i = 0; (i + 3) < ad_data_len; i += 4) {
437 uint32_t uuid = (static_cast<uint32_t>(ad_data[i + 3]) << 24) |
438 (static_cast<uint32_t>(ad_data[i + 2]) << 16) | (static_cast<uint32_t>(ad_data[i + 1]) << 8) |
439 ad_data[i];
440 this->service_uuids_.push_back(ESPBTUUID::from_uint32(uuid));
441 }
442 break;
443
444 case 0x06: // Incomplete List of 128-bit Service UUIDs
445 case 0x07: // Complete List of 128-bit Service UUIDs
446 for (uint8_t i = 0; (i + 15) < ad_data_len; i += 16)
447 this->service_uuids_.push_back(ESPBTUUID::from_raw(&ad_data[i]));
448 break;
449
450 case 0xFF: // Manufacturer Specific Data
451 if (ad_data_len >= 2) {
452 uint16_t company_id = (static_cast<uint16_t>(ad_data[1]) << 8) | ad_data[0];
453 ServiceData sd;
454 sd.uuid = ESPBTUUID::from_uint16(company_id);
455 sd.data.assign(ad_data + 2, ad_data + ad_data_len);
456 this->manufacturer_datas_.push_back(std::move(sd));
457 }
458 break;
459
460 case 0x16: // Service Data — 16-bit UUID
461 if (ad_data_len >= 2) {
462 uint16_t uuid = (static_cast<uint16_t>(ad_data[1]) << 8) | ad_data[0];
463 ServiceData sd;
464 sd.uuid = ESPBTUUID::from_uint16(uuid);
465 sd.data.assign(ad_data + 2, ad_data + ad_data_len);
466 this->service_datas_.push_back(std::move(sd));
467 }
468 break;
469
470 case 0x20: // Service Data — 32-bit UUID
471 if (ad_data_len >= 4) {
472 uint32_t uuid = (static_cast<uint32_t>(ad_data[3]) << 24) | (static_cast<uint32_t>(ad_data[2]) << 16) |
473 (static_cast<uint32_t>(ad_data[1]) << 8) | ad_data[0];
474 ServiceData sd;
475 sd.uuid = ESPBTUUID::from_uint32(uuid);
476 sd.data.assign(ad_data + 4, ad_data + ad_data_len);
477 this->service_datas_.push_back(std::move(sd));
478 }
479 break;
480
481 case 0x21: // Service Data — 128-bit UUID
482 if (ad_data_len >= 16) {
483 ServiceData sd;
484 sd.uuid = ESPBTUUID::from_raw(ad_data);
485 sd.data.assign(ad_data + 16, ad_data + ad_data_len);
486 this->service_datas_.push_back(std::move(sd));
487 }
488 break;
489
490 default:
491 break;
492 }
493 }
494}
495
496} // namespace esphome::ble_device_base
struct PACKED esphome::ble_device_base::ESPBLEiBeacon::BeaconData beacon_data_
static optional< ESPBLEiBeacon > from_manufacturer_data(const ServiceData &data)
const char * address_str_to(char *buf) const
Buffer overload: writes "XX:XX:XX:XX:XX:XX\0" into buf (>= 18 bytes), returns buf.
void parse_scan_rst(const esp32_ble::BLEScanResult &scan_result)
Historical esp32 ingest (esp32 builds only): parse an ESP-IDF scan result.
std::vector< ESPBTUUID > service_uuids_
Definition ble_device.h:216
void from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_type, const uint8_t *data, uint16_t data_len)
Populate from a raw scan result delivered by a BLE tracker backend.
static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE
Definition ble_device.h:158
void parse_adv_(const uint8_t *payload, uint16_t len)
std::vector< ServiceData > service_datas_
Definition ble_device.h:218
const esp32_ble::BLEScanResult * scan_result_
Definition ble_device.h:220
std::vector< ServiceData > manufacturer_datas_
Definition ble_device.h:217
std::string address_str() const
Return MAC as "XX:XX:XX:XX:XX:XX" string.
uint64_t address_uint64() const
Return MAC as packed uint64 (byte 0 in LSB — matches esp32's address_uint64).
bool resolve_irk(const uint8_t *irk) const
Resolve a Resolvable Private Address against a 16-byte IRK (Bluetooth "ah" function,...
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Source compatibility with the historical esp32_ble API (esp32 builds only).
static ESPBTUUID from_uint16(uint16_t uuid)
static ESPBTUUID from_raw(const uint8_t *data)
Construct from raw 16-byte little-endian UUID.
bool contains(uint8_t data1, uint8_t data2) const
True if the UUID value contains the adjacent byte pair (data1, data2).
void to_128bit_(uint8_t out[16]) const
static ESPBTUUID from_raw_reversed(const uint8_t *data)
Construct from raw 16-byte big-endian UUID (reversed on store).
union esphome::ble_device_base::ESPBTUUID::@21 uuid_
static ESPBTUUID from_uint32(uint32_t uuid)
ESPBTUUID as_128bit() const
Expand to the 128-bit Bluetooth Base UUID form.
bool operator==(const ESPBTUUID &other) const
const char * to_str(char *buf) const
Write "0xABCD" / "0xABCDEF01" / the dashed 128-bit form into buf (>= UUID_STR_LEN bytes) and return b...
void aes128_encrypt_block(const uint8_t key[16], const uint8_t in[16], uint8_t out[16])
AES-128 single-block encrypt (the same software cipher CCM uses).
ESPHOME_ALWAYS_INLINE char format_hex_pretty_char(uint8_t v)
Convert a nibble (0-15) to uppercase hex char (used for pretty printing)
Definition helpers.h:1269
const void size_t len
Definition hal.h:64
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
Definition helpers.cpp:274
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:340
size_t size_t pos
Definition helpers.h:1052
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:1400
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)
Definition helpers.h:1467
static void uint32_t
uint16_t length
Definition tt21100.cpp:0