From 871194517d9f0a274543b3ed66904846636371eb Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 13 May 2026 11:02:19 -0500 Subject: [PATCH] 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 Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com> Co-authored-by: Jason P Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- src/graphics/Screen.cpp | 19 +- src/graphics/draw/NotificationRenderer.cpp | 197 ++++++++++++++++++--- src/graphics/draw/NotificationRenderer.h | 6 + src/nimble/NimbleBluetooth.cpp | 37 +--- src/platform/nrf52/NRF52Bluetooth.cpp | 32 +--- 5 files changed, 202 insertions(+), 89 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 80e00ed69..044db3637 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -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); diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 8d031cf73..d7eba0be4 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -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(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(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; } } diff --git a/src/graphics/draw/NotificationRenderer.h b/src/graphics/draw/NotificationRenderer.h index 45b05be9c..fc8a32c64 100644 --- a/src/graphics/draw/NotificationRenderer.h +++ b/src/graphics/draw/NotificationRenderer.h @@ -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); diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 5a4d150ae..ac52a99bc 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -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 diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index 52e45cccc..4af8b41f6 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -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;