mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-08 14:12:05 +00:00
Merge remote-tracking branch 'baseui/StandaloneAddons' into unify-tft
This commit is contained in:
commit
763c80e571
54
.vscode/settings.json
vendored
54
.vscode/settings.json
vendored
@ -10,5 +10,59 @@
|
||||
},
|
||||
"[powershell]": {
|
||||
"editor.defaultFormatter": "ms-vscode.powershell"
|
||||
},
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"*.xbm": "cpp"
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "buzz.h"
|
||||
#include "main.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "modules/CannedMessageModule.h"
|
||||
#include "power.h"
|
||||
#include "sleep.h"
|
||||
#ifdef ARCH_PORTDUINO
|
||||
@ -26,6 +27,7 @@
|
||||
using namespace concurrency;
|
||||
|
||||
ButtonThread *buttonThread; // Declared extern in header
|
||||
extern CannedMessageModule* cannedMessageModule;
|
||||
volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BUTTON_EVENT_NONE;
|
||||
|
||||
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
|
||||
@ -118,6 +120,17 @@ ButtonThread::ButtonThread() : OSThread("Button")
|
||||
|
||||
void ButtonThread::switchPage()
|
||||
{
|
||||
// Prevent screen switch if CannedMessageModule is focused and intercepting input
|
||||
#if HAS_SCREEN
|
||||
extern CannedMessageModule* cannedMessageModule;
|
||||
|
||||
if (cannedMessageModule && cannedMessageModule->isInterceptingAndFocused()) {
|
||||
LOG_DEBUG("User button ignored during canned message input");
|
||||
return; // Skip screen change
|
||||
}
|
||||
#endif
|
||||
|
||||
// Default behavior if not blocked
|
||||
#ifdef BUTTON_PIN
|
||||
#if !defined(USERPREFS_BUTTON_PIN)
|
||||
if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) !=
|
||||
@ -135,8 +148,8 @@ void ButtonThread::switchPage()
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) &&
|
||||
(settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) ||
|
||||
@ -219,11 +232,17 @@ int32_t ButtonThread::runOnce()
|
||||
|
||||
case BUTTON_EVENT_DOUBLE_PRESSED: {
|
||||
LOG_BUTTON("Double press!");
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
|
||||
#ifdef ELECROW_ThinkNode_M1
|
||||
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Send GPS position immediately
|
||||
sendAdHocPosition();
|
||||
|
||||
// Show temporary on-screen confirmation banner for 3 seconds
|
||||
screen->showOverlayBanner("Ad-hoc Ping Sent", 3000);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -235,8 +254,15 @@ int32_t ButtonThread::runOnce()
|
||||
case 3:
|
||||
if (!config.device.disable_triple_click && (gps != nullptr)) {
|
||||
gps->toggleGpsMode();
|
||||
if (screen)
|
||||
|
||||
const char* statusMsg = (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED)
|
||||
? "GPS Enabled"
|
||||
: "GPS Disabled";
|
||||
|
||||
if (screen) {
|
||||
screen->forceDisplay(true); // Force a new UI frame, then force an EInk update
|
||||
screen->showOverlayBanner(statusMsg, 3000);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#elif defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2)
|
||||
@ -280,9 +306,12 @@ int32_t ButtonThread::runOnce()
|
||||
case BUTTON_EVENT_LONG_PRESSED: {
|
||||
LOG_BUTTON("Long press!");
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
|
||||
if (screen) {
|
||||
screen->startAlert("Shutting down...");
|
||||
// Show shutdown message as a temporary overlay banner
|
||||
screen->showOverlayBanner("Shutting Down..."); // Display for 3 seconds
|
||||
}
|
||||
|
||||
playBeep();
|
||||
break;
|
||||
}
|
||||
@ -294,6 +323,7 @@ int32_t ButtonThread::runOnce()
|
||||
playShutdownMelody();
|
||||
delay(3000);
|
||||
power->shutdown();
|
||||
nodeDB->saveToDisk();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ class ButtonThread : public concurrency::OSThread
|
||||
bool isBuzzing() { return buzzer_flag; }
|
||||
void setScreenFlag(bool flag) { screen_flag = flag; }
|
||||
bool getScreenFlag() { return screen_flag; }
|
||||
bool isInterceptingAndFocused();
|
||||
|
||||
// Disconnect and reconnect interrupts for light sleep
|
||||
#ifdef ARCH_ESP32
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -90,7 +90,7 @@ class Screen
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
#define DegD(i) (i * 1e-7)
|
||||
|
||||
extern bool hasUnreadMessage;
|
||||
namespace
|
||||
{
|
||||
/// A basic 2D point class for drawing
|
||||
@ -181,9 +181,23 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
public:
|
||||
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
|
||||
|
||||
size_t frameCount = 0; // Total number of active frames
|
||||
~Screen();
|
||||
|
||||
// Which frame we want to be displayed, after we regen the frameset by calling setFrames
|
||||
enum FrameFocus : uint8_t {
|
||||
FOCUS_DEFAULT, // No specific frame
|
||||
FOCUS_PRESERVE, // Return to the previous frame
|
||||
FOCUS_FAULT,
|
||||
FOCUS_TEXTMESSAGE,
|
||||
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
|
||||
};
|
||||
|
||||
// Regenerate the normal set of frames, focusing a specific frame if requested
|
||||
// Call when a frame should be added / removed, or custom frames should be cleared
|
||||
void setFrames(FrameFocus focus = FOCUS_DEFAULT);
|
||||
|
||||
std::vector<const uint8_t *> indicatorIcons; // Per-frame custom icon pointers
|
||||
Screen(const Screen &) = delete;
|
||||
Screen &operator=(const Screen &) = delete;
|
||||
|
||||
@ -260,6 +274,8 @@ class Screen : public concurrency::OSThread
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void showOverlayBanner(const String &message, uint32_t durationMs = 3000);
|
||||
|
||||
void startFirmwareUpdateScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
@ -600,30 +616,26 @@ class Screen : public concurrency::OSThread
|
||||
// - Used to dismiss the currently shown frame (txt; waypoint) by CardKB combo
|
||||
struct FramesetInfo {
|
||||
struct FramePositions {
|
||||
uint8_t fault = 0;
|
||||
uint8_t textMessage = 0;
|
||||
uint8_t waypoint = 0;
|
||||
uint8_t focusedModule = 0;
|
||||
uint8_t log = 0;
|
||||
uint8_t settings = 0;
|
||||
uint8_t wifi = 0;
|
||||
uint8_t fault = 255;
|
||||
uint8_t textMessage = 255;
|
||||
uint8_t waypoint = 255;
|
||||
uint8_t focusedModule = 255;
|
||||
uint8_t log = 255;
|
||||
uint8_t settings = 255;
|
||||
uint8_t wifi = 255;
|
||||
uint8_t deviceFocused = 255;
|
||||
uint8_t memory = 255;
|
||||
} positions;
|
||||
|
||||
uint8_t frameCount = 0;
|
||||
} framesetInfo;
|
||||
|
||||
// Which frame we want to be displayed, after we regen the frameset by calling setFrames
|
||||
enum FrameFocus : uint8_t {
|
||||
FOCUS_DEFAULT, // No specific frame
|
||||
FOCUS_PRESERVE, // Return to the previous frame
|
||||
FOCUS_FAULT,
|
||||
FOCUS_TEXTMESSAGE,
|
||||
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
|
||||
};
|
||||
|
||||
// Regenerate the normal set of frames, focusing a specific frame if requested
|
||||
// Call when a frame should be added / removed, or custom frames should be cleared
|
||||
void setFrames(FrameFocus focus = FOCUS_DEFAULT);
|
||||
struct DismissedFrames {
|
||||
bool textMessage = false;
|
||||
bool waypoint = false;
|
||||
bool wifi = false;
|
||||
bool memory = false;
|
||||
} dismissedFrames;
|
||||
|
||||
/// Try to start drawing ASAP
|
||||
void setFastFramerate();
|
||||
@ -691,4 +703,7 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
extern String alertBannerMessage;
|
||||
extern uint32_t alertBannerUntil;
|
||||
|
||||
#endif
|
256
src/graphics/SharedUIDisplay.cpp
Normal file
256
src/graphics/SharedUIDisplay.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
#include "graphics/SharedUIDisplay.h"
|
||||
#include "RTC.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
#include "main.h"
|
||||
#include "meshtastic/config.pb.h"
|
||||
#include "power.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <graphics/images.h>
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// === Shared External State ===
|
||||
bool hasUnreadMessage = false;
|
||||
bool isMuted = false;
|
||||
|
||||
// === Internal State ===
|
||||
bool isBoltVisibleShared = true;
|
||||
uint32_t lastBlinkShared = 0;
|
||||
bool isMailIconVisible = true;
|
||||
uint32_t lastMailBlink = 0;
|
||||
|
||||
// *********************************
|
||||
// * Rounded Header when inverted *
|
||||
// *********************************
|
||||
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r)
|
||||
{
|
||||
// Draw the center and side rectangles
|
||||
display->fillRect(x + r, y, w - 2 * r, h); // center bar
|
||||
display->fillRect(x, y + r, r, h - 2 * r); // left edge
|
||||
display->fillRect(x + w - r, y + r, r, h - 2 * r); // right edge
|
||||
|
||||
// Draw the rounded corners using filled circles
|
||||
display->fillCircle(x + r + 1, y + r, r); // top-left
|
||||
display->fillCircle(x + w - r - 1, y + r, r); // top-right
|
||||
display->fillCircle(x + r + 1, y + h - r - 1, r); // bottom-left
|
||||
display->fillCircle(x + w - r - 1, y + h - r - 1, r); // bottom-right
|
||||
}
|
||||
|
||||
// *************************
|
||||
// * Common Header Drawing *
|
||||
// *************************
|
||||
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y)
|
||||
{
|
||||
constexpr int HEADER_OFFSET_Y = 1;
|
||||
y += HEADER_OFFSET_Y;
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
const int xOffset = 4;
|
||||
const int highlightHeight = FONT_HEIGHT_SMALL - 1;
|
||||
const bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
|
||||
const bool isBold = config.display.heading_bold;
|
||||
|
||||
const int screenW = display->getWidth();
|
||||
const int screenH = display->getHeight();
|
||||
|
||||
const bool useBigIcons = (screenW > 128);
|
||||
|
||||
// === Inverted Header Background ===
|
||||
if (isInverted) {
|
||||
drawRoundedHighlight(display, x, y, screenW, highlightHeight, 2);
|
||||
display->setColor(BLACK);
|
||||
} else {
|
||||
if (screenW > 128) {
|
||||
display->drawLine(0, 20, screenW, 20);
|
||||
} else {
|
||||
display->drawLine(0, 14, screenW, 14);
|
||||
}
|
||||
}
|
||||
|
||||
// === Battery State ===
|
||||
int chargePercent = powerStatus->getBatteryChargePercent();
|
||||
bool isCharging = powerStatus->getIsCharging() == meshtastic::OptionalBool::OptTrue;
|
||||
static uint32_t lastBlinkShared = 0;
|
||||
static bool isBoltVisibleShared = true;
|
||||
uint32_t now = millis();
|
||||
|
||||
#ifndef USE_EINK
|
||||
if (isCharging && now - lastBlinkShared > 500) {
|
||||
isBoltVisibleShared = !isBoltVisibleShared;
|
||||
lastBlinkShared = now;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool useHorizontalBattery = (screenW > 128 && screenW >= screenH);
|
||||
const int textY = y + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
||||
|
||||
// === Battery Icons ===
|
||||
if (useHorizontalBattery) {
|
||||
int batteryX = 2;
|
||||
int batteryY = HEADER_OFFSET_Y + 2;
|
||||
display->drawXbm(batteryX, batteryY, 29, 15, batteryBitmap_h);
|
||||
if (isCharging && isBoltVisibleShared)
|
||||
display->drawXbm(batteryX + 9, batteryY + 1, 9, 13, lightning_bolt_h);
|
||||
else {
|
||||
display->drawXbm(batteryX + 8, batteryY, 12, 15, batteryBitmap_sidegaps_h);
|
||||
int fillWidth = 24 * chargePercent / 100;
|
||||
display->fillRect(batteryX + 1, batteryY + 1, fillWidth, 13);
|
||||
}
|
||||
} else {
|
||||
int batteryX = 1;
|
||||
int batteryY = HEADER_OFFSET_Y + 1;
|
||||
#ifdef USE_EINK
|
||||
batteryY += 2;
|
||||
#endif
|
||||
display->drawXbm(batteryX, batteryY, 7, 11, batteryBitmap_v);
|
||||
if (isCharging && isBoltVisibleShared)
|
||||
display->drawXbm(batteryX + 1, batteryY + 3, 5, 5, lightning_bolt_v);
|
||||
else {
|
||||
display->drawXbm(batteryX - 1, batteryY + 4, 8, 3, batteryBitmap_sidegaps_v);
|
||||
int fillHeight = 8 * chargePercent / 100;
|
||||
int fillY = batteryY - fillHeight;
|
||||
display->fillRect(batteryX + 1, fillY + 10, 5, fillHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// === Battery % Display ===
|
||||
char chargeStr[4];
|
||||
snprintf(chargeStr, sizeof(chargeStr), "%d", chargePercent);
|
||||
int chargeNumWidth = display->getStringWidth(chargeStr);
|
||||
const int batteryOffset = useHorizontalBattery ? 28 : 6;
|
||||
#ifdef USE_EINK
|
||||
const int percentX = x + xOffset + batteryOffset - 2;
|
||||
#else
|
||||
const int percentX = x + xOffset + batteryOffset;
|
||||
#endif
|
||||
display->drawString(percentX, textY, chargeStr);
|
||||
display->drawString(percentX + chargeNumWidth - 1, textY, "%");
|
||||
if (isBold) {
|
||||
display->drawString(percentX + 1, textY, chargeStr);
|
||||
display->drawString(percentX + chargeNumWidth, textY, "%");
|
||||
}
|
||||
|
||||
// === Time and Right-aligned Icons ===
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||
char timeStr[10] = "--:--"; // Fallback display
|
||||
int timeStrWidth = display->getStringWidth("12:34"); // Default alignment
|
||||
int timeX = screenW - xOffset - timeStrWidth + 4;
|
||||
|
||||
if (rtc_sec > 0) {
|
||||
// === Build Time String ===
|
||||
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
|
||||
|
||||
if (config.display.use_12h_clock) {
|
||||
bool isPM = hour >= 12;
|
||||
hour %= 12;
|
||||
if (hour == 0)
|
||||
hour = 12;
|
||||
snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
|
||||
}
|
||||
|
||||
timeStrWidth = display->getStringWidth(timeStr);
|
||||
timeX = screenW - xOffset - timeStrWidth + 4;
|
||||
|
||||
// === Show Mail or Mute Icon to the Left of Time ===
|
||||
int iconRightEdge = timeX - 2;
|
||||
|
||||
static bool isMailIconVisible = true;
|
||||
static uint32_t lastMailBlink = 0;
|
||||
bool showMail = false;
|
||||
|
||||
#ifndef USE_EINK
|
||||
if (hasUnreadMessage) {
|
||||
if (now - lastMailBlink > 500) {
|
||||
isMailIconVisible = !isMailIconVisible;
|
||||
lastMailBlink = now;
|
||||
}
|
||||
showMail = isMailIconVisible;
|
||||
}
|
||||
#else
|
||||
if (hasUnreadMessage) {
|
||||
showMail = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (showMail) {
|
||||
if (useHorizontalBattery) {
|
||||
int iconW = 16, iconH = 12;
|
||||
int iconX = iconRightEdge - iconW;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
|
||||
display->drawRect(iconX, iconY, iconW + 1, iconH);
|
||||
display->drawLine(iconX, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
||||
display->drawLine(iconX + iconW, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
||||
} else {
|
||||
int iconX = iconRightEdge - mail_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
|
||||
}
|
||||
} else if (isMuted) {
|
||||
if (useBigIcons) {
|
||||
int iconX = iconRightEdge - mute_symbol_big_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mute_symbol_big_width, mute_symbol_big_height, mute_symbol_big);
|
||||
} else {
|
||||
int iconX = iconRightEdge - mute_symbol_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mute_symbol_width, mute_symbol_height, mute_symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// === Draw Time ===
|
||||
display->drawString(timeX, textY, timeStr);
|
||||
if (isBold)
|
||||
display->drawString(timeX - 1, textY, timeStr);
|
||||
|
||||
} else {
|
||||
// === No Time Available: Mail/Mute Icon Moves to Far Right ===
|
||||
int iconRightEdge = screenW - xOffset;
|
||||
|
||||
static bool isMailIconVisible = true;
|
||||
static uint32_t lastMailBlink = 0;
|
||||
bool showMail = false;
|
||||
|
||||
if (hasUnreadMessage) {
|
||||
if (now - lastMailBlink > 500) {
|
||||
isMailIconVisible = !isMailIconVisible;
|
||||
lastMailBlink = now;
|
||||
}
|
||||
showMail = isMailIconVisible;
|
||||
}
|
||||
|
||||
if (showMail) {
|
||||
if (useHorizontalBattery) {
|
||||
int iconW = 16, iconH = 12;
|
||||
int iconX = iconRightEdge - iconW;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
|
||||
display->drawRect(iconX, iconY, iconW + 1, iconH);
|
||||
display->drawLine(iconX, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
||||
display->drawLine(iconX + iconW, iconY, iconX + iconW / 2, iconY + iconH - 4);
|
||||
} else {
|
||||
int iconX = iconRightEdge - mail_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
|
||||
}
|
||||
} else if (isMuted) {
|
||||
if (useBigIcons) {
|
||||
int iconX = iconRightEdge - mute_symbol_big_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mute_symbol_big_width, mute_symbol_big_height, mute_symbol_big);
|
||||
} else {
|
||||
int iconX = iconRightEdge - mute_symbol_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mute_symbol_width, mute_symbol_height, mute_symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display->setColor(WHITE); // Reset for other UI
|
||||
}
|
||||
|
||||
} // namespace graphics
|
45
src/graphics/SharedUIDisplay.h
Normal file
45
src/graphics/SharedUIDisplay.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <OLEDDisplay.h>
|
||||
|
||||
namespace graphics {
|
||||
|
||||
// =======================
|
||||
// Shared UI Helpers
|
||||
// =======================
|
||||
|
||||
// Compact line layout
|
||||
#define compactFirstLine ((FONT_HEIGHT_SMALL - 1) * 1)
|
||||
#define compactSecondLine ((FONT_HEIGHT_SMALL - 1) * 2) - 2
|
||||
#define compactThirdLine ((FONT_HEIGHT_SMALL - 1) * 3) - 4
|
||||
#define compactFourthLine ((FONT_HEIGHT_SMALL - 1) * 4) - 6
|
||||
#define compactFifthLine ((FONT_HEIGHT_SMALL - 1) * 5) - 8
|
||||
|
||||
// Standard line layout
|
||||
#define standardFirstLine (FONT_HEIGHT_SMALL + 1) * 1
|
||||
#define standardSecondLine (FONT_HEIGHT_SMALL + 1) * 2
|
||||
#define standardThirdLine (FONT_HEIGHT_SMALL + 1) * 3
|
||||
#define standardFourthLine (FONT_HEIGHT_SMALL + 1) * 4
|
||||
|
||||
// More Compact line layout
|
||||
#define moreCompactFirstLine compactFirstLine
|
||||
#define moreCompactSecondLine (moreCompactFirstLine + (FONT_HEIGHT_SMALL - 5))
|
||||
#define moreCompactThirdLine (moreCompactSecondLine + (FONT_HEIGHT_SMALL - 5))
|
||||
#define moreCompactFourthLine (moreCompactThirdLine + (FONT_HEIGHT_SMALL - 5))
|
||||
#define moreCompactFifthLine (moreCompactFourthLine + (FONT_HEIGHT_SMALL - 5))
|
||||
|
||||
// Quick screen access
|
||||
#define SCREEN_WIDTH display->getWidth()
|
||||
#define SCREEN_HEIGHT display->getHeight()
|
||||
|
||||
// Shared state (declare inside namespace)
|
||||
extern bool hasUnreadMessage;
|
||||
extern bool isMuted;
|
||||
|
||||
// Rounded highlight (used for inverted headers)
|
||||
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
|
||||
|
||||
// Shared battery/time/mail header
|
||||
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y);
|
||||
|
||||
} // namespace graphics
|
227
src/graphics/emotes.cpp
Normal file
227
src/graphics/emotes.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
#include "emotes.h"
|
||||
|
||||
namespace graphics {
|
||||
|
||||
// Always define Emote list and count
|
||||
const Emote emotes[] = {
|
||||
#ifndef EXCLUDE_EMOJI
|
||||
// --- Thumbs ---
|
||||
{"\U0001F44D", thumbup, thumbs_width, thumbs_height}, // 👍 Thumbs Up
|
||||
{"\U0001F44E", thumbdown, thumbs_width, thumbs_height}, // 👎 Thumbs Down
|
||||
|
||||
// --- Smileys (Multiple Unicode Aliases) ---
|
||||
{"\U0001F60A", smiley, smiley_width, smiley_height}, // 😊 Smiling Face with Smiling Eyes
|
||||
{"\U0001F600", smiley, smiley_width, smiley_height}, // 😀 Grinning Face
|
||||
{"\U0001F642", smiley, smiley_width, smiley_height}, // 🙂 Slightly Smiling Face
|
||||
{"\U0001F609", smiley, smiley_width, smiley_height}, // 😉 Winking Face
|
||||
{"\U0001F601", smiley, smiley_width, smiley_height}, // 😁 Grinning Face with Smiling Eyes
|
||||
|
||||
// --- Question/Alert ---
|
||||
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
|
||||
{"\u203C\uFE0F", bang, bang_width, bang_height}, // ‼️ Double Exclamation Mark
|
||||
|
||||
// --- Laughing Faces ---
|
||||
{"\U0001F602", haha, haha_width, haha_height}, // 😂 Face with Tears of Joy
|
||||
{"\U0001F923", haha, haha_width, haha_height}, // 🤣 Rolling on the Floor Laughing
|
||||
{"\U0001F606", haha, haha_width, haha_height}, // 😆 Smiling with Open Mouth and Closed Eyes
|
||||
{"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat
|
||||
{"\U0001F604", haha, haha_width, haha_height}, // 😄 Grinning Face with Smiling Eyes
|
||||
|
||||
// --- Gestures and People ---
|
||||
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height},// 👋 Waving Hand
|
||||
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
|
||||
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
|
||||
|
||||
// --- Weather ---
|
||||
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
|
||||
{"\u2600\uFE0F", sun, sun_width, sun_height}, // ☀️ Sun (with variation selector)
|
||||
{"\U0001F327\uFE0F", rain, rain_width, rain_height}, // 🌧️ Cloud with Rain
|
||||
{"\u2601\uFE0F", cloud, cloud_width, cloud_height}, // ☁️ Cloud
|
||||
{"\U0001F32B\uFE0F", fog, fog_width, fog_height}, // 🌫️ Fog
|
||||
|
||||
// --- Misc Faces ---
|
||||
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
|
||||
|
||||
// --- Hearts (Multiple Unicode Aliases) ---
|
||||
{"\u2764\uFE0F", heart, heart_width, heart_height}, // ❤️ Red Heart
|
||||
{"\U0001F9E1", heart, heart_width, heart_height}, // 🧡 Orange Heart
|
||||
{"\U00002763", heart, heart_width, heart_height}, // ❣ Heart Exclamation
|
||||
{"\U00002764", heart, heart_width, heart_height}, // ❤ Red Heart (legacy)
|
||||
{"\U0001F495", heart, heart_width, heart_height}, // 💕 Two Hearts
|
||||
{"\U0001F496", heart, heart_width, heart_height}, // 💖 Sparkling Heart
|
||||
{"\U0001F497", heart, heart_width, heart_height}, // 💗 Growing Heart
|
||||
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
|
||||
|
||||
// --- Objects ---
|
||||
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
|
||||
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height} // 🔔 Bell
|
||||
#endif
|
||||
};
|
||||
|
||||
const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
|
||||
|
||||
#ifndef EXCLUDE_EMOJI
|
||||
const unsigned char thumbup[] PROGMEM = {
|
||||
0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
|
||||
0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
|
||||
0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
|
||||
0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
|
||||
0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char thumbdown[] PROGMEM = {
|
||||
0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
|
||||
0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
|
||||
0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
|
||||
0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
|
||||
0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char smiley[] PROGMEM = {
|
||||
0x00, 0xfe, 0x0f, 0x00, 0x80, 0x01, 0x30, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02,
|
||||
0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x10, 0x02, 0x0e, 0x0e, 0x10, 0x02, 0x09, 0x12, 0x10,
|
||||
0x01, 0x09, 0x12, 0x20, 0x01, 0x0f, 0x1e, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
|
||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x81, 0x00, 0x20, 0x20,
|
||||
0x82, 0x00, 0x20, 0x10, 0x02, 0x01, 0x10, 0x10, 0x04, 0x02, 0x08, 0x08, 0x04, 0xfc, 0x07, 0x08, 0x08, 0x00, 0x00, 0x04,
|
||||
0x10, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x40, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x30, 0x00, 0x00, 0xfe, 0x0f, 0x00
|
||||
};
|
||||
|
||||
const unsigned char question[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
|
||||
0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
|
||||
0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
|
||||
0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char bang[] PROGMEM = {
|
||||
0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
|
||||
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
|
||||
0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
|
||||
0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
|
||||
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
|
||||
};
|
||||
|
||||
const unsigned char haha[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00,
|
||||
0x00, 0xFC, 0x0F, 0x00, 0x00, 0x1F, 0x3E, 0x00, 0x80, 0x03, 0x70, 0x00, 0xC0, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC2, 0x00,
|
||||
0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0xC1, 0x1F, 0x60, 0x80, 0x8F, 0x31, 0x30, 0x0E, 0x80, 0x31, 0x30, 0x10, 0x30, 0x1F,
|
||||
0x30, 0x08, 0x58, 0x00, 0x30, 0x04, 0x6C, 0x03, 0x60, 0x00, 0xF3, 0x01, 0x60, 0xC0, 0xFC, 0x01, 0x80, 0x38, 0xBF, 0x01,
|
||||
0xE0, 0xC5, 0xDF, 0x00, 0xB0, 0xF9, 0xEF, 0x00, 0x30, 0xF1, 0x73, 0x00, 0xB0, 0x1D, 0x3E, 0x00, 0xF0, 0xFD, 0x0F, 0x00,
|
||||
0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char wave_icon[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x00,
|
||||
0x00, 0x0C, 0x9C, 0x01, 0x80, 0x17, 0x20, 0x01, 0x80, 0x26, 0x46, 0x02, 0x80, 0x44, 0x88, 0x02, 0xC0, 0x89, 0x8A, 0x02,
|
||||
0x40, 0x93, 0x8B, 0x02, 0x40, 0x26, 0x13, 0x00, 0x80, 0x44, 0x16, 0x00, 0xC0, 0x89, 0x24, 0x00, 0x40, 0x93, 0x60, 0x00,
|
||||
0x40, 0x26, 0x40, 0x00, 0x80, 0x0C, 0x80, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x40, 0x06, 0x80, 0x00,
|
||||
0x50, 0x0C, 0x80, 0x00, 0x50, 0x08, 0x40, 0x00, 0x90, 0x10, 0x20, 0x00, 0xB0, 0x21, 0x10, 0x00, 0x20, 0x47, 0x18, 0x00,
|
||||
0x40, 0x80, 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char cowboy[] PROGMEM = {
|
||||
0x00, 0xF0, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x3C, 0xFE, 0x1F, 0x0F,
|
||||
0xFE, 0xFE, 0xDF, 0x1F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F,
|
||||
0x3E, 0xC0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x0E, 0x1C, 0x04, 0x00, 0x0E, 0x1C, 0x00,
|
||||
0x04, 0x0E, 0x1C, 0x08, 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x04, 0x08, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x08,
|
||||
0x8C, 0x07, 0x70, 0x0C, 0x88, 0xFC, 0x4F, 0x04, 0x88, 0x01, 0x40, 0x04, 0x90, 0xFF, 0x7F, 0x02, 0x30, 0x03, 0x30, 0x03,
|
||||
0x60, 0x0E, 0x9C, 0x01, 0xC0, 0xF8, 0xC7, 0x00, 0x80, 0x01, 0x60, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0xF8, 0x07, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char deadmau5[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
|
||||
0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
|
||||
0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
|
||||
0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
|
||||
0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
|
||||
0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
|
||||
0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
|
||||
0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char sun[] PROGMEM = {
|
||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
|
||||
0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
|
||||
0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
|
||||
0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
|
||||
0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
|
||||
0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char rain[] PROGMEM = {
|
||||
0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
|
||||
0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
|
||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
|
||||
0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
|
||||
0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
|
||||
0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char cloud[] PROGMEM = {
|
||||
0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
|
||||
0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
|
||||
0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
|
||||
0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
|
||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
|
||||
0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
|
||||
};
|
||||
|
||||
const unsigned char fog[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
|
||||
0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
|
||||
0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char devil[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x03, 0xC0, 0x01, 0x38, 0x07, 0x7C, 0x0F, 0x38, 0x1F, 0x03, 0x30, 0x1E,
|
||||
0xFE, 0x01, 0xE0, 0x1F, 0x7E, 0x00, 0x80, 0x1F, 0x3C, 0x00, 0x00, 0x0F, 0x1C, 0x00, 0x00, 0x0E, 0x18, 0x00, 0x00, 0x06,
|
||||
0x08, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x0E, 0x1C, 0x0C,
|
||||
0x0C, 0x18, 0x06, 0x0C, 0x0C, 0x1C, 0x06, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x0C, 0x06, 0x0C,
|
||||
0x08, 0x00, 0x00, 0x06, 0x18, 0x02, 0x10, 0x06, 0x10, 0x0C, 0x0C, 0x03, 0x30, 0xF8, 0x07, 0x03, 0x60, 0xE0, 0x80, 0x01,
|
||||
0xC0, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x70, 0x00, 0x00, 0x06, 0x1C, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char heart[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
|
||||
0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
|
||||
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
|
||||
0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
|
||||
0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
|
||||
0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
|
||||
};
|
||||
|
||||
const unsigned char poo[] PROGMEM = {
|
||||
0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x8C, 0x07, 0x00, 0x00, 0x0C, 0x06, 0x00,
|
||||
0x00, 0x24, 0x0C, 0x00, 0x00, 0x34, 0x08, 0x00, 0x00, 0x1F, 0x08, 0x00, 0xC0, 0x0F, 0x08, 0x00, 0xC0, 0x00, 0x3C, 0x00,
|
||||
0x60, 0x00, 0x7C, 0x00, 0x60, 0x00, 0xC6, 0x00, 0x20, 0x00, 0xCB, 0x00, 0xA0, 0xC7, 0xFF, 0x00, 0xE0, 0x7F, 0xF7, 0x00,
|
||||
0xF0, 0x18, 0xE3, 0x03, 0x78, 0x18, 0x41, 0x03, 0x6C, 0x9B, 0x5D, 0x06, 0x64, 0x9B, 0x5D, 0x04, 0x44, 0x1A, 0x41, 0x04,
|
||||
0x4C, 0xD8, 0x63, 0x06, 0xF8, 0xFC, 0x36, 0x06, 0xFE, 0x0F, 0x9C, 0x1F, 0x07, 0x03, 0xC0, 0x30, 0x03, 0x00, 0x78, 0x20,
|
||||
0x01, 0x00, 0x1F, 0x20, 0x03, 0xE0, 0x03, 0x20, 0x07, 0x7E, 0x04, 0x30, 0xFE, 0x0F, 0xFC, 0x1F, 0xF0, 0x00, 0xF0, 0x0F,
|
||||
};
|
||||
|
||||
const unsigned char bell_icon[] PROGMEM = {
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000,
|
||||
0b00000011, 0b00000000, 0b00000000, 0b11111100, 0b00001111, 0b00000000, 0b00000000, 0b00001111, 0b00111100, 0b00000000,
|
||||
0b00000000, 0b00000011, 0b00110000, 0b00000000, 0b10000000, 0b00000001, 0b01100000, 0b00000000, 0b11000000, 0b00000000,
|
||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
|
||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
|
||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
|
||||
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000, 0b10000000, 0b00000000, 0b01100000, 0b00000000,
|
||||
0b10000000, 0b00000001, 0b01110000, 0b00000000, 0b10000000, 0b00000011, 0b00110000, 0b00000000, 0b00000000, 0b00000011,
|
||||
0b00011000, 0b00000000, 0b00000000, 0b00000110, 0b11110000, 0b11111111, 0b11111111, 0b00000011, 0b00000000, 0b00001100,
|
||||
0b00001100, 0b00000000, 0b00000000, 0b00011000, 0b00000110, 0b00000000, 0b00000000, 0b11111000, 0b00000111, 0b00000000,
|
||||
0b00000000, 0b11100000, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace graphics
|
||||
|
85
src/graphics/emotes.h
Normal file
85
src/graphics/emotes.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace graphics {
|
||||
|
||||
// === Emote List ===
|
||||
struct Emote {
|
||||
const char *label;
|
||||
const unsigned char *bitmap;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
extern const Emote emotes[/* numEmotes */];
|
||||
extern const int numEmotes;
|
||||
|
||||
#ifndef EXCLUDE_EMOJI
|
||||
// === Emote Bitmaps ===
|
||||
#define thumbs_height 25
|
||||
#define thumbs_width 25
|
||||
extern const unsigned char thumbup[] PROGMEM;
|
||||
extern const unsigned char thumbdown[] PROGMEM;
|
||||
|
||||
#define smiley_height 30
|
||||
#define smiley_width 30
|
||||
extern const unsigned char smiley[] PROGMEM;
|
||||
|
||||
#define question_height 25
|
||||
#define question_width 25
|
||||
extern const unsigned char question[] PROGMEM;
|
||||
|
||||
#define bang_height 30
|
||||
#define bang_width 30
|
||||
extern const unsigned char bang[] PROGMEM;
|
||||
|
||||
#define haha_height 30
|
||||
#define haha_width 30
|
||||
extern const unsigned char haha[] PROGMEM;
|
||||
|
||||
#define wave_icon_height 30
|
||||
#define wave_icon_width 30
|
||||
extern const unsigned char wave_icon[] PROGMEM;
|
||||
|
||||
#define cowboy_height 30
|
||||
#define cowboy_width 30
|
||||
extern const unsigned char cowboy[] PROGMEM;
|
||||
|
||||
#define deadmau5_height 30
|
||||
#define deadmau5_width 60
|
||||
extern const unsigned char deadmau5[] PROGMEM;
|
||||
|
||||
#define sun_height 30
|
||||
#define sun_width 30
|
||||
extern const unsigned char sun[] PROGMEM;
|
||||
|
||||
#define rain_height 30
|
||||
#define rain_width 30
|
||||
extern const unsigned char rain[] PROGMEM;
|
||||
|
||||
#define cloud_height 30
|
||||
#define cloud_width 30
|
||||
extern const unsigned char cloud[] PROGMEM;
|
||||
|
||||
#define fog_height 25
|
||||
#define fog_width 25
|
||||
extern const unsigned char fog[] PROGMEM;
|
||||
|
||||
#define devil_height 30
|
||||
#define devil_width 30
|
||||
extern const unsigned char devil[] PROGMEM;
|
||||
|
||||
#define heart_height 30
|
||||
#define heart_width 30
|
||||
extern const unsigned char heart[] PROGMEM;
|
||||
|
||||
#define poo_height 30
|
||||
#define poo_width 30
|
||||
extern const unsigned char poo[] PROGMEM;
|
||||
|
||||
#define bell_icon_width 30
|
||||
#define bell_icon_height 30
|
||||
extern const unsigned char bell_icon[] PROGMEM;
|
||||
#endif // EXCLUDE_EMOJI
|
||||
|
||||
} // namespace graphics
|
@ -37,181 +37,275 @@ const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1,
|
||||
const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5};
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUDE_EMOJI
|
||||
#define thumbs_height 25
|
||||
#define thumbs_width 25
|
||||
static unsigned char thumbup[] PROGMEM = {
|
||||
0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
|
||||
0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
|
||||
0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
|
||||
0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
|
||||
0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
|
||||
// === Horizontal battery ===
|
||||
// Basic battery design and all related pieces
|
||||
const unsigned char batteryBitmap_h[] PROGMEM = {
|
||||
0b11111110, 0b00000000, 0b11110000, 0b00000111, 0b00000001, 0b00000000, 0b00000000, 0b00001000, 0b00000001, 0b00000000,
|
||||
0b00000000, 0b00001000, 0b00000001, 0b00000000, 0b00000000, 0b00001000, 0b00000001, 0b00000000, 0b00000000, 0b00001000,
|
||||
0b00000001, 0b00000000, 0b00000000, 0b00011000, 0b00000001, 0b00000000, 0b00000000, 0b00011000, 0b00000001, 0b00000000,
|
||||
0b00000000, 0b00011000, 0b00000001, 0b00000000, 0b00000000, 0b00011000, 0b00000001, 0b00000000, 0b00000000, 0b00011000,
|
||||
0b00000001, 0b00000000, 0b00000000, 0b00001000, 0b00000001, 0b00000000, 0b00000000, 0b00001000, 0b00000001, 0b00000000,
|
||||
0b00000000, 0b00001000, 0b00000001, 0b00000000, 0b00000000, 0b00001000, 0b11111110, 0b00000000, 0b11110000, 0b00000111};
|
||||
|
||||
// This is the left and right bars for the fill in
|
||||
const unsigned char batteryBitmap_sidegaps_h[] PROGMEM = {
|
||||
0b11111111, 0b00001111, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11111111, 0b00001111};
|
||||
|
||||
// Lightning Bolt
|
||||
const unsigned char lightning_bolt_h[] PROGMEM = {
|
||||
0b11110000, 0b00000000, 0b11110000, 0b00000000, 0b01110000, 0b00000000, 0b00111000, 0b00000000, 0b00111100,
|
||||
0b00000000, 0b11111100, 0b00000000, 0b01111110, 0b00000000, 0b00111000, 0b00000000, 0b00110000, 0b00000000,
|
||||
0b00010000, 0b00000000, 0b00010000, 0b00000000, 0b00001000, 0b00000000, 0b00001000, 0b00000000};
|
||||
|
||||
// === Vertical battery ===
|
||||
// Basic battery design and all related pieces
|
||||
const unsigned char batteryBitmap_v[] PROGMEM = {0b00011100, 0b00111110, 0b01000001, 0b01000001, 0b00000000, 0b00000000,
|
||||
0b00000000, 0b01000001, 0b01000001, 0b01000001, 0b00111110};
|
||||
// This is the left and right bars for the fill in
|
||||
const unsigned char batteryBitmap_sidegaps_v[] PROGMEM = {0b10000010, 0b10000010, 0b10000010};
|
||||
// Lightning Bolt
|
||||
const unsigned char lightning_bolt_v[] PROGMEM = {0b00000100, 0b00000110, 0b00011111, 0b00001100, 0b00000100};
|
||||
|
||||
#define mail_width 10
|
||||
#define mail_height 7
|
||||
static const unsigned char mail[] PROGMEM = {
|
||||
0b11111111, 0b00, // Top line
|
||||
0b10000001, 0b00, // Edges
|
||||
0b11000011, 0b00, // Diagonals start
|
||||
0b10100101, 0b00, // Inner M part
|
||||
0b10011001, 0b00, // Inner M part
|
||||
0b10000001, 0b00, // Edges
|
||||
0b11111111, 0b00 // Bottom line
|
||||
};
|
||||
|
||||
static unsigned char thumbdown[] PROGMEM = {
|
||||
0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
|
||||
0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
|
||||
0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
|
||||
0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
|
||||
0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
|
||||
// 📬 Mail / Message
|
||||
const uint8_t icon_mail[] PROGMEM = {
|
||||
0b00000000, // (padding)
|
||||
0b11111111, // ████████ top border
|
||||
0b10000001, // █ █ sides
|
||||
0b11000011, // ██ ██ diagonal
|
||||
0b10100101, // █ █ █ █ inner M
|
||||
0b10011001, // █ ██ █ inner M
|
||||
0b10000001, // █ █ sides
|
||||
0b11111111 // ████████ bottom
|
||||
};
|
||||
|
||||
#define smiley_height 30
|
||||
#define smiley_width 30
|
||||
static unsigned char smiley[] PROGMEM = {
|
||||
0x00, 0xfe, 0x0f, 0x00, 0x80, 0x01, 0x30, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02,
|
||||
0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x10, 0x02, 0x0e, 0x0e, 0x10, 0x02, 0x09, 0x12, 0x10,
|
||||
0x01, 0x09, 0x12, 0x20, 0x01, 0x0f, 0x1e, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
|
||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x81, 0x00, 0x20, 0x20,
|
||||
0x82, 0x00, 0x20, 0x10, 0x02, 0x01, 0x10, 0x10, 0x04, 0x02, 0x08, 0x08, 0x04, 0xfc, 0x07, 0x08, 0x08, 0x00, 0x00, 0x04,
|
||||
0x10, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x40, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x30, 0x00, 0x00, 0xfe, 0x0f, 0x00};
|
||||
|
||||
#define question_height 25
|
||||
#define question_width 25
|
||||
static unsigned char question[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
|
||||
0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
|
||||
0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
|
||||
0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 📍 GPS Screen / Location Pin
|
||||
const unsigned char icon_compass[] PROGMEM = {
|
||||
0x3C, // Row 0: ..####..
|
||||
0x52, // Row 1: .#..#.#.
|
||||
0x91, // Row 2: #...#..#
|
||||
0x91, // Row 3: #...#..#
|
||||
0x91, // Row 4: #...#..#
|
||||
0x81, // Row 5: #......#
|
||||
0x42, // Row 6: .#....#.
|
||||
0x3C // Row 7: ..####..
|
||||
};
|
||||
|
||||
#define bang_height 30
|
||||
#define bang_width 30
|
||||
static unsigned char bang[] PROGMEM = {
|
||||
0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
|
||||
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
|
||||
0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
|
||||
0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
|
||||
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
|
||||
const uint8_t icon_radio[] PROGMEM = {
|
||||
0x0F, // Row 0: ####....
|
||||
0x10, // Row 1: ....#...
|
||||
0x27, // Row 2: ###..#..
|
||||
0x48, // Row 3: ...#..#.
|
||||
0x93, // Row 4: ##..#..#
|
||||
0xA4, // Row 5: ..#..#.#
|
||||
0xA8, // Row 6: ...#.#.#
|
||||
0xA9 // Row 7: #..#.#.#
|
||||
};
|
||||
|
||||
#define haha_height 30
|
||||
#define haha_width 30
|
||||
static unsigned char haha[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00,
|
||||
0x00, 0xFC, 0x0F, 0x00, 0x00, 0x1F, 0x3E, 0x00, 0x80, 0x03, 0x70, 0x00, 0xC0, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC2, 0x00,
|
||||
0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0xC1, 0x1F, 0x60, 0x80, 0x8F, 0x31, 0x30, 0x0E, 0x80, 0x31, 0x30, 0x10, 0x30, 0x1F,
|
||||
0x30, 0x08, 0x58, 0x00, 0x30, 0x04, 0x6C, 0x03, 0x60, 0x00, 0xF3, 0x01, 0x60, 0xC0, 0xFC, 0x01, 0x80, 0x38, 0xBF, 0x01,
|
||||
0xE0, 0xC5, 0xDF, 0x00, 0xB0, 0xF9, 0xEF, 0x00, 0x30, 0xF1, 0x73, 0x00, 0xB0, 0x1D, 0x3E, 0x00, 0xF0, 0xFD, 0x0F, 0x00,
|
||||
0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 🪙 Memory Icon
|
||||
const uint8_t icon_memory[] PROGMEM = {
|
||||
0x24, // Row 0: ..#..#..
|
||||
0x3C, // Row 1: ..####..
|
||||
0xC3, // Row 2: ##....##
|
||||
0x5A, // Row 3: .#.##.#.
|
||||
0x5A, // Row 4: .#.##.#.
|
||||
0xC3, // Row 5: ##....##
|
||||
0x3C, // Row 6: ..####..
|
||||
0x24 // Row 7: ..#..#..
|
||||
};
|
||||
|
||||
#define wave_icon_height 30
|
||||
#define wave_icon_width 30
|
||||
static unsigned char wave_icon[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x00,
|
||||
0x00, 0x0C, 0x9C, 0x01, 0x80, 0x17, 0x20, 0x01, 0x80, 0x26, 0x46, 0x02, 0x80, 0x44, 0x88, 0x02, 0xC0, 0x89, 0x8A, 0x02,
|
||||
0x40, 0x93, 0x8B, 0x02, 0x40, 0x26, 0x13, 0x00, 0x80, 0x44, 0x16, 0x00, 0xC0, 0x89, 0x24, 0x00, 0x40, 0x93, 0x60, 0x00,
|
||||
0x40, 0x26, 0x40, 0x00, 0x80, 0x0C, 0x80, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x40, 0x06, 0x80, 0x00,
|
||||
0x50, 0x0C, 0x80, 0x00, 0x50, 0x08, 0x40, 0x00, 0x90, 0x10, 0x20, 0x00, 0xB0, 0x21, 0x10, 0x00, 0x20, 0x47, 0x18, 0x00,
|
||||
0x40, 0x80, 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 🌐 Wi-Fi
|
||||
const uint8_t icon_wifi[] PROGMEM = {0b00000000, 0b00011000, 0b00111100, 0b01111110,
|
||||
0b11011011, 0b00011000, 0b00011000, 0b00000000};
|
||||
|
||||
const uint8_t icon_nodes[] PROGMEM = {
|
||||
0xF9, // Row 0 #..#######
|
||||
0x00, // Row 1
|
||||
0xF9, // Row 2 #..#######
|
||||
0x00, // Row 3
|
||||
0xF9, // Row 4 #..#######
|
||||
0x00, // Row 5
|
||||
0xF9, // Row 6 #..#######
|
||||
0x00 // Row 7
|
||||
};
|
||||
|
||||
#define cowboy_height 30
|
||||
#define cowboy_width 30
|
||||
static unsigned char cowboy[] PROGMEM = {
|
||||
0x00, 0xF0, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x3C, 0xFE, 0x1F, 0x0F,
|
||||
0xFE, 0xFE, 0xDF, 0x1F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F,
|
||||
0x3E, 0xC0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x0E, 0x1C, 0x04, 0x00, 0x0E, 0x1C, 0x00,
|
||||
0x04, 0x0E, 0x1C, 0x08, 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x04, 0x08, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x08,
|
||||
0x8C, 0x07, 0x70, 0x0C, 0x88, 0xFC, 0x4F, 0x04, 0x88, 0x01, 0x40, 0x04, 0x90, 0xFF, 0x7F, 0x02, 0x30, 0x03, 0x30, 0x03,
|
||||
0x60, 0x0E, 0x9C, 0x01, 0xC0, 0xF8, 0xC7, 0x00, 0x80, 0x01, 0x60, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0xF8, 0x07, 0x00,
|
||||
// ➤ Chevron Triangle Arrow Icon (8x8)
|
||||
const uint8_t icon_list[] PROGMEM = {
|
||||
0x10, // Row 0: ...#....
|
||||
0x10, // Row 1: ...#....
|
||||
0x38, // Row 2: ..###...
|
||||
0x38, // Row 3: ..###...
|
||||
0x7C, // Row 4: .#####..
|
||||
0x6C, // Row 5: .##.##..
|
||||
0xC6, // Row 6: ##...##.
|
||||
0x82 // Row 7: #.....#.
|
||||
};
|
||||
|
||||
#define deadmau5_height 30
|
||||
#define deadmau5_width 60
|
||||
static unsigned char deadmau5[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
|
||||
0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
|
||||
0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
|
||||
0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
|
||||
0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
|
||||
0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
|
||||
0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
|
||||
0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 📶 Signal Bars Icon (left to right, small to large with spacing)
|
||||
const uint8_t icon_signal[] PROGMEM = {
|
||||
0b00000000, // ░░░░░░░
|
||||
0b10000000, // ░░░░░░░
|
||||
0b10100000, // ░░░░█░█
|
||||
0b10100000, // ░░░░█░█
|
||||
0b10101000, // ░░█░█░█
|
||||
0b10101000, // ░░█░█░█
|
||||
0b10101010, // █░█░█░█
|
||||
0b11111111 // ███████
|
||||
};
|
||||
|
||||
#define sun_width 30
|
||||
#define sun_height 30
|
||||
static unsigned char sun[] PROGMEM = {
|
||||
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
|
||||
0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
|
||||
0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
|
||||
0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
|
||||
0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
|
||||
0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
|
||||
// ↔️ Distance / Measurement Icon (double-ended arrow)
|
||||
const uint8_t icon_distance[] PROGMEM = {
|
||||
0b00000000, // ░░░░░░░░
|
||||
0b10000001, // █░░░░░█ arrowheads
|
||||
0b01000010, // ░█░░░█░
|
||||
0b00100100, // ░░█░█░░
|
||||
0b00011000, // ░░░██░░ center
|
||||
0b00100100, // ░░█░█░░
|
||||
0b01000010, // ░█░░░█░
|
||||
0b10000001 // █░░░░░█
|
||||
};
|
||||
|
||||
#define rain_width 30
|
||||
#define rain_height 30
|
||||
static unsigned char rain[] PROGMEM = {
|
||||
0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
|
||||
0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
|
||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
|
||||
0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
|
||||
0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
|
||||
0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
|
||||
// ⚠️ Error / Fault
|
||||
const uint8_t icon_error[] PROGMEM = {
|
||||
0b00011000, // ░░░██░░░
|
||||
0b00011000, // ░░░██░░░
|
||||
0b00011000, // ░░░██░░░
|
||||
0b00011000, // ░░░██░░░
|
||||
0b00000000, // ░░░░░░░░
|
||||
0b00011000, // ░░░██░░░
|
||||
0b00000000, // ░░░░░░░░
|
||||
0b00000000 // ░░░░░░░░
|
||||
};
|
||||
|
||||
#define cloud_height 30
|
||||
#define cloud_width 30
|
||||
static unsigned char cloud[] PROGMEM = {
|
||||
0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
|
||||
0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
|
||||
0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
|
||||
0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
|
||||
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
|
||||
0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
|
||||
// 🏠 Optimized Home Icon (8x8)
|
||||
const uint8_t icon_home[] PROGMEM = {
|
||||
0b00011000, // ██
|
||||
0b00111100, // ████
|
||||
0b01111110, // ██████
|
||||
0b11111111, // ███████
|
||||
0b11000011, // ██ ██
|
||||
0b11011011, // ██ ██ ██
|
||||
0b11011011, // ██ ██ ██
|
||||
0b11111111 // ███████
|
||||
};
|
||||
|
||||
#define fog_height 25
|
||||
#define fog_width 25
|
||||
static unsigned char fog[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
|
||||
0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
|
||||
0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 🔧 Generic module (gear-like shape)
|
||||
const uint8_t icon_module[] PROGMEM = {
|
||||
0b00011000, // ░░░██░░░
|
||||
0b00111100, // ░░████░░
|
||||
0b01111110, // ░██████░
|
||||
0b11011011, // ██░██░██
|
||||
0b11011011, // ██░██░██
|
||||
0b01111110, // ░██████░
|
||||
0b00111100, // ░░████░░
|
||||
0b00011000 // ░░░██░░░
|
||||
};
|
||||
|
||||
#define devil_height 30
|
||||
#define devil_width 30
|
||||
static unsigned char devil[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x03, 0xC0, 0x01, 0x38, 0x07, 0x7C, 0x0F, 0x38, 0x1F, 0x03, 0x30, 0x1E,
|
||||
0xFE, 0x01, 0xE0, 0x1F, 0x7E, 0x00, 0x80, 0x1F, 0x3C, 0x00, 0x00, 0x0F, 0x1C, 0x00, 0x00, 0x0E, 0x18, 0x00, 0x00, 0x06,
|
||||
0x08, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x0E, 0x1C, 0x0C,
|
||||
0x0C, 0x18, 0x06, 0x0C, 0x0C, 0x1C, 0x06, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x0C, 0x06, 0x0C,
|
||||
0x08, 0x00, 0x00, 0x06, 0x18, 0x02, 0x10, 0x06, 0x10, 0x0C, 0x0C, 0x03, 0x30, 0xF8, 0x07, 0x03, 0x60, 0xE0, 0x80, 0x01,
|
||||
0xC0, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x70, 0x00, 0x00, 0x06, 0x1C, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#define mute_symbol_width 8
|
||||
#define mute_symbol_height 8
|
||||
const uint8_t mute_symbol[] PROGMEM = {
|
||||
0b00011001, // █
|
||||
0b00100110, // █
|
||||
0b00100100, // ████
|
||||
0b01001010, // █ █ █
|
||||
0b01010010, // █ █ █
|
||||
0b01100010, // ████████
|
||||
0b11111111, // █ █
|
||||
0b10011000, // █
|
||||
};
|
||||
|
||||
#define heart_height 30
|
||||
#define heart_width 30
|
||||
static unsigned char heart[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
|
||||
0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
|
||||
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
|
||||
0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
|
||||
0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
|
||||
0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
|
||||
#define mute_symbol_big_width 16
|
||||
#define mute_symbol_big_height 16
|
||||
const uint8_t mute_symbol_big[] PROGMEM = {0b00000001, 0b00000000, 0b11000010, 0b00000011, 0b00110100, 0b00001100, 0b00011000,
|
||||
0b00001000, 0b00011000, 0b00010000, 0b00101000, 0b00010000, 0b01001000, 0b00010000,
|
||||
0b10001000, 0b00010000, 0b00001000, 0b00010001, 0b00001000, 0b00010010, 0b00001000,
|
||||
0b00010100, 0b00000100, 0b00101000, 0b11111100, 0b00111111, 0b01000000, 0b00100010,
|
||||
0b10000000, 0b01000001, 0b00000000, 0b10000000};
|
||||
|
||||
// Bell icon for Alert Message
|
||||
#define bell_alert_width 8
|
||||
#define bell_alert_height 8
|
||||
const unsigned char bell_alert[] PROGMEM = {0b00011000, 0b00100100, 0b00100100, 0b01000010,
|
||||
0b01000010, 0b01000010, 0b11111111, 0b00011000};
|
||||
|
||||
|
||||
#define key_symbol_width 8
|
||||
#define key_symbol_height 8
|
||||
const uint8_t key_symbol[] PROGMEM = {
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000110,
|
||||
0b11111001,
|
||||
0b10101001,
|
||||
0b10000110,
|
||||
0b00000000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
#define poo_width 30
|
||||
#define poo_height 30
|
||||
static unsigned char poo[] PROGMEM = {
|
||||
0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x8C, 0x07, 0x00, 0x00, 0x0C, 0x06, 0x00,
|
||||
0x00, 0x24, 0x0C, 0x00, 0x00, 0x34, 0x08, 0x00, 0x00, 0x1F, 0x08, 0x00, 0xC0, 0x0F, 0x08, 0x00, 0xC0, 0x00, 0x3C, 0x00,
|
||||
0x60, 0x00, 0x7C, 0x00, 0x60, 0x00, 0xC6, 0x00, 0x20, 0x00, 0xCB, 0x00, 0xA0, 0xC7, 0xFF, 0x00, 0xE0, 0x7F, 0xF7, 0x00,
|
||||
0xF0, 0x18, 0xE3, 0x03, 0x78, 0x18, 0x41, 0x03, 0x6C, 0x9B, 0x5D, 0x06, 0x64, 0x9B, 0x5D, 0x04, 0x44, 0x1A, 0x41, 0x04,
|
||||
0x4C, 0xD8, 0x63, 0x06, 0xF8, 0xFC, 0x36, 0x06, 0xFE, 0x0F, 0x9C, 0x1F, 0x07, 0x03, 0xC0, 0x30, 0x03, 0x00, 0x78, 0x20,
|
||||
0x01, 0x00, 0x1F, 0x20, 0x03, 0xE0, 0x03, 0x20, 0x07, 0x7E, 0x04, 0x30, 0xFE, 0x0F, 0xFC, 0x1F, 0xF0, 0x00, 0xF0, 0x0F,
|
||||
#define placeholder_width 8
|
||||
#define placeholder_height 8
|
||||
const uint8_t placeholder[] PROGMEM = {
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111,
|
||||
0b11111111
|
||||
};
|
||||
|
||||
#define icon_node_width 8
|
||||
#define icon_node_height 8
|
||||
static const uint8_t icon_node[] PROGMEM = {
|
||||
0x10, // #
|
||||
0x10, // # ← antenna
|
||||
0x10, // #
|
||||
0xFE, // ####### ← device top
|
||||
0x82, // # #
|
||||
0xAA, // # # # # ← body with pattern
|
||||
0x92, // # # #
|
||||
0xFE // ####### ← device base
|
||||
};
|
||||
|
||||
#define bluetoothdisabled_width 8
|
||||
#define bluetoothdisabled_height 8
|
||||
const uint8_t bluetoothdisabled[] PROGMEM = {
|
||||
0b11101100,
|
||||
0b01010100,
|
||||
0b01001100,
|
||||
0b01010100,
|
||||
0b01001100,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000
|
||||
};
|
||||
|
||||
#define smallbulletpoint_width 8
|
||||
#define smallbulletpoint_height 8
|
||||
const uint8_t smallbulletpoint[] PROGMEM = {
|
||||
0b00000011,
|
||||
0b00000011,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000,
|
||||
0b00000000
|
||||
};
|
||||
#endif
|
||||
|
||||
#include "img/icon.xbm"
|
||||
static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
|
@ -19,6 +19,7 @@
|
||||
#define INPUT_BROKER_MSG_FN_SYMBOL_ON 0xf1
|
||||
#define INPUT_BROKER_MSG_FN_SYMBOL_OFF 0xf2
|
||||
#define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA
|
||||
#define INPUT_BROKER_MSG_TAB 0x09
|
||||
|
||||
typedef struct _InputEvent {
|
||||
const char *source;
|
||||
|
@ -1228,9 +1228,9 @@ void setup()
|
||||
LOG_WARN("LoRa chip does not support 2.4GHz. Revert to unset");
|
||||
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
|
||||
nodeDB->saveToDisk(SEGMENT_CONFIG);
|
||||
|
||||
if (!rIf->reconfigure()) {
|
||||
LOG_WARN("Reconfigure failed, rebooting");
|
||||
if (screen)
|
||||
screen->startAlert("Rebooting...");
|
||||
rebootAtMsec = millis() + 5000;
|
||||
}
|
||||
|
@ -299,6 +299,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
if (node != NULL) {
|
||||
node->is_favorite = true;
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
if (screen) screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -308,6 +309,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
if (node != NULL) {
|
||||
node->is_favorite = false;
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
if (screen) screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1118,7 +1120,7 @@ void AdminModule::reboot(int32_t seconds)
|
||||
{
|
||||
LOG_INFO("Reboot in %d seconds", seconds);
|
||||
if (screen)
|
||||
screen->startAlert("Rebooting...");
|
||||
screen->showOverlayBanner("Rebooting...", 0); // stays on screen
|
||||
rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,27 +3,42 @@
|
||||
#include "ProtobufModule.h"
|
||||
#include "input/InputBroker.h"
|
||||
|
||||
// ============================
|
||||
// Enums & Defines
|
||||
// ============================
|
||||
|
||||
enum cannedMessageModuleRunState {
|
||||
CANNED_MESSAGE_RUN_STATE_DISABLED,
|
||||
CANNED_MESSAGE_RUN_STATE_INACTIVE,
|
||||
CANNED_MESSAGE_RUN_STATE_ACTIVE,
|
||||
CANNED_MESSAGE_RUN_STATE_FREETEXT,
|
||||
CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE,
|
||||
CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED,
|
||||
CANNED_MESSAGE_RUN_STATE_MESSAGE,
|
||||
CANNED_MESSAGE_RUN_STATE_ACTION_SELECT,
|
||||
CANNED_MESSAGE_RUN_STATE_ACTION_UP,
|
||||
CANNED_MESSAGE_RUN_STATE_ACTION_DOWN,
|
||||
CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION,
|
||||
CANNED_MESSAGE_RUN_STATE_FREETEXT,
|
||||
CANNED_MESSAGE_RUN_STATE_MESSAGE_SELECTION
|
||||
};
|
||||
|
||||
enum cannedMessageDestinationType {
|
||||
CANNED_MESSAGE_DESTINATION_TYPE_NONE,
|
||||
CANNED_MESSAGE_DESTINATION_TYPE_NODE,
|
||||
CANNED_MESSAGE_DESTINATION_TYPE_CHANNEL
|
||||
};
|
||||
|
||||
enum CannedMessageModuleIconType { shift, backspace, space, enter };
|
||||
|
||||
#define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50
|
||||
#define CANNED_MESSAGE_MODULE_MESSAGES_SIZE 800
|
||||
|
||||
#ifndef CANNED_MESSAGE_MODULE_ENABLE
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 0
|
||||
#endif
|
||||
|
||||
// ============================
|
||||
// Data Structures
|
||||
// ============================
|
||||
|
||||
struct Letter {
|
||||
String character;
|
||||
float width;
|
||||
@ -33,71 +48,60 @@ struct Letter {
|
||||
int rectHeight;
|
||||
};
|
||||
|
||||
#define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50
|
||||
/**
|
||||
* Sum of CannedMessageModuleConfig part sizes.
|
||||
*/
|
||||
#define CANNED_MESSAGE_MODULE_MESSAGES_SIZE 800
|
||||
struct NodeEntry {
|
||||
meshtastic_NodeInfoLite *node;
|
||||
uint32_t lastHeard;
|
||||
};
|
||||
|
||||
#ifndef CANNED_MESSAGE_MODULE_ENABLE
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 0
|
||||
#endif
|
||||
// ============================
|
||||
// Main Class
|
||||
// ============================
|
||||
|
||||
class CannedMessageModule : public SinglePortModule, public Observable<const UIFrameEvent *>, private concurrency::OSThread
|
||||
{
|
||||
CallbackObserver<CannedMessageModule, const InputEvent *> inputObserver =
|
||||
CallbackObserver<CannedMessageModule, const InputEvent *>(this, &CannedMessageModule::handleInputEvent);
|
||||
|
||||
public:
|
||||
class CannedMessageModule : public SinglePortModule,
|
||||
public Observable<const UIFrameEvent *>,
|
||||
private concurrency::OSThread {
|
||||
public:
|
||||
CannedMessageModule();
|
||||
|
||||
// === Message navigation ===
|
||||
const char *getCurrentMessage();
|
||||
const char *getPrevMessage();
|
||||
const char *getNextMessage();
|
||||
const char *getMessageByIndex(int index);
|
||||
const char *getNodeName(NodeNum node);
|
||||
|
||||
// === State/UI ===
|
||||
bool shouldDraw();
|
||||
bool hasMessages();
|
||||
// void eventUp();
|
||||
// void eventDown();
|
||||
// void eventSelect();
|
||||
void showTemporaryMessage(const String &message);
|
||||
void resetSearch();
|
||||
void updateFilteredNodes();
|
||||
bool isInterceptingAndFocused();
|
||||
bool isCharInputAllowed() const;
|
||||
String drawWithCursor(String text, int cursor);
|
||||
|
||||
// === Admin Handlers ===
|
||||
void handleGetCannedMessageModuleMessages(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response);
|
||||
void handleSetCannedMessageModuleMessages(const char *from_msg);
|
||||
|
||||
void showTemporaryMessage(const String &message);
|
||||
|
||||
String drawWithCursor(String text, int cursor);
|
||||
|
||||
#ifdef RAK14014
|
||||
cannedMessageModuleRunState getRunState() const { return runState; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
-Override the wantPacket method. We need the Routing Messages to look for ACKs.
|
||||
*/
|
||||
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
|
||||
{
|
||||
if (p->rx_rssi != 0) {
|
||||
this->lastRxRssi = p->rx_rssi;
|
||||
// === Packet Interest Filter ===
|
||||
virtual bool wantPacket(const meshtastic_MeshPacket *p) override {
|
||||
if (p->rx_rssi != 0) lastRxRssi = p->rx_rssi;
|
||||
if (p->rx_snr > 0) lastRxSnr = p->rx_snr;
|
||||
return (p->decoded.portnum == meshtastic_PortNum_ROUTING_APP) ? waitingForAck : false;
|
||||
}
|
||||
|
||||
if (p->rx_snr > 0) {
|
||||
this->lastRxSnr = p->rx_snr;
|
||||
}
|
||||
|
||||
switch (p->decoded.portnum) {
|
||||
case meshtastic_PortNum_ROUTING_APP:
|
||||
return waitingForAck;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
// === Thread Entry Point ===
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
// === Transmission ===
|
||||
void sendText(NodeNum dest, ChannelIndex channel, const char *message, bool wantReplies);
|
||||
|
||||
void drawHeader(OLEDDisplay *display, int16_t x, int16_t y, char* buffer);
|
||||
int splitConfiguredMessages();
|
||||
int getNextIndex();
|
||||
int getPrevIndex();
|
||||
@ -105,17 +109,14 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
||||
#if defined(USE_VIRTUAL_KEYBOARD)
|
||||
void drawKeyboard(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
String keyForCoordinates(uint x, uint y);
|
||||
bool shift = false;
|
||||
int charSet = 0;
|
||||
void drawShiftIcon(OLEDDisplay *display, int x, int y, float scale = 1);
|
||||
void drawBackspaceIcon(OLEDDisplay *display, int x, int y, float scale = 1);
|
||||
void drawEnterIcon(OLEDDisplay *display, int x, int y, float scale = 1);
|
||||
#endif
|
||||
|
||||
char highlight = 0x00;
|
||||
|
||||
// === Input Handling ===
|
||||
int handleInputEvent(const InputEvent *event);
|
||||
virtual bool wantUIFrame() override { return this->shouldDraw(); }
|
||||
virtual bool wantUIFrame() override { return shouldDraw(); }
|
||||
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() override { return this; }
|
||||
virtual bool interceptingKeyboardInput() override;
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
|
||||
@ -123,38 +124,70 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
||||
meshtastic_AdminMessage *request,
|
||||
meshtastic_AdminMessage *response) override;
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
* @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered
|
||||
* for it
|
||||
*/
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
|
||||
void loadProtoForModule();
|
||||
bool saveProtoForModule();
|
||||
|
||||
void installDefaultCannedMessageModuleConfig();
|
||||
|
||||
int currentMessageIndex = -1;
|
||||
cannedMessageModuleRunState runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
char payload = 0x00;
|
||||
unsigned int cursor = 0;
|
||||
String freetext = ""; // Text Buffer for Freetext Editor
|
||||
NodeNum dest = NODENUM_BROADCAST;
|
||||
ChannelIndex channel = 0;
|
||||
cannedMessageDestinationType destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||
uint8_t numChannels = 0;
|
||||
ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0};
|
||||
NodeNum incoming = NODENUM_BROADCAST;
|
||||
bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
|
||||
bool waitingForAck = false; // Are currently interested in routing packets?
|
||||
float lastRxSnr = 0;
|
||||
int32_t lastRxRssi = 0;
|
||||
private:
|
||||
// === Input Observers ===
|
||||
CallbackObserver<CannedMessageModule, const InputEvent *> inputObserver =
|
||||
CallbackObserver<CannedMessageModule, const InputEvent *>(this, &CannedMessageModule::handleInputEvent);
|
||||
|
||||
// === Display and UI ===
|
||||
int displayHeight = 64;
|
||||
int destIndex = 0;
|
||||
int scrollIndex = 0;
|
||||
int visibleRows = 0;
|
||||
bool needsUpdate = true;
|
||||
bool shouldRedraw = false;
|
||||
unsigned long lastUpdateMillis = 0;
|
||||
String searchQuery;
|
||||
String nodeSelectionInput;
|
||||
String freetext;
|
||||
String temporaryMessage;
|
||||
|
||||
// === Message Storage ===
|
||||
char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE + 1];
|
||||
char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT];
|
||||
int messagesCount = 0;
|
||||
int currentMessageIndex = -1;
|
||||
|
||||
// === Routing & Acknowledgment ===
|
||||
NodeNum dest = NODENUM_BROADCAST; // Destination node for outgoing messages (default: broadcast)
|
||||
NodeNum incoming = NODENUM_BROADCAST; // Source node from which last ACK/NACK was received
|
||||
NodeNum lastSentNode = 0; // Tracks the most recent node we sent a message to (for UI display)
|
||||
ChannelIndex channel = 0; // Channel index used when sending a message
|
||||
|
||||
bool ack = false; // True = ACK received, False = NACK or failed
|
||||
bool waitingForAck = false; // True if we're expecting an ACK and should monitor routing packets
|
||||
bool lastAckWasRelayed = false; // True if the ACK was relayed through intermediate nodes
|
||||
uint8_t lastAckHopStart = 0; // Hop start value from the received ACK packet
|
||||
uint8_t lastAckHopLimit = 0; // Hop limit value from the received ACK packet
|
||||
|
||||
float lastRxSnr = 0; // SNR from last received ACK (used for diagnostics/UI)
|
||||
int32_t lastRxRssi = 0; // RSSI from last received ACK (used for diagnostics/UI)
|
||||
|
||||
// === State Tracking ===
|
||||
cannedMessageModuleRunState runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
cannedMessageDestinationType destSelect = CANNED_MESSAGE_DESTINATION_TYPE_NONE;
|
||||
char highlight = 0x00;
|
||||
char payload = 0x00;
|
||||
unsigned int cursor = 0;
|
||||
unsigned long lastTouchMillis = 0;
|
||||
String temporaryMessage;
|
||||
std::vector<uint8_t> activeChannelIndices;
|
||||
std::vector<NodeEntry> filteredNodes;
|
||||
|
||||
bool isInputSourceAllowed(const InputEvent *event);
|
||||
bool isUpEvent(const InputEvent *event);
|
||||
bool isDownEvent(const InputEvent *event);
|
||||
bool isSelectEvent(const InputEvent *event);
|
||||
bool handleTabSwitch(const InputEvent *event);
|
||||
int handleDestinationSelectionInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect);
|
||||
bool handleMessageSelectorInput(const InputEvent *event, bool isUp, bool isDown, bool isSelect);
|
||||
bool handleFreeTextInput(const InputEvent *event);
|
||||
bool handleSystemCommandInput(const InputEvent *event);
|
||||
|
||||
#if defined(USE_VIRTUAL_KEYBOARD)
|
||||
Letter keyboard[2][4][10] = {{{{"Q", 20, 0, 0, 0, 0},
|
||||
|
@ -21,13 +21,6 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
|
||||
bool wasBroadcast = isBroadcast(mp.to);
|
||||
|
||||
// Show new nodes on LCD screen
|
||||
if (wasBroadcast) {
|
||||
String lcd = String("Joined: ") + p.long_name + "\n";
|
||||
if (screen)
|
||||
screen->print(lcd.c_str());
|
||||
}
|
||||
|
||||
// if user has changed while packet was not for us, inform phone
|
||||
if (hasChanged && !wasBroadcast && !isToUs(&mp))
|
||||
service->sendToPhone(packetPool.allocCopy(mp));
|
||||
|
@ -17,6 +17,10 @@
|
||||
#include "target_specific.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
#include "graphics/SharedUIDisplay.h"
|
||||
#include "graphics/images.h"
|
||||
#include "buzz.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR_EXTERNAL
|
||||
|
||||
@ -26,6 +30,9 @@
|
||||
#include "Sensor/RCWL9620Sensor.h"
|
||||
#include "Sensor/nullSensor.h"
|
||||
|
||||
namespace graphics {
|
||||
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y);
|
||||
}
|
||||
#if __has_include(<Adafruit_AHTX0.h>)
|
||||
#include "Sensor/AHT10.h"
|
||||
AHT10Sensor aht10Sensor;
|
||||
@ -331,119 +338,166 @@ bool EnvironmentTelemetryModule::wantUIFrame()
|
||||
|
||||
void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
// === Setup display ===
|
||||
display->clear();
|
||||
display->setFont(FONT_SMALL);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
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");
|
||||
// Draw shared header bar (battery, time, etc.)
|
||||
graphics::drawCommonHeader(display, x, y);
|
||||
|
||||
// === Draw Title (Centered under header) ===
|
||||
const int highlightHeight = FONT_HEIGHT_SMALL - 1;
|
||||
const int titleY = y + 1 + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
||||
const char *titleStr = (SCREEN_WIDTH > 128) ? "Environment" : "Env.";
|
||||
|
||||
const int centerX = x + SCREEN_WIDTH / 2;
|
||||
|
||||
// Use black text on white background if in inverted mode
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
|
||||
display->setColor(BLACK);
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->drawString(centerX, titleY, titleStr); // Centered title
|
||||
if (config.display.heading_bold)
|
||||
display->drawString(centerX + 1, titleY, titleStr); // Bold effect via 1px offset
|
||||
|
||||
// Restore text color & alignment
|
||||
display->setColor(WHITE);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
// === Row spacing setup ===
|
||||
const int rowHeight = FONT_HEIGHT_SMALL - 4;
|
||||
int currentY = compactFirstLine;
|
||||
|
||||
// === Show "No Telemetry" if no data available ===
|
||||
if (!lastMeasurementPacket) {
|
||||
display->drawString(x, currentY, "No Telemetry");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode the last measurement packet
|
||||
meshtastic_Telemetry lastMeasurement;
|
||||
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
|
||||
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
|
||||
|
||||
// Decode the telemetry message from the latest received packet
|
||||
const meshtastic_Data &p = lastMeasurementPacket->decoded;
|
||||
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
|
||||
display->drawString(x, y, "Measurement Error");
|
||||
LOG_ERROR("Unable to decode last packet");
|
||||
meshtastic_Telemetry telemetry;
|
||||
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &telemetry)) {
|
||||
display->drawString(x, currentY, "No Telemetry");
|
||||
return;
|
||||
}
|
||||
|
||||
// Display "Env. From: ..." on its own
|
||||
display->drawString(x, y, "Env. From: " + String(lastSender) + " (" + String(agoSecs) + "s)");
|
||||
const auto &m = telemetry.variant.environment_metrics;
|
||||
|
||||
// Prepare sensor data strings
|
||||
String sensorData[10];
|
||||
int sensorCount = 0;
|
||||
// Check if any telemetry field has valid data
|
||||
bool hasAny = m.has_temperature || m.has_relative_humidity || m.barometric_pressure != 0 ||
|
||||
m.iaq != 0 || m.voltage != 0 || m.current != 0 || m.lux != 0 ||
|
||||
m.white_lux != 0 || m.weight != 0 || m.distance != 0 || m.radiation != 0;
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.has_temperature ||
|
||||
lastMeasurement.variant.environment_metrics.has_relative_humidity) {
|
||||
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C";
|
||||
if (moduleConfig.telemetry.environment_display_fahrenheit) {
|
||||
last_temp =
|
||||
String(UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F";
|
||||
if (!hasAny) {
|
||||
display->drawString(x, currentY, "No Telemetry");
|
||||
return;
|
||||
}
|
||||
|
||||
sensorData[sensorCount++] =
|
||||
"Temp/Hum: " + last_temp + " / " + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%";
|
||||
// === First line: Show sender name + time since received (left), and first metric (right) ===
|
||||
const char *sender = getSenderShortName(*lastMeasurementPacket);
|
||||
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
|
||||
String agoStr = (agoSecs > 864000) ? "?" :
|
||||
(agoSecs > 3600) ? String(agoSecs / 3600) + "h" :
|
||||
(agoSecs > 60) ? String(agoSecs / 60) + "m" :
|
||||
String(agoSecs) + "s";
|
||||
|
||||
String leftStr = String(sender) + " (" + agoStr + ")";
|
||||
display->drawString(x, currentY, leftStr); // Left side: who and when
|
||||
|
||||
// === Collect sensor readings as label strings (no icons) ===
|
||||
std::vector<String> entries;
|
||||
|
||||
if (m.has_temperature) {
|
||||
String tempStr = moduleConfig.telemetry.environment_display_fahrenheit
|
||||
? "Tmp: " + String(UnitConversions::CelsiusToFahrenheit(m.temperature), 1) + "°F"
|
||||
: "Tmp: " + String(m.temperature, 1) + "°C";
|
||||
entries.push_back(tempStr);
|
||||
}
|
||||
if (m.has_relative_humidity)
|
||||
entries.push_back("Hum: " + String(m.relative_humidity, 0) + "%");
|
||||
if (m.barometric_pressure != 0)
|
||||
entries.push_back("Prss: " + String(m.barometric_pressure, 0) + " hPa");
|
||||
if (m.iaq != 0) {
|
||||
String aqi = "IAQ: " + String(m.iaq);
|
||||
const char *bannerMsg = nullptr; // Default: no banner
|
||||
|
||||
if (m.iaq <= 25) aqi += " (Excellent)";
|
||||
else if (m.iaq <= 50) aqi += " (Good)";
|
||||
else if (m.iaq <= 100) aqi += " (Moderate)";
|
||||
else if (m.iaq <= 150) aqi += " (Poor)";
|
||||
else if (m.iaq <= 200) {
|
||||
aqi += " (Unhealthy)";
|
||||
bannerMsg = "Unhealthy IAQ";
|
||||
}
|
||||
else if (m.iaq <= 300) {
|
||||
aqi += " (Very Unhealthy)";
|
||||
bannerMsg = "Very Unhealthy IAQ";
|
||||
}
|
||||
else {
|
||||
aqi += " (Hazardous)";
|
||||
bannerMsg = "Hazardous IAQ";
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) {
|
||||
sensorData[sensorCount++] =
|
||||
"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA";
|
||||
entries.push_back(aqi);
|
||||
|
||||
// === IAQ alert logic ===
|
||||
static uint32_t lastAlertTime = 0;
|
||||
uint32_t now = millis();
|
||||
|
||||
bool isOwnTelemetry = lastMeasurementPacket->from == nodeDB->getNodeNum();
|
||||
bool isCooldownOver = (now - lastAlertTime > 60000);
|
||||
|
||||
if (isOwnTelemetry && bannerMsg && isCooldownOver) {
|
||||
LOG_INFO("drawFrame: IAQ %d (own) — showing banner: %s", m.iaq, bannerMsg);
|
||||
screen->showOverlayBanner(bannerMsg, 3000);
|
||||
|
||||
// Only buzz if IAQ is over 200
|
||||
if (m.iaq > 200 && moduleConfig.external_notification.enabled && !externalNotificationModule->getMute()) {
|
||||
playLongBeep();
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.voltage != 0) {
|
||||
sensorData[sensorCount++] = "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
|
||||
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA";
|
||||
lastAlertTime = now;
|
||||
}
|
||||
}
|
||||
if (m.voltage != 0 || m.current != 0)
|
||||
entries.push_back(String(m.voltage, 1) + "V / " + String(m.current, 0) + "mA");
|
||||
if (m.lux != 0)
|
||||
entries.push_back("Light: " + String(m.lux, 0) + "lx");
|
||||
if (m.white_lux != 0)
|
||||
entries.push_back("White: " + String(m.white_lux, 0) + "lx");
|
||||
if (m.weight != 0)
|
||||
entries.push_back("Weight: " + String(m.weight, 0) + "kg");
|
||||
if (m.distance != 0)
|
||||
entries.push_back("Level: " + String(m.distance, 0) + "mm");
|
||||
if (m.radiation != 0)
|
||||
entries.push_back("Rad: " + String(m.radiation, 2) + " µR/h");
|
||||
|
||||
// === Show first available metric on top-right of first line ===
|
||||
if (!entries.empty()) {
|
||||
String valueStr = entries.front();
|
||||
int rightX = SCREEN_WIDTH - display->getStringWidth(valueStr);
|
||||
display->drawString(rightX, currentY, valueStr);
|
||||
entries.erase(entries.begin()); // Remove from queue
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.iaq != 0) {
|
||||
sensorData[sensorCount++] = "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq);
|
||||
// === Advance to next line for remaining telemetry entries ===
|
||||
currentY += rowHeight;
|
||||
|
||||
// === Draw remaining entries in 2-column format (left and right) ===
|
||||
for (size_t i = 0; i < entries.size(); i += 2) {
|
||||
// Left column
|
||||
display->drawString(x, currentY, entries[i]);
|
||||
|
||||
// Right column if it exists
|
||||
if (i + 1 < entries.size()) {
|
||||
int rightX = SCREEN_WIDTH / 2;
|
||||
display->drawString(rightX, currentY, entries[i + 1]);
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.distance != 0) {
|
||||
sensorData[sensorCount++] = "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm";
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.weight != 0) {
|
||||
sensorData[sensorCount++] = "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg";
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.radiation != 0) {
|
||||
sensorData[sensorCount++] = "Rad: " + String(lastMeasurement.variant.environment_metrics.radiation, 2) + "µR/h";
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.lux != 0) {
|
||||
sensorData[sensorCount++] = "Illuminance: " + String(lastMeasurement.variant.environment_metrics.lux, 2) + "lx";
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.white_lux != 0) {
|
||||
sensorData[sensorCount++] = "W_Lux: " + String(lastMeasurement.variant.environment_metrics.white_lux, 2) + "lx";
|
||||
}
|
||||
|
||||
static int scrollOffset = 0;
|
||||
static bool scrollingDown = true;
|
||||
static uint32_t lastScrollTime = millis();
|
||||
|
||||
// Determine how many lines we can fit on display
|
||||
// Calculated once only: display dimensions don't change during runtime.
|
||||
static int maxLines = 0;
|
||||
if (!maxLines) {
|
||||
const int16_t paddingTop = _fontHeight(FONT_SMALL); // Heading text
|
||||
const int16_t paddingBottom = 8; // Indicator dots
|
||||
maxLines = (display->getHeight() - paddingTop - paddingBottom) / _fontHeight(FONT_SMALL);
|
||||
assert(maxLines > 0);
|
||||
}
|
||||
|
||||
// Draw as many lines of data as we can fit
|
||||
int linesToShow = min(maxLines, sensorCount);
|
||||
for (int i = 0; i < linesToShow; i++) {
|
||||
int index = (scrollOffset + i) % sensorCount;
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), sensorData[index]);
|
||||
}
|
||||
|
||||
// Only scroll if there are more than 3 sensor data lines
|
||||
if (sensorCount > 3) {
|
||||
// Update scroll offset every 5 seconds
|
||||
if (millis() - lastScrollTime > 5000) {
|
||||
if (scrollingDown) {
|
||||
scrollOffset++;
|
||||
if (scrollOffset + linesToShow >= sensorCount) {
|
||||
scrollingDown = false;
|
||||
}
|
||||
} else {
|
||||
scrollOffset--;
|
||||
if (scrollOffset <= 0) {
|
||||
scrollingDown = true;
|
||||
}
|
||||
}
|
||||
lastScrollTime = millis();
|
||||
}
|
||||
currentY += rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "power.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include "graphics/SharedUIDisplay.h"
|
||||
|
||||
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
||||
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
||||
@ -21,6 +22,10 @@
|
||||
#include "graphics/ScreenFonts.h"
|
||||
#include <Throttle.h>
|
||||
|
||||
namespace graphics {
|
||||
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y);
|
||||
}
|
||||
|
||||
int32_t PowerTelemetryModule::runOnce()
|
||||
{
|
||||
if (sleepOnNextExecution == true) {
|
||||
@ -103,13 +108,33 @@ bool PowerTelemetryModule::wantUIFrame()
|
||||
|
||||
void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->clear();
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
graphics::drawCommonHeader(display, x, y); // Shared UI header
|
||||
|
||||
// === Draw title (aligned with header baseline) ===
|
||||
const int highlightHeight = FONT_HEIGHT_SMALL - 1;
|
||||
const int textY = y + 1 + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
|
||||
const char *titleStr = (SCREEN_WIDTH > 128) ? "Power Telem." : "Power";
|
||||
const int centerX = x + SCREEN_WIDTH / 2;
|
||||
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->drawString(centerX, textY, titleStr);
|
||||
if (config.display.heading_bold) {
|
||||
display->drawString(centerX + 1, textY, titleStr);
|
||||
}
|
||||
display->setColor(WHITE);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
if (lastMeasurementPacket == nullptr) {
|
||||
// In case of no valid packet, display "Power Telemetry", "No measurement"
|
||||
display->drawString(x, y, "Power Telemetry");
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
|
||||
display->drawString(x, compactFirstLine, "No measurement");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -120,29 +145,31 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
|
||||
|
||||
const meshtastic_Data &p = lastMeasurementPacket->decoded;
|
||||
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
|
||||
display->drawString(x, y, "Measurement Error");
|
||||
display->drawString(x, compactFirstLine, "Measurement Error");
|
||||
LOG_ERROR("Unable to decode last packet");
|
||||
return;
|
||||
}
|
||||
|
||||
// Display "Pow. From: ..."
|
||||
display->drawString(x, y, "Pow. From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
|
||||
display->drawString(x, compactFirstLine, "Pow. From: " + String(lastSender) + " (" + String(agoSecs) + "s)");
|
||||
|
||||
// Display current and voltage based on ...power_metrics.has_[channel/voltage/current]... flags
|
||||
if (lastMeasurement.variant.power_metrics.has_ch1_voltage || lastMeasurement.variant.power_metrics.has_ch1_current) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Ch1: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 2) + "V " +
|
||||
String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA");
|
||||
const auto &m = lastMeasurement.variant.power_metrics;
|
||||
int lineY = compactSecondLine;
|
||||
|
||||
auto drawLine = [&](const char *label, float voltage, float current) {
|
||||
display->drawString(x, lineY, String(label) + ": " + String(voltage, 2) + "V " + String(current, 0) + "mA");
|
||||
lineY += _fontHeight(FONT_SMALL);
|
||||
};
|
||||
|
||||
if (m.has_ch1_voltage || m.has_ch1_current) {
|
||||
drawLine("Ch1", m.ch1_voltage, m.ch1_current);
|
||||
}
|
||||
if (lastMeasurement.variant.power_metrics.has_ch2_voltage || lastMeasurement.variant.power_metrics.has_ch2_current) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Ch2: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 2) + "V " +
|
||||
String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA");
|
||||
if (m.has_ch2_voltage || m.has_ch2_current) {
|
||||
drawLine("Ch2", m.ch2_voltage, m.ch2_current);
|
||||
}
|
||||
if (lastMeasurement.variant.power_metrics.has_ch3_voltage || lastMeasurement.variant.power_metrics.has_ch3_current) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Ch3: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 2) + "V " +
|
||||
String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA");
|
||||
if (m.has_ch3_voltage || m.has_ch3_current) {
|
||||
drawLine("Ch3", m.ch3_voltage, m.ch3_current);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
|
||||
// Handle inverted display
|
||||
// Unsure of expected behavior: for now, copy drawNodeInfo
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
|
||||
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
|
||||
|
||||
// Decode the waypoint
|
||||
@ -184,7 +184,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
|
||||
// Undo color-inversion, if set prior to drawing header
|
||||
// Unsure of expected behavior? For now: copy drawNodeInfo
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ void powerCommandsCheck()
|
||||
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
|
||||
if (shutdownAtMsec && screen) {
|
||||
screen->startAlert("Shutting down...");
|
||||
screen->showOverlayBanner("Shutting Down...", 0); // stays on screen
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -56,6 +56,7 @@ extern "C" {
|
||||
#define TFT_WIDTH 240
|
||||
#define TFT_OFFSET_X 0
|
||||
#define TFT_OFFSET_Y 0
|
||||
|
||||
// #define TFT_OFFSET_ROTATION 0
|
||||
// #define SCREEN_ROTATE
|
||||
// #define SCREEN_TRANSITION_FRAMERATE 5
|
||||
|
@ -1,3 +1,4 @@
|
||||
#ifndef HAS_TFT
|
||||
#define BUTTON_PIN 0
|
||||
#define BUTTON_PIN_SECONDARY 21 // Second built-in button
|
||||
#define BUTTON_SECONDARY_CANNEDMESSAGES // By default, use the secondary button as canned message input
|
||||
@ -69,3 +70,4 @@
|
||||
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
#endif // HAS_TFT
|
Loading…
Reference in New Issue
Block a user