11#include " sp140/ble/ble_core.h"
22
33#include < Arduino.h>
4- #include < algorithm >
5- #include < cctype >
4+ #include < cstdio >
5+ #include < esp_mac.h >
66#include < freertos/FreeRTOS.h>
77#include < freertos/task.h>
88#include < freertos/timers.h>
99
1010#include " sp140/ble.h"
11+ #include " sp140/lvgl/lvgl_updates.h"
1112#include " sp140/ble/ble_ids.h"
1213#include " sp140/ble/config_service.h"
1314#include " sp140/ble/fastlink_service.h"
1819
1920namespace {
2021
21- constexpr const char * kAdvertisingName = " OpenPPG " ;
22+ constexpr size_t kAdvertisingNameCapacity = 32 ;
2223constexpr TickType_t kConnTuneDelayTicks = pdMS_TO_TICKS(1200 );
2324constexpr TickType_t kPairingTimeoutTicks = pdMS_TO_TICKS(60000 );
2425constexpr TickType_t kAdvertisingWatchdogTicks = pdMS_TO_TICKS(1000 );
2526TimerHandle_t gConnTuneTimer = nullptr ;
2627TimerHandle_t gPairingTimer = nullptr ;
2728TimerHandle_t gAdvertisingWatchdogTimer = nullptr ;
29+ char gAdvertisingName [kAdvertisingNameCapacity ];
2830bool pairingModeActive = false ;
2931bool pairingModeTransitionActive = false ;
3032
3133// Store the active connection handle for conn param updates
3234uint16_t activeConnHandle = 0 ;
3335
36+ bool shouldAdvertiseWhilePowered ();
37+ bool startAdvertising (NimBLEServer *server);
38+
39+ // Builds gAdvertisingName from the BT MAC and returns the full MAC address
40+ // as an uppercase string for use as a unique device ID. Must be called before
41+ // NimBLEDevice::init() so the name is ready for the init call.
42+ std::string initAdvertisingName () {
43+ constexpr const char *kBase = " OpenPPG SP140" ;
44+ uint8_t mac[6 ] = {};
45+ if (esp_read_mac (mac, ESP_MAC_BT) != ESP_OK) {
46+ snprintf (gAdvertisingName , sizeof (gAdvertisingName ), " %s" , kBase );
47+ USBSerial.println (" [BLE] Failed to read BT MAC, using base advertising name" );
48+ return " " ;
49+ }
50+
51+ snprintf (gAdvertisingName , sizeof (gAdvertisingName ), " %s [%02X%02X]" ,
52+ kBase , mac[4 ], mac[5 ]);
53+
54+ char uniqueId[18 ];
55+ snprintf (uniqueId, sizeof (uniqueId), " %02X:%02X:%02X:%02X:%02X:%02X" ,
56+ mac[0 ], mac[1 ], mac[2 ], mac[3 ], mac[4 ], mac[5 ]);
57+ return uniqueId;
58+ }
59+
3460void stopPairingModeTimer () {
3561 if (gPairingTimer != nullptr ) {
3662 xTimerStop (gPairingTimer , 0 );
@@ -63,6 +89,7 @@ size_t syncWhiteListFromBonds() {
6389void onPairingTimeout (TimerHandle_t timer) {
6490 (void )timer;
6591 pairingModeActive = false ;
92+ stopBLEPairingIconFlash ();
6693 USBSerial.println (" [BLE] Pairing mode expired, re-enabling whitelist" );
6794 restartBLEAdvertising ();
6895}
@@ -116,7 +143,7 @@ bool startAdvertising(NimBLEServer *server) {
116143 adv.setLegacyAdvertising (true );
117144 adv.setConnectable (true );
118145 adv.setScannable (true );
119- adv.setName (kAdvertisingName );
146+ adv.setName (gAdvertisingName );
120147 adv.setFlags (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
121148
122149 // High-frequency advertising for "instant" connection
@@ -161,7 +188,6 @@ bool startAdvertising(NimBLEServer *server) {
161188 // Configure payload once — NimBLE accumulates addServiceUUID calls
162189 static bool payloadConfigured = false ;
163190 if (!payloadConfigured) {
164- advertising->setName (kAdvertisingName );
165191 advertising->setDiscoverableMode (BLE_GAP_DISC_MODE_GEN);
166192 advertising->setConnectableMode (BLE_GAP_CONN_MODE_UND);
167193 advertising->addServiceUUID (NimBLEUUID (CONFIG_SERVICE_UUID));
@@ -170,6 +196,7 @@ bool startAdvertising(NimBLEServer *server) {
170196 advertising->setMaxInterval (48 ); // 30ms (48 * 0.625ms)
171197 payloadConfigured = true ;
172198 }
199+ advertising->setName (gAdvertisingName );
173200
174201 // Open advertising only during the explicit pairing window. Normal runtime
175202 // advertising only accepts bonded devices from the controller whitelist.
@@ -271,6 +298,7 @@ class BleServerConnectionCallbacks : public NimBLEServerCallbacks {
271298 if (pairingModeActive) {
272299 pairingModeActive = false ;
273300 stopPairingModeTimer ();
301+ stopBLEPairingIconFlash ();
274302 }
275303 }
276304
@@ -312,8 +340,10 @@ class BleServerConnectionCallbacks : public NimBLEServerCallbacks {
312340} // namespace
313341
314342void setupBLE () {
315- // Initialize NimBLE with device name
316- NimBLEDevice::init (kAdvertisingName );
343+ const std::string uniqueId = initAdvertisingName ();
344+
345+ // Initialize NimBLE with the computed controller-specific device name.
346+ NimBLEDevice::init (gAdvertisingName );
317347
318348 // Require bonded LE Secure Connections. The controller has no input/output,
319349 // so pairing stays frictionless ("Just Works") while reconnects restore an
@@ -349,17 +379,17 @@ void setupBLE() {
349379 xTimerStart (gAdvertisingWatchdogTimer , 0 );
350380 }
351381
352- NimBLEAddress bleAddress = NimBLEDevice::getAddress ();
353- std::string uniqueId = bleAddress.toString ();
354- std::transform (
355- uniqueId.begin (), uniqueId.end (), uniqueId.begin (),
356- [](unsigned char c) { return static_cast <char >(std::toupper (c)); });
357-
358382 initConfigBleService (pServer, uniqueId);
359383 initFastLinkBleService (pServer);
360384 initOtaBleService (pServer);
361385
362386 USBSerial.println (" BLE device ready" );
387+
388+ #ifdef BLE_PAIR_ON_BOOT
389+ USBSerial.println (" [BLE] BLE_PAIR_ON_BOOT: entering pairing mode automatically" );
390+ enterBLEPairingMode ();
391+ startBLEPairingIconFlash ();
392+ #endif
363393}
364394
365395void requestFastConnParams () {
0 commit comments