Ble banner (#8902)

* Drop unneeded Sizeof() instances

* Use SimpleBanner for BLE pin

* Support for different font sizes on notification banner

* Fix NRF52 BLE cppcheck shadow warning

Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/de12b52c-49d5-452a-b3fb-344724649270

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
This commit is contained in:
Jonathan Bennett
2026-05-13 11:02:19 -05:00
committed by GitHub
co-authored by GitHub Ben Meadors HarukiToreda Jason P copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> thebentern
parent 778d1ad90f
commit 871194517d
5 changed files with 202 additions and 89 deletions
+10 -9
View File
@@ -230,6 +230,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
#endif
// Store the message and set the expiration timestamp
strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255);
NotificationRenderer::parseBannerMessageWithFonts(NotificationRenderer::alertBannerMessage);
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
NotificationRenderer::alertBannerUntil =
(banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs;
@@ -239,9 +240,9 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
NotificationRenderer::alertBannerCallback = banner_overlay_options.bannerCallback;
NotificationRenderer::curSelected = banner_overlay_options.InitialSelected;
NotificationRenderer::pauseBanner = false;
NotificationRenderer::current_notification_type = notificationTypeEnum::selection_picker;
NotificationRenderer::current_notification_type = banner_overlay_options.notificationType;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
ui->setTargetFPS(60);
updateUiFrame(ui);
}
@@ -263,7 +264,7 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct
NotificationRenderer::current_notification_type = notificationTypeEnum::node_picker;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
ui->setTargetFPS(60);
updateUiFrame(ui);
}
@@ -287,7 +288,7 @@ void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t
NotificationRenderer::currentNumber = 0;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
ui->setTargetFPS(60);
updateUiFrame(ui);
}
@@ -310,7 +311,7 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
// Set the overlay using the same pattern as other notification types
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
ui->setTargetFPS(60);
updateUiFrame(ui);
}
@@ -695,7 +696,7 @@ void Screen::setup()
static OverlayCallback overlays[] = {
graphics::UIRenderer::drawNavigationBar // Custom indicator icons for each frame
};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 1);
// Enable UTF-8 to display mapping
dispdev->setFontTableLookupFunction(customFontTableLookup);
@@ -1312,7 +1313,7 @@ void Screen::setFrames(FrameFocus focus)
// Add overlays: frame icons and alert banner)
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
prevFrame = -1; // Force drawFavoriteNode to pick a new node (because our list just changed)
@@ -1687,7 +1688,7 @@ int Screen::handleInputEvent(const InputEvent *event)
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
NotificationRenderer::inEvent = *event;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
setFastFramerate(); // Draw ASAP
updateUiFrame(ui);
return 0;
@@ -1702,7 +1703,7 @@ int Screen::handleInputEvent(const InputEvent *event)
if (NotificationRenderer::isOverlayBannerShowing()) {
NotificationRenderer::inEvent = *event;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setOverlays(overlays, 2);
setFastFramerate(); // Draw ASAP
updateUiFrame(ui);
+173 -24
View File
@@ -66,6 +66,110 @@ uint32_t pow_of_10(uint32_t n)
return ret;
}
char graphics::NotificationRenderer::alertBannerLines[MAX_LINES + 1][64] = {};
uint8_t graphics::NotificationRenderer::alertBannerLineCount = 0;
graphics::NotificationRenderer::BannerFont graphics::NotificationRenderer::alertBannerLineFonts[MAX_LINES + 1] = {};
static inline graphics::NotificationRenderer::BannerFont parseFontTagPrefix(const char *&p)
{
// Tags must be at the start of the line:
// [S] small, [M] medium, [L] large
if (p && p[0] == '[' && p[2] == ']' && p[1] != '\0') {
char t = p[1];
if (t == 'S') {
p += 3;
return graphics::NotificationRenderer::BANNER_FONT_SMALL;
}
if (t == 'M') {
p += 3;
return graphics::NotificationRenderer::BANNER_FONT_MEDIUM;
}
if (t == 'L') {
p += 3;
return graphics::NotificationRenderer::BANNER_FONT_LARGE;
}
}
return graphics::NotificationRenderer::BANNER_FONT_DEFAULT;
}
static inline const uint8_t *fontForBannerLine(graphics::NotificationRenderer::BannerFont f)
{
switch (f) {
case graphics::NotificationRenderer::BANNER_FONT_SMALL:
return FONT_SMALL;
case graphics::NotificationRenderer::BANNER_FONT_MEDIUM:
return FONT_MEDIUM;
case graphics::NotificationRenderer::BANNER_FONT_LARGE:
return FONT_LARGE;
case graphics::NotificationRenderer::BANNER_FONT_DEFAULT:
default:
return FONT_SMALL;
}
}
static inline uint8_t effectiveLineHeightForBannerLine(graphics::NotificationRenderer::BannerFont f)
{
uint8_t height = FONT_HEIGHT_SMALL;
switch (f) {
case graphics::NotificationRenderer::BANNER_FONT_MEDIUM:
height = FONT_HEIGHT_MEDIUM;
break;
case graphics::NotificationRenderer::BANNER_FONT_LARGE:
height = FONT_HEIGHT_LARGE;
break;
case graphics::NotificationRenderer::BANNER_FONT_SMALL:
case graphics::NotificationRenderer::BANNER_FONT_DEFAULT:
default:
height = FONT_HEIGHT_SMALL;
break;
}
return (height > 3) ? (height - 3) : height;
}
void graphics::NotificationRenderer::parseBannerMessageWithFonts(const char *message)
{
alertBannerLineCount = 0;
for (uint8_t i = 0; i < (MAX_LINES + 1); i++) {
alertBannerLines[i][0] = '\0';
alertBannerLineFonts[i] = BANNER_FONT_DEFAULT;
}
if (!message || !message[0]) {
return;
}
const char *p = message;
while (*p && alertBannerLineCount < (MAX_LINES + 1)) {
const char *lineStart = p;
while (*p && *p != '\n') {
p++;
}
char tmp[64] = {0};
size_t len = (size_t)(p - lineStart);
if (len > (sizeof(tmp) - 1)) {
len = sizeof(tmp) - 1;
}
memcpy(tmp, lineStart, len);
tmp[len] = '\0';
// Tag at start
const char *tp = tmp;
BannerFont f = parseFontTagPrefix(tp);
alertBannerLineFonts[alertBannerLineCount] = f;
// Store stripped text
strncpy(alertBannerLines[alertBannerLineCount], tp, sizeof(alertBannerLines[0]) - 1);
alertBannerLines[alertBannerLineCount][sizeof(alertBannerLines[0]) - 1] = '\0';
alertBannerLineCount++;
if (*p == '\n') {
p++;
}
}
}
// Used on boot when a certificate is being created
void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -375,23 +479,37 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
const char *lineStarts[MAX_LINES + 1] = {0};
uint16_t lineCount = 0;
char lineBuffer[40] = {0};
bool useTaggedTextBanner =
(current_notification_type == notificationTypeEnum::text_banner && alertBannerOptions == 0 && alertBannerLineCount > 0);
// Parse lines
char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
lineStarts[lineCount] = alertBannerMessage;
if (useTaggedTextBanner) {
lineCount = std::min<uint8_t>(alertBannerLineCount, MAX_LINES);
for (uint16_t i = 0; i < lineCount; i++) {
lineStarts[i] = alertBannerLines[i];
lineLengths[i] = strlen(lineStarts[i]);
display->setFont(fontForBannerLine(alertBannerLineFonts[i]));
lineWidths[i] = display->getStringWidth(lineStarts[i], lineLengths[i], true);
if (lineWidths[i] > maxWidth)
maxWidth = lineWidths[i];
}
} else {
char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
lineStarts[lineCount] = alertBannerMessage;
while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
if (lineStarts[lineCount + 1][0] == '\n')
lineStarts[lineCount + 1] += 1;
lineWidths[lineCount] = display->getStringWidth(lineStarts[lineCount], lineLengths[lineCount], true);
if (lineWidths[lineCount] > maxWidth)
maxWidth = lineWidths[lineCount];
lineCount++;
while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
if (lineStarts[lineCount + 1][0] == '\n')
lineStarts[lineCount + 1] += 1;
lineWidths[lineCount] = display->getStringWidth(lineStarts[lineCount], lineLengths[lineCount], true);
if (lineWidths[lineCount] > maxWidth)
maxWidth = lineWidths[lineCount];
lineCount++;
}
}
// Measure option widths
display->setFont(FONT_SMALL);
for (int i = 0; i < alertBannerOptions; i++) {
optionWidths[i] = display->getStringWidth(optionsArrayPtr[i], strlen(optionsArrayPtr[i]), true);
if (optionWidths[i] > maxWidth)
@@ -505,6 +623,10 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
bool needs_bell = false;
uint16_t lineWidths[totalLines] = {0};
uint16_t lineLengths[totalLines] = {0};
BannerFont lineFonts[totalLines] = {};
uint8_t lineEffectiveHeights[totalLines] = {0};
const char *renderLines[totalLines] = {0};
bool useTaggedBannerFonts = (current_notification_type == notificationTypeEnum::text_banner && alertBannerOptions == 0);
if (maxWidth != 0)
is_picker = true;
@@ -517,11 +639,22 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
uint16_t widestLineWithBars = 0;
while (lines[lineCount] != nullptr) {
auto newlinePointer = strchr(lines[lineCount], '\n');
const char *renderText = lines[lineCount];
BannerFont lineFont = BANNER_FONT_DEFAULT;
if (useTaggedBannerFonts && lineCount < alertBannerLineCount) {
renderText = alertBannerLines[lineCount];
lineFont = alertBannerLineFonts[lineCount];
}
renderLines[lineCount] = renderText;
lineFonts[lineCount] = lineFont;
lineEffectiveHeights[lineCount] = effectiveLineHeightForBannerLine(lineFont);
display->setFont(fontForBannerLine(lineFont));
auto newlinePointer = strchr(renderText, '\n');
if (newlinePointer)
lineLengths[lineCount] = (newlinePointer - lines[lineCount]); // Check for newlines first
else // if the newline wasn't found, then pull string length from strlen
lineLengths[lineCount] = strlen(lines[lineCount]);
lineLengths[lineCount] = (newlinePointer - renderText);
else
lineLengths[lineCount] = strlen(renderText);
if (current_notification_type == notificationTypeEnum::node_picker) {
char measureBuffer[64] = {0};
@@ -533,7 +666,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
// Consider extra width for signal bars on lines that contain "Signal:"
uint16_t potentialWidth = lineWidths[lineCount];
if (graphics::bannerSignalBars >= 0 && strncmp(lines[lineCount], "Signal:", 7) == 0) {
if (graphics::bannerSignalBars >= 0 && strncmp(renderText, "Signal:", 7) == 0) {
const int totalBars = 5;
const int barWidth = 3;
const int barSpacing = 2;
@@ -569,8 +702,21 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
uint16_t screenHeight = display->height();
uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
uint8_t visibleTotalLines = std::min<uint8_t>(lineCount, (screenHeight - vPadding * 2) / effectiveLineHeight);
uint16_t contentHeight = visibleTotalLines * effectiveLineHeight;
uint8_t visibleTotalLines = 0;
uint16_t contentHeight = 0;
const uint16_t availableHeight = (screenHeight > (vPadding * 2)) ? (screenHeight - vPadding * 2) : 0;
for (uint8_t i = 0; i < lineCount; i++) {
uint8_t thisLineHeight = lineEffectiveHeights[i] ? lineEffectiveHeights[i] : effectiveLineHeight;
if (contentHeight + thisLineHeight > availableHeight) {
break;
}
contentHeight += thisLineHeight;
visibleTotalLines++;
}
if (visibleTotalLines == 0 && lineCount > 0) {
visibleTotalLines = 1;
contentHeight = lineEffectiveHeights[0] ? lineEffectiveHeights[0] : effectiveLineHeight;
}
uint16_t boxHeight = contentHeight + vPadding * 2;
if (visibleTotalLines == 1) {
boxHeight += (currentResolution == ScreenResolution::High) ? 4 : 3;
@@ -616,15 +762,18 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
// Draw Content
int16_t lineY = boxTop + vPadding;
for (int i = 0; i < lineCount; i++) {
for (int i = 0; i < visibleTotalLines; i++) {
display->setFont(fontForBannerLine(lineFonts[i]));
int16_t thisLineHeight = lineEffectiveHeights[i] ? lineEffectiveHeights[i] : effectiveLineHeight;
int16_t textX = boxLeft + (boxWidth - lineWidths[i]) / 2;
if (needs_bell && i == 0) {
int bellY = lineY + (FONT_HEIGHT_SMALL - 8) / 2;
int fontHeight = thisLineHeight + 3;
int bellY = lineY + (fontHeight - 8) / 2;
display->drawXbm(textX - 10, bellY, 8, 8, bell_alert);
display->drawXbm(textX + lineWidths[i] + 2, bellY, 8, 8, bell_alert);
}
char lineBuffer[lineLengths[i] + 1];
strncpy(lineBuffer, lines[i], lineLengths[i]);
strncpy(lineBuffer, renderLines[i], lineLengths[i]);
lineBuffer[lineLengths[i]] = '\0';
// Determine if this is a pop-up or a pick list
if (alertBannerOptions > 0 && i == 0) {
@@ -658,7 +807,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
display->drawString(textX, lineY - yOffset, lineBuffer);
}
display->setColor(WHITE);
lineY += (effectiveLineHeight - 2 - background_yOffset);
lineY += (thisLineHeight - 2 - background_yOffset);
} else {
// Pop-up
// If this is the Signal line, center text + bars as one group
@@ -716,7 +865,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
display->drawString(textX, lineY, lineBuffer);
}
}
lineY += (effectiveLineHeight);
lineY += thisLineHeight;
}
}
+6
View File
@@ -31,6 +31,12 @@ class NotificationRenderer
static bool pauseBanner;
enum BannerFont : uint8_t { BANNER_FONT_DEFAULT = 0, BANNER_FONT_SMALL, BANNER_FONT_MEDIUM, BANNER_FONT_LARGE };
static char alertBannerLines[MAX_LINES + 1][64]; // parsed text per line
static uint8_t alertBannerLineCount;
static BannerFont alertBannerLineFonts[MAX_LINES + 1];
static void parseBannerMessageWithFonts(const char *message);
static void resetBanner();
static void showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs);
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
+8 -29
View File
@@ -587,51 +587,30 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
virtual uint32_t onPassKeyRequest() override
#endif
{
uint32_t passkey = config.bluetooth.fixed_pin;
uint32_t configuredPasskey = config.bluetooth.fixed_pin;
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
LOG_INFO("Use random passkey");
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
passkey = random(100000, 999999);
configuredPasskey = random(100000, 999999);
}
LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
LOG_INFO("*** Enter passkey %d on the peer side ***", configuredPasskey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
std::string passkey = std::to_string(configuredPasskey);
meshtastic::BluetoothStatus newStatus(passkey);
bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (screen) {
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
snprintf(btPIN, sizeof(btPIN), "%06u", passkey);
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 12;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
#if !defined(M5STACK_UNITC6L)
display->setFont(FONT_SMALL);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
display->drawString(x_offset + x, y_offset + y, "Enter this code");
#endif
display->setFont(FONT_LARGE);
char pin[8];
snprintf(pin, sizeof(pin), "%.3s %.3s", btPIN, btPIN + 3);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
display->drawString(x_offset + x, y_offset + y, pin);
display->setFont(FONT_SMALL);
char deviceName[64];
snprintf(deviceName, sizeof(deviceName), "Name: %s", getDeviceName());
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
display->drawString(x_offset + x, y_offset + y, deviceName);
});
std::string ble_message = "Bluetooth\nPIN\n[M]" + passkey.substr(0, 3) + " " + passkey.substr(3, 6);
screen->showSimpleBanner(ble_message.c_str(), 30000);
}
#endif
passkeyShowing = true;
return passkey;
return configuredPasskey;
}
#ifdef NIMBLE_TWO
+5 -27
View File
@@ -386,34 +386,12 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
meshtastic::BluetoothStatus newStatus(textkey);
bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN && \
!defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
#if HAS_SCREEN && !defined(MESHTASTIC_EXCLUDE_SCREEN)
if (screen) {
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 12;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
display->setFont(FONT_SMALL);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
display->drawString(x_offset + x, y_offset + y, "Enter this code");
display->setFont(FONT_LARGE);
String displayPin(btPIN);
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
display->drawString(x_offset + x, y_offset + y, pin);
display->setFont(FONT_SMALL);
String deviceName = "Name: ";
deviceName.concat(getDeviceName());
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
display->drawString(x_offset + x, y_offset + y, deviceName);
});
std::string configuredPasskeyText = std::to_string(configuredPasskey);
std::string ble_message =
"Bluetooth\nPIN\n[M]" + configuredPasskeyText.substr(0, 3) + " " + configuredPasskeyText.substr(3, 6);
screen->showSimpleBanner(ble_message.c_str(), 30000);
}
#endif
passkeyShowing = true;