From 042555134185b522c00c85f8590a7b1c280601fd Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 25 Jun 2024 11:26:02 -0500 Subject: [PATCH] Display alerts (#4170) * Move static functions into Screen.h, show compass during calibration * Move to _fontHeight macro to avoid collision * Move some alert functions to new alert handler * Catch missed reboot code * ESP32 fixes * Bump esp8266-oled-ssd1306 * Fixes for when a device has no screen * Use new startAlert(char*) helper class * Add EINK bits back to alert handling * Add noop class for no-display devices --------- Co-authored-by: Ben Meadors --- platformio.ini | 5 +- src/AccelerometerThread.h | 35 +++- src/ButtonThread.cpp | 7 +- src/commands.h | 6 +- src/graphics/Screen.cpp | 166 ++---------------- src/graphics/Screen.h | 148 ++++++++++++---- src/graphics/ScreenFonts.h | 8 +- src/main.cpp | 2 +- src/mesh/NodeDB.cpp | 2 +- src/modules/AdminModule.cpp | 6 +- src/modules/CannedMessageModule.cpp | 4 +- .../Telemetry/EnvironmentTelemetry.cpp | 14 +- src/modules/Telemetry/PowerTelemetry.cpp | 12 +- src/nimble/NimbleBluetooth.cpp | 30 +++- src/platform/nrf52/NRF52Bluetooth.cpp | 28 ++- src/shutdown.h | 2 +- 16 files changed, 252 insertions(+), 223 deletions(-) diff --git a/platformio.ini b/platformio.ini index 23ff53a10..720525f09 100644 --- a/platformio.ini +++ b/platformio.ini @@ -80,7 +80,7 @@ monitor_speed = 115200 lib_deps = jgromes/RadioLib@~6.6.0 - https://github.com/meshtastic/esp8266-oled-ssd1306.git#2b40affbe7f7dc63b6c00fa88e7e12ed1f8e1719 ; ESP8266_SSD1306 + https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 @@ -150,5 +150,4 @@ lib_deps = mprograms/QMC5883LCompass@^1.2.0 - https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee - + https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee \ No newline at end of file diff --git a/src/AccelerometerThread.h b/src/AccelerometerThread.h index f45511cca..0f04de057 100644 --- a/src/AccelerometerThread.h +++ b/src/AccelerometerThread.h @@ -16,6 +16,8 @@ #include #ifdef RAK_4631 #include "Fusion/Fusion.h" +#include "graphics/Screen.h" +#include "graphics/ScreenFonts.h" #include #endif @@ -101,7 +103,11 @@ class AccelerometerThread : public concurrency::OSThread bmx160.getAllData(&magAccel, NULL, &gAccel); // expirimental calibrate routine. Limited to between 10 and 30 seconds after boot - if (millis() > 10 * 1000 && millis() < 30 * 1000) { + if (millis() > 12 * 1000 && millis() < 30 * 1000) { + if (!showingScreen) { + showingScreen = true; + screen->startAlert((FrameCallback)drawFrameCalibration); + } if (magAccel.x > highestX) highestX = magAccel.x; if (magAccel.x < lowestX) @@ -114,6 +120,9 @@ class AccelerometerThread : public concurrency::OSThread highestZ = magAccel.z; if (magAccel.z < lowestZ) lowestZ = magAccel.z; + } else if (showingScreen && millis() >= 30 * 1000) { + showingScreen = false; + screen->endAlert(); } int highestRealX = highestX - (highestX + lowestX) / 2; @@ -255,11 +264,33 @@ class AccelerometerThread : public concurrency::OSThread Adafruit_LIS3DH lis; Adafruit_LSM6DS3TRC lsm; SensorBMA423 bmaSensor; + bool BMA_IRQ = false; #ifdef RAK_4631 + bool showingScreen = false; RAK_BMX160 bmx160; float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0; + + static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) + { + int x_offset = display->width() / 2; + int y_offset = display->height() <= 80 ? 0 : 32; + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(x, y, "Calibrating\nCompass"); + int16_t compassX = 0, compassY = 0; + + // coordinates for the center of the compass/circle + if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) { + compassX = x + display->getWidth() - getCompassDiam(display) / 2 - 5; + compassY = y + display->getHeight() / 2; + } else { + compassX = x + display->getWidth() - getCompassDiam(display) / 2 - 5; + compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2; + } + display->drawCircle(compassX, compassY, getCompassDiam(display) / 2); + drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180); + } #endif - bool BMA_IRQ = false; }; #endif \ No newline at end of file diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp index 4b3bb3fbc..1b85166d2 100644 --- a/src/ButtonThread.cpp +++ b/src/ButtonThread.cpp @@ -181,8 +181,9 @@ int32_t ButtonThread::runOnce() case BUTTON_EVENT_LONG_PRESSED: { LOG_BUTTON("Long press!\n"); powerFSM.trigger(EVENT_PRESS); - if (screen) - screen->startShutdownScreen(); + if (screen) { + screen->startAlert("Shutting down..."); + } playBeep(); break; } @@ -322,4 +323,4 @@ void ButtonThread::userButtonPressedLongStop() if (millis() > c_holdOffTime) { btnEvent = BUTTON_EVENT_LONG_RELEASED; } -} +} \ No newline at end of file diff --git a/src/commands.h b/src/commands.h index 03ede5982..f2b783010 100644 --- a/src/commands.h +++ b/src/commands.h @@ -8,13 +8,11 @@ enum class Cmd { SET_ON, SET_OFF, ON_PRESS, - START_BLUETOOTH_PIN_SCREEN, + START_ALERT_FRAME, + STOP_ALERT_FRAME, START_FIRMWARE_UPDATE_SCREEN, - STOP_BLUETOOTH_PIN_SCREEN, STOP_BOOT_SCREEN, PRINT, - START_SHUTDOWN_SCREEN, - START_REBOOT_SCREEN, SHOW_PREV_FRAME, SHOW_NEXT_FRAME }; \ No newline at end of file diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index eb92d824e..234381aa5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -379,7 +379,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int // in the array of "drawScreen" functions; however, // the passed-state doesn't quite reflect the "current" // screen, so we have to detect it. - if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == INCOMING) { + if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == TransitionRelationship_INCOMING) { // if we're transitioning from the end of the frame list back around to the first // frame, then we want this to be `0` module_frame = state->transitionFrameTarget; @@ -393,31 +393,6 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int pi.drawFrame(display, state, x, y); } -static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -{ - int x_offset = display->width() / 2; - int y_offset = display->height() <= 80 ? 0 : 32; - 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); -} - static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { display->setTextAlignment(TEXT_ALIGN_CENTER); @@ -1307,49 +1282,6 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const } } #endif -namespace -{ - -/// A basic 2D point class for drawing -class Point -{ - public: - float x, y; - - Point(float _x, float _y) : x(_x), y(_y) {} - - /// Apply a rotation around zero (standard rotation matrix math) - void rotate(float radian) - { - float cos = cosf(radian), sin = sinf(radian); - float rx = x * cos + y * sin, ry = -x * sin + y * cos; - - x = rx; - y = ry; - } - - void translate(int16_t dx, int dy) - { - x += dx; - y += dy; - } - - void scale(float f) - { - // We use -f here to counter the flip that happens - // on the y axis when drawing and rotating on screen - x *= f; - y *= -f; - } -}; - -} // namespace - -static void drawLine(OLEDDisplay *d, const Point &p1, const Point &p2) -{ - d->drawLine(p1.x, p1.y, p2.x, p2.y); -} - /** * Given a recent lat/lon return a guess of the heading the user is walking on. * @@ -1380,31 +1312,6 @@ static float estimatedHeading(double lat, double lon) return b; } -static uint16_t getCompassDiam(OLEDDisplay *display) -{ - uint16_t diam = 0; - uint16_t offset = 0; - - if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) - offset = FONT_HEIGHT_SMALL; - - // get the smaller of the 2 dimensions and subtract 20 - if (display->getWidth() > (display->getHeight() - offset)) { - diam = display->getHeight() - offset; - // if 2/3 of the other size would be smaller, use that - if (diam > (display->getWidth() * 2 / 3)) { - diam = display->getWidth() * 2 / 3; - } - } else { - diam = display->getWidth(); - if (diam > ((display->getHeight() - offset) * 2 / 3)) { - diam = (display->getHeight() - offset) * 2 / 3; - } - } - - return diam - 20; -}; - /// We will skip one node - the one for us, so we just blindly loop over all /// nodes static size_t nodeIndex; @@ -1428,7 +1335,7 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp drawLine(display, leftArrow, tip); drawLine(display, rightArrow, tip); } - +/* // Draw north static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading) { @@ -1449,7 +1356,7 @@ static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t com drawLine(display, N1, N3); drawLine(display, N2, N4); drawLine(display, N1, N4); -} +}*/ // Get a string representation of the time passed since something happened static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength) @@ -2023,13 +1930,22 @@ int32_t Screen::runOnce() case Cmd::SHOW_NEXT_FRAME: handleShowNextFrame(); break; - case Cmd::START_BLUETOOTH_PIN_SCREEN: - handleStartBluetoothPinScreen(cmd.bluetooth_pin); + case Cmd::START_ALERT_FRAME: { + showingBootScreen = false; // this should avoid the edge case where an alert triggers before the boot screen goes away + showingNormalScreen = false; + alertFrames[0] = alertFrame; +#ifdef USE_EINK + EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please + EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update + handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?) +#endif + setFrameImmediateDraw(alertFrames); break; + } case Cmd::START_FIRMWARE_UPDATE_SCREEN: handleStartFirmwareUpdateScreen(); break; - case Cmd::STOP_BLUETOOTH_PIN_SCREEN: + case Cmd::STOP_ALERT_FRAME: case Cmd::STOP_BOOT_SCREEN: EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame setFrames(); @@ -2038,12 +1954,6 @@ int32_t Screen::runOnce() handlePrint(cmd.print_text); free(cmd.print_text); break; - case Cmd::START_SHUTDOWN_SCREEN: - handleShutdownScreen(); - break; - case Cmd::START_REBOOT_SCREEN: - handleRebootScreen(); - break; default: LOG_ERROR("Invalid screen cmd\n"); } @@ -2284,17 +2194,6 @@ void Screen::setFrames() setFastFramerate(); // Draw ASAP } -void Screen::handleStartBluetoothPinScreen(uint32_t pin) -{ - LOG_DEBUG("showing bluetooth screen\n"); - showingNormalScreen = false; - EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame - - static FrameCallback frames[] = {drawFrameBluetooth}; - snprintf(btPIN, sizeof(btPIN), "%06u", pin); - setFrameImmediateDraw(frames); -} - void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) { ui->disableAllIndicators(); @@ -2302,41 +2201,6 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) setFastFramerate(); } -void Screen::handleShutdownScreen() -{ - LOG_DEBUG("showing shutdown screen\n"); - showingNormalScreen = false; -#ifdef USE_EINK - EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please - EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update - handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?) -#endif - - auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { - drawFrameText(display, state, x, y, "Shutting down..."); - }; - static FrameCallback frames[] = {frame}; - - setFrameImmediateDraw(frames); -} - -void Screen::handleRebootScreen() -{ - LOG_DEBUG("showing reboot screen\n"); - showingNormalScreen = false; -#ifdef USE_EINK - EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please - EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update - handleSetOn(true); // Power-on to show rebooting screen (PowerFSM should handle?) -#endif - - auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { - drawFrameText(display, state, x, y, "Rebooting..."); - }; - static FrameCallback frames[] = {frame}; - setFrameImmediateDraw(frames); -} - void Screen::handleStartFirmwareUpdateScreen() { LOG_DEBUG("showing firmware screen\n"); diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index b1bbffc3b..a8aca3657 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -21,11 +21,9 @@ class Screen void print(const char *) {} void doDeepSleep() {} void forceDisplay(bool forceUiUpdate = false) {} - void startBluetoothPinScreen(uint32_t pin) {} - void stopBluetoothPinScreen() {} - void startRebootScreen() {} - void startShutdownScreen() {} void startFirmwareUpdateScreen() {} + void startAlert(const char *) {} + void endAlert() {} }; } // namespace graphics #else @@ -34,6 +32,8 @@ class Screen #include #include "../configuration.h" +#include "gps/GeoCoord.h" +#include "graphics/ScreenFonts.h" #ifdef USE_ST7567 #include @@ -173,15 +173,29 @@ class Screen : public concurrency::OSThread void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); } void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); } - /// Starts showing the Bluetooth PIN screen. - // - // Switches over to a static frame showing the Bluetooth pairing screen - // with the PIN. - void startBluetoothPinScreen(uint32_t pin) + // generic alert start + void startAlert(FrameCallback _alertFrame) + { + alertFrame = _alertFrame; + ScreenCmd cmd; + cmd.cmd = Cmd::START_ALERT_FRAME; + enqueueCmd(cmd); + } + + void startAlert(const char *_alertMessage) + { + startAlert([_alertMessage](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { + uint16_t x_offset = display->width() / 2; + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(FONT_MEDIUM); + display->drawString(x_offset + x, 26 + y, _alertMessage); + }); + } + + void endAlert() { ScreenCmd cmd; - cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN; - cmd.bluetooth_pin = pin; + cmd.cmd = Cmd::STOP_ALERT_FRAME; enqueueCmd(cmd); } @@ -192,20 +206,6 @@ class Screen : public concurrency::OSThread enqueueCmd(cmd); } - void startShutdownScreen() - { - ScreenCmd cmd; - cmd.cmd = Cmd::START_SHUTDOWN_SCREEN; - enqueueCmd(cmd); - } - - void startRebootScreen() - { - ScreenCmd cmd; - cmd.cmd = Cmd::START_REBOOT_SCREEN; - enqueueCmd(cmd); - } - // Function to allow the AccelerometerThread to set the heading if a sensor provides it // Mutex needed? void setHeading(long _heading) @@ -224,9 +224,6 @@ class Screen : public concurrency::OSThread void setFunctionSymbal(std::string sym); void removeFunctionSymbal(std::string sym); - /// Stops showing the bluetooth PIN screen. - void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); } - /// Stops showing the boot screen. void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); } @@ -362,6 +359,7 @@ class Screen : public concurrency::OSThread bool isAUTOOled = false; private: + FrameCallback alertFrames[1]; struct ScreenCmd { Cmd cmd; union { @@ -387,11 +385,8 @@ class Screen : public concurrency::OSThread void handleOnPress(); void handleShowNextFrame(); void handleShowPrevFrame(); - void handleStartBluetoothPinScreen(uint32_t pin); void handlePrint(const char *text); void handleStartFirmwareUpdateScreen(); - void handleShutdownScreen(); - void handleRebootScreen(); /// Rebuilds our list of frames (screens) to default ones. void setFrames(); @@ -429,6 +424,9 @@ class Screen : public concurrency::OSThread bool digitalWatchFace = true; #endif + /// callback for current alert frame + FrameCallback alertFrame; + /// Queue of commands to execute in doTask. TypedQueue cmdQueue; /// Whether we are using a display @@ -455,4 +453,92 @@ class Screen : public concurrency::OSThread }; } // namespace graphics +namespace +{ +/// A basic 2D point class for drawing +class Point +{ + public: + float x, y; + + Point(float _x, float _y) : x(_x), y(_y) {} + + /// Apply a rotation around zero (standard rotation matrix math) + void rotate(float radian) + { + float cos = cosf(radian), sin = sinf(radian); + float rx = x * cos + y * sin, ry = -x * sin + y * cos; + + x = rx; + y = ry; + } + + void translate(int16_t dx, int dy) + { + x += dx; + y += dy; + } + + void scale(float f) + { + // We use -f here to counter the flip that happens + // on the y axis when drawing and rotating on screen + x *= f; + y *= -f; + } +}; + +} // namespace + +static void drawLine(OLEDDisplay *d, const Point &p1, const Point &p2) +{ + d->drawLine(p1.x, p1.y, p2.x, p2.y); +} + +static uint16_t getCompassDiam(OLEDDisplay *display) +{ + uint16_t diam = 0; + uint16_t offset = 0; + + if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) + offset = FONT_HEIGHT_SMALL; + + // get the smaller of the 2 dimensions and subtract 20 + if (display->getWidth() > (display->getHeight() - offset)) { + diam = display->getHeight() - offset; + // if 2/3 of the other size would be smaller, use that + if (diam > (display->getWidth() * 2 / 3)) { + diam = display->getWidth() * 2 / 3; + } + } else { + diam = display->getWidth(); + if (diam > ((display->getHeight() - offset) * 2 / 3)) { + diam = (display->getHeight() - offset) * 2 / 3; + } + } + + return diam - 20; +}; + +// Draw north +static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading) +{ + // If north is supposed to be at the top of the compass we want rotation to be +0 + if (config.display.compass_north_top) + myHeading = -0; + + Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f); + Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f); + Point *rosePoints[] = {&N1, &N2, &N3, &N4}; + + for (int i = 0; i < 4; i++) { + // North on compass will be negative of heading + rosePoints[i]->rotate(-myHeading); + rosePoints[i]->scale(getCompassDiam(display)); + rosePoints[i]->translate(compassX, compassY); + } + drawLine(display, N1, N3); + drawLine(display, N2, N4); + drawLine(display, N1, N4); +} #endif \ No newline at end of file diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 4b34563f7..8a48d053e 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -28,8 +28,8 @@ #define FONT_LARGE ArialMT_Plain_24 // Height: 28 #endif -#define fontHeight(font) ((font)[1] + 1) // height is position 1 +#define _fontHeight(font) ((font)[1] + 1) // height is position 1 -#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) -#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) -#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE) +#define FONT_HEIGHT_SMALL _fontHeight(FONT_SMALL) +#define FONT_HEIGHT_MEDIUM _fontHeight(FONT_MEDIUM) +#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 9ec4fa82d..462eaa0f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -930,7 +930,7 @@ void setup() nodeDB->saveToDisk(SEGMENT_CONFIG); if (!rIf->reconfigure()) { LOG_WARN("Reconfigure failed, rebooting\n"); - screen->startRebootScreen(); + screen->startAlert("Rebooting..."); rebootAtMsec = millis() + 5000; } } diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 31fb983f4..1dc6d7883 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -180,7 +180,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset) if (didFactoryReset) { LOG_INFO("Rebooting due to factory reset"); - screen->startRebootScreen(); + screen->startAlert("Rebooting..."); rebootAtMsec = millis() + (5 * 1000); } diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 814686609..3a3901433 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -137,7 +137,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH if (BleOta::getOtaAppVersion().isEmpty()) { LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); - screen->startRebootScreen(); + screen->startAlert("Rebooting..."); } else { screen->startFirmwareUpdateScreen(); BleOta::switchToOtaApp(); @@ -145,7 +145,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta } #else LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s); - screen->startRebootScreen(); + screen->startAlert("Rebooting..."); #endif rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); break; @@ -811,7 +811,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch void AdminModule::reboot(int32_t seconds) { LOG_INFO("Rebooting in %d seconds\n", seconds); - screen->startRebootScreen(); + screen->startAlert("Rebooting..."); rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000); } diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index f513e045f..be414dce1 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -597,14 +597,14 @@ int32_t CannedMessageModule::runOnce() // handle fn+s for shutdown case 0x9b: if (screen) - screen->startShutdownScreen(); + screen->startAlert("Shutting down..."); shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; break; // and fn+r for reboot case 0x90: if (screen) - screen->startRebootScreen(); + screen->startAlert("Rebooting..."); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; break; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 8f899401b..b69b2bfae 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -188,7 +188,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt if (lastMeasurementPacket == nullptr) { // If there's no valid packet, display "Environment" display->drawString(x, y, "Environment"); - display->drawString(x, y += fontHeight(FONT_SMALL), "No measurement"); + display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement"); return; } @@ -213,31 +213,31 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt } // Continue with the remaining details - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Temp/Hum: " + last_temp + " / " + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) { - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"); } if (lastMeasurement.variant.environment_metrics.voltage != 0) { - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " + String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"); } if (lastMeasurement.variant.environment_metrics.iaq != 0) { - display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); + display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); } if (lastMeasurement.variant.environment_metrics.distance != 0) - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"); if (lastMeasurement.variant.environment_metrics.weight != 0) - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"); } diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp index cb864f4f3..fb5aee375 100644 --- a/src/modules/Telemetry/PowerTelemetry.cpp +++ b/src/modules/Telemetry/PowerTelemetry.cpp @@ -108,7 +108,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s display->drawString(x, y, "Power Telemetry"); if (lastMeasurementPacket == nullptr) { display->setFont(FONT_SMALL); - display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); + display->drawString(x, y += _fontHeight(FONT_MEDIUM), "No measurement"); return; } @@ -120,22 +120,22 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s auto &p = lastMeasurementPacket->decoded; if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) { display->setFont(FONT_SMALL); - display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); + display->drawString(x, y += _fontHeight(FONT_MEDIUM), "Measurement Error"); LOG_ERROR("Unable to decode last packet"); return; } display->setFont(FONT_SMALL); String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; - display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); + display->drawString(x, y += _fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); if (lastMeasurement.variant.power_metrics.ch1_voltage != 0) { - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " + String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " + String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); - display->drawString(x, y += fontHeight(FONT_SMALL), + display->drawString(x, y += _fontHeight(FONT_SMALL), "Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " + String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); } diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 48f945b0a..78ef5a1d3 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -82,7 +82,33 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey); powerFSM.trigger(EVENT_BLUETOOTH_PAIR); - screen->startBluetoothPinScreen(passkey); +#if HAS_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 : 32; + 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); + }); +#endif passkeyShowing = true; return passkey; @@ -94,7 +120,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks if (passkeyShowing) { passkeyShowing = false; - screen->stopBluetoothPinScreen(); + screen->endAlert(); } } diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index a14829285..56d7ed167 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -290,7 +290,31 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke { LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3); powerFSM.trigger(EVENT_BLUETOOTH_PAIR); - screen->startBluetoothPinScreen(configuredPasskey); + 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 : 32; + 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); + }); if (match_request) { uint32_t start_time = millis(); while (millis() < start_time + 30000) { @@ -307,7 +331,7 @@ void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_statu LOG_INFO("BLE pairing success\n"); else LOG_INFO("BLE pairing failed\n"); - screen->stopBluetoothPinScreen(); + screen->endAlert(); } void NRF52Bluetooth::sendLog(const char *logMessage) diff --git a/src/shutdown.h b/src/shutdown.h index 54fb3071b..3f191eea8 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -38,7 +38,7 @@ void powerCommandsCheck() #if defined(ARCH_ESP32) || defined(ARCH_NRF52) if (shutdownAtMsec) { - screen->startShutdownScreen(); + screen->startAlert("Shutting down..."); } #endif