Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
// for the documentation about the extensions.json format
"recommendations": [
"ms-vscode.cpptools-extension-pack",
"pioarduino.pioarduino-ide",
"platformio.platformio-ide"
"pioarduino.pioarduino-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
Expand Down
1 change: 1 addition & 0 deletions inc/sp140/ble/ble_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ void requestNormalConnParams();
// Temporarily disable whitelist filtering so a new device can bond.
// Advertising reopens for ~60 seconds then whitelisting is restored.
void enterBLEPairingMode();
bool isBLEPairingModeActive();

#endif // INC_SP140_BLE_BLE_CORE_H_
2 changes: 2 additions & 0 deletions inc/sp140/lvgl/lvgl_updates.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,7 @@ void stopCriticalBorderFlashDirect();
// BLE pairing icon flash functions
void startBLEPairingIconFlash();
void stopBLEPairingIconFlash();
void showBLEStatusIcon();
void hideBLEStatusIcon();

#endif // INC_SP140_LVGL_LVGL_UPDATES_H_
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ build_flags =
-D CORE_DEBUG_LEVEL=2
-D CONFIG_ARDUINO_LOOP_STACK_SIZE=8192
-Wno-error=format
-D BLE_PAIR_ON_BOOT
;-D BLE_PAIR_ON_BOOT
build_type = debug
debug_speed = 12000
debug_tool = esp-builtin
Expand Down
42 changes: 15 additions & 27 deletions src/sp140/ble/ble_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <freertos/timers.h>

#include "sp140/ble.h"
#include "sp140/lvgl/lvgl_updates.h"
#include "sp140/ble/ble_ids.h"
#include "sp140/ble/config_service.h"
#include "sp140/ble/fastlink_service.h"
Expand Down Expand Up @@ -63,6 +62,7 @@ void stopPairingModeTimer() {
}
}


size_t syncWhiteListFromBonds() {
// Reconcile the whitelist to the current bond store. Advertising must be
// stopped before calling this — the BLE controller rejects whitelist changes
Expand All @@ -89,7 +89,6 @@ size_t syncWhiteListFromBonds() {
void onPairingTimeout(TimerHandle_t timer) {
(void)timer;
pairingModeActive = false;
stopBLEPairingIconFlash();
USBSerial.println("[BLE] Pairing mode expired, re-enabling whitelist");
restartBLEAdvertising();
}
Expand All @@ -112,7 +111,8 @@ void applyPreferredLinkParams(TimerHandle_t timer) {

bool shouldAdvertiseWhilePowered() {
return !pairingModeTransitionActive &&
(pairingModeActive || NimBLEDevice::getNumBonds() > 0);
(pairingModeActive ||
NimBLEDevice::getNumBonds() > 0);
}

bool startAdvertising(NimBLEServer *server) {
Expand All @@ -136,6 +136,10 @@ bool startAdvertising(NimBLEServer *server) {
return false;
}

// Bonded devices can always reconnect via whitelist advertising —
// no reconnect window gating. Power draw is negligible for
// whitelist-only advertising.

#if CONFIG_BT_NIMBLE_EXT_ADV
// Legacy connectable undirected advertising via the extended API.
// Legacy PDUs avoid NimBLE's extended-adv EBUSY state machine bug.
Expand All @@ -161,26 +165,13 @@ bool startAdvertising(NimBLEServer *server) {
// Flutter app's `startScan()` filters for CONFIG_SERVICE_UUID.
adv.addServiceUUID(NimBLEUUID(CONFIG_SERVICE_UUID));

// Scan response: manufacturer data with pairing-mode flag so the Flutter app
// can hide non-pairable controllers from the connect list.
// Format: Espressif company ID (0x02E5 LE) + 1 flag byte.
NimBLEExtAdvertisement scanRsp(BLE_HCI_LE_PHY_1M, BLE_HCI_LE_PHY_1M);
scanRsp.setLegacyAdvertising(true);
scanRsp.setScannable(true);
const uint8_t mfrData[] = {0xE5, 0x02,
static_cast<uint8_t>(allowOpenAdvertising ? 0x01 : 0x00)};
scanRsp.setManufacturerData(mfrData, sizeof(mfrData));

advertising->removeAll();
const bool configured = advertising->setInstanceData(kExtAdvInstance, adv);
const bool scanRspConfigured =
configured ? advertising->setScanResponseData(kExtAdvInstance, scanRsp)
: false;
const bool started =
configured && scanRspConfigured && advertising->start(kExtAdvInstance);
configured && advertising->start(kExtAdvInstance);
USBSerial.printf(
"[BLE] Ext adv cfg=%d scanRsp=%d start=%d mode=%s bonds=%u wl=%u\n",
configured, scanRspConfigured, started,
"[BLE] Ext adv cfg=%d start=%d mode=%s bonds=%u wl=%u\n",
configured, started,
allowOpenAdvertising ? "OPEN" : "BONDED",
static_cast<unsigned>(bondCount), static_cast<unsigned>(whiteListCount));
return started;
Expand All @@ -206,12 +197,6 @@ bool startAdvertising(NimBLEServer *server) {
advertising->setScanFilter(false, true);
}

// Manufacturer data with pairing-mode flag (updated every restart).
// Espressif company ID (0x02E5 LE) + 1 flag byte.
const std::string mfrPayload = {'\xE5', '\x02',
static_cast<char>(allowOpenAdvertising ? 0x01 : 0x00)};
advertising->setManufacturerData(mfrPayload);

const bool started = advertising->start();
USBSerial.printf("[BLE] Legacy adv start=%s mode=%s bonds=%u whitelist=%u\n",
started ? "OK" : "FAIL",
Expand Down Expand Up @@ -298,7 +283,6 @@ class BleServerConnectionCallbacks : public NimBLEServerCallbacks {
if (pairingModeActive) {
pairingModeActive = false;
stopPairingModeTimer();
stopBLEPairingIconFlash();
}
}

Expand Down Expand Up @@ -388,8 +372,10 @@ void setupBLE() {
#ifdef BLE_PAIR_ON_BOOT
USBSerial.println("[BLE] BLE_PAIR_ON_BOOT: entering pairing mode automatically");
enterBLEPairingMode();
startBLEPairingIconFlash();
#endif
if (shouldAdvertiseWhilePowered()) {
restartBLEAdvertising();
}
}

void requestFastConnParams() {
Expand Down Expand Up @@ -468,3 +454,5 @@ void enterBLEPairingMode() {
USBSerial.println("[BLE] Pairing mode active for 60s");
restartBLEAdvertising();
}

bool isBLEPairingModeActive() { return pairingModeActive; }
52 changes: 51 additions & 1 deletion src/sp140/lvgl/lvgl_updates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "../../../inc/sp140/globals.h"
#include "../../../inc/sp140/vibration_pwm.h"
#include "../../../inc/sp140/shared-config.h"
#include "../../../inc/sp140/ble.h"
#include "../../../inc/sp140/ble/ble_core.h"
#include <math.h>

// Flash timer globals - definitions
Expand Down Expand Up @@ -308,7 +310,21 @@ void startBLEPairingIconFlash() {
}
}

void stopBLEPairingIconFlash() {
void showBLEStatusIcon() {
if (xSemaphoreTake(lvglMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
if (ble_pairing_flash_timer != NULL) {
lv_timer_del(ble_pairing_flash_timer);
ble_pairing_flash_timer = NULL;
}
if (ble_pairing_icon != NULL) {
lv_obj_remove_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
}
isFlashingBLEPairingIcon = false;
xSemaphoreGive(lvglMutex);
}
}

void hideBLEStatusIcon() {
if (xSemaphoreTake(lvglMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
if (ble_pairing_flash_timer != NULL) {
lv_timer_del(ble_pairing_flash_timer);
Expand All @@ -322,6 +338,10 @@ void stopBLEPairingIconFlash() {
}
}

void stopBLEPairingIconFlash() {
hideBLEStatusIcon();
}

// Update the climb rate indicator
void updateClimbRateIndicator(float climbRate) {
// Clamp climb rate to displayable range (-0.6 to +0.6 m/s)
Expand Down Expand Up @@ -833,6 +853,36 @@ void updateLvglMainScreen(
lv_obj_set_style_image_recolor(arm_fail_warning_icon_img, original_arm_fail_icon_color, LV_PART_MAIN);
}

// Keep the BLE icon synced from the UI task so missed callback updates recover.
if (ble_pairing_icon != NULL) {
if (deviceConnected) {
if (ble_pairing_flash_timer != NULL) {
lv_timer_del(ble_pairing_flash_timer);
ble_pairing_flash_timer = NULL;
}
lv_obj_remove_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
isFlashingBLEPairingIcon = false;
} else if (isBLEPairingModeActive()) {
if (!isFlashingBLEPairingIcon) {
isFlashingBLEPairingIcon = true;
lv_obj_remove_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
ble_pairing_flash_timer = lv_timer_create(ble_pairing_flash_timer_cb, 500, NULL);
if (ble_pairing_flash_timer == NULL) {
isFlashingBLEPairingIcon = false;
lv_obj_add_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
USBSerial.println("Error: Failed to create BLE pairing flash timer!");
}
}
} else {
if (ble_pairing_flash_timer != NULL) {
lv_timer_del(ble_pairing_flash_timer);
ble_pairing_flash_timer = NULL;
}
lv_obj_add_flag(ble_pairing_icon, LV_OBJ_FLAG_HIDDEN);
isFlashingBLEPairingIcon = false;
}
}

// Update climb rate indicator
static float lastAltitude = 0.0f;
static uint32_t lastAltitudeTime = 0;
Expand Down
12 changes: 9 additions & 3 deletions src/sp140/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,8 +769,10 @@ void setup() {
setLEDColor(LED_GREEN);

// Show splash screen (blocking)
lv_obj_t* splash_screen = NULL;
if (xSemaphoreTake(lvglMutex, portMAX_DELAY) == pdTRUE) {
displayLvglSplash(deviceData, 2000);
splash_screen = lv_screen_active();
// Keep mutex held through main screen setup
} else {
USBSerial.println("Failed to acquire LVGL mutex for splash");
Expand All @@ -782,9 +784,14 @@ void setup() {
setupMainScreen(deviceData.theme == 1);
}

// Load main screen
// Force the first main-screen repaint before other tasks can contend for SPI.
if (main_screen != NULL) {
lv_screen_load(main_screen);
lv_obj_invalidate(main_screen);
lv_refr_now(main_display);
if (splash_screen != NULL && splash_screen != main_screen) {
lv_obj_delete(splash_screen);
splash_screen = NULL;
}
USBSerial.println("Main screen loaded");
} else {
USBSerial.println("Error: Main screen object is NULL after setup attempt");
Expand Down Expand Up @@ -932,7 +939,6 @@ void buttonHandlerTask(void *parameter) {
currentHoldTime >= BLE_PAIRING_HOLD_MS && !pairingHoldHandled) {
enterBLEPairingMode();
pulseVibeMotor();
startBLEPairingIconFlash();
USBSerial.println("[BLE] Pairing mode activated via button hold");
pairingHoldHandled = true;
}
Expand Down
Loading