* 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 <benmmeadors@gmail.com>
pull/4186/head
Jonathan Bennett 2024-06-25 11:26:02 -05:00 zatwierdzone przez GitHub
rodzic 626aa762df
commit 0425551341
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
16 zmienionych plików z 252 dodań i 223 usunięć

Wyświetl plik

@ -80,7 +80,7 @@ monitor_speed = 115200
lib_deps = lib_deps =
jgromes/RadioLib@~6.6.0 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 mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
@ -150,5 +150,4 @@ lib_deps =
mprograms/QMC5883LCompass@^1.2.0 mprograms/QMC5883LCompass@^1.2.0
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee

Wyświetl plik

@ -16,6 +16,8 @@
#include <Wire.h> #include <Wire.h>
#ifdef RAK_4631 #ifdef RAK_4631
#include "Fusion/Fusion.h" #include "Fusion/Fusion.h"
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include <Rak_BMX160.h> #include <Rak_BMX160.h>
#endif #endif
@ -101,7 +103,11 @@ class AccelerometerThread : public concurrency::OSThread
bmx160.getAllData(&magAccel, NULL, &gAccel); bmx160.getAllData(&magAccel, NULL, &gAccel);
// expirimental calibrate routine. Limited to between 10 and 30 seconds after boot // 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) if (magAccel.x > highestX)
highestX = magAccel.x; highestX = magAccel.x;
if (magAccel.x < lowestX) if (magAccel.x < lowestX)
@ -114,6 +120,9 @@ class AccelerometerThread : public concurrency::OSThread
highestZ = magAccel.z; highestZ = magAccel.z;
if (magAccel.z < lowestZ) if (magAccel.z < lowestZ)
lowestZ = magAccel.z; lowestZ = magAccel.z;
} else if (showingScreen && millis() >= 30 * 1000) {
showingScreen = false;
screen->endAlert();
} }
int highestRealX = highestX - (highestX + lowestX) / 2; int highestRealX = highestX - (highestX + lowestX) / 2;
@ -255,11 +264,33 @@ class AccelerometerThread : public concurrency::OSThread
Adafruit_LIS3DH lis; Adafruit_LIS3DH lis;
Adafruit_LSM6DS3TRC lsm; Adafruit_LSM6DS3TRC lsm;
SensorBMA423 bmaSensor; SensorBMA423 bmaSensor;
bool BMA_IRQ = false;
#ifdef RAK_4631 #ifdef RAK_4631
bool showingScreen = false;
RAK_BMX160 bmx160; RAK_BMX160 bmx160;
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0; 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 #endif
bool BMA_IRQ = false;
}; };
#endif #endif

Wyświetl plik

@ -181,8 +181,9 @@ int32_t ButtonThread::runOnce()
case BUTTON_EVENT_LONG_PRESSED: { case BUTTON_EVENT_LONG_PRESSED: {
LOG_BUTTON("Long press!\n"); LOG_BUTTON("Long press!\n");
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);
if (screen) if (screen) {
screen->startShutdownScreen(); screen->startAlert("Shutting down...");
}
playBeep(); playBeep();
break; break;
} }
@ -322,4 +323,4 @@ void ButtonThread::userButtonPressedLongStop()
if (millis() > c_holdOffTime) { if (millis() > c_holdOffTime) {
btnEvent = BUTTON_EVENT_LONG_RELEASED; btnEvent = BUTTON_EVENT_LONG_RELEASED;
} }
} }

Wyświetl plik

@ -8,13 +8,11 @@ enum class Cmd {
SET_ON, SET_ON,
SET_OFF, SET_OFF,
ON_PRESS, ON_PRESS,
START_BLUETOOTH_PIN_SCREEN, START_ALERT_FRAME,
STOP_ALERT_FRAME,
START_FIRMWARE_UPDATE_SCREEN, START_FIRMWARE_UPDATE_SCREEN,
STOP_BLUETOOTH_PIN_SCREEN,
STOP_BOOT_SCREEN, STOP_BOOT_SCREEN,
PRINT, PRINT,
START_SHUTDOWN_SCREEN,
START_REBOOT_SCREEN,
SHOW_PREV_FRAME, SHOW_PREV_FRAME,
SHOW_NEXT_FRAME SHOW_NEXT_FRAME
}; };

Wyświetl plik

@ -379,7 +379,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
// in the array of "drawScreen" functions; however, // in the array of "drawScreen" functions; however,
// the passed-state doesn't quite reflect the "current" // the passed-state doesn't quite reflect the "current"
// screen, so we have to detect it. // 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 // if we're transitioning from the end of the frame list back around to the first
// frame, then we want this to be `0` // frame, then we want this to be `0`
module_frame = state->transitionFrameTarget; module_frame = state->transitionFrameTarget;
@ -393,31 +393,6 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
pi.drawFrame(display, state, x, y); 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) static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
@ -1307,49 +1282,6 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
} }
} }
#endif #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. * 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; 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 /// We will skip one node - the one for us, so we just blindly loop over all
/// nodes /// nodes
static size_t nodeIndex; 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, leftArrow, tip);
drawLine(display, rightArrow, tip); drawLine(display, rightArrow, tip);
} }
/*
// Draw north // Draw north
static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading) 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, N1, N3);
drawLine(display, N2, N4); drawLine(display, N2, N4);
drawLine(display, N1, N4); drawLine(display, N1, N4);
} }*/
// Get a string representation of the time passed since something happened // Get a string representation of the time passed since something happened
static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength) static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
@ -2023,13 +1930,22 @@ int32_t Screen::runOnce()
case Cmd::SHOW_NEXT_FRAME: case Cmd::SHOW_NEXT_FRAME:
handleShowNextFrame(); handleShowNextFrame();
break; break;
case Cmd::START_BLUETOOTH_PIN_SCREEN: case Cmd::START_ALERT_FRAME: {
handleStartBluetoothPinScreen(cmd.bluetooth_pin); 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; break;
}
case Cmd::START_FIRMWARE_UPDATE_SCREEN: case Cmd::START_FIRMWARE_UPDATE_SCREEN:
handleStartFirmwareUpdateScreen(); handleStartFirmwareUpdateScreen();
break; break;
case Cmd::STOP_BLUETOOTH_PIN_SCREEN: case Cmd::STOP_ALERT_FRAME:
case Cmd::STOP_BOOT_SCREEN: case Cmd::STOP_BOOT_SCREEN:
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
setFrames(); setFrames();
@ -2038,12 +1954,6 @@ int32_t Screen::runOnce()
handlePrint(cmd.print_text); handlePrint(cmd.print_text);
free(cmd.print_text); free(cmd.print_text);
break; break;
case Cmd::START_SHUTDOWN_SCREEN:
handleShutdownScreen();
break;
case Cmd::START_REBOOT_SCREEN:
handleRebootScreen();
break;
default: default:
LOG_ERROR("Invalid screen cmd\n"); LOG_ERROR("Invalid screen cmd\n");
} }
@ -2284,17 +2194,6 @@ void Screen::setFrames()
setFastFramerate(); // Draw ASAP 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) void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
{ {
ui->disableAllIndicators(); ui->disableAllIndicators();
@ -2302,41 +2201,6 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
setFastFramerate(); 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() void Screen::handleStartFirmwareUpdateScreen()
{ {
LOG_DEBUG("showing firmware screen\n"); LOG_DEBUG("showing firmware screen\n");

Wyświetl plik

@ -21,11 +21,9 @@ class Screen
void print(const char *) {} void print(const char *) {}
void doDeepSleep() {} void doDeepSleep() {}
void forceDisplay(bool forceUiUpdate = false) {} void forceDisplay(bool forceUiUpdate = false) {}
void startBluetoothPinScreen(uint32_t pin) {}
void stopBluetoothPinScreen() {}
void startRebootScreen() {}
void startShutdownScreen() {}
void startFirmwareUpdateScreen() {} void startFirmwareUpdateScreen() {}
void startAlert(const char *) {}
void endAlert() {}
}; };
} // namespace graphics } // namespace graphics
#else #else
@ -34,6 +32,8 @@ class Screen
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
#include "../configuration.h" #include "../configuration.h"
#include "gps/GeoCoord.h"
#include "graphics/ScreenFonts.h"
#ifdef USE_ST7567 #ifdef USE_ST7567
#include <ST7567Wire.h> #include <ST7567Wire.h>
@ -173,15 +173,29 @@ class Screen : public concurrency::OSThread
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); } void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); } void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
/// Starts showing the Bluetooth PIN screen. // generic alert start
// void startAlert(FrameCallback _alertFrame)
// Switches over to a static frame showing the Bluetooth pairing screen {
// with the PIN. alertFrame = _alertFrame;
void startBluetoothPinScreen(uint32_t pin) 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; ScreenCmd cmd;
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN; cmd.cmd = Cmd::STOP_ALERT_FRAME;
cmd.bluetooth_pin = pin;
enqueueCmd(cmd); enqueueCmd(cmd);
} }
@ -192,20 +206,6 @@ class Screen : public concurrency::OSThread
enqueueCmd(cmd); 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 // Function to allow the AccelerometerThread to set the heading if a sensor provides it
// Mutex needed? // Mutex needed?
void setHeading(long _heading) void setHeading(long _heading)
@ -224,9 +224,6 @@ class Screen : public concurrency::OSThread
void setFunctionSymbal(std::string sym); void setFunctionSymbal(std::string sym);
void removeFunctionSymbal(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. /// Stops showing the boot screen.
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); } void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
@ -362,6 +359,7 @@ class Screen : public concurrency::OSThread
bool isAUTOOled = false; bool isAUTOOled = false;
private: private:
FrameCallback alertFrames[1];
struct ScreenCmd { struct ScreenCmd {
Cmd cmd; Cmd cmd;
union { union {
@ -387,11 +385,8 @@ class Screen : public concurrency::OSThread
void handleOnPress(); void handleOnPress();
void handleShowNextFrame(); void handleShowNextFrame();
void handleShowPrevFrame(); void handleShowPrevFrame();
void handleStartBluetoothPinScreen(uint32_t pin);
void handlePrint(const char *text); void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen(); void handleStartFirmwareUpdateScreen();
void handleShutdownScreen();
void handleRebootScreen();
/// Rebuilds our list of frames (screens) to default ones. /// Rebuilds our list of frames (screens) to default ones.
void setFrames(); void setFrames();
@ -429,6 +424,9 @@ class Screen : public concurrency::OSThread
bool digitalWatchFace = true; bool digitalWatchFace = true;
#endif #endif
/// callback for current alert frame
FrameCallback alertFrame;
/// Queue of commands to execute in doTask. /// Queue of commands to execute in doTask.
TypedQueue<ScreenCmd> cmdQueue; TypedQueue<ScreenCmd> cmdQueue;
/// Whether we are using a display /// Whether we are using a display
@ -455,4 +453,92 @@ class Screen : public concurrency::OSThread
}; };
} // namespace graphics } // 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 #endif

Wyświetl plik

@ -28,8 +28,8 @@
#define FONT_LARGE ArialMT_Plain_24 // Height: 28 #define FONT_LARGE ArialMT_Plain_24 // Height: 28
#endif #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_SMALL _fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) #define FONT_HEIGHT_MEDIUM _fontHeight(FONT_MEDIUM)
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE) #define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE)

Wyświetl plik

@ -930,7 +930,7 @@ void setup()
nodeDB->saveToDisk(SEGMENT_CONFIG); nodeDB->saveToDisk(SEGMENT_CONFIG);
if (!rIf->reconfigure()) { if (!rIf->reconfigure()) {
LOG_WARN("Reconfigure failed, rebooting\n"); LOG_WARN("Reconfigure failed, rebooting\n");
screen->startRebootScreen(); screen->startAlert("Rebooting...");
rebootAtMsec = millis() + 5000; rebootAtMsec = millis() + 5000;
} }
} }

Wyświetl plik

@ -180,7 +180,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
if (didFactoryReset) { if (didFactoryReset) {
LOG_INFO("Rebooting due to factory reset"); LOG_INFO("Rebooting due to factory reset");
screen->startRebootScreen(); screen->startAlert("Rebooting...");
rebootAtMsec = millis() + (5 * 1000); rebootAtMsec = millis() + (5 * 1000);
} }

Wyświetl plik

@ -137,7 +137,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
if (BleOta::getOtaAppVersion().isEmpty()) { if (BleOta::getOtaAppVersion().isEmpty()) {
LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s);
screen->startRebootScreen(); screen->startAlert("Rebooting...");
} else { } else {
screen->startFirmwareUpdateScreen(); screen->startFirmwareUpdateScreen();
BleOta::switchToOtaApp(); BleOta::switchToOtaApp();
@ -145,7 +145,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
} }
#else #else
LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s); LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s);
screen->startRebootScreen(); screen->startAlert("Rebooting...");
#endif #endif
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break; break;
@ -811,7 +811,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
void AdminModule::reboot(int32_t seconds) void AdminModule::reboot(int32_t seconds)
{ {
LOG_INFO("Rebooting in %d seconds\n", seconds); LOG_INFO("Rebooting in %d seconds\n", seconds);
screen->startRebootScreen(); screen->startAlert("Rebooting...");
rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000); rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000);
} }

Wyświetl plik

@ -597,14 +597,14 @@ int32_t CannedMessageModule::runOnce()
// handle fn+s for shutdown // handle fn+s for shutdown
case 0x9b: case 0x9b:
if (screen) if (screen)
screen->startShutdownScreen(); screen->startAlert("Shutting down...");
shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000; shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break; break;
// and fn+r for reboot // and fn+r for reboot
case 0x90: case 0x90:
if (screen) if (screen)
screen->startRebootScreen(); screen->startAlert("Rebooting...");
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break; break;

Wyświetl plik

@ -188,7 +188,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
if (lastMeasurementPacket == nullptr) { if (lastMeasurementPacket == nullptr) {
// If there's no valid packet, display "Environment" // If there's no valid packet, display "Environment"
display->drawString(x, y, "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; return;
} }
@ -213,31 +213,31 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
} }
// Continue with the remaining details // Continue with the remaining details
display->drawString(x, y += fontHeight(FONT_SMALL), display->drawString(x, y += _fontHeight(FONT_SMALL),
"Temp/Hum: " + last_temp + " / " + "Temp/Hum: " + last_temp + " / " +
String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 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"); "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
} }
if (lastMeasurement.variant.environment_metrics.voltage != 0) { 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 / " + "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"); String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
} }
if (lastMeasurement.variant.environment_metrics.iaq != 0) { 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) 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"); "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
if (lastMeasurement.variant.environment_metrics.weight != 0) 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"); "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
} }

Wyświetl plik

@ -108,7 +108,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
display->drawString(x, y, "Power Telemetry"); display->drawString(x, y, "Power Telemetry");
if (lastMeasurementPacket == nullptr) { if (lastMeasurementPacket == nullptr) {
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); display->drawString(x, y += _fontHeight(FONT_MEDIUM), "No measurement");
return; return;
} }
@ -120,22 +120,22 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
auto &p = lastMeasurementPacket->decoded; auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) { if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->setFont(FONT_SMALL); 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"); LOG_ERROR("Unable to decode last packet");
return; return;
} }
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; 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) { 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 / " + "Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); 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 / " + "Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); 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 / " + "Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA");
} }

Wyświetl plik

@ -82,7 +82,33 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey); LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR); 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; passkeyShowing = true;
return passkey; return passkey;
@ -94,7 +120,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
if (passkeyShowing) { if (passkeyShowing) {
passkeyShowing = false; passkeyShowing = false;
screen->stopBluetoothPinScreen(); screen->endAlert();
} }
} }

Wyświetl plik

@ -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); LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR); 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) { if (match_request) {
uint32_t start_time = millis(); uint32_t start_time = millis();
while (millis() < start_time + 30000) { 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"); LOG_INFO("BLE pairing success\n");
else else
LOG_INFO("BLE pairing failed\n"); LOG_INFO("BLE pairing failed\n");
screen->stopBluetoothPinScreen(); screen->endAlert();
} }
void NRF52Bluetooth::sendLog(const char *logMessage) void NRF52Bluetooth::sendLog(const char *logMessage)

Wyświetl plik

@ -38,7 +38,7 @@ void powerCommandsCheck()
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) #if defined(ARCH_ESP32) || defined(ARCH_NRF52)
if (shutdownAtMsec) { if (shutdownAtMsec) {
screen->startShutdownScreen(); screen->startAlert("Shutting down...");
} }
#endif #endif