Refresh nRF52 Companion BLE name without reboot

This commit is contained in:
mikecarper committed 2026-09-23 14:12:37 -07:00
1 parent a569f55dc7
commit 960936183c
4 files changed
+86 -16

No files matched your search

+8
View File
@@ -6663,11 +6663,19 @@ bool MyMesh::applyAndSaveBluetoothName(const char* value, char* reply,
if (use_default) {
snprintf(reply, reply_size,
#if defined(NRF52_PLATFORM)
"OK - Bluetooth name follows %s<node name>; advertising refreshes after disconnect",
#else
"OK - Bluetooth name follows %s<node name>; reboot required",
#endif
BLE_NAME_PREFIX);
} else {
snprintf(reply, reply_size,
#if defined(NRF52_PLATFORM)
"OK - Bluetooth name saved as '%s'; advertising refreshes after disconnect", value);
#else
"OK - Bluetooth name saved as '%s'; reboot required", value);
#endif
}
return true;
}
+21
View File
@@ -2323,6 +2323,24 @@ void halt() {
startCompanionBluetooth();
}
#if defined(NRF52_PLATFORM)
static void serviceCompanionBluetoothName() {
if (!companion_bluetooth_initialized) return;
static uint32_t last_check = 0;
const uint32_t now = millis();
if ((uint32_t)(now - last_check) < 250) return;
last_check = now;
const CompanionNodePrefs* prefs = the_mesh.getNodePrefs();
if (prefs == nullptr) return;
const bool custom =
mesh::companion::hasCustomBluetoothName(prefs->bluetooth_name);
bluetooth_interface.refreshName(custom ? "" : BLE_NAME_PREFIX,
custom ? prefs->bluetooth_name
: prefs->node_name);
}
#endif
static void serviceCompanionBluetoothIdentity() {
if (!companion_bluetooth_initialized) return;
@@ -3472,5 +3490,8 @@ void loop() {
#endif
serviceCompanionBluetoothIdentity();
serviceCompanionBluetoothControl();
#if defined(NRF52_PLATFORM)
serviceCompanionBluetoothName();
#endif
#endif
}
+53 -16
View File
@@ -30,6 +30,27 @@
static SerialBLEInterface* instance = nullptr;
static bool formatDeviceName(char* dest, size_t capacity,
const char* prefix, const char* name) {
if (dest == nullptr || capacity == 0 || prefix == nullptr || name == nullptr) {
return false;
}
char resolved_name[32];
const char* suffix = name;
if (strcmp(name, "@@MAC") == 0) {
ble_gap_addr_t addr;
if (sd_ble_gap_addr_get(&addr) == NRF_SUCCESS) {
snprintf(resolved_name, sizeof(resolved_name),
"%02X%02X%02X%02X%02X%02X",
addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2],
addr.addr[1], addr.addr[0]);
suffix = resolved_name;
}
}
const int length = snprintf(dest, capacity, "%s%s", prefix, suffix);
return length >= 0 && length < (int)capacity;
}
#if defined(COMPANION_RADIO_FULL) && COMPANION_RADIO_FULL
// Bluefruit's bonded CCCD flash write resets some nRF52 Full Companions.
// Retain the small SoftDevice system-attribute image in RAM for reconnects
@@ -616,22 +637,8 @@ bool SerialBLEInterface::begin(const char* prefix, const char* name,
}
}
char resolved_name[32];
const char* suffix = name;
if (strcmp(name, "@@MAC") == 0) {
ble_gap_addr_t addr;
if (sd_ble_gap_addr_get(&addr) == NRF_SUCCESS) {
snprintf(resolved_name, sizeof(resolved_name),
"%02X%02X%02X%02X%02X%02X",
addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2],
addr.addr[1], addr.addr[0]);
suffix = resolved_name;
}
}
char dev_name[32+16];
const int dev_name_len = snprintf(dev_name, sizeof(dev_name), "%s%s",
prefix, suffix);
if (dev_name_len < 0 || dev_name_len >= (int)sizeof(dev_name)) {
char dev_name[sizeof(_active_name)];
if (!formatDeviceName(dev_name, sizeof(dev_name), prefix, name)) {
strncpy(_begin_failure, "Bluetooth device name is too long",
sizeof(_begin_failure) - 1);
_begin_failure[sizeof(_begin_failure) - 1] = 0;
@@ -660,6 +667,7 @@ bool SerialBLEInterface::begin(const char* prefix, const char* name,
Bluefruit.setTxPower(BLE_TX_POWER);
Bluefruit.setName(dev_name);
memcpy(_active_name, dev_name, strlen(dev_name) + 1);
Bluefruit.Security.setMITM(true);
Bluefruit.Security.setPIN(charpin);
@@ -752,6 +760,35 @@ bool SerialBLEInterface::begin(const char* prefix, const char* name,
return true;
}
bool SerialBLEInterface::refreshName(const char* prefix, const char* name) {
if (!_begin_ready || _conn_handle != BLE_CONN_HANDLE_INVALID) return false;
char desired[sizeof(_active_name)];
if (!formatDeviceName(desired, sizeof(desired), prefix, name)) return false;
if (strcmp(desired, _active_name) == 0) return true;
const bool was_advertising = isAdvertising();
if (was_advertising && !Bluefruit.Advertising.stop()) return false;
Bluefruit.setName(desired);
char actual[sizeof(_active_name)];
const size_t actual_len = Bluefruit.getName(actual, sizeof(actual));
if (actual_len != strlen(desired)
|| memcmp(actual, desired, actual_len) != 0) {
if (was_advertising) startAdvertising("advertising failed after name refresh");
return false;
}
// Directed bonded-only advertisements have no payload to refresh.
if (!_bonded_only) {
Bluefruit.ScanResponse.clearData();
Bluefruit.ScanResponse.addName();
}
memcpy(_active_name, desired, strlen(desired) + 1);
if (was_advertising) startAdvertising("advertising failed after name refresh");
return true;
}
bool SerialBLEInterface::takeSuccessfulConnection(
mesh::companion::BluetoothPeerIdentity* peer) {
if (!_successfulConnectionPending.load(std::memory_order_acquire)) {
+4
View File
@@ -65,6 +65,7 @@ class SerialBLEInterface : public BaseSerialInterface {
bool _begin_attempted = false;
bool _begin_ready = false;
char _begin_failure[96] = {};
char _active_name[48] = {};
bool _isDeviceConnected;
uint16_t _conn_handle;
unsigned long _last_health_check;
@@ -166,6 +167,9 @@ public:
bool clear_bonds = false, bool stealth_pair_once = false,
const mesh::companion::BluetoothPeerIdentity*
bonded_only_peer = nullptr);
// Refresh the GAP and scan-response name after a saved node/Bluetooth name
// changes. A connected phone is left alone; the next idle pass applies it.
bool refreshName(const char* prefix, const char* name);
// Kept after a failed begin so a normal Companion terminal can report the
// cause even when early USB logging was not available.
const char* beginFailure() const { return _begin_failure; }