Skip to content

Commit 2d408f3

Browse files
authored
Merge pull request #108 from openppg/unique-ble
Use runtime BLE name based on MAC address
2 parents 11304d0 + d3cb716 commit 2d408f3

11 files changed

Lines changed: 170 additions & 13 deletions

File tree

inc/sp140/lvgl/lvgl_main_screen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extern lv_obj_t* motor_temp_bg; // Background rectangle for Motor temp section
5353
extern lv_obj_t* cruise_icon_img; // Cruise control icon image object
5454
extern lv_obj_t* charging_icon_img; // Charging icon image object
5555
extern lv_obj_t* arm_fail_warning_icon_img; // Arm fail warning icon
56+
extern lv_obj_t* ble_pairing_icon; // BLE pairing mode icon (Bluetooth symbol)
5657

5758
// Climb rate indicator objects
5859
extern lv_obj_t* climb_rate_divider_lines[13];

inc/sp140/lvgl/lvgl_updates.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ extern lv_color_t original_arm_fail_icon_color;
2525
extern lv_timer_t* critical_border_flash_timer;
2626
extern bool isFlashingCriticalBorder;
2727

28+
extern lv_timer_t* ble_pairing_flash_timer;
29+
extern bool isFlashingBLEPairingIcon;
30+
2831
// Main update function
2932
void updateLvglMainScreen(
3033
const STR_DEVICE_DATA_140_V1& deviceData,
@@ -73,4 +76,8 @@ bool isCriticalBorderFlashing();
7376
void startCriticalBorderFlashDirect();
7477
void stopCriticalBorderFlashDirect();
7578

79+
// BLE pairing icon flash functions
80+
void startBLEPairingIconFlash();
81+
void stopBLEPairingIconFlash();
82+
7683
#endif // INC_SP140_LVGL_LVGL_UPDATES_H_

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ build_flags =
4242
-D CORE_DEBUG_LEVEL=2
4343
-D CONFIG_ARDUINO_LOOP_STACK_SIZE=8192
4444
-Wno-error=format
45+
-D BLE_PAIR_ON_BOOT
4546
build_type = debug
4647
debug_speed = 12000
4748
debug_tool = esp-builtin

src/sp140/ble/ble_core.cpp

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
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"
@@ -18,19 +19,44 @@
1819

1920
namespace {
2021

21-
constexpr const char *kAdvertisingName = "OpenPPG";
22+
constexpr size_t kAdvertisingNameCapacity = 32;
2223
constexpr TickType_t kConnTuneDelayTicks = pdMS_TO_TICKS(1200);
2324
constexpr TickType_t kPairingTimeoutTicks = pdMS_TO_TICKS(60000);
2425
constexpr TickType_t kAdvertisingWatchdogTicks = pdMS_TO_TICKS(1000);
2526
TimerHandle_t gConnTuneTimer = nullptr;
2627
TimerHandle_t gPairingTimer = nullptr;
2728
TimerHandle_t gAdvertisingWatchdogTimer = nullptr;
29+
char gAdvertisingName[kAdvertisingNameCapacity];
2830
bool pairingModeActive = false;
2931
bool pairingModeTransitionActive = false;
3032

3133
// Store the active connection handle for conn param updates
3234
uint16_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+
3460
void stopPairingModeTimer() {
3561
if (gPairingTimer != nullptr) {
3662
xTimerStop(gPairingTimer, 0);
@@ -63,6 +89,7 @@ size_t syncWhiteListFromBonds() {
6389
void 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

314342
void 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

365395
void requestFastConnParams() {

src/sp140/lvgl/lvgl_main_screen.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ lv_obj_t* motor_temp_bg = NULL; // Background rectangle for Motor temp section
4141
lv_obj_t* cruise_icon_img = NULL; // Cruise control icon image object
4242
lv_obj_t* charging_icon_img = NULL; // Charging icon image object
4343
lv_obj_t* arm_fail_warning_icon_img = NULL; // Arm fail warning icon
44+
lv_obj_t* ble_pairing_icon = NULL; // BLE pairing mode icon
4445

4546
// Climb rate indicator horizontal divider lines (13 lines total)
4647
lv_obj_t* climb_rate_divider_lines[13] = {NULL};
@@ -524,6 +525,14 @@ void setupMainScreen(bool darkMode) {
524525
lv_obj_move_foreground(arm_fail_warning_icon_img); // Ensure icon is on top
525526
lv_obj_add_flag(arm_fail_warning_icon_img, LV_OBJ_FLAG_HIDDEN); // Hide initially
526527

528+
// Create BLE pairing icon (Bluetooth symbol, initially hidden)
529+
ble_pairing_icon = lv_label_create(main_screen);
530+
lv_label_set_text(ble_pairing_icon, LV_SYMBOL_BLUETOOTH);
531+
lv_obj_set_style_text_font(ble_pairing_icon, &lv_font_montserrat_14, 0);
532+
lv_obj_set_style_text_color(ble_pairing_icon, LVGL_BLUE, 0);
533+
lv_obj_set_pos(ble_pairing_icon, 103, 72);
534+
lv_obj_add_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN); // Hide initially
535+
527536
// Create climb rate indicator horizontal divider lines in the far-right section
528537
const int climb_section_start_y = 37;
529538
const int climb_section_end_y = 128;

src/sp140/lvgl/lvgl_updates.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ lv_color_t original_arm_fail_icon_color;
2020
lv_timer_t* critical_border_flash_timer = NULL;
2121
bool isFlashingCriticalBorder = false;
2222

23+
lv_timer_t* ble_pairing_flash_timer = NULL;
24+
bool isFlashingBLEPairingIcon = false;
25+
2326
// Timer callback declarations
2427
static void cruise_flash_timer_cb(lv_timer_t* timer);
2528
static void arm_fail_flash_timer_cb(lv_timer_t* timer);
2629
static void critical_border_flash_timer_cb(lv_timer_t* timer);
30+
static void ble_pairing_flash_timer_cb(lv_timer_t* timer);
2731

2832
// --- Cruise Icon Flashing Implementation ---
2933
static void cruise_flash_timer_cb(lv_timer_t* timer) {
@@ -261,6 +265,63 @@ void stopCriticalBorderFlashDirect() {
261265
isFlashingCriticalBorder = false;
262266
}
263267

268+
// --- BLE Pairing Icon Flashing Implementation ---
269+
static void ble_pairing_flash_timer_cb(lv_timer_t* timer) {
270+
if (ble_pairing_icon == NULL) {
271+
if (ble_pairing_flash_timer != NULL) {
272+
lv_timer_del(ble_pairing_flash_timer);
273+
ble_pairing_flash_timer = NULL;
274+
}
275+
isFlashingBLEPairingIcon = false;
276+
return;
277+
}
278+
279+
if (lv_obj_has_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN)) {
280+
lv_obj_remove_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
281+
} else {
282+
lv_obj_add_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
283+
}
284+
}
285+
286+
void startBLEPairingIconFlash() {
287+
if (xSemaphoreTake(lvglMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
288+
if (ble_pairing_icon == NULL) {
289+
xSemaphoreGive(lvglMutex);
290+
return;
291+
}
292+
293+
if (ble_pairing_flash_timer != NULL) {
294+
lv_timer_del(ble_pairing_flash_timer);
295+
ble_pairing_flash_timer = NULL;
296+
}
297+
298+
isFlashingBLEPairingIcon = true;
299+
lv_obj_remove_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
300+
ble_pairing_flash_timer = lv_timer_create(ble_pairing_flash_timer_cb, 500, NULL);
301+
if (ble_pairing_flash_timer == NULL) {
302+
isFlashingBLEPairingIcon = false;
303+
lv_obj_add_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
304+
USBSerial.println("Error: Failed to create BLE pairing flash timer!");
305+
}
306+
307+
xSemaphoreGive(lvglMutex);
308+
}
309+
}
310+
311+
void stopBLEPairingIconFlash() {
312+
if (xSemaphoreTake(lvglMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
313+
if (ble_pairing_flash_timer != NULL) {
314+
lv_timer_del(ble_pairing_flash_timer);
315+
ble_pairing_flash_timer = NULL;
316+
}
317+
if (ble_pairing_icon != NULL) {
318+
lv_obj_add_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
319+
}
320+
isFlashingBLEPairingIcon = false;
321+
xSemaphoreGive(lvglMutex);
322+
}
323+
}
324+
264325
// Update the climb rate indicator
265326
void updateClimbRateIndicator(float climbRate) {
266327
// Clamp climb rate to displayable range (-0.6 to +0.6 m/s)

src/sp140/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,7 @@ void buttonHandlerTask(void *parameter) {
932932
currentHoldTime >= BLE_PAIRING_HOLD_MS && !pairingHoldHandled) {
933933
enterBLEPairingMode();
934934
pulseVibeMotor();
935+
startBLEPairingIconFlash();
935936
USBSerial.println("[BLE] Pairing mode activated via button hold");
936937
pairingHoldHandled = true;
937938
}

test/test_screenshots/emulator_display.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ void emulator_teardown() {
334334
cruise_icon_img = NULL;
335335
charging_icon_img = NULL;
336336
arm_fail_warning_icon_img = NULL;
337+
ble_pairing_icon = NULL;
337338
for (int i = 0; i < 13; i++) climb_rate_divider_lines[i] = NULL;
338339
for (int i = 0; i < 12; i++) climb_rate_fill_sections[i] = NULL;
339340
critical_border = NULL;
@@ -358,15 +359,19 @@ void emulator_teardown() {
358359
extern lv_timer_t* cruise_flash_timer;
359360
extern lv_timer_t* arm_fail_flash_timer;
360361
extern lv_timer_t* critical_border_flash_timer;
362+
extern lv_timer_t* ble_pairing_flash_timer;
361363
extern bool isFlashingCruiseIcon;
362364
extern bool isFlashingArmFailIcon;
363365
extern bool isFlashingCriticalBorder;
366+
extern bool isFlashingBLEPairingIcon;
364367
cruise_flash_timer = NULL;
365368
arm_fail_flash_timer = NULL;
366369
critical_border_flash_timer = NULL;
370+
ble_pairing_flash_timer = NULL;
367371
isFlashingCruiseIcon = false;
368372
isFlashingArmFailIcon = false;
369373
isFlashingCriticalBorder = false;
374+
isFlashingBLEPairingIcon = false;
370375

371376
memset(framebuffer, 0, sizeof(framebuffer));
372377
}
60.1 KB
Binary file not shown.
60.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)