Browse Source

Fix BLE name showing as T1000-E-BOOT for long node names

The Nordic SoftDevice S140 defaults to a 31-byte maximum GAP device
name. When "MeshCore-" (9 bytes) + node_name exceeds 31 bytes (i.e.
node names >= 23 chars), sd_ble_gap_device_name_set() silently fails
and the name falls back to USB_PRODUCT ("T1000-E-BOOT").

Add UTF-8 safe middle-truncation that preserves the beginning and end
of the node name (where users place emoji and device-type identifiers),
fitting the result within the 29-byte scan response limit so it
advertises as COMPLETE_LOCAL_NAME.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
pull/1801/head
🚀 Andrew R. DeFilippis 4 months ago
parent
commit
90cdbec140
No known key found for this signature in database GPG Key ID: 6DB626D42AD3331E
  1. 99
      src/helpers/nrf52/SerialBLEInterface.cpp

99
src/helpers/nrf52/SerialBLEInterface.cpp

@ -22,6 +22,95 @@
// RX drain buffer size for overflow protection // RX drain buffer size for overflow protection
#define BLE_RX_DRAIN_BUF_SIZE 32 #define BLE_RX_DRAIN_BUF_SIZE 32
// Maximum BLE device name length that fits in a scan response AD element.
// 31 (BLE_GAP_ADV_SET_DATA_SIZE_MAX) - 2 (AD length + type bytes) = 29
#define BLE_NAME_MAX_LEN 29
static size_t utf8CharLen(uint8_t lead_byte) {
if (lead_byte < 0x80) return 1;
if ((lead_byte & 0xE0) == 0xC0) return 2;
if ((lead_byte & 0xF0) == 0xE0) return 3;
if ((lead_byte & 0xF8) == 0xF0) return 4;
return 1; // invalid lead byte — treat as single byte to avoid infinite loops
}
// Build a BLE device name from prefix + node name, middle-truncating with
// ".." if the result would exceed BLE_NAME_MAX_LEN bytes.
// Truncation is UTF-8 safe and preserves the beginning and end of the name.
static void buildBLEName(char* dest, size_t dest_size,
const char* prefix, const char* name)
{
size_t prefix_len = strlen(prefix);
size_t name_len = strlen(name);
// Fast path: fits without truncation
if (prefix_len + name_len <= BLE_NAME_MAX_LEN) {
snprintf(dest, dest_size, "%s%s", prefix, name);
return;
}
size_t name_budget = BLE_NAME_MAX_LEN - prefix_len;
const char sep[] = "..";
const size_t sep_len = 2;
// If budget is too small for meaningful middle-truncation (need at least
// 1 char + sep + 1 char), just take the head
if (name_budget <= sep_len + 2) {
memcpy(dest, prefix, prefix_len);
size_t i = 0;
while (i < name_budget && i < name_len) {
size_t cl = utf8CharLen((uint8_t)name[i]);
if (i + cl > name_budget) break;
i += cl;
}
memcpy(dest + prefix_len, name, i);
dest[prefix_len + i] = '\0';
return;
}
size_t content_budget = name_budget - sep_len;
size_t head_target = content_budget / 2;
size_t tail_target = content_budget - head_target;
// Walk forward: collect head (complete UTF-8 characters up to head_target bytes)
size_t head_len = 0;
{
size_t i = 0;
while (i < name_len) {
size_t cl = utf8CharLen((uint8_t)name[i]);
if (i + cl > head_target) break;
i += cl;
}
head_len = i;
}
// Walk backward: collect tail (complete UTF-8 characters up to tail_target bytes)
size_t tail_start = name_len;
size_t tail_len = 0;
{
size_t i = name_len;
while (i > 0 && tail_len < tail_target) {
// Find start of previous UTF-8 character
size_t prev = i - 1;
while (prev > 0 && ((uint8_t)name[prev] & 0xC0) == 0x80)
prev--;
size_t cl = i - prev;
if (tail_len + cl > tail_target) break;
tail_len += cl;
i = prev;
}
tail_start = name_len - tail_len;
}
// Assemble: prefix + head + ".." + tail
size_t pos = 0;
memcpy(dest + pos, prefix, prefix_len); pos += prefix_len;
memcpy(dest + pos, name, head_len); pos += head_len;
memcpy(dest + pos, sep, sep_len); pos += sep_len;
memcpy(dest + pos, name + tail_start, tail_len); pos += tail_len;
dest[pos] = '\0';
}
static SerialBLEInterface* instance = nullptr; static SerialBLEInterface* instance = nullptr;
void SerialBLEInterface::onConnect(uint16_t connection_handle) { void SerialBLEInterface::onConnect(uint16_t connection_handle) {
@ -133,8 +222,8 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
// Bluefruit.autoConnLed(false); // Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin(); Bluefruit.begin();
char dev_name[32+16]; // Resolve "@@MAC" now that the SoftDevice is active
if (strcmp(name, "@@MAC") == 0) { if (strcmp(name, "@@MAC") == 0) {
ble_gap_addr_t addr; ble_gap_addr_t addr;
if (sd_ble_gap_addr_get(&addr) == NRF_SUCCESS) { if (sd_ble_gap_addr_get(&addr) == NRF_SUCCESS) {
@ -142,7 +231,11 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2], addr.addr[1], addr.addr[0]); addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2], addr.addr[1], addr.addr[0]);
} }
} }
sprintf(dev_name, "%s%s", prefix, name);
// Build the BLE name with middle-truncation if needed to fit within the
// SoftDevice's default 31-byte GAP name limit and 29-byte scan response limit
char dev_name[32+16];
buildBLEName(dev_name, sizeof(dev_name), prefix, name);
// Connection interval units: 1.25ms, supervision timeout units: 10ms // Connection interval units: 1.25ms, supervision timeout units: 10ms
ble_gap_conn_params_t ppcp_params; ble_gap_conn_params_t ppcp_params;

Loading…
Cancel
Save