[core] Add a name truncation helper and keep the MAC suffix in the nRF52 BLE name (#19768)

This commit is contained in:
J. Nick Koston
2026-10-11 20:22:11 -10:00
committed by GitHub
parent 36082b23c2
commit eb8a793f6e
6 changed files with 95 additions and 39 deletions
+2 -13
View File
@@ -350,19 +350,8 @@ bool ESP32BLE::ble_setup_() {
}
} else {
const auto &app_name = App.get_name();
size_t name_len = app_name.length();
if (name_len > 20) {
if (App.is_name_add_mac_suffix_enabled()) {
// Keep first 13 chars and last 7 chars (MAC suffix), remove middle
memcpy(name_buffer, app_name.c_str(), 13);
memcpy(name_buffer + 13, app_name.c_str() + name_len - 7, 7);
} else {
memcpy(name_buffer, app_name.c_str(), 20);
}
name_buffer[20] = '\0';
} else {
memcpy(name_buffer, app_name.c_str(), name_len + 1); // Include null terminator
}
truncate_name_to(name_buffer, sizeof(name_buffer), app_name.c_str(), app_name.length(),
App.is_name_add_mac_suffix_enabled() ? MAC_ADDRESS_SUFFIX_WITH_SEPARATOR_LEN : 0);
device_name = name_buffer;
}
+5 -18
View File
@@ -1048,30 +1048,17 @@ void WiFiComponent::setup_ap_config_() {
return;
if (this->ap_.ssid_.empty()) {
// Build AP SSID from app name without heap allocation
// WiFi SSID max is 32 bytes, with MAC suffix we keep first 25 + last 7
// Build AP SSID from app name without heap allocation; WiFi SSID max is 32 bytes
static constexpr size_t AP_SSID_MAX_LEN = 32;
static constexpr size_t AP_SSID_PREFIX_LEN = 25;
static constexpr size_t AP_SSID_SUFFIX_LEN = 7;
const auto &app_name = App.get_name();
const char *name_ptr = app_name.c_str();
size_t name_len = app_name.length();
if (name_len <= AP_SSID_MAX_LEN) {
if (app_name.length() <= AP_SSID_MAX_LEN) {
// Name fits, use directly
this->ap_.set_ssid(name_ptr);
this->ap_.set_ssid(app_name.c_str());
} else {
// Name too long, need to truncate into stack buffer
char ssid_buf[AP_SSID_MAX_LEN + 1];
if (App.is_name_add_mac_suffix_enabled()) {
// Keep first 25 chars and last 7 chars (MAC suffix), remove middle
memcpy(ssid_buf, name_ptr, AP_SSID_PREFIX_LEN);
memcpy(ssid_buf + AP_SSID_PREFIX_LEN, name_ptr + name_len - AP_SSID_SUFFIX_LEN, AP_SSID_SUFFIX_LEN);
} else {
memcpy(ssid_buf, name_ptr, AP_SSID_MAX_LEN);
}
ssid_buf[AP_SSID_MAX_LEN] = '\0';
truncate_name_to(ssid_buf, sizeof(ssid_buf), app_name.c_str(), app_name.length(),
App.is_name_add_mac_suffix_enabled() ? MAC_ADDRESS_SUFFIX_WITH_SEPARATOR_LEN : 0);
this->ap_.set_ssid(ssid_buf);
}
}
@@ -1,6 +1,8 @@
#ifdef USE_ZEPHYR
#include "ble_server.h"
#include "esphome/core/application.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/settings/settings.h>
@@ -13,13 +15,8 @@ static k_work advertise_work; // NOLINT(cppcoreguidelines-avoid-non-const-globa
BLEServer *global_ble_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
static const bt_data AD[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
// What the advertisement has left for the name: the flags take 3 bytes and the name header 2
static constexpr size_t MAX_ADV_NAME_LEN = BT_GAP_ADV_MAX_ADV_DATA_LEN - 3 - 2;
static const bt_data SD[] = {
#ifdef USE_OTA
@@ -36,7 +33,19 @@ static void advertise(k_work *work) {
ESP_LOGE(TAG, "Advertising failed to stop (rc %d)", rc);
}
rc = bt_le_adv_start(ADV_PARAM, AD, ARRAY_SIZE(AD), SD, ARRAY_SIZE(SD));
// Zephyr would cut the end of a long name, which is where the MAC suffix is
char name[MAX_ADV_NAME_LEN + 1];
const auto &app_name = App.get_name();
const size_t name_len =
truncate_name_to(name, sizeof(name), app_name.c_str(), app_name.length(),
App.is_name_add_mac_suffix_enabled() ? MAC_ADDRESS_SUFFIX_WITH_SEPARATOR_LEN : 0);
const uint8_t name_type = name_len < app_name.length() ? BT_DATA_NAME_SHORTENED : BT_DATA_NAME_COMPLETE;
const bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(name_type, name, static_cast<uint8_t>(name_len)),
};
rc = bt_le_adv_start(ADV_PARAM, ad, ARRAY_SIZE(ad), SD, ARRAY_SIZE(SD));
if (rc) {
ESP_LOGE(TAG, "Advertising failed to start (rc %d)", rc);
return;
+20
View File
@@ -293,6 +293,26 @@ size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *na
return total_len;
}
size_t truncate_name_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, size_t keep_suffix_len) {
if (buffer_size == 0) {
return 0;
}
const size_t max_len = buffer_size - 1;
if (name_len <= max_len) {
memcpy(buffer, name, name_len);
buffer[name_len] = '\0';
return name_len;
}
if (keep_suffix_len > max_len) {
keep_suffix_len = max_len;
}
const size_t head_len = max_len - keep_suffix_len;
memcpy(buffer, name, head_len);
memcpy(buffer + head_len, name + name_len - keep_suffix_len, keep_suffix_len);
buffer[max_len] = '\0';
return max_len;
}
// Parsing & formatting
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) {
+10
View File
@@ -1256,6 +1256,16 @@ static constexpr size_t MAX_NAME_WITH_SUFFIX_SIZE = 128;
size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep,
const char *suffix_ptr, size_t suffix_len);
/// Length of the suffix name_add_mac_suffix appends: "-" plus 6 characters of the MAC address
static constexpr size_t MAC_ADDRESS_SUFFIX_WITH_SEPARATOR_LEN = 7;
/// Copy a name into a buffer, cutting it when it does not fit.
/// A name that is cut keeps its last keep_suffix_len characters (for example a MAC suffix) and loses
/// the middle instead; pass 0 to cut the end.
/// @param buffer Output buffer; holds at most buffer_size - 1 characters plus the null terminator
/// @return Length written (excluding null terminator)
size_t truncate_name_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, size_t keep_suffix_len);
///@}
/// @name Parsing & formatting
+41
View File
@@ -7,6 +7,47 @@
namespace esphome::core::testing {
// --- truncate_name_to() ---
TEST(TruncateNameTo, NameThatFitsIsCopied) {
char buffer[21];
EXPECT_EQ(truncate_name_to(buffer, sizeof(buffer), "kitchen", 7, 7), 7u);
EXPECT_STREQ(buffer, "kitchen");
}
TEST(TruncateNameTo, NameOfExactlyTheLimitIsCopied) {
char buffer[8];
EXPECT_EQ(truncate_name_to(buffer, sizeof(buffer), "kitchen", 7, 0), 7u);
EXPECT_STREQ(buffer, "kitchen");
}
TEST(TruncateNameTo, CutsTheEndWithoutASuffix) {
char buffer[21];
const char *name = "a-very-long-device-name-here";
EXPECT_EQ(truncate_name_to(buffer, sizeof(buffer), name, strlen(name), 0), 20u);
EXPECT_STREQ(buffer, "a-very-long-device-n");
}
TEST(TruncateNameTo, KeepsTheSuffixAndRemovesTheMiddle) {
char buffer[21];
const char *name = "a-very-long-device-name-a1b2c3";
EXPECT_EQ(truncate_name_to(buffer, sizeof(buffer), name, strlen(name), 7), 20u);
EXPECT_STREQ(buffer, "a-very-long-d-a1b2c3");
}
TEST(TruncateNameTo, SuffixLongerThanTheBufferIsCut) {
char buffer[5];
const char *name = "name-a1b2c3";
EXPECT_EQ(truncate_name_to(buffer, sizeof(buffer), name, strlen(name), 7), 4u);
EXPECT_STREQ(buffer, "b2c3");
}
TEST(TruncateNameTo, ZeroBufferSize) {
char buffer[1] = {'x'};
EXPECT_EQ(truncate_name_to(buffer, 0, "name", 4, 0), 0u);
EXPECT_EQ(buffer[0], 'x');
}
// --- format_hex_to() ---
TEST(FormatHexTo, Basic) {